7

我为正在编写的游戏编写了一个文件解析器,以便我自己轻松更改游戏的各个方面(例如角色/阶段/碰撞数据)。例如,我可能有一个这样的字符类:

class Character
{
public:
    int x, y; // Character's location
    Character* teammate;
}

我设置我的解析器从文件中读取具有类似于 C++ 语法的数据结构

Character Sidekick
{
    X = 12
    Y = 0
}

Character AwesomeDude
{
    X = 10
    Y = 50
    Teammate = Sidekick
}

这将创建两个数据结构并将它们放在一个 map<std::string, Character*>中,其中键字符串是我给它的任何名称(在本例中为 Sidekick 和 AwesomeDude)。当我的解析器看到一个指向类的指针时,比如队友指针,它足够聪明,可以在地图中查找以获取指向该数据结构的指针。问题是我不能将 Sidekick 的队友声明为 AwesomeDude,因为它还没有被放置到 Character map 中。

我正在尝试找到解决此问题的最佳方法,以便我可以让我的数据结构引用尚未添加到地图中的对象。我能想到的两个最简单的解决方案是(a)添加转发声明数据结构的能力或(b)让解析器读取文件两次,一次是用指向空数据结构的指针填充映射,第二次是通过并填写它们。

(a) 的问题是我还可以决定在类上调用哪个构造函数,如果我转发声明某些内容,我必须让构造函数与其余数据分开,这可能会造成混淆。(b) 的问题是我可能想在他们自己的文件中声明 Sidekick 和 AwesomeDude。我必须让我的解析器能够读取一个文件列表,而不是一次读取一个文件(我猜这还不错,尽管有时我可能想从一个文件中读取一个文件列表文件)。(b) 还有一个缺点,就是不能使用稍后在构造函数本身中声明的数据结构,但我认为这没什么大不了的。

哪种方式听起来更好?有没有我没有想到的第三种选择?似乎应该通过指针引用或绑定或其他东西来解决这个问题...... :-/ 我想这有点主观,基于我想给自己的功能,但欢迎任何输入。

4

6 回答 6

13

当您第一次遇到引用时,只需将其存储为引用即可。然后,您可以将字符、引用或其他任何内容放在“需要稍后解决的引用”列表中。

文件完成后,遍历那些有引用并解决它们的文件。

于 2009-04-28T19:58:42.020 回答
5

好吧,你要求第三种选择。您不必使用 XML,但如果遵循以下结构,则使用 SAX 解析器构建数据结构将非常简单。

无论如何,每个角色都不是引用队友,而是引用一个团队(在这种情况下为蓝队)。这将解耦循环引用问题。只要确保在角色之前列出团队即可。

<team>Blue</team>

<character>
    <name>Sidekick</name>
    <X>12</X>
    <Y>0</Y>
    <teamref>Blue</teamref>
</character>

<character>
    <name>Sidekick</name>
    <X>10</X>
    <Y>50</Y>
    <teamref>Blue</teamref>
</character>
于 2009-04-28T20:22:13.757 回答
2

就个人而言,我会选择b)。将您的代码拆分为 Parser 和 Validator 类,它们都在相同的数据结构上运行。Parser 将读取并解析一个文件,填充数据结构并将任何对象引用存储为它们的文本名称,暂时将真实指针保留在您的结构中。

加载完文件后,使用 Validator 类来验证和解析任何引用,填充“真实”指针。您将需要考虑如何构建数据以使这些查找变得又快又好。

于 2009-04-28T20:03:27.843 回答
1

威尔准确地说出了我要写的东西。只需保留未解决的引用列表或其他内容。

并且一旦你读完文件,如果有未解决的引用,不要忘记抛出一个错误 =P

于 2009-04-28T20:02:14.617 回答
0

不要在地图中存储 Character 对象,而是存储 Character 的代理。然后,代理将在加载对象时包含指向实际 Character 对象的指针。Character::teammate 的类型将更改为此代理类型。当您阅读地图中尚未包含的参考资料时,您将创建一个代理并使用该代理。当您加载地图中已有空代理的角色时,使用新加载的角色填充它。您可能还想添加一个计数器来跟踪您在地图中有多少个空代理,以便您知道何时加载了所有引用的字符。

另一层间接......它总是使编程更容易和更慢。

于 2009-04-28T20:44:03.517 回答
0

一种选择是撤销该义务。Map负责填写参考

template<T> class SymbolMap // I never could rememeber C++ template syntax
{
   ...

   /// fill in target with thing name
   /// if no name yet, add it to the list of thing that will be name
   void Set(T& target, std::string name);

   /// define name as target
   /// go back and fill in anything that needs to be name
   void Define(T target, std::string name);

   /// make sure everything is resolved
   ~SymbolMap()
}

这不会与价值/移动语义很好地交互,但我怀疑不会有太多。

于 2009-04-28T22:47:13.237 回答