You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何重构代码避免从类外部触发事件?游戏碰撞场景实践

解决碰撞检测系统触发GameObject内部碰撞事件的问题

这是游戏架构里很常见的设计痛点——既要让专门的碰撞系统负责检测逻辑,又要保持每个GameObject的事件封装性,不让内部的触发方法暴露成public。我给你几个优雅的解决方案:

方案1:通过接口暴露受控的触发入口

最推荐的方式是定义一个专门的接口,让所有需要响应碰撞的GameObject实现它。这个接口只暴露触发碰撞事件的必要方法,而GameObject内部依然保持OnCollided的protected访问权限,完美符合封装原则。

代码实现:

首先定义接口:

public interface ICollidable
{
    void TriggerCollision(GameEventArgs e);
}

然后修改你的GameObject类实现这个接口:

public class GameObject : ICollidable
{
    protected event GameEventHandler Collided;

    protected void OnCollided(GameEventArgs e)
    {
        Collided?.Invoke(this, e);
    }

    // 实现接口方法,内部调用OnCollided,相当于给碰撞系统开了个受控入口
    public void TriggerCollision(GameEventArgs e)
    {
        OnCollided(e);
    }
}

最后在CollisionSystem里,只需要调用接口方法即可:

public class CollisionSystem
{
    public void HandleCollision(GameObject gameObject1, GameObject gameObject2)
    {
        // 准备碰撞事件参数,比如把碰撞的另一方传递过去,方便后续逻辑处理
        var args1 = new GameEventArgs { OtherObject = gameObject2 };
        var args2 = new GameEventArgs { OtherObject = gameObject1 };

        // 触发两个物体的碰撞事件,先判断是否实现了接口,避免空引用
        (gameObject1 as ICollidable)?.TriggerCollision(args1);
        (gameObject2 as ICollidable)?.TriggerCollision(args2);
    }
}

为什么这个方案好:

  • 保持了OnCollided的protected属性,遵循“事件应该由类内部触发”的设计理念,TriggerCollision只是一个受控的外部入口
  • 接口的方式灵活性很高,你可以让不同类型的GameObject按需实现,不需要响应碰撞的物体直接跳过即可
  • 可以通过GameEventArgs传递更多碰撞细节(比如碰撞点、碰撞法线),让事件订阅者能做出更精准的响应

方案2:使用内部访问修饰符(仅限C#)

如果你不想引入接口,也可以把触发方法设为internal,然后通过特性让碰撞系统所在的程序集访问它。不过这个方案耦合度比接口高,更适合小型项目:

代码实现:

修改GameObject:

public class GameObject
{
    protected event GameEventHandler Collided;

    protected void OnCollided(GameEventArgs e)
    {
        Collided?.Invoke(this, e);
    }

    internal void TriggerCollision(GameEventArgs e)
    {
        OnCollided(e);
    }
}

然后在GameObject所在项目的AssemblyInfo.cs里添加:

[assembly: InternalsVisibleTo("YourCollisionSystemAssemblyName")]

这样CollisionSystem就能直接调用gameObject.TriggerCollision()了。

额外小建议

  • 建议给GameEventArgs补充更多实用属性,比如CollisionPoint、CollisionNormal,让事件处理逻辑能获取更详细的碰撞信息
  • 如果游戏里有大量不需要响应碰撞的GameObject,碰撞系统可以先判断对象是否实现了ICollidable再触发,避免不必要的调用

内容的提问来源于stack exchange,提问作者D646

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:13:23