如何独立于其他实例与预制实例交互?

问题描述 投票:0回答:1

我是 C# 和 Unity3D 的新手,我在使用事件方面遇到困难,所以我需要帮助。 所以我正在制作超休闲游戏来提高我的技能。在游戏中,我希望如果玩家击中一个项目会发生不同的事情。例如:触发炸弹对撞机将摧毁炸弹并播放爆炸动画,触发美元-摧毁它,播放美元动画,增加分数。 问题 !!!当玩家击中炸弹时,所有炸弹和金钱都会被摧毁并播放动画。我想独立地影响每个 prafab。我没有任何静电场。

编辑器中行为的简短视频
https://gfycat.com/farawaynegativedore

我有从抽象类 Item 继承的类 Bomb 和 Money。 (炸弹和金钱仅在方法上有所不同,因此我只包括炸弹)。

在 Item.cs 中,我引用了一个事件脚本。

public abstract class Item : MonoBehaviour
{
    protected OnCollisionEvent _onCollisionEventScript;
 
    private void Awake()
    {
        _onCollisionEventScript = FindObjectOfType<OnCollisionEvent>();
    }
 
    protected virtual void HandleAnimation() { }
 
    protected virtual void DestroyOnHitting() { }

在 Bomb.cs 中,我订阅了一个事件。

public class Bomb : Item
{
    private void OnEnable()
    {
        _onCollisionEventScript.InteractOnCollision += HandleAnimation;
        _onCollisionEventScript.InteractOnCollision += DestroyOnHitting;
    }

    private void OnDisable()
    {
        _onCollisionEventScript.InteractOnCollision -= HandleAnimation;
        _onCollisionEventScript.InteractOnCollision -= DestroyOnHitting;
    }

    protected override void HandleAnimation()
    {
        Debug.Log("EXPLOSION");
    }

    protected override void DestroyOnHitting()
    {
        Destroy(gameObject);
    }
}

在附加到 Player 的 OnCollisionEvent.cs 中,我调用了“voidOnTriggerEnter()”中的事件

public class OnCollisionEvent : MonoBehaviour
{
    public event UnityAction InteractOnCollision;
 
    private void OnTriggerEnter(Collider other)
    {
        InteractOnCollision?.Invoke();
    }
}

在 SpawManager.cs 中,我从预制件列表中沿 X 轴的固定位置和沿 Y 轴的随机位置生成随机预制件

public class SpawnManager : MonoBehaviour
{
    [SerializeField] private List<Item> ItemPrefabs;
    [SerializeField] private float SpawnRepeatRate;
 
    private readonly List<Item> _items = new();
    private float _time = 0f;
 
    private void Start()
    {
        foreach (var itemPrefab in ItemPrefabs)
            _items.Add(itemPrefab);
    }
 
    private void Update()
    {
        _time += Time.deltaTime;
 
        Item randomItem = _items[Random.Range(0, _items.Count)];
 
        if (_time >= SpawnRepeatRate)
        {
            Instantiate(randomItem, GetRandomSpawnPosition(), Quaternion.identity);
 
            _time = 0f;
        }
    }
 
    private Vector3 GetRandomSpawnPosition()
    {
        int[] CoordinatesOnY = new int[11];
        CoordinatesOnY[0] = 2;
 
        for (int i = 1; i < CoordinatesOnY.Length; i++)
            CoordinatesOnY[i] = CoordinatesOnY[i - 1] + 2;
 
        int randomSpawnPositionAlongY = Random.Range(CoordinatesOnY[0], CoordinatesOnY.Length);
 
        Vector3 spawnPosition = new Vector3(24f, randomSpawnPositionAlongY, 0f);
 
        return spawnPosition;
    }
}

我是在使用事件和传递引用上还是在实现 SpawnManager 和传递引用上犯了错误?

我没有任何静态字段。也许我用错了事件。我认为在 SpawnManager.cs 中我应该创建一个引用预制件的列表/数组,但我不知道如何实现它。

c# unity3d events prefab
1个回答
0
投票

所以你的问题是你所有的炸弹和金钱都听同一个事件。那

FindObjectOfType<OnCollisionEvent>();
基本上几乎等于拥有它
static
在你的情况下。

你应该颠倒这个逻辑,要么让玩家的触发器处理应该发生的事情

public class OnCollisionEvent : MonoBehaviour
{
    private void OnTriggerEnter(Collider other)
    {
        if(other.TryGetComponent<Item>(out var item))
        {
            // only trigger the callback/method on the specific item you currently collide with
            // I would pass on the reference so you can use GetComponent to access other 
            // components on the player e.g. for damage and money management
            item.TriggerEvent(this);
        }
    }
}

然后在你的项目类别上你可以简单地有一个

public abstract class Item : MonoBehaviour
{
    public void TriggerEvent(OnCollisionEvent player)
    {
        HandleAnimation(player);

        DestroyOnHitting();
    }
 
    protected virtual void HandleAnimation(OnCollisionEvent  player) {
        // inheritors can use the player reference to access other components and 
        // e.g. inflict damage or increase money etc
    }
 
    protected virtual void DestroyOnHitting() { }
}

替代方案是完全反转逻辑流程,而是处理项目上的事件

public abstract class Item : MonoBehaviour
{
    protected void OnTriggerEnter(Collider other)
    {
        // `Player` would be a central component on your player object 
        // that can have further references like e.g. to the damage and money management etc
        if(other.TryGetComponent<Player>(out var player))
        {
            HandleAnimation(player);

            DestroyOnHitting();
        }
    }
 
    protected virtual void HandleAnimation(Player player) { }
 
    protected virtual void DestroyOnHitting() { }
}

并完全摆脱

OnCollisionEvent

© www.soinside.com 2019 - 2024. All rights reserved.