【问题标题】:FindObjectOfType returning nullFindObjectOfType 返回 null
【发布时间】:2023-04-10 14:21:03
【问题描述】:

我遇到的问题是我捡起一个掉落的物品,将弹药添加到枪中。

构建一个包含所有方法和变量的 Gun 类。 构建了从 Gun 类派生的 Rifle 类 步枪完美无问题

我现在正在添加一个“拾取”系统,其中 x 数量的敌人会掉落拾取物。

这是要拾取物品的脚本

public class AddARAmmo : MonoBehaviour
{
    private Rifle rifle;
    private void Awake()
    {
        rifle = FindObjectOfType<Rifle>();
    }
    private void OnTriggerEnter(Collider other)
    {
        if (other.tag == string.Format("Player"))
        {
           rifle.AddAmmo(30);
            Destroy(gameObject);
        }

    }
}

步枪和枪的脚本有点长,但这里是来自 Gun 基类的相关内容是公共抽象类......

public int bulletsInStock;
 public void AddAmmo(int ammoToAdd)
{
    bulletsInStock += ammoToAdd; 
    UpdateAmmo();// updates on screen Ammo
}
......

然后在步枪班

 public override void Modifiers() // This is where the guns starts are stored
{
    bulletSpeed = 2777f;
    bulletsInStock = 200;
    bulletsInMag = 30;
    bulletPoolSize = 40;
    desiredRPS = 15;
    muzzleFlashPoolSize = 10;
}

我得到一个未设置为实例的对象引用

步枪脚本位于游戏层次结构中的步枪上,因此应该可以找到它。 有人看错了吗?

这是完整的 Gun 脚本

public abstract class Gun : MonoBehaviour
{
    [SerializeField] protected GameObject muzzleFlash;// spawns on barrelEnd
    [SerializeField] protected Transform muzzleFlashFolder;
    [SerializeField] protected Transform bulletFolder;// is the parent of bullets
    [SerializeField] protected Transform barrelEnd;// Gameobject at the end of barrel
    [SerializeField] protected Rigidbody bullet; // The bullet Prefab
    [SerializeField] protected Text ammo; // OSD
    [SerializeField] protected Text weaponType; // OSD
    [HideInInspector] protected float bulletSpeed;
    [HideInInspector] public int bulletsInStock;
    [HideInInspector] protected int bulletsInMag;
    [HideInInspector] protected float desiredRPS;// Rounds Per Second
    [HideInInspector] protected List<Rigidbody> poolOfBullets; // Make pool for bullets
    [HideInInspector] protected int bulletPoolSize; // The size off the buletpool 10 works really well
    [HideInInspector] protected List<GameObject> muzzleFlashPool;// pool for muzzleflash
    [HideInInspector] protected int muzzleFlashPoolSize; // size of the muzzle pool
    [HideInInspector] protected int bulletsLeft; // In mag
    [HideInInspector] protected bool isReloading = false;
    [HideInInspector] protected float timeLeft;// for fire speed
    [HideInInspector] protected float fireSpeedTimer;
    [HideInInspector] protected Weapons weaponsScript;
    [HideInInspector] protected PlayerController playerController;
    protected void FixedUpdateStuff()
    {
        if (playerController.canMove && Input.GetAxisRaw(string.Format("Fire1")) > 0)
        {
            FireSpeedControl();// call the fire timer controller
        }
        if (playerController.canMove && Input.GetAxisRaw(string.Format("Fire1")) == 0)
        {
            timeLeft = 0f;
        }
        if (playerController.canMove && Input.GetKeyDown(KeyCode.R) && !isReloading)
        {
            Reload();
        }
        UpdateAmmoOnInput();
    }
    protected void UpdateStuff()
    {
        if (gameObject.activeInHierarchy)// when a gun become active it updates OSD
        {
            UpdateWeaponType();// With its Name
        }
    }
    protected void RPSFinder()//  finds the Rounds Per Second the gun will fire
    {
        fireSpeedTimer = (100 / desiredRPS) / 100;
        timeLeft = fireSpeedTimer;
    }

    protected void Fire()// Instatiates a clone of the desired bullet and fires it at bulletSpeed
    {
        if (!Empty())
        {
            Rigidbody bulletClones = GetPooledBullet();
            if (bulletClones != null)
            {

bulletClones.transform.SetPositionAndRotation(barrelEnd.position, barrelEnd.rotation);
                bulletClones.gameObject.SetActive(true);
            }
            GameObject muzzleFlashClone = GetMuzzleFlash();
            if (muzzleFlashClone != null)
            {
                muzzleFlashClone.transform.position = barrelEnd.position;
                muzzleFlashClone.gameObject.SetActive(true);
            }
            bulletClones.AddForce(-bulletClones.transform.up * bulletSpeed * .304f); //add the force in FPS * .304 = MPS
            bulletsLeft--;// the holder to know how many bullets are left in the magazine
            isReloading = false;// Gun cannot reload unless it has been fired
            UpdateAmmo();// Updates the on screen ammo count and the stock usage
            return;
        }
    }

    protected void Reload()
    {// this removes full magazine from the stock and the stock can still go negitive FIX FIX FIX FIX FIX FIX FIX
        if (bulletsInStock > 0)
        {
            isReloading = true;
            bulletsInStock -= bulletsInMag;
            bulletsLeft = bulletsInMag;
            UpdateAmmo();
        }
    }

    protected bool Empty()// Checks the magazine to see if there are bullets in it
    {
        if (bulletsLeft == 0)
            return true;
        else
            return false;
}

    protected void FireSpeedControl()// controls the RPS fired by the gun Controled by Update() Input
    {
        if (timeLeft > 0f)
        {
            timeLeft -= Time.deltaTime;
        }
        else if (timeLeft <= 0f)
        {
            Fire();
            timeLeft = fireSpeedTimer;
        }
    }

    protected Rigidbody GetPooledBullet()// retrieve a preInstatiated bullet from the pool to use when shooting
    {
        for (int i = 0; i < poolOfBullets.Count; i++)
        {
            if (!poolOfBullets[i].gameObject.activeInHierarchy)
            {
                return poolOfBullets[i];
            }
        }
        return null;
    }

    protected GameObject GetMuzzleFlash()
    {
        for (int i = 0; i < muzzleFlashPool.Count; i++)
        {
            if (!muzzleFlashPool[i].gameObject.activeInHierarchy)
            {
                return muzzleFlashPool[i];
            }
        }
        return null;
    }

    protected void UpdateAmmo()// Update the on screen ammo information
    {
        ammo.text = bulletsLeft + string.Format( " : ") + bulletsInStock;
    }

    protected abstract void UpdateWeaponType();

    protected void UpdateAmmoOnInput()
    {
        if (weaponsScript.updateAmmo)
        {
            UpdateAmmo();
            weaponsScript.updateAmmo = false;
        }
    }


    public abstract void Modifiers();

    protected void StartStuff()
    {
        Modifiers();// Call first to store indvidual gun stats
        playerController = FindObjectOfType<PlayerController>();
        weaponsScript = FindObjectOfType<Weapons>();
        poolOfBullets = new List<Rigidbody>();
        for (int i = 0; i < bulletPoolSize; i++)
        {
            Rigidbody bulletClone = (Rigidbody)Instantiate(bullet);
            bulletClone.gameObject.SetActive(false);// Builds the Inspector list 
            poolOfBullets.Add(bulletClone); //and populates the elements with clones
            bulletClone.transform.parent = bulletFolder.transform;
        }
        muzzleFlashPool = new List<GameObject>();
        for (int i = 0; i < muzzleFlashPoolSize; i++)
        {
            GameObject muzzleFlashClone = (GameObject)Instantiate(muzzleFlash);
            muzzleFlashClone.gameObject.SetActive(false);
            muzzleFlashPool.Add(muzzleFlashClone);
            muzzleFlashClone.transform.parent = muzzleFlashFolder.transform;
        }
            bulletsLeft = bulletsInMag;
            ammo.text = string.Format( " 0 : 0 ");
            RPSFinder();// Run last to set the RPS of the gun
        }
        public void AddAmmo(int ammoToAdd)
        {
            bulletsInStock += ammoToAdd;
            UpdateAmmo();
        }

    }
}

这是完整的步枪脚本

public class Rifle : Gun
{
    //All variables are stored in the "Gun" Script 
    //Copy this onto guns 

    void Start()
    {
        StartStuff();
    }
    private void FixedUpdate()
    {
        FixedUpdateStuff();
    }

    void Update()
    {
        UpdateStuff();
    }

    public override void Modifiers() // This is where the guns starts are stored
    {
        bulletSpeed = 2777f;
        bulletsInStock = 200;
        bulletsInMag = 30;
        bulletPoolSize = 40;
        desiredRPS = 15;
        muzzleFlashPoolSize = 10;
    }

    protected override void UpdateWeaponType()
    {
        weaponType.text = string.Format("Assault Rifle");
    }


}

【问题讨论】:

  • 你能给我们看看Rifle 脚​​本吗?
  • 在哪里抛出异常?
  • 步枪.AddAmmo(30);在 AddARAmmo 脚本中
  • 那么rifle 为空。该脚本在Awake 方法运行之前运行,或者FindObjectOfType 方法返回null。添加支票if (rifle != null) rifle.AddAmmo(30); 并找出它是什么。

标签: c# unity3d polymorphism abstract-class base-class


【解决方案1】:

FindObjectOfType 可能返回 null 的三个原因:

假设要查找的脚本名称是Rifle

FindObjectOfType&lt;Rifle&gt;() 正在返回null

1.Rifle 脚本附加到的游戏对象处于非活动状态。您必须确保 GameObject 处于活动状态。

2。步枪根本没有附加到任何游戏对象上。确保 Rifle 脚本已附加到 GameObject.

脚本是启用还是禁用都没有关系,只要它附加到的 GameObject 处于活动状态,它就应该找到它。请参阅#1

3.加载新场景时:

当您使用 SceneManager.LoadScene 等函数触发场景加载时 而Application.LoadLevelFindObjectOfType 在场景加载完成之前将无法找到任何东西。

请参阅here,了解如何检查场景何时完成加载。


如果您仍然有不应该出现的问题,只需找到 GameObject,然后从中获取 Rifle 组件。假设 GameObject 的名称也是 “Rifle”

GameObject.Find("Rifle").GetComponent<Rifle>();

【讨论】:

  • 场景加载时游戏对象处于非活动状态。但是当我调用引用时,对象可以是活动的或非活动的,它将为空。但我会考虑到这一点做一些测试
  • 我有一个部分在发现发生之前将所有枪支设置为非活动状态,现在它可以工作了。有时只需要回归基础。
  • 是的,不活跃是发生这种情况的原因。很高兴您发现了问题。
  • +1 可通过屏幕截图进行清晰简单的解释。这三个是唯一的潜在原因,绝对没有其他机会吗?如果你的信息来自官方文件,你能引用吗?
  • @Matthias 此答案仅适用于FindObjectOfType 函数,doc 中的任何地方均未提及这些。答案基于我过去使用FindObjectOfType 时的经验。我想不出任何其他可能的原因导致它不起作用。
【解决方案2】:

我想出了如何为遇到类似问题的任何人解决此问题。 问题是当敌人丢下物品时,Awake() 方法就会运行。运行时枪在场景中处于非活动状态,因此 FindObjectOfType 没有找到参考脚本,因为如上所述,它必须在场景中处于活动状态才能找到。

所以我所做的是创建了一个名为 EnemyDrops 的 Holder 脚本,该脚本调用了枪支的 findobjectoftypes。这样,在初始游戏开始时调用就完成了。

然后我更改了拾取器以找到 EnemyDrops 脚本(位于空游戏对象上)并将调用发送给它。

EnemyDrops 脚本

private Handgun handgun;

private void Awake()
{
    handgun = FindObjectOfType<Handgun>();
}

public void AddHandgunAmmo(int x)
{
    handgun.AddAmmo(x);
}  

以及新的拾取脚本

public class AddHandgunAmmo : MonoBehaviour
{
    private EnemyDrops enemyDrops;
    private void Awake()
    {
        enemyDrops = FindObjectOfType<EnemyDrops>();
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.tag == string.Format("Player"))
        {
          enemyDrops.AddHandgunAmmo(50);
            Destroy(gameObject);
        }
    }
} 

正如您所见,通过方法传递值仍然有效,只需要有一个“中间人”来传递信息。但这很好用

感谢大家的反馈,希望对大家有所帮助

【讨论】:

    猜你喜欢
    • 2022-01-22
    • 1970-01-01
    • 2020-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-21
    • 2020-06-19
    • 2011-10-15
    相关资源
    最近更新 更多