炉石传说似乎做得很相关,老实说,我认为实现灵活性的最佳方法是通过具有面向数据设计的ECS引擎。一直试图制作一个炉石克隆,但事实证明这是不可能的。所有的边缘情况。如果您面对许多此类奇怪的情况,那可能是解决问题的最佳方法。不过,根据最近尝试该技术的经验,我颇有偏见。
编辑:根据您想要的灵活性和优化类型,甚至可能甚至不需要ECS。这只是完成此操作的一种方式。DOD我错误地认为它是过程编程,尽管它们关系密切。我的意思是。您应该考虑完全或至少不使用OOP,而应将注意力集中在数据及其组织方式上。避免继承和方法。而是专注于公共功能(系统)来操纵您的卡数据。每个动作都不是模板化的事物或逻辑,而是原始数据。然后系统在哪里使用它执行逻辑。整数切换大小写或使用整数访问函数指针数组有助于有效地从输入数据中找出所需的逻辑。
遵循的基本规则是,应避免将逻辑与数据直接捆绑在一起,应避免使数据尽可能相互依赖(可能适用例外),并且当您想要灵活的逻辑感到无法承受时...考虑将其转换为数据。
这样做有好处。每张卡可以具有一个枚举值或一个或多个字符串来表示其动作。该实习生允许您通过文本或json文件设计卡,并允许程序自动导入它们。如果您将玩家的动作作为数据列表,则可以提供更大的灵活性,尤其是在一张牌依赖炉石传说等过去的逻辑,或者您想随时保存游戏或游戏重播的情况下。有可能更轻松地创建AI。特别是在使用“实用程序系统”而不是“行为树”时。建立网络也变得更加容易,因为无需找出如何获取整个可能的多态对象以在线上进行传输以及事后如何设置序列化,您已经拥有的游戏对象不过是简单的数据而已,最终变得非常容易移动。最后但并非最不重要的一点是,这使您可以更轻松地进行优化,因为您不必浪费时间担心代码,而是可以更好地组织数据,从而使处理器可以更轻松地处理数据。Python可能在这里有问题,但会查找“缓存行”及其与游戏开发者的关系。也许对原型设计并不重要,但是在将来,它将很方便。
一些有用的链接。
注意:ECS允许在运行时动态添加/删除变量(称为组件)。ECS可能的外观示例c程序(有很多实现方法)。
unsigned int textureID = ECSRegisterComponent("texture", sizeof(struct Texture));
unsigned int positionID = ECSRegisterComponent("position", sizeof(struct Point2DI));
for (unsigned int i = 0; i < 10; i++) {
void *newEnt = ECSGetNewEntity();
struct Point2DI pos = { 0 + i * 64, 0 };
struct Texture tex;
getTexture("test.png", &tex);
ECSAddComponentToEntity(newEnt, &pos, positionID);
ECSAddComponentToEntity(newEnt, &tex, textureID);
}
void *ent = ECSGetParentEntity(textureID, 3);
ECSDestroyEntity(ent);
创建一堆具有纹理和位置数据的实体,最后销毁一个具有纹理成分的实体,该实体恰好位于纹理成分数组的第三个索引处。看起来很古怪,但却是做事的一种方式。这是一个如何渲染具有纹理组件的所有内容的示例。
unsigned int textureCount;
unsigned int positionID = ECSGetComponentTypeFromName("position");
unsigned int textureID = ECSGetComponentTypeFromName("texture");
struct Texture *textures = ECSGetAllComponentsOfType(textureID, &textureCount);
for (unsigned int i = 0; i < textureCount; i++) {
void *parentEntity = ECSGetParentEntity(textureID, i);
struct Point2DI *drawPos = ECSGetComponentFromEntity(positionID, parentEntity);
if (drawPos) {
struct Texture *t = &textures[i];
drawTexture(t, drawPos->x, drawPos->y);
}
}