自动转换通用List <entity> </entity>

时间:2010-11-16 15:19:09

标签: c# list generics casting xna

我正在使用以下简化结构构建XNA游戏:

class Entity
{
    Vector3 Speed;
    Matrix World;
}

class Mirror : Entity {}

class Lightbeam : Entity {}

class CollisionDetector
{
    /*
    ...
    */

    public override void Update(GameTime gameTime)
    {
        List<Entity> entities = entityManager.level.CurrentSection.Entities;

        for (int i = 0; i < entities.Count - 1; i++)
        {
            for (int j = i + 1; j < entities.Count; j++)
            {
                if(entities[i].boundingBox.Intersects(entities[j].boundingBox)) 
                {
                    collisionResponder.Collide(entities[i], entities[j]);
                }
            }
        }
        base.Update(gameTime);
    }
}

class CollisionResponder
{
    public void Collide(Entity e1, Entity e2)
    {
        Console.WriteLine("Normal entity collision response");
    }

    public void Collide(Mirror mirror, Lightbeam beam)
    {
        Collide(beam, mirror);
    }

    public void Collide(Lightbeam beam, Mirror mirror)
    {
        Console.WriteLine("Specific lightbeam with mirror collision response");
    }
}

我想要实现的是当碰撞检测器检测到光束和镜子之间的碰撞时,会调用碰撞(Lightbeam beam,Mirror mirror)方法(因此实体[i]是镜像,实体[j]是光束,反之亦然)。但是,由于实体列表存储了Entity类型的对象,因此调用了Collide(Entity e1,Entity e2)方法。

我试图克服这个问题:

  • 如果检测到碰撞,请检查哪些类型的实体发生碰撞并调用相应的方法。这是一个非常难看的解决方案,因为每次添加新的碰撞类型时都应该更改该方法。
  • 使用泛型:
    碰撞(实体e1,实体e2)          其中T:Lightbeam          其中U:镜子

    然而,这个解决方案并没有让编译器满意。

  • 我找到了构建器模式和工厂模式的链接,但似乎并没有解决我的问题。

在我看来,有一个简单的解决方案,但我在互联网上找不到任何东西(使用如下关键字的组合:泛型,子类,方法,重载,列表,自动转换)。

3 个答案:

答案 0 :(得分:1)

CollisionResponder应该是一个具有单一方法的接口

void Collide(Entity e1, Entity e2)

在CollisionDetector中保留这些列表,并在发生碰撞时依次调用每个列表。在每个实现中,您可以使用“is”运算符来检查运行时类型,并查看是否要执行任何逻辑。

编译器在编译时无法知道函数的参数类型是什么 - 只有运行时检查可以做你需要的。

答案 1 :(得分:1)

你的主要问题是你无法提前知道你是在比较镜子与光束,带镜子的光束,带镜子的镜子还是带光束的光束。您还建议可能会添加更多实体类型,以便可能的比较开始膨胀,正如您正确指出的那样,所有元素都作为基本实体进行处理。

似乎最简单的答案是在比较循环之前将光束和镜子分开,然后你有一个明确定义的比较关系。您可以使用LINQ执行此操作,但我理解,从可预测的性能方面来看,这可能并不理想。

如果您无法在游戏数据结构中单独存放它们,则可以构建两个列表:

List<Entity> entities = entityManager.level.CurrentSection.Entities;
List<Mirror> mirrors = new List<Mirror>();
List<Lightbeam> lightbeams = new List<Lightbeam>();
for (int i = 0; i < entities.Count - 1; i++)
{
    if (entities[i] is Mirror)
        mirrors.Add((Mirror)entities[i]);
    if (entities[i] is Lightbeam)
        lightbeams.Add((Lightbeam)entities[i]);
}

你循环然后变成:

for (int i = 0; i < mirrors.Count - 1; i++)
{
    for (int j = 0; j < lightbeams.Count; j++)
    {
        if(mirrors[i].boundingBox.Intersects(lightbeams[j].boundingBox)) 
        {
            collisionResponder.Collide(mirrors[i], lightbeams[j]);
        }
    }
}

代码仍然需要针对添加的每个新实体类型进行更新,但您的比较会很清楚。我怀疑你在实体类型之间的分裂很可能是光束和障碍所以我仍然只期望看到两个集合,因为我也期望障碍不能占据相同的边界框并且会受到限制用户界面。考虑到这一点,您永远不需要检查UI之外的障碍物之间的碰撞,只需检查光束与障碍物之间的碰撞。无论如何,将它们视为数据结构中的层并将它们分开维护是有意义的。

答案 2 :(得分:0)

您可以使用关键字dynamic

以下是一个例子:

public class Program
{
    static void Main(string[] args)
    {
        List<IEntity> entities = new List<IEntity>();
        entities.Add(new Mirror(1));
        entities.Add(new Mirror(2));
        entities.Add(new LightBeam(1));
        entities.Add(new LightBeam(2));

        //I also fixed your for-loops, you don't need to do entities.Count - 1
        for (int i = 0; i < entities.Count; i++)
        {
            for (int j = i + 1; j < entities.Count; j++)
                Collide((dynamic)entities[i], (dynamic)entities[j]);
        }

        Console.ReadLine();
    }

    public static void Collide(Entity e0, Entity e1)
    {
        Console.WriteLine("Collision: IEntity {0}[{1}] and IEntity {2}[{3}].", e0.Name, e0.ID, e1.Name, e1.ID);
    }

    public static void Collide(LightBeam lb0, Mirror m0)
    {
        Collide(m0, lb0);
    }
    public static void Collide(Mirror m0, LightBeam lb0)
    {
        Console.WriteLine("Special Collision: Mirror {0}[{1}] and LightBeam {2}[{3}].", m0.Name, m0.ID, lb0.Name, lb0.ID);
    }
}

//Interfaces are our friends :)
public interface IEntity
{
    String Name { get; }
    Int32 ID { get; }
}

public abstract class Entity : IEntity
{
    protected Entity(Int32 id = 0)
    {
        ID = id;
    }

    public Int32 ID { get; private set; }
    public abstract String Name { get; }
}

public class Mirror : Entity
{
    public Mirror(Int32 id = 0)
        : base(id)
    {
    }

    public override String Name
    {
        get { return "Mirror"; }
    }
}

public class LightBeam : Entity
{
    public LightBeam(Int32 id = 0)
        : base(id)
    {
    }

    public override String Name
    {
        get { return "LightBeam"; }
    }
}

输出:

Collision: IEntity Mirror[1] and IEntity Mirror[2].
Special Collission: Mirror Mirror[1] and LightBeam LightBeam[1].
Special Collission: Mirror Mirror[1] and LightBeam LightBeam[2].
Special Collission: Mirror Mirror[2] and LightBeam LightBeam[1].
Special Collission: Mirror Mirror[2] and LightBeam LightBeam[2].
Collision: IEntity LightBeam[1] and IEntity LightBeam[2].