如何通过协程重新生成拾取器?

时间:2018-12-31 12:52:24

标签: c# unity3d

在我的黄金捡拾器被炸死后,很难重新产生它们。这个想法是,如果玩家未能捡起5个金条,激活一个检查点,然后死亡,则当前的金币将被销毁,一旦屏幕从黑色褪色,它将重置。

我目前在我的健康管理器中有一个协程,如果玩家死亡并将其重置,协程可以正常运行。我有一个Gold Pickup脚本,如果没有捡拾它们会销毁黄金。我似乎无法让他们重新实例化。我尝试在Health Manager的协程和Gold Pickup脚本中添加实例化代码。似乎没有任何作用。如果我没有收到说“数组索引超出范围”的错误,则是“对象引用未设置为对象的实例”等。

public class GoldPickup : MonoBehaviour{
    public int value;
    public GameObject pickupEffect;
    public GameObject[] goldBarArray;
    public HealthManager healthManager;
    public Checkpoint checkpoint;

    private Vector3 goldRespawnPoint;
    private Quaternion goldStartPosition;

    void Start()
    {
        //To destroy multiple objects at once, use FindGameObjectsWithTag.
        //GetComponent is considered more efficient than FindObjectOfType, but the latter avoids any errors saying an object reference hasn't been set.
        goldBarArray = GameObject.FindGameObjectsWithTag("Gold");
        healthManager = FindObjectOfType<HealthManager>();
        //FindObjectOfType<Checkpoint>();
        checkpoint = FindObjectOfType<Checkpoint>();
        goldRespawnPoint = transform.position;
        goldStartPosition = transform.rotation;
    }

    public void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.CompareTag("Player"))
        {
            FindObjectOfType<GameManager>().AddGold(value);
            Instantiate(pickupEffect, transform.position, transform.rotation);
            Destroy(gameObject);
        }
    }

    public void DestroyGold()
    {
        //For Statics, an object reference isn't necessary. Use the FindObjectOfType to find the appropriate script and reference the Type, such as HealthManager.
        if (checkpoint.checkpoint1On == false)
        {
            foreach (GameObject Gold in goldBarArray)
            {
                Destroy(Gold);
                Instantiate(goldBarArray[5], goldRespawnPoint, goldStartPosition);
                goldRespawnPoint = transform.position;
                goldStartPosition = transform.rotation;
                //healthManager.RespawnCo();
            }
        }

    }
    /*public void GoldReset()
    {
        if (healthManager.isRespawning == true)
        {
            if (checkpoint.checkpoint1On == false)
            {
                StartCoroutine("GoldRespawnCo");
            }
        }

        else if (_respawnCoroutine != null)
        {
            StopCoroutine(_respawnCoroutine);
            _respawnCoroutine = StartCoroutine("GoldRespawnCo");
        }*/

    /*public IEnumerator GoldRespawnCo()
    {
        if (checkpoint.checkpoint1On == false)
        {
            Instantiate(goldPrefab, goldRespawnPoint, goldStartPosition);
            transform.position = goldRespawnPoint;
            transform.rotation = goldStartPosition;
        }
        else
        {
            yield return null;
        }
    }*/

    /*if (thePlayer.gameObject.activeInHierarchy == false)
    {
        Destroy(gameObject);
        Instantiate(goldBar, transform.position, transform.rotation);
    }
    else
    {
        if (thePlayer.gameObject.activeInHierarchy == true)
        {
            transform.position = respawnPoint;
            transform.rotation = startPosition;
        }
    }*/
}

    public class HealthManager : MonoBehaviour
    //The counters will count down and will keep counting down based on the length variables
    public int maxHealth;
    public int currentHealth;
    public PlayerController thePlayer;
    //public GoldPickup goldPickup;
    //public GoldPickup[] goldPickup;
    public float invincibilityLength;
    public Renderer playerRenderer;
    public float flashLength;
    public float respawnLength;
    public GameObject deathEffect;
    public Image blackScreen;
    public float fadeSpeed;
    public float waitForFade;
    public bool isRespawning;
    //public GameObject goldBar;
    //To reference another script's function, such as in the DeathTrigger script, make a public DeathTrigger, give it a reference name, and put it into the Start function. Use the reference name and assign it using GetComponent. Call another script's method by using the reference name, followed by a dot and the name of the method. Eg: deathTrigger.DestroyGold().

    private Quaternion startPosition;
    //private Quaternion goldPosition;
    private float flashCounter;
    private float invincibilityCounter;
    private Vector3 respawnPoint;
    //private Vector3 goldRespawnPoint;
    private bool isFadetoBlack;
    private bool isFadefromBlack;
    //private Coroutine _respawnCoroutine;
    //private Vector3 goldRespawnPoint;
    //private Quaternion goldStartPosition;

    void Start()
    {
        currentHealth = maxHealth;
        respawnPoint = thePlayer.transform.position;
        startPosition = thePlayer.transform.rotation;
        //goldPickup = GetComponent<GoldPickup>();
        //goldRespawnPoint = goldBar.transform.position;
        //goldStartPosition = goldBar.transform.rotation;
        //goldRespawnPoint = transform.position;
        //goldStartPosition = transform.rotation;
        //goldPickup = FindObjectOfType<GoldPickup>();
        //goldRespawnPoint = goldBar.transform.position;
        //goldPosition = goldBar.transform.rotation;
    }

    void Update()
    {
        //These functions are checked every frame until the player takes damage
        if (invincibilityCounter > 0)
        {
            invincibilityCounter -= Time.deltaTime;
            flashCounter -= Time.deltaTime;
            if (flashCounter <= 0)
            //The Flash Counter is currently set at 0.1 and will be within the 0 region as it counts down. During this period, the playerRenderer will alternate between on and off
            {
                playerRenderer.enabled = !playerRenderer.enabled;
                //The Flash Counter will keep counting down and reloop depending on the Flash Length time
                flashCounter = flashLength;
            }
            //This makes sure after the flashing and invincibility has worn off that the player renderer is always turned back on so you can see the player
            if (invincibilityCounter <= 0)
            {
                playerRenderer.enabled = true;
            }
        }

        if (isFadetoBlack)
        {
            blackScreen.color = new Color(blackScreen.color.r, blackScreen.color.g, blackScreen.color.b, Mathf.MoveTowards(blackScreen.color.a, 1f, fadeSpeed * Time.deltaTime));
            if (blackScreen.color.a == 1f)
            {
                isFadetoBlack = false;
            }
        }
        if (isFadefromBlack)
        {
            blackScreen.color = new Color(blackScreen.color.r, blackScreen.color.g, blackScreen.color.b, Mathf.MoveTowards(blackScreen.color.a, 0f, fadeSpeed * Time.deltaTime));
            if (blackScreen.color.a == 0f)
            {
                isFadefromBlack = false;
            }
        }
    }

    public void HurtPlayer(int damage, Vector3 direction)
    {
        //If the invincibility countdown reaches zero it stops, making you no longer invincible and prone to taking damage again
        if (invincibilityCounter <= 0)
        {
            currentHealth -= damage;
            if (currentHealth <= 0)
            {
                Respawn();
            }

            else
            {
                thePlayer.Knockback(direction);
                invincibilityCounter = invincibilityLength;
                playerRenderer.enabled = false;
                flashCounter = flashLength;
            }
        }
    }

    public void Respawn()
    {
        //A StartCoroutine must be set up before the IEnumerator can begin
        if (!isRespawning)
        {
            StartCoroutine("RespawnCo");
        }
    }

    //IEnumerators or Coroutines will execute the code separately at specified times while the rest of the code in a codeblock will carry on executing as normal.
    //To prevent an error appearing below the name of the Coroutine, be sure to place a yield return somewhere within the code block. Either yield return null or a new WaitForSeconds.
    public IEnumerator RespawnCo()
    {
        if (GameManager.currentGold < 5)
        {
            isRespawning = true;
            thePlayer.gameObject.SetActive(false);
            Instantiate(deathEffect, respawnPoint, startPosition);
            yield return new WaitForSeconds(respawnLength);
            isFadetoBlack = true;
            yield return new WaitForSeconds(waitForFade);
            //To reference another script's function quickly and just the once, use the FindObjectOfType function. This is considered to be slow however.
            FindObjectOfType<GoldPickup>().DestroyGold();
            //GetComponent<GoldPickup>().DestroyGold();
            //Instantiate(goldBar, goldRespawnPoint, Quaternion.identity);
            isFadefromBlack = true;
            //goldRespawnPoint = goldBar.transform.position;
            //goldStartPosition = goldBar.transform.rotation;
            isRespawning = false;
            thePlayer.gameObject.SetActive(true);
            thePlayer.transform.position = respawnPoint;
            thePlayer.transform.rotation = startPosition;
            currentHealth = maxHealth;
            invincibilityCounter = invincibilityLength;
            playerRenderer.enabled = false;
            flashCounter = flashLength;
            GameManager.currentGold = 0;
            GetComponent<GameManager>().SetCountText();
            StopCoroutine("RespawnCo");

            /*isRespawning = true;
            thePlayer.gameObject.SetActive(false);
            yield return new WaitForSeconds(respawnLength);
            isFadetoBlack = true;
            yield return new WaitForSeconds(waitForFade);
            isFadefromBlack = true;
            invincibilityCounter = invincibilityLength;
            playerRenderer.enabled = false;
            flashCounter = flashLength;
            SceneManager.LoadScene("Level 1");
            GameManager.currentGold = 0;*/
        }

        else if(GameManager.currentGold >= 5)
        {
            isRespawning = true;
            thePlayer.gameObject.SetActive(false);
            Instantiate(deathEffect, respawnPoint, startPosition);
            yield return new WaitForSeconds(respawnLength);
            isFadetoBlack = true;
            yield return new WaitForSeconds(waitForFade);
            isFadefromBlack = true;
            isRespawning = false;
            thePlayer.gameObject.SetActive(true);
            thePlayer.transform.position = respawnPoint;
            thePlayer.transform.rotation = startPosition;
            currentHealth = maxHealth;
            invincibilityCounter = invincibilityLength;
            playerRenderer.enabled = false;
            flashCounter = flashLength;
        }
    }

    /*public void HealPlayer(int healAmount)
    {
        currentHealth += healAmount;
        if(currentHealth > maxHealth)
        {
            currentHealth = maxHealth;
        }
    }*/

    public void SetSpawnPoint(Vector3 newPosition)
    {
        respawnPoint = newPosition;
    }


public class Checkpoint : MonoBehaviour

    public HealthManager theHealthManager;
    public Renderer cpRenderer;
    public Renderer postRenderer;
    public SpriteRenderer pcRenderer;
    public Material cpOff;
    public Material cpOn;
    public Material postOff;
    public Material postOn;
    public GameObject[] infoPanels;
    public bool checkpoint1On;

    //Make sure to assign a value to a bool with '=' and in an 'if' statement somewhere in the code to prevent warnings.
    //private bool checkpoint1IsActivated;
    private bool infoPanel1Activated;

    void Start()
    {
        theHealthManager = FindObjectOfType<HealthManager>();
    }

    void Update()
    //Key presses are better handled in the Update function and will recognise keys being pressed once every frame.
    {
        if (checkpoint1On == true)
        {
            if (infoPanel1Activated == false)
            {
                if (Input.GetKeyDown(KeyCode.Space))
                {
                    infoPanels[0].SetActive(true);
                    infoPanel1Activated = true;
                }
            }
            else
            {
                if (infoPanel1Activated == true)
                {
                    if (Input.GetKeyDown(KeyCode.Space))
                    {
                        infoPanels[0].SetActive(false);
                        infoPanel1Activated = false;
                    }
                }
            }
        }
    }

    public void Checkpoint1On()
    {
        cpRenderer.material = cpOn;
        postRenderer.material = postOn;
        pcRenderer.color = new Color(1f, 1f, 1f, 1f);
        checkpoint1On = true;
    }

    //[] makes a variable an Array (a list). The 'foreach' loop will check through all the Checkpoint objects

    //Checkpoint[] checkpoints = FindObjectsOfType<Checkpoint>();

    //For each Checkpoint Array called 'checkpoints', look for 'cp' and turn the others in the list off

    /*foreach (Checkpoint cp in checkpoints)
    {
        cp.CheckpointOff();
    }
    theRenderer.material = cpOn;*/

    public void Checkpoint1Off()
    {
        cpRenderer.material = cpOff;
        postRenderer.material = postOff;
        pcRenderer.color = new Color(1f, 1f, 1f, 5f);
        checkpoint1On = false;
    }

    public void OnTriggerStay(Collider other)
    {
        if (other.gameObject.CompareTag("Player"))
        {
            if (GameManager.currentGold >= 5)
            {
                if (Input.GetKeyDown(KeyCode.Return))
                {
                    theHealthManager.SetSpawnPoint(transform.position);
                    Checkpoint1On();
                    checkpoint1On = true;
                }
            }
            else if (GameManager.currentGold <= 5)
            {
                checkpoint1On = false;
            }
        }
    }

1 个答案:

答案 0 :(得分:1)

DestroyGold()函数中,您可以像这样实例化黄金:

foreach (GameObject Gold in goldBarArray)
{
        Destroy(Gold);
        Instantiate(goldBarArray[5], goldRespawnPoint, goldStartPosition);
        goldRespawnPoint = transform.position;
        goldStartPosition = transform.rotation;
        //healthManager.RespawnCo();
}

但是transform.positiontransform.rotation仅获得当前对象的位置和旋转(即脚本附加到的对象)。因此,您不仅要在同一位置产卵所有金币,还要在保存脚本的对象的位置产卵金币,而不是在您实际想要的位置产卵金币!

在不了解场景中物体的情况下,我可以告诉您的是:尝试创建Transform[]来存储要重生黄金的位置。另外,在foreach循环中调用goldRespawnPoint之前,请确保已分配goldStartPositionInstantiate()。最后,只是一个一般性的提示:切勿在{{1​​}}语句中使用variable == truevariable == false。您可以分别使用ifif(variable)。它将具有相同的功能,同时更具可读性,并减少了您需要编写的代码量。

编辑1:为了回应评论,我添加了用于实现这些建议的特定代码示例。

首先,您可能会因为if(!variable)而遇到超出范围的错误。由于数组从索引0开始,因此只能访问大小为goldBarArray[5]的数组中的元素n-1。有关如何在下一步中解决此问题的更多信息。

现在使用n数组。在声明公共变量的区域(在脚本顶部),添加以下行

Transform

然后,回到Unity,您将能够在Inspector中分配这些生成点。

编辑2:另外,在public Transform[] spawnPoints; 循环中,您尝试实例化场景中的一个金条,但这些金条将被foreach删除。声明。相反,您应该从不会被破坏的预制实例化。为此,请添加

Destroy(Gold);

使用其余的公共变量。然后,在编辑器中,通过将一个金条从“层次结构”拖动到“资产”文件夹中来创建预制件。最后,在“检查器”中将该预制设置为public GameObject goldPrefab; 的值。

现在,您实际上可以稍微清理一下goldPrefab循环。您可以摆脱foreachgoldRespawnPoint行,因为重生位置将包含在我们刚刚创建的goldStartPosition数组中。同样,在不知道场景的结构的情况下,我只需要对可行的方法做出有根据的猜测。尝试以下循环:

Transform