游戏重启后,游戏对象属性值没有重置

3

概述
考虑一个简单的硬币收集游戏。有两个脚本:CollectableItem和ItemVault。CollectableItem具有value属性,告诉物品的价值,并添加到coin预设中。ItemVault具有CurrentAmount属性,存储收集到的硬币的总价值,并添加到player预设中。

问题
在Unity Editor内重新启动游戏时,ItemVault对象的CurrentAmount属性值不会重置。

代码

CollectableItem.cs

public class CollectableItem : MonoBehaviour
{
    public float value = 1f;
    public ItemVault vault;

    private ItemVault m_vault;

    void Start()
    {
        m_vault = vault.GetComponent<ItemVault>();
        Debug.Log(m_vault.CurrentAmount); //Does not reset
    }

    //isTrigger
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(vault != null)
        {
            m_vault.ChangeAmountBy(this.value);
        }
        Destroy(gameObject);
    }
}

ItemVault.cs

public class ItemVault : MonoBehaviour
{
    public float startingAmount; // 0

    public float CurrentAmount { get; private set; }

    void Start()
    {
        CurrentAmount = startingAmount;
        Debug.Log(CurrentAmount); // 0
    }

    public void ChangeAmountBy(float amount)
    {
        CurrentAmount += amount;
        Debug.Log(CurrentAmount); // CurrentAmount does not reset
    }
}

注意:

  1. prefabs中添加了 CollectableItemItemVault
  2. ItemVaultStart函数确实将CurrentAmount的值记录为0,但CollectableItemStart函数会记录上一次运行时CurrentAmount的最后一个值。
  3. 重新启动编辑器本身会重置该值。
  4. 我已经尝试将Script Execution Order更改为先调用ItemVault再调用CollectableItem,但结果相同。
  5. Awake()中实例化属性而不是在Start()中也会得到同样的结果。

你所说的“预制件”,是指实际的“预制件”(=资源),还是在场景中的实例?而你所说的“游戏重启”,是指退出并重新进入PlayMode,还是在你的场景中有一些重启功能? - derHugo
请注意,vaultm_vault是多余的...您已经有一个ItemVault引用,因此不需要/使用再次调用GetComponent<ItemVault>() - derHugo
@derHugo 是的,我指的是“Assets”预制件,而且当我说游戏重新启动时,我是指退出并重新进入播放模式。 - Chaitanya
1个回答

2

首先:

确保在 _vault 中实际上是引用了在场景中存在的对象引用,而不是预制体资产——否则,是的,您将在预制件上执行该方法,而不是在场景加载/退出播放模式时被重置。

您可以通过开始播放模式,然后在层次结构中单击一次检查器中的 vault 插槽来确认这一点。这将突出显示引用的对象,无论是在层次结构中(预期)还是在资产中(不应发生!)。


如果这没有帮助,则您的问题可能是时间问题。

如果您的 CollectableItem.StartItemVault.Start 有机会执行之前执行,那么您可能会看到不同的值。

我通常遵循以下规则:

  • Awake: 初始化自己,因此任何不依赖其他组件的内容
  • Start: 现在访问已经在 Awake 中初始化的其他组件的值

所以在您的情况下,我会这样做:

public class CollectableItem : MonoBehaviour
{
    public float value = 1f;

    // In general rather already reference this via the Inspector
    [SerializeField] private ItemVault _vault;

    private void Awake()
    {
        // Ass fallback get it on runtime
        if(!_vault) _vault = vault.GetComponent<ItemVault>();
    }

    private void Start()
    {
        Debug.Log(_vault.CurrentAmount);
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(_vault)
        {
            _vault.ChangeAmountBy(this.value);
        }
        Destroy(gameObject);
    }
}

并且

public class ItemVault : MonoBehaviour
{ 
    [SerializeField] private float startingAmount; // 0

    public float CurrentAmount { get; private set; }

    private void Awake()
    {
        CurrentAmount = startingAmount;
        Debug.Log(CurrentAmount);
    }

    public void ChangeAmountBy(float amount)
    {
        CurrentAmount += amount;
        Debug.Log(CurrentAmount);
    }
}

我在Awake()函数中初始化了变量,但在CollectableItemAwake()中仍然得到旧值。 - Chaitanya
我也尝试通过Unity编辑器项目设置更改脚本执行顺序,使ItemVault.csCollectableItem.cs之前被调用,但仍然没有成功。 - Chaitanya
正如你所看到的,我说过你应该在Start中调试值,而不是在Awake中,因为在CollectableItem.Awake中,该值可能尚未初始化。 - derHugo
这就是我正在做的事情;-) 实际上,我尝试了两种方法。结果相同。此外,ChangeAmountBy(float)函数每次也会给出旧值,这让我想知道问题是否出在我通过检查器传递的vault引用上。 - Chaitanya
1
那么,您正在修改预制件...这永远不会被重置。当重新启动编辑器时,您只是没有保存;) 确保您只引用场景中对象的组件...换句话说:为什么您在那里引用预制件? - derHugo
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接