有许多不同的方法来处理这个问题。我最喜欢的处理方式类似于您发布的选项一。每个实体都有一个唯一的类型 ID。您可以使用更基于反射的方法,而不是使用一个长的 switch 语句来创建实体。这是一种稍慢的方式,但它确实提供了一个非常漂亮和干净的界面。
请记住,有很多方法可以处理这个问题,这只是我的首选方式。每个游戏的处理方式都不同,你真的只需要选择你喜欢的东西。
例子:
public enum EntityIDs {
Zombie( EntityZombie.class ),
Werewolf( EntityWerewolf.class );
// And so on for all of your entities
public Class< ? extends Entity > entityClass;
private EntityIDs( Class< ? extends Entity > cl ) {
this.entityClass = cl;
}
public static Entity createEntity( int id ) {
Class< ? extends Entity > cl = EntityIDs.values()[ id ].entityClass;
return cl.newInstance();
}
}
然后,您将收到来自服务器的类型 ID 和公共数据,例如位置。该 ID 可以是实体类型的序号,或者您决定将其映射到其类型的其他方式。然后,您可以使用 entityClass 字段以反射方式创建实体。当然,这要求您仍然列出枚举中的每个实体,但创建一个新实体就像一行一样简单。这确实要求所有实体子类化一个公共超类,例如本例中的实体。它还要求每个实体子类都有一个公共构造函数,通常只是一个在构造后完成初始化的默认构造函数。
拥有一个共同的超类确实简化了您的部分问题。您可以将所有常见数据(例如位置)放在超类中。当你想移动一个实体时,你不需要关心它是什么类型的实体,你只需要知道哪个实体以及它在哪里移动。
World world = ...;
Entity entity = world.getEntity( entityId );
if( entity != null ) {
entity.move( newX, newY );
}
我想再次重申,这只是一种方法,绝不是唯一的方法。这可以很容易地扩展到许多真正取决于您的偏好的不同解决方案。这绝不是一个完整的示例,而只是我首选解决方案的一般概念。
这确实会带来性能成本,因为反射不是很快。它通常由每个调用的多个方法调用组成。您可以将其更改为使用工厂模式,而不是每个实体类型都有一个负责创建每个实体的 EntityCreator。