我是 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 中我应该创建一个引用预制件的列表/数组,但我不知道如何实现它。
所以你的问题是你所有的炸弹和金钱都听同一个事件。那
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
。