首页 > 解决方案 > Unity 问题:如何使用 GetAxis 从一个预制件切换到另一个预制件?

问题描述

我想绑定向上和向下箭头键,以便在按下时循环显示不同的精灵。如果到达一端,它将循环回到第一个精灵。我尝试使用以下代码:

public class PhaseChanger : MonoBehaviour
{
    // saved for efficiency
    [SerializeField]
    public GameObject prefabMoon0;
    [SerializeField]
    public GameObject prefabMoon1;
    [SerializeField]
    public GameObject prefabMoon2;
    [SerializeField]
    public GameObject prefabMoon3;
    [SerializeField]
    public GameObject prefabMoon4;

    // needed for new phase
    GameObject currentPhase;

    bool previousFramePhaseChangeInput = false;

    /// <summary>
    /// Start is called before the first frame update
    /// </summary>
    void Start()
    {
        currentPhase = Instantiate<GameObject>(prefabMoon0);
    }

    /// <summary>
    /// Update is called once per frame
    /// </summary>
    void Update()
    {
        // change phase on up arrow or down arrow
        if (Input.GetAxis("ChangePhase") > 0)
        {
            // only change phase on first input frame
            if (!previousFramePhaseChangeInput)
            {
                previousFramePhaseChangeInput = true;

                // Save current position and destroy current phase
                Destroy(currentPhase);

                // instantiate next phase
                if (currentPhase = prefabMoon0)
                {
                    currentPhase = Instantiate(prefabMoon1);
                }
                else if (currentPhase = prefabMoon1)
                {
                    currentPhase = Instantiate(prefabMoon2);
                }
                else if (currentPhase = prefabMoon2)
                {
                    currentPhase = Instantiate(prefabMoon3);
                }
                else if (currentPhase = prefabMoon3)
                {
                    currentPhase = Instantiate(prefabMoon4); 
            else
            {
                // no phase change input
                previousFramePhaseChangeInput = false;
            }
        }
    }
}

当我将脚本附加到我的主相机并运行它时,我可以使用向上箭头进行一次更改,然后在后续按下时不会发生任何其他事情。

我觉得我真的很接近完成这项工作,但我也可能在做整个事情时效率低下。帮助将不胜感激,谢谢!

另外:我知道我在帖子中说过精灵,并且正在共享一个调用预制件的脚本。我不知道如何只使用精灵而不为每个精灵制作预制件来解决这个问题。是否可以在没有为每个精灵单独的预制件的情况下做到这一点?

标签: c#unity3d

解决方案


问题

  • 首先,您正在使用作业

    currentPhase = XY
    

    你应该在哪里使用

    currentPhase == XY
    

    它仍然编译的原因是UnityEngine.Object->bool的隐式转换运算符。基本上你的分配等于写作

    currentPhase = XY;
    if(currentPhase)
    
  • 无论哪种方式,它都不会像这样工作,因为您正在使用Instantiate创建一个预制件的新克隆,该预制件当然具有与克隆它的原始预制件不同的引用。

    所以即使你的支票看起来像

    if(currentPhase == XY)
    

    他们将永远是true

解决方案

我宁愿将所有预制件/实例存储在一个数组中,而不是检查引用相等性

public GameObject[] phases;

然后简单地为这个数组设置一个int 索引,这样你就可以通过增加索引来简单地移动到数组中的下一个元素。

private int currentPhase;

你可以增加它并使用例如

currentPhase = (currentPhase + 1) % phases.Length;

所以它总是会从上到下成长0phases.Length - 1然后从头再来0

然后我不知道您的用例的确切要求,但我建议不要一直使用InstantiateDestroy而是已经将所有对象作为您的对象下的实例,然后(取消)激活它们!

你可以这样做,例如

public GameObject[] phases;
private int currentPhase;

private void Awake ()
{
    Init();
}

private void Update ()
{
    if (Input.GetAxis("ChangePhase") > 0)
    {
        if (!previousFramePhaseChangeInput)
        {
            previousFramePhaseChangeInput = true;

            NextPhase();
        }
    }
    else
    {
        previousFramePhaseChangeInput = false;
    }
}

// Disables all phases except the first one and sets the current index to 0
private void Init()
{
    for(var i = 1; i < phases.Length; i++)
    {
        phases[i].SetActive(false);
    }

    phases[0].SetActive(true);
    currentPhase = 0;
}

// Disables the current phase and enables the next one
// wraps around at the end of the array
public void NextPhase()
{
    phases[currentPhase].SetActive(false);

    // increase the counter and wrap around at the end of the array
    currentPhase = (currentPhase + 1) % phases.Length;

    phases[currentPhase].SetActive(true);
}

如果您仍然想要Instantiate这些对象,因为它们已经在场景中是没有选择的(无论出于何种原因),您可以在调用之前立即执行此操作,Init例如

public GameObject[] phasePrefabs;

private GameObject[] phases;

private void Awake ()
{
    var amount = phasePrefabs.Length;
    phases = new GameObject [amount];

    for(var i = 0; i < amount; i++)
    {
        phases[i] = Instantiate(phasePrefabs[i]);
    }

    Init();
}

虽然如前所述,我更愿意立即拥有它们,因为这样更不容易出错;)


推荐阅读