簡體   English   中英

子彈在銷毀時沒有被實例化 - Unity2D

[英]Bullet not getting Instantiated when destroyed - Unity2D

我是使用統一的新手,我仍在學習 C#。 因此,如果下面提到的問題看起來有點奇怪或容易解決,請多多包涵。

我正在創建一個項目以嘗試從炮塔發射子彈,我在我的子彈腳本中包含了一個 function,它會在子彈越過某些邊界后摧毀子彈,並在我的 bulletSpawner 腳本中包含一個 function 來實例化子彈,如果它被摧毀了。 出於某種原因,每當我播放和射擊子彈並且它越過邊界時,它都不會被克隆

這是子彈腳本;

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Bullet : MonoBehaviour
{
    [SerializeField]
    private float Power = 50f;

    private Rigidbody2D myBody;

    private SpriteRenderer sr;

    private float posX;

    private float posY;

    private void Awake()
    {
        myBody = GetComponent<Rigidbody2D>();
        sr = GetComponent<SpriteRenderer>();
    }

    void Update()
    {
        Shoot();
        destroyBullet();
    }

    void Shoot()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
           myBody.AddForce(new Vector2(Power, Power - myBody.gravityScale), 
           ForceMode2D.Impulse);
        }
    }

    void destroyBullet()
    {
        posX = transform.position.x;
        posY = transform.position.y;
        if (posX > 100 || posX < -100 || posY > 100 || posY < -100)
            Destroy(gameObject);
    }
}//class

這是 BulletSpawner 腳本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BulletSpawner : MonoBehaviour
{
    public Transform bullet; 

    void Update()
    {
        StartCoroutine(SpawnBullet());
    }

    IEnumerator SpawnBullet()
    {
        while (!GameObject.FindWithTag("Bullet"))
        {
            yield return new WaitForSeconds(3);

            Instantiate(bullet);

        }//while
    }//Ienum        
}//class

注意:我已將 Bullet Prefab 附加到檢查器面板中的項目符號上

每當子彈超出范圍或被破壞時,我都會收到此錯誤:

MissingReferenceException:“Transform”類型的 object 已被破壞,但您仍在嘗試訪問它。

我知道它已被破壞,但我想找到一種方法來訪問它,以便它可以克隆子彈(可以再次發射,破壞等等......)

請告知我可以做些什么來修復此代碼或任何替代方法,以防止將來發生此類錯誤。 對於任何反饋,我們都表示感謝:)...

提前致謝!

因此,如前所述,主要問題是您使用原始場景實例作為Instantiate的原始bullet ,但隨后銷毀了該原始項目。

如果您要使用銷毀和實例化,您寧願將預制資產作為bullet原始資產。

然而,在 MickyD 提到之后:無論如何,你總是一次只有一顆子彈,所以你可能根本不需要摧毀它。 只需重用相同的項目符號實例。

然后關於Update中的射擊和你的協程,這是我寧願做的

子彈本身根本不做任何事情,而只是被動的。

然后做

public class BulletSpawner : MonoBehaviour
{
    // Now again the one from the Scene!
    public Rigidbody2D bullet; 

    // Yes if Start is IEnumerator it is automatically run as Coroutine by Unity
    IEnumerator Start ()
    {
        // Disable the Rigidbody of the bullet so it doesn't fall down
        bullet.isKinematic = true;

        // Huh?! This is okey in an IEnumerator as long as you yield inside
        while(true)
        {
            // wait until Space is pressed
            yield return new WaitUntil (() => Input.GetKeyDown(KeyCode.Space));

            // enable the Rigidbody
            bullet.isKinematic = false;
            // shoot the bullet
            bullet.AddForce(new Vector2(Power, Power - bullet.gravityScale), ForceMode2D.Impulse);

            // Now wait until it is out of bounds
            //TODO: .. or it hits something ;)
            yield return new WaitUntil (() => bullet.position.magnitude > 100f /*TODO || hasHitSomething */);

            // Then reset it's movement
            bullet.velocity = Vector2.zero;
            bullet.angularVelocity = 0;
            // Reset position
            bullet.position = Vector2.zero;
            bullet.rotation = 0;
            bullet.transform.position = Vector2.zero;
            bullet.transform.rotation = Quaternion.identity;
            // disable the Rigidbody for the next iteration
            bullet.isKinematic = true;

            // Disable the entire object
            bullet.gameObject.SetActive(false);

            // Wait for the cooldown
            yield return new WaitForSeconds (3);

            // enable the object (but not yet the Rigidbody)
            bullet.gameObject.SetActive(true);
        }
    }
}

為了檢查hasHitSomething我會在子彈上放一個專用組件,例如

public class Bullet : MonoBehaviour
{
    public bool hasCollided;

    private void OnCollisionEnter2D(Collision2D collision)
    {
        hasCollided = true;
    }
}

廣告然后

yield return new WaitUntil (() => bullet.position.magnitude > 100f || bullet.GetComponent<Bullet>().hasCollided);

// reset the flag
bullet.GetComponent<Bullet>().hasCollided = false;

當然,您通常應該存儲Bullet引用而不是重復使用GetComponent

這個答案只是對 derHugo 上面寫的代碼稍作修正,

這是編輯后的版本,更改在下面的**中標記...

public class BulletSpawner : MonoBehaviour
{
   
    public Rigidbody2D bullet;
    public Transform bulletPos;//change**

    [SerializeField]
    private float Power = 50f;

    void Start()
    {
        StartCoroutine(SpawnBullets());
    }

    void Update() { 

    }
    
    IEnumerator SpawnBullets()
    {
        
        bullet.simulated = false;
       
        while (true)
        {
            
            yield return new WaitUntil(() => Input.GetKeyDown(KeyCode.Space));
           
            bullet.simulated = true;
            
            bullet.AddForce(new Vector2(Power, Power - bullet.gravityScale), 
            ForceMode2D.Impulse);

            
            yield return new WaitUntil(() => bullet.position.magnitude > 100f  
            || hasHitSomething */);

    
            bullet.velocity = Vector2.zero;
            bullet.angularVelocity = 0;
          
            bulletPos.position = Vector2.zero;//change**
            bullet.rotation = 0;
           
            bullet.simulated = false;

         
            bullet.gameObject.SetActive(false);

     
            yield return new WaitForSeconds(3);

         
            bullet.gameObject.SetActive(true);
        }
    }
}    

我已經完成了兩項更改,因為當子彈被發射並出界時,子彈的 position 不會 go 回到原來的 position (因為這是代碼,子彈被聲明為)我的 Bullet object 的剛體(這意味着剛體將把它的 position 恢復到原點,而不是 Bullet object itelf 的剛體...聲明組件沒有意義。項目符號 object 並將項目符號更改為項目符號位置

bullet.position = Vector2.zero;

這是為了確保子彈 object 而不是子彈剛體將其 position 恢復到原點。

不要忘記在bulletSpawner中把子彈的transform組件附加到腳本上,這樣就可以順利運行了……

只是想包括這個,以免再次犯同樣的錯誤。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM