【问题标题】:Unity Question: How Can I Switch From One prefab To Another With GetAxis?Unity 问题:如何使用 GetAxis 从一个预制件切换到另一个预制件?
【发布时间】:2021-08-23 16:48:08
【问题描述】:

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

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;
            }
        }
    }
}

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

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

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

【问题讨论】:

  • 似乎您在 if 语句中分配而不是比较引用。你试过纠正它吗?此外,你有没有试过Array
  • 我以前使用过 foreach 数组,但我不确定这是否是我需要的。您能否进一步解释一下这是如何应用的?我也不确定你所说的“纠正它”是什么意思。谢谢。
  • if-statementsif (currentPhase = prefabMoon0),而它应该是if (currentPhase == prefabMoon0)==,而不是=。除了使用if-elseif,您可以简单地遍历每个索引并在需要时增加其值,例如++index; index %= prefabArray.Length,并使用此index 值来分配新的预制件或使用object pooling
  • 感谢您的回答。我正在阅读对象池。我很抱歉,但我对脚本和 Unity 很陌生;你能引导我完成你上面建议的解决方案吗?我将不胜感激。
  • @nIcEcOw 这是一个问题,但还不够(见my answer);)

标签: c# unity3d


【解决方案1】:

问题

  • 首先你正在使用分配

    currentPhase = XY
    

    你应该在哪里使用

    currentPhase == XY
    

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

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

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

    if(currentPhase == XY)
    

    他们永远是true

解决方案

我宁愿将所有预制件/实例存储在 array

中,而不是检查引用相等性
public GameObject[] phases;

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

private int currentPhase;

您可以增加它并使用例如使其环绕

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

所以它总是会从0 增长到phases.Length - 1,然后从0 重新开始。

然后我不知道您的用例的确切要求,但我建议不要一直使用InstantiateDestroy,而是已经将所有对象作为您的对象下的实例,并且只是 (de)activate 他们!

你可以这样做,例如

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();
}

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

【讨论】:

  • 非常好的解释 +1 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-25
  • 2012-06-13
  • 2019-11-15
  • 1970-01-01
相关资源
最近更新 更多