许多小型多态类(用作属性,消息或事件)的灵活替代方法


9

我的游戏中有两个类非常有用,但逐渐变得很痛苦。消息和属性(属性本质上是一个组件)。

它们都从基类派生并包含一个静态ID,因此系统只能关注他们想要的对象。效果很好...除了...

随着游戏的扩展,我一直在不断开发新的消息类型和属性类型。每次我需要编写2个文件(hpp和cpp)和一堆样板文件,以获得本质上是classID和一种或两种标准数据类型或指针。

它开始使玩转和测试新想法成为真正的琐事。我希望当我想创建一个新的消息或属性类型时,我希望能够键入类似

ShootableProperty:  int gunType, float shotspeed;

ItemCollectedMessage:  int itemType;

而不是创建头文件和cpp文件,而是编写一个构造函数,包括父类等。

大约20到40行(包括警卫和其他所有内容)在我脑海中逻辑上是1或2行。

是否有一些编程模式可以解决此问题?

用脚本(我一无所知)呢?有没有办法定义一堆几乎相同的类?


这正是一个类的样子:

// Velocity.h

#ifndef VELOCITY_H_
#define VELOCITY_H_

#include "Properties.h"

#include <SFML/System/Vector2.hpp>

namespace LaB
{
namespace P
{

class Velocity: public LaB::Property
{
public:
    static const PropertyID id;

    Velocity(float vx = 0.0, float vy = 0.0)
    : velocity(vx,vy) {}
    Velocity(sf::Vector2f v) : velocity(v) {};

    sf::Vector2f velocity;
};

} /* namespace P */
} /* namespace LaB */
#endif /* LaB::P_VELOCITY_H_ */



// Velocity.cpp

#include "Properties/Velocity.h"

namespace LaB
{
namespace P
{

const PropertyID Velocity::id = Property::registerID();

} /* namespace P */
} /* namespace LaB */

所有这些仅适用于2D向量和ID说要期待2D向量。(当然,某些属性具有更复杂的元素,但这是相同的想法)


3
看一看,这可能会对您有所帮助。gameprogrammingpatterns.com/type-object.html

1
所有这些看起来像模板可能会有所帮助,只是不能使用静态初始化。在这种情况下,当然可以使它变得容易得多,但是如果没有更多信息来说明您要使用这些ID以及为什么要使用继承,就很难说清楚。使用公共继承但不使用虚函数绝对是一种代码味道……这不是多态!
ltjax 2013年

您是否找到实现它的第三方库?
鲍里斯(Boris)

Answers:


1

C ++功能强大,但是很冗长。如果您想要的是很多不同的小型多态类,那么是的,它将需要大量的源代码来进行声明和定义。真的,没有任何事情要做。

现在,正如ltjax所说,您在这里所做的并不是完全多态的,至少对于您提供的代码而言。我看不到一个通用的接口,该接口会隐藏子类的特定实现。除了可能的类ID,但这实际上与真实的类ID无关:它是名称。这似乎只是一堆包含一些数据的类,没有什么真正复杂的。

但这并不能解决您的问题:您希望用最少的代码创建大量消息和属性。更少的代码意味着更少的错误,因此这是一个明智的决定。不幸的是,没有一个解决方案。在不确切知道您打算如何处理这些消息和属性的情况下,很难说出最适合您的需求。因此,让我公开一下您的选择:

  1. 使用C ++模板。模板是让编译器为您“编写代码”的好方法。随着C ++语言不断发展以更好地支持它们(例如,您现在可以使用可变数量的模板参数),它在C ++世界中正变得越来越突出。有专门用于通过模板自动生成代码的整个学科:模板元编程。问题是:很难。如果您打算使用非编程人员,不要指望非程序员能够自己添加新属性。

  2. 使用普通的C宏。这是一所古老的学校,易于滥用,并且容易出错。它们也很容易创建。宏只是预处理器执行的美化复制粘贴,因此它们实际上非常适合创建几乎没有变化的几乎相同的大量事物。但是不要期望其他程序员会喜欢您使用它们,因为宏通常用于隐藏整个程序设计中的缺陷。但是,有时它们会有所帮助。

  3. 另一种选择是使用外部工具为您生成代码。在先前的答案中已经提到因此我将不对此进行扩展。

  4. 还有其他一些语言,它们的详细程度较低,可让您更轻松地创建类。因此,如果您已经具有脚本绑定(或计划使用它们),则可以选择在脚本中定义这些类。但是,从C ++访问它们将非常复杂,并且您将失去简化创建类的大部分好处。因此,这可能仅在您计划用另一种语言完成大部分游戏逻辑时才可行。

  5. 最后但并非最不重要的一点是,您应该考虑使用数据驱动的设计。您可以使用与您自己提议的布局类似的布局,在简单的文本文件中定义这些“类”。您必须为其创建自定义格式和解析器,或使用几个已经可用的选项之一(.ini,XML,JSON和诸如此类)。然后在C ++方面,您需要创建一个支持这些不同类型对象集合的系统。这几乎等同于脚本编制方法(并且可能需要更多工作),只是您将能够根据需要更精确地对其进行调整。而且,如果您将其简化得足够简单,则非程序员可能可以自己创建新事物。


0

如何使用代码生成工具为您提供帮助?

例如,您可以在一个小的文本文件中定义您的消息类型和成员,并让代码生成工具对其进行解析,并将所有样板C ++文件编写为预构建步骤。

有诸如ANTLR或LEX / YACC之类的现有解决方案,根据属性和消息的复杂性,不难推出自己的解决方案。


只是LEX / YACC方法的一种替代方法,当我不得不进行非常小的修改就生成非常相似的文件时,我将使用一个简单的小型python脚本和一个包含标签的C ++代码模板文件。python脚本在模板中搜索这些标签,将其替换为所创建元素的代表性标记。
胜利者

0

我建议您研究一下Google的协议缓冲区(http://code.google.com/p/protobuf/)。它们是处理通用消息的一种非常聪明的方法。您只需以类似struct的模式指定属性,然后代码生成器就可以为您生成所有类(Java,C ++或C#)。所有生成的类都具有文本和二进制解析器,这使其对于基于文本的消息初始化和序列化都很有用。


我的消息传递中心是程序的核心-您知道协议缓冲区是否会产生大量开销吗?
Bryan 2013年

我认为它们不会产生大量开销,因为API只是每个消息的Builder类和消息本身的类。Google将它们用作基础架构的核心。例如,Google App Engine实体在持久化之前都已转换为协议缓冲区。如果您有疑问,建议您在当前的实现和协议缓冲区之间进行比较测试。如果您认为开销可以接受,请使用它们。
dsilva.vinicius
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.