在我的黄金捡拾器被炸死后,很难重新产生它们。这个想法是,如果玩家未能捡起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;
}
}
}
答案 0 :(得分:1)
在DestroyGold()
函数中,您可以像这样实例化黄金:
foreach (GameObject Gold in goldBarArray)
{
Destroy(Gold);
Instantiate(goldBarArray[5], goldRespawnPoint, goldStartPosition);
goldRespawnPoint = transform.position;
goldStartPosition = transform.rotation;
//healthManager.RespawnCo();
}
但是transform.position
和transform.rotation
仅获得当前对象的位置和旋转(即脚本附加到的对象)。因此,您不仅要在同一位置产卵所有金币,还要在保存脚本的对象的位置产卵金币,而不是在您实际想要的位置产卵金币!
在不了解场景中物体的情况下,我可以告诉您的是:尝试创建Transform[]
来存储要重生黄金的位置。另外,在foreach循环中调用goldRespawnPoint
之前,请确保已分配goldStartPosition
和Instantiate()
。最后,只是一个一般性的提示:切勿在{{1}}语句中使用variable == true
或variable == false
。您可以分别使用if
或if(variable)
。它将具有相同的功能,同时更具可读性,并减少了您需要编写的代码量。
编辑1:为了回应评论,我添加了用于实现这些建议的特定代码示例。
首先,您可能会因为if(!variable)
而遇到超出范围的错误。由于数组从索引0开始,因此只能访问大小为goldBarArray[5]
的数组中的元素n-1
。有关如何在下一步中解决此问题的更多信息。
现在使用n
数组。在声明公共变量的区域(在脚本顶部),添加以下行
Transform
然后,回到Unity,您将能够在Inspector中分配这些生成点。
编辑2:另外,在public Transform[] spawnPoints;
循环中,您尝试实例化场景中的一个金条,但这些金条将被foreach
删除。声明。相反,您应该从不会被破坏的预制实例化。为此,请添加
Destroy(Gold);
使用其余的公共变量。然后,在编辑器中,通过将一个金条从“层次结构”拖动到“资产”文件夹中来创建预制件。最后,在“检查器”中将该预制设置为public GameObject goldPrefab;
的值。
现在,您实际上可以稍微清理一下goldPrefab
循环。您可以摆脱foreach
和goldRespawnPoint
行,因为重生位置将包含在我们刚刚创建的goldStartPosition
数组中。同样,在不知道场景的结构的情况下,我只需要对可行的方法做出有根据的猜测。尝试以下循环:
Transform