了解 Unity 的 GameObject.Find()、GetComponent() 和 objects 回收

Understanding Unity's GameObject.Find(), GetComponent() and objects recycling

Unity 新手。

所以我创建了一个简单的枪口闪光粒子动画,当玩家靠近他时应该在敌人的枪上显示它,模拟没有实际子弹的射击。但是我在这部分得到了一个空引用异常 muzzleFlash.Play(); 我相信这是因为我实际上并没有用我拥有的代码在启动函数中获得枪口火焰组件,实际上我知道在调试之后就是这样我发现的模式。我很难弄清楚如何访问该组件。下面是我的代码,我还张贴了我的层次结构的图片。提前致谢。

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

public class StaticShootingEnemy : MonoBehaviour
{

    [SerializeField] private float _range = 12f;
    private Transform _player;

    private bool _alive;
    private float _distance;
    private ParticleSystem muzzleFlash;

    // Use this for initialization
    void Start()
    {
        _player = GameObject.Find("Player").transform;
        _alive = true;
        muzzleFlash = (ParticleSystem)this.gameObject.GetComponent("muzzleFLash");

    }

    // Update is called once per frame
    void Update()
    {
        _distance = Vector3.Distance(this.transform.position, _player.transform.position);
        if (_alive && _distance < _range)
            AttackPlayer();
    }


    private void AttackPlayer()
    {
        //Turning enemy to look at player
        transform.LookAt(_player);
        Ray ray = new Ray(transform.position, transform.forward);
        RaycastHit hit;
        if (Physics.SphereCast(ray, 0.75f, out hit))
        {
            //TODO: Fix enemy shooting fast when gettting close to him.
            GameObject hitObject = hit.transform.gameObject;
            if (hitObject.GetComponent<PlayerController>())
            {
                muzzleFlash.Play();
                Debug.Log("Player Hit!");
            }
            else
                muzzleFlash.Stop();
        }
    }

    public void SetAlive(bool alive)
    {
        _alive = alive;
    }

}

您的脚本可能附加了 object "muzzleFlash" 作为 child 到 object。因此,在这种情况下,您最好引用名为 muzzleFlash 的 ParticleSystem object。

[SerializeField] private ParticleSystem muzzleFlash; // drag and drop your ParticleSystem muzzleFlash in inspector

或者至少你可以找到像这样的 muzzleFlash

GameObject muzzleFlashObj = GameObject.Find("muzzleFlash"); ParticleSystem muzzleFlash = muzzleFlashObj.GetComponent<ParticleSystem>();

在你的例子中它是空的,因为在 object 上可能没有名为 MuzzleFlash 的组件。你要获取的组件是ParticleSystem。

staticshootingenemy 脚本在哪个组件上?如果它与粒子系统不在同一个组件上,那么它找不到它,因为 this.gameObject.GetComponent("muzzleFLash") 在该组件上不存在。您可以使用GameObject.Find("muzzleFLash")搜索粒子系统。

所以回到你的评论,你可以为你的枪口火焰实现一个类似池的东西。

public class MuzzleFlashEffect : MonoBehaviour
{
    [SerializeField] private ParticleSystem particleEffect;
    private Queue<MuzzleFlashEffect> poolQueue;

    public void SetPoolQueue(Queue<MuzzleFlashEffect> queue)
    {
        poolQueue = queue;
    }

    public void Play()
    {
        StartCoroutine(Playing());
    }

    private IEnumerator Playing()
    {
        particleEffect.Play();

        while (particleEffect.isPlaying)
        {
            yield return null; // wait until particle animation is done, then recycle effect
        }

        particleEffect.Stop();
        poolQueue.Enqueue(this);  // recycle this effect
    }

    // you can do the same thing for Animation as well, or even write some abstract PoolableVFX class that would be usefull for Animation , ParticleSystems etc.. 
}

//assume you have some game controller that manage what is going on in the scene
public class GameController : MonoBehaviour
{
    [SerializeField] private MuzzleFlashEffect muzzleFlashPrefab;
    private Queue<MuzzleFlashEffect> poolQueue = new Queue<MuzzleFlashEffect>(10); // 10 is enough i guess and it's good to set it at instantiation to avoid memory fragmentation

    private MuzzleFlashEffect GetMuzzleFlash(Vector3 pos, Quaternion rot)
    {
        MuzzleFlashEffect muzzleFlash;

        // if we already have some effects, then play them, otherwise make a new one and recycle it then
        if (poolQueue.Count > 0)
        {
            muzzleFlash = poolQueue.Dequeue();
        }
        else
        {
            muzzleFlash = Instantiate(muzzleFlashPrefab);
            muzzleFlash.SetPoolQueue(poolQueue);
        }

        muzzleFlash.transform.position = pos;
        muzzleFlash.transform.rotation = rot;

        return muzzleFlash;
    }

    void Update()
    {
        // your fancy logic ...


        GameObject mutantGunEnd = new GameObject("mutant");
        //assume that here you want your muzzle flash effect, so you do:

        var muzzleFlash = GetMuzzleFlash(mutantGunEnd.transform.position, mutantGunEnd.transform.rotation); // or you might want to pass mutantGunEnd.transform.forward instead, it depends... 
        muzzleFlash.Play();


        // your fancy logic ...
    }
}

因此,在这种情况下,您只拥有所需数量的 ParticleEffect 实例并节省一些资源。您还可以为要回收的任何类型的 object 创建一个通用通用池。 (您想回收而不是实例化,因为实例化 cpu 很昂贵)。 M.b 这有点矫枉过正,但我​​只是想在这里分享一下我对此的看法