繁体   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