会员: 密码:  免费注册 | 忘记密码 | 会员登录 网页功能: 加入收藏 设为首页 网站搜索  
游戏开发 > 程序设计 > 设计思想
创建模块化游戏 I(翻译)(Creating Moddable Games with XML and Scripting Part I)
发表日期:2007-03-28 19:08:34作者: 出处:  

  现在你知道这个系列的教程开头会讲什么,我现在要向你介绍基本的XML文档。读完这一小节过后,你应该完全明白XML是什么,以及如何创建它,并以模块化的思维把它应用到你的游戏中去。我建议你先读一下 Richard Fine 在GameDev上的<XML in Games>一文,它对这一节里我要说的话题进行了很精彩的描述。

The XML document specifications require each valid document to begin with an XML file declaration:

  XML规范描述每一个有效的XML文件都由一个XML文件声明开头:

  <?xml version="1.0" encoding="utf-8"?>

Quite simply, this declaration is stating that the document is XML version 1.0 and is encoded with the UTF-8 character set.

  非常简单,这个声明描述了这个XML文件遵循XML规范1.0版,并且使用UTF-8编码。

XML is a meta-language; in short you are able to define your own set of tokens (or 'tags'), rules and structures. Most XML files are defined by a DTD, or Document Type Definition which dictates the rules and structure of the document by supplying a list of legal tokens. DTD rule creation is important as it allows the XML parser to quickly accept or reject documents on loading. With that said, we are using TinyXml and it is a non-validating parser; it will ignore any DTD it finds and just processes the document elements with no rules. This is fine for our purposes as we can build our own validation rules into the parsing stage.

  XML是一种元语言;简单地说你可以使用它来定义你自己的标记(它是一种标记性语言),规则以及结构体。大多数XML文件由DTD来定义,DTD是Document Type Definition的缩写,DTD用来检测XML文件是否规范,标记是否正确。DTD的创建规则可以让XML分析器很快地进行正确地分析。根据这样的说法,我们决定使用TinyXml,TinyXml就是一种XML分析器,它不处理DTD,它对XML进行分析时不依照任何DTD的规则(实际上忽略了DTD)。

XML documents have a strict hierarchical structure; elements are contained within other elements, which in turn are contained within higher elements in the document tree. This tree-like structure needs a root, the document root, and is the single element which all other elements are descended from. Every element on the next hierarchical level is called a child of an element and elements with the same root element are said to be siblings.

  XML文件拥有很严格的层次结构;元素被其他元素包含,其他元素又被更高层次的元素包含。XML文件里的元素是以树型结构组织的。这个树的根部就是文档(XML文档本身)。在树型层次中,低一层的元素是其高层元素的孩子,高层元素就是其父母;拥有相同父母的元素就是兄弟。(与树数据结构一样)

The hierarchical structure of XML allows for easy parsing of the tree and can introduce great flexibility when it comes to manipulating the data. Whole branches can be detached and placed elsewhere with little difficulty.

  XML的树型结构使得分析起XML文件时很容易,且在操作数据时也很方便。整个层次结构中的任一分支都可以很容易地被方在任何地方。

Let's take a look at a basic declaration of a jsInvaders level:

  让我们看一下jsInvaders层次的定义:(作者在这一系列教程中,将开发一个名为jsInvaders的示例游戏)
  <?xml version="1.0" encoding="utf-8"?>
  <invadersgame>
    <level>
      <alien />
      <alien />
      <alien />
    </level>
  </invadersgame>


You should be able to see that the document root element is <invadersgame>. The root then contains a level element, which in turn contains three alien elements. In order to clarify this, I'll break down the basic jsInvaders level document:

  你应该可以看到,文档元素(最顶层的元素,也就是根元素)就是<invadersgame>,然后根元素包含了一个 level 元素,这个 level 元素又包含了三个 alien 元素。为了更为清晰地说明这一点,我把以上文档分开来说:

  <invadersgame> is the document root <invadersgame>是文档根元素
  <level> is a child of <invadersgame> <level>是<invadersgame>的一个孩子。
  <alien> is a child of <level> <alien>是<level>的孩子。
  The <alien> elements in a <level> element are siblings to each other
  <level>包含的每一个<alien>是其他<alien> 的兄弟。

If you're used to Object Oriented Programming (OOP), then you'll probably be able to picture how you'd map these elements into classes. By referring to the code attached to this article, you'll see how I've begun mapping these objects into C++ classes for use in the game. So far in the code I have the game classes sketched out for dealing with levels, aliens and the player ship. I won't go into detail about much of the rest of the game classes as it is beyond the scope of this article.

  如果你习惯了面向对象编程,那么很有可能你会把上面那种结构抽象为类结构。在这篇文章里,我就是把这样的结构对应地抽象为C++类的。我已经有了对应于level, alien 以及 player的类。在这个教程里,我不会讲太多游戏编程方面的东西,那不在本教程讨论的范围内。

If you examine the file jsiAlien.h, you'll see that I have defined several properties for the object. Things like 'color', 'position' and 'points value' are all properties of the Alien object that will need values to be of use in the game. XML document elements can also have attributes assigned to them, meaning that you can represent the data for objects such as the Alien object with an XML element tag.

  如果你查看 jsiAlien.h 这个文件,你就会看到我为一个类定义了很多属性。颜色,位置,以及会给玩家多少分数都是游戏中 alien 对象需要的属性。XML文件的元素也可以拥有属性,这意味着你可以把游戏中alien对象的属性设置进XML文件对应的元素中去:

  <alien xpos="0" ypos="100" color="red" points="10" />

If you look at the declaration above you will see that I have assigned 4 properties to the XML element that represents the Alien. In this example the alien is red, is positioned at (0, 100) and awards 10 points to the player when killed. Taking this forward, we begin to build the XML file that defines a basic game level.

  你会看到以上定义中我为XML元素alien分配了4个属性。在这个例子中,alien是红色,位置在(0,100)处,而且当被玩家杀死后它会给玩家奖赏10点分数。我们来定义一个基本的游戏关卡:

  <?xml version="1.0" encoding="utf-8"?>
  <invadersgame>
    <level>
      <!-- here's where we declare the aliens -->
      <alien xpos="0" ypos="100" color="red" points="10" />
      <alien xpos="100" ypos="100" color="green" points="10" />
      <alien xpos="200" ypos="100" color="blue" points="10" />
    </level>
  </invadersgame>


It should be apparent that we now wish to have 3 aliens of different colours, each 100 units apart horizontally. You'll notice that I've begun commenting my XML document with the XML comment tags, <!-- this is a comment -->. I personally find commenting all of my code to be good practice, even in this simple example :). The benefits of good commenting will reveal themselves later on when the files start getting bigger.

  很显然,我们在这个文件里希望有3个alien,每一个拥有不同的颜色,且在横坐标上相隔100个单位。在这个文件里我加入了注释<!-- this is a comment -->。我个人觉得加入注释是很好的习惯。

Parsing the XML

分析XML


Now that you understand the concept of XML and how document elements relate to the data contained within class objects, I will take you through the process of parsing the XML document. The act of 'parsing' is when the raw document is read in by your program and transformed into the elements that make up the document tree. With jsInvaders, the parsing stage will consist of reading the XML document into memory and creating objects within the game environment based on the data within it.

  现在你已经知道了XML的基本概念,并知道XML中的元素如何对应着C++中的类,我将要告诉你如何去分析一个XML文件。分析也就在你的程序中读进XML文件,然后得到其内表达的树型结构。在jsInvaders里,程序需要先把XML文件读进内存,然后设置游戏相关数据。

For this section you will require an XML parser; I am using TinyXml, a small open-sourced library that was developed for easy handling of XML documents. Bear in mind that there are several XML parsers available which all follow the basic concepts, so pick one you feel comfortable with if you don't wish to use TinyXml.

  在这一节里,你需要个XML分析器;我使用TinyXml,它是一个小巧的并且开源的XML分析库。记住其实有很多XML的分析器,如果你不喜欢TinyXml,你完全可以使用你自己喜欢的。

With TinyXml, all of the elements are contained within a TiXmlDocument structure. The first thing you need to do is create an instance of this object and use it to load and parse the XML file. This is shown in the code fragment below (taken from jsiGame.cpp):

  使用TinyXml,所有的元素都被包含在一个TiXmlDocument的结构体中(其实是一个类,但是基本一样)。你需要做的第一件事情就是创建一个该类(TxXmlDocument)的对象,然后使用它装载一个XML文件并分析。以下是示例代码(选自jsiGame.cpp):

int jsiGame::loadXmlLevels(const char *filename)
{
    TiXmlDocument *xmlDoc = new TiXmlDocument(filename);
   
if (!xmlDoc->LoadFile())
   
{
       
// Fatal error, cannot load
   
return 0;
   
}

// Do the document parsing here

   
xmlDoc->Clear();

// Delete our allocated document and return success ;)
   
delete xmlDoc;
   
return 1;
}


The TiMxlDocument->LoadFile() method will fail if there is an error with the document; for example it may not exist at the specified location or there may be an error with the actual structure of the XML document itself. After you have processed the XML document, you have to call the Clear() method of the TiXmlDocument in order to free up the memory allocated to loading and parsing the document.

  在TiXmlDocument->LoadFile()中,如果提供的文件有什么错误,该函数就会失败;这些错误包括提供的文件不存在,或者文件是错误的。在使用完XML文件后,你需要调用TiXmlDocument::Clear来释放分配的空间.

With the document structure we defined earlier to hand, we can begin parsing the XML file with TinyXml. To get the document root, we call FirstChildElement() from the document that was just loaded.

  我们可以使用刚才得到的TiXmlDocument对象来进一步得到树型结构中的根元素,使用FirstChildElement得到

TiXmlElement *xGame = 0;
xGame = xmlDoc->FirstChildElement("invadersgame");

if (!xGame)
{
   
// The game element couldn't be found,
   
// we have to say goodbye!
   
// clear and delete the doc before exiting
   
xmlDoc->Clear();
   
delete xmlDoc;

   
return 0;
}


The TiXmlDocument class is based on TiXmlElement, so the FirstChildElement() call can be used with any TinyXml element. If the requested element couldn't be found, the call will return 0, so it's important to check for failures and handle them appropriately. In the example above, the object xGame now holds a pointer to the root element in the document. This element will now be used as a root for parsing the level data.

TiXmlElement *xLevel = 0;
xLevel = xGame->FirstChildElement("level");

if (!xLevel)
{
   
// No level elements were found
   
// return with an error
   
// clear and delete the doc before exiting
   
xmlDoc->Clear();
   
delete xmlDoc;
   
return 0;
}


// the level has been found, parse it here...
As before, the object xLevel will point to the first level element tag. When jsInvaders encounters a level element, a new instance of a level class is created which is then passed the relevant TiXmlElement for parsing the level data.

To parse the alien elements contained within a level, a similar procedure is adopted. The main difference in the next step is that there will be several alien elements (siblings) and that we will be parsing the attributes from the XML file and using them to fill the jsiAlien class.

Because the alien elements are siblings to each other, we can iterate through the level element searching for more occurrences of the object using TiXmlElement->NextSiblingElement().This is shown in the code fragment below (taken from jsiLevel.cpp):

int jsiLevel::parseXmlLevel(TiXmlElement *xLevel)
{
   
TiXmlElement *xAlien = 0;
   
int nAlienCount = 0;

   
xAlien = xLevel->FirstChildElement("alien");
   
// Loop through each alien in the level
   
while(xAlien)
   
{
       
// create a new alien
       
jsiAlien *alien = createAlien();
       
// Parse this particular alien

       
// grab the position data from the attributes
       
// check it exists before adding the attribute
       
if (xAlien->Attribute("xpos"))
       
{
           
alien->position.x = (float)atof(xAlien->Attribute("xpos"));
       
}
       
if (xAlien->Attribute("ypos"))
       
{
           
alien->position.y = (float)atof(xAlien->Attribute("ypos"));
       
}

       
alien->color = jsiUtility::colourFromText( xAlien->Attribute("color") );

       
// move to the next element
       
xAlien = xAlien->NextSiblingElement("alien");
       
// increment our counter
       
++nAlienCount;
   
}
   
return 1;
}


You will notice that every time an alien element is encountered, the game requests that the level creates an instance of the jsiAlien object. It should be apparent that the number of aliens created in the level maps 1:1 to the XML document :)

Getting an attribute associated with an element is easy, it's just a matter of calling TiXmlElement->Attribute( <name> ).

if (xAlien->Attribute("xpos"))
{
   
alien->position.x (float)atof(xAlien->Attribute("xpos"));
}


Like other TinyXml functions, the Attribute call will return 0 if the attribute doesn't exist. I'm checking for the existence of an attribute before I read it, this allows the author of the XML document to exclude attributes they won't need. It's worth remembering that all attributes are stored as text and you will need to convert them to your desired format before you store them in your objects.

At this stage, we've actually covered all the XML we'll need for now. When it comes to loading textures and scripts, it's a simple case of adding more elements to the XML document and parsing them in the level loading routines. In any case, you'll see that I've expanded the XML document slightly in the next instalment to account for the extra features the game has.

Naturally, TinyXml has more features that the ones we're using here (including the creation and saving of XML documents) so once you get to grips with how it works, it may be useful to start exploring how these work for yourself. I won't be covering them here because I don't need them in this particular project, but it's worth bearing in mind that you might need them someday.

That's it for the first part of this series. I'm hoping that we covered enough XML parsing to feel comfortable with what we're trying to achieve here and how to use TinyXml in your own projects. I urge you to experiment, if only by adding configuration options or score saving routines to use XML data, I guarantee that you'll be bitten by the bug and see how useful a file format it is.

Next time around, I'll be covering how to set up a scripting environment within your project and how to link the game objects we've just created to the scripting runtime. In particular, I'll be focusing on creating a script API that will be used to control the motions and behaviours of objects within the game.

Until next time...

Oli



译者的理解:

目前我觉得,XML文件,就等同于一个结构更为清晰,可读性更强的配置文件! 游戏读取里面的内容,然后根据其内容对游戏内的一些数据进行设置!
要很好的理解Parser库,例如TinyXml,我觉得事先应该对XML进行了解!尤其是一些XML的术语---这些术语很大程度上就会对应于TinyXml库的一些接口。

关于XML的一些术语:

1.DOM全称是document object model(文档对象模型),DOM是用来干什么的呢?假设把你的文档看成一个单独的对象,DOM就是如何用HTML或者XML对这个对象进行操作和控制的标准。

2.元素在HTML我们已经有所了解,它是组成HTML文档的最小单位,在XML中也一样。一个元素由一个标识来定义,包括开始和结束标识以及其中的内容,就象这样:<author>ajie</author>

3.属性是对标识进一步的描述和说明,一个标识可以有多个属性,例如font的属性还有size。XML中的属性与HTML中的属性是一样的,每个属性都有它自己的名字和数值,属性是标识的一部分。

4.标识是用来定义元素的。在XML中,标识必须成对出现,将数据包围在中间。标识的名称和元素的名称是一样的。例如这样一个元素:

<author>ajie</author>

其中<author>就是标识。

5.父元素是指包含有其它元素的元素,被包含的元素称为它的子元素。


XML文档包含三个部分:

1. 一个XML文档声明;

在所有XML文档的第一行都有一个XML声明。这个声明表示这个文档是一个XML文档,它遵循的是哪个XML版本的规范。一个XML的声明语句就象这样:<?XML version="1.0"?>

2. 一个关于文档类型的定义;

3. 用XML标识创建的内容。


书写XML需要注意的:

1.所有的标记都必须要有一个相应的结束标记;

2.所有的XML标记都必须合理嵌套;

3.所有XML标记都区分大小写;

4.所有标记的属性必须用""括起来;

5.名字中可以包含字母、数字以及其它字母;

6.名字不能以数字或"_" (下划线) 开头;

7.名字不能以字母 XML (或 XML 或 XML ..) 开头;

8.名字中不能包含空格。
 

返回顶部】 【打印本页】 【关闭窗口

关于我们 / 给我留言 / 版权举报 / 意见建议 / 网站编程QQ群   
Copyright ©2003- 2024 Lihuasoft.net webmaster(at)lihuasoft.net 加载时间 0.00237