如何避免从接口转换为类

idl*_*ire 9 c# oop interface

在尝试为游戏设计碰撞检测组件时,我提出了以下解决方案.我定义了一个类似于ICollideable的界面:

interface ICollideable
{
    Sprite Sprite { get; }
    int    Damage { get; }

    void HandleCollision(ICollideable collidedWith);
}
Run Code Online (Sandbox Code Playgroud)

基本上,任何想要参与碰撞检测的游戏对象都必须实现此接口,然后将其自身注册到检测器,该检测器维护一个ICollideable列表.当它检测到碰撞时,它会调用对象上的HandleCollision方法并传入对它碰撞的对象的引用.

我喜欢这个,因为它允许我将所有碰撞算法保存在一个地方,让游戏对象自己决定如何处理碰撞.但由于后者,我发现我必须检查底层对象类型.例如,我不希望玩家互相碰撞,因此在玩家类中可能会有以下内容:

void HandleCollision(ICollideable collidedWith)
{
    if (!(collidedWith is Player)) { // do stuff }
}
Run Code Online (Sandbox Code Playgroud)

等等,我想知道这是否告诉我我的设计不好以及替代品可能是什么.

第二个问题,进一步沿着第一个问题.出于评分的目的,如果一个敌人被一个射弹摧毁,有人需要知道射弹类的"拥有玩家"成员.但是,我的其他碰撞物都没有或者不需要这个属性,所以我发现自己想做(在Enemy HandleCollision中):

void HandleCollision(ICollideable collidedWith)
{
    if (collidedWith is Projectile) {
        Health -= collidedWith.Damage;

        if (Health <= 0) {
            Player whoDestroyedMe = (collidedWith as Projectile).FiredBy
            // ...
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我不清楚如何用更好的设计来处理这个问题.任何见解将不胜感激.

编辑:我想把焦点放在第二个问题上,因为我的直觉告诉我处理这个问题的方法将解决第一个问题.至于第一个问题,我想到了一种抽象这种行为的方法.我可以定义一个枚举:

enum Team
{
    Player,
    Enemy,
    Neither
}
Run Code Online (Sandbox Code Playgroud)

并让ICollideables实现此属性.然后碰撞检测器根本不记录同一"团队"上的可碰撞物之间的碰撞.因此,玩家和玩家弹丸将在一个团队中,敌人和敌人的弹丸将在另一个团队中,并且环境(可能会损坏两者)都不会出现.它不必是枚举,可以是int或字符串或任何东西,并且认为具有相同值的对象不会相互冲突.

我喜欢用简单属性建模行为的想法.例如,如果我想打开"允许友好射击",我所要做的就是创建一个Team值不是玩家团队值的Projectiles.但是,我仍然可能有这种情况还不够.例如,玩家可能拥有暂时不受射弹影响但不能防止与敌人直接碰撞的盾牌,等等.

Ste*_*aye 1

对于第一种情况,我将向 ICollidable 添加以下额外方法:

bool CanCollideWith(ICollidable collidedWith)
Run Code Online (Sandbox Code Playgroud)

顾名思义,它会返回 true 或 false,具体取决于它是否可以与传入的对象发生碰撞。

您的 Player.HandleCollision 方法只会执行其操作,因为调用方法可以执行该测试,如果不需要,甚至不会调用该方法。

  • 但我仍然需要在该方法中执行所有“if (collidedWith is ...)”测试,所以我不确定这如何更好。 (2认同)