|
||||||||||||||||||
Add Forum to Favorites | Send Topic To a Friend | View Forum FAQ | Track this topic |
Last Thread Next Thread ![]() |
| Creating Moddable Games with XML and Scripting Part I |
|
![]() kumikana Member since: 9/4/2002 From: Finland |
||||
|
|
||||
| Great inspiring article. It just happens that awhile a go I started to think along the same lines. For my simple 2D game, how cool it would be if the gamelevel data was stored in a xml file, that could be modified easily. Then about two weeks ago I startted to code my own XML parser / handler / serializer, inspired by the great Enginuity series, especialy by the chapter V ( http://www.gamedev.net/reference/articles/article2011.asp ). So heres the fruit of my work: http://koti.mbnet.fi/kumikana/soodaus/cpp/CXml/xml%20parser.zip You can read the sources at: http://koti.mbnet.fi/kumikana/soodaus/cpp/CXml/ Coolest thing ( or atleast I think its the coolest feature ) is the simply saving and loading of objects. I'll post an example source to help you understand the parser better. #include <iostream> #include "cxml.h" class CPerson { public: void Serialize( CXmlFileSys* filesys ) { if ( filesys->IsReading() ) { XML_ConvertTo( filesys, myName ); XML_ConvertTo( filesys, myAge ); } else if ( filesys->IsWriting() ) { XML_ConvertFrom( filesys, myName ); XML_ConvertFrom( filesys, myAge ); } } unsigned int myAge; std::string myName; }; int main() { CPerson person; person.myAge = 21; person.myName = "Pete"; XmlSaveToFile( person, "person.xml", "person" ); CPerson other_person; XmlLoadFromFile( other_person, "person.xml", "person" ); std::cout << other_person.myName << ", " << other_person.myAge << std::endl; // The output: Pete, 21 return 0; } The xml file the upper code will generate:
<person>
<myName>
Pete
</myName>
<myAge>
21
</myAge>
</person>
|
||||
|
||||
![]() Pxtl Member since: 5/28/2004 From: Canada |
||||
|
|
||||
| Tried that myself once with Python and xml - problem: python was overkill. Way too feature-full, no sandbox, so you couldn't run untrusted code on it anyways, and users would confuse themselves coding classes in python when they were meant to be done in xml. Anybody know anything about Zope's sandboxed script(python) system? |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
| Nice article. Looking forward to the second. |
||||
|
||||
![]() Houdini Member since: 3/16/2000 From: Sylvania, OH, United States |
||||
|
|
||||
| Looks pretty good so far. However, as it's written it's not very modular. For instance, if the user wants to create a "super alien" the jsiLevel::parseXmlLevel() function must be modified to support this. You should seriously consider using an object factory to automatically create objects. Then mod DLLs could register new classes with the object factory, and the object factory could take care of instantiating the proper object, then let the object itself read in it's own attributes. Something like this: int jsiLevel::parseXmlLevel(TiXmlElement *xLevel) { TiXmlElement *xElement = 0; std::string strElementName = ""; int nEntityCount = 0; strElementName = xLevel->FirstChildElementName(); // Loop through each alien in the level while(strElementName != "") { // Move to element xElement = xLevel->FirstChildElement(strElementName); // create a new alien jsiEntity *entity = objectFactory.Create(strElementName); // Parse this particular entity. The class itself will // grab all appropriate data from the attributes in the // given XML element. *entity << xElement; // grab next element name strElementName = xLevel->NextSiblingElementName(); // increment our counter ++nEntityCount; } return 1; } - Houdini |
||||
|
||||
![]() Seriema Member since: 6/15/2001 From: Stockholm, Sweden |
||||
|
|
||||
| Nice article! We use XML for all data in our game. I think Houdini had a nice point there! Now to comment your article. Since I've been using TinyXML for our game. And I've even submitted a patch to TinyXML for copying Nodes that was accepted and implemented. I have noticed this: 1) TiXmlDocument *xmlDoc = new TiXmlDocument(filename); why? You don't need to allocate that on the heap, just unnecessary costs (and dangers). Make it a normal object, works fine :) Plus, if you must allocate it. Be safe and use a std::auto_ptr (when passing ptrs around then you just pass the underlying pointer and not a auto_ptr. Yes boost::shared_ptr rox0rz but then this example would have to rely on yet another library, boost). Pointers for the elements etc. are essential though so you're correct there. 2) // Fatal error, cannot load Please teach the reader how to check for errors using TinyXML. When I get home I'll post my "get error func" here :) but for now, TiXmlDocument:: ErrorDesc(), ErrorRow() and ErrorCol(). Oh, and the filename is veeery usefull too: TiXmlDocument::Value(). Also, all TiXmlNode's can access their document with TiXmlNode::GetDocument(). Correct error reporting will surely help your readers! 3) // clear and delete the doc before exiting xmlDoc->Clear(); delete xmlDoc; why? The document will be cleared in the destructor. That's like calling std::vector::clear() before destroying the vector. It's unnecessary and makes the reader think it's super important. 4) // Loop through each alien in the level You're being safe when checking for attributes, but not fully. Since what happens if it could read a alien, but not the position? Then you'd have a runtime bugg hard to spot. This is one of the reasons I have a error reporting func just for TiXml. If you don't want to 'else' all your if( attribute )'s then you could just check TiXmlDocument::Error() in the end. You said "this allows the author of the XML document to exclude attributes they won't need", my experience is that's just error prone. Because some attributes MUST be set, like the position. Or say, texture. Thus you're enforcing a constraint on the writer: Make sure you know what attributes can be omitted! And you're also putting a constraint on yourself: Make sure you have documents about what attributes can be omitted and their default values! So you're not saving anyone anything, just making it harder. Trust me, I know =/ 5) if (xAlien->Attribute("xpos")) alien->position.x = (float)atof(xAlien->Attribute("xpos")); You should really use the correct functions for retrieving attributes. Instead of baking in atof() here and there, I think you should use the overloaded Attribute functions. TiXmlElement::Attribute(const char *name, int *i), and Attribute(const char *name, double *d). I hacked in a float version that uses the double version but casts the result. You could use doubles just for TiXml's sake or you could use a dummy and cast it if you want. Still better than atof. If you did what you did because there's no float version, please say so in the article. And if you're gonna query if the existing attribute exists, use the correct function TiXmlElement::QueryIntAttribute() and QueryDoubleAttribute(). They don't extract anything and are thus more effective than extracting and checking if it worked, and they add readability to your code. I know you're making a beginners guide, but that's why I think it's even more important to highligth these parts. Teaching good practice shouldn't be omitted for the sake of making it n00b-friendly. =) I hope I've been constructive in my criticism. I really enjoyed the article and looking forward to the other parts! =) [ ThumbView: Adds thumbnail support for DDS, PCX, TGA and 16 other imagetypes for Windows XP Explorer. ] [ Chocolate peanuts: Brazilian recipe for home made chocolate covered peanuts. Pure coding pleasure. ] |
||||
|
||||
![]() evolutional Moderator - Alternative Game Libraries Member since: 1/25/2002 From: Leeds, United Kingdom |
||||||
|
|
||||||
Quote: Great idea, that would really make serialising attributes a lot easier, especially in the future work I will be doing in allowing user-extesible attributes. Quote: Interestingly enough I've been playing with this idea recently (the article there is a few months old). I've been looking at implementing attributes that can be filled by the Xml. The later parts (part 3, specifically) will deal with this issue as well as linking scripts to events in the game. Quote:I completely agree. I did wrongly assume that the Xml file would be valid, but I think for a beginning Xml article error checking should be a must. I think I'll work some of that into the next part. Quote:I agree, I actually have a document about what is required and not, it just didn't ship with the article code. This first part serves more as a basic intro to Xml parsing than a catch-all. Most of the bringing together stuff is focussed in part 3 (and possibly an additional 4). Quote: You've been very helpful in the criticsm you've offered I'll be sure to note your suggestions for the next parts.Thanks for the response so far! Oli Wilkinson, MCTS SQL Server 2005 Personal Sites: [Journal] | [evolutional] | [Manta-X] | [jsInvaders] | [GameMonkey articles] Useful links: [TinyXml] | [W3c XML Spec] | [SpiderMonkey javascript] | [boost] Useful links: [Psionic's 3d Game Resources] | [NaturalDocs] | [Code on the Cob] | [AngelCode Reference DB] Longing for the good ol' days, where people made games and not engines |
||||||
|
||||||
![]() Seriema Member since: 6/15/2001 From: Stockholm, Sweden |
||||
|
|
||||
| Since kumikana posted his code I'd just like to say that I've made a IOBind for TinyXml. What that means is that you don't have to write a separate save and load, or as in kumikana's place: check if it's saving or loading. it basicly works like this; IOBind *binder = new XmlBindOut("filename.xml"); int myInt=0; BIND(myInt, "myInteger", "This int stores my integer", binder); That bakes into a value holder, and the macro supplies __FILE__ and __LINE__. So if something goes wrong the error report will hold the XML filename, the position in the XML where it went wrong, what TinyXML operation that failed and what the values were (like "Tried to access attribute 'banana' in element 'chocolate'"), and also what source file and line it went wrong. The supplied name (myInteger) will become the attribute name, the description isn't used in the xml but could be used for comments. Of course, sometimes you have to differentiate between read and write. But I'm trying to keep that at a minimum. What I'm having trouble with is extending it with user types. Like cTexture and similar. I need it to be customized by both read and write, and the user type. So the easy way is to insert a function taking a cTexture& in the hierarchy, but that would bind my IOBind to my project... Well I'm waiting for a opportunity to have time over to test my IOBind design on our particle system. But I don't have the time now =( Either way, our game is open source so IOBind is available there for those interested. clicky [ ThumbView: Adds thumbnail support for DDS, PCX, TGA and 16 other imagetypes for Windows XP Explorer. ] [ Chocolate peanuts: Brazilian recipe for home made chocolate covered peanuts. Pure coding pleasure. ] |
||||
|
||||
![]() Seriema Member since: 6/15/2001 From: Stockholm, Sweden |
||||
|
|
||||
Quote: This is another reason to really give a good XML (ie. TinyXML usage) foundation in the first part. Putting "correct usage" together with "how to bind things" doesn't look good. "correct usage" should go hand in hand with "this is how you use it". My suggestion is that you edit your first article, instead of craming the corrections to the next articles. The stuff I pointed out are quite subtle but still important. Sorry for going on like this, but I really liked your article and would like to see it as THE source for XML usage in moddable games :) [ ThumbView: Adds thumbnail support for DDS, PCX, TGA and 16 other imagetypes for Windows XP Explorer. ] [ Chocolate peanuts: Brazilian recipe for home made chocolate covered peanuts. Pure coding pleasure. ] |
||||
|
||||
![]() Houdini Member since: 3/16/2000 From: Sylvania, OH, United States |
||||
|
|
||||
Quote: Quote: Excellent! I'm really looking forward to seeing your next few articles then! They should be very helpful. - Houdini |
||||
|
||||
![]() kumikana Member since: 9/4/2002 From: Finland |
||||
|
|
||||
Quote: That was so cool feature that I just had to rip it ;) Hope you don't mind. It does make the Serialize( CFileSys* filesys ) function so much cleaner looking. Thanks man. |
||||
|
||||
![]() Seriema Member since: 6/15/2001 From: Stockholm, Sweden |
||||
|
|
||||
Quote: Glad you liked it =) I might as well share another idea involving that. I have a templated DataInfo struct. It is templated to the type that you're binding (in my example, int). The struct holds a reference to the variable, the line and file info, the name, and the comment. So my IOBind interface only takes DataInfo's as parameters. Overloading by DataInfo template type. Like so: bool IOBind::bind( DataInfo<int>& d ); bool IOBind::bind( DataInfo<float>& d); etc... This allows me to add/remove info from the struct without having to change the parameters in IOBind. This is great since some IOBind implementations might just want the variable, nothing else. So adding info to DataInfo won't affect anyone. I might write an article about my IOBind as soon as it's done. But that might take a while... [ ThumbView: Adds thumbnail support for DDS, PCX, TGA and 16 other imagetypes for Windows XP Explorer. ] [ Chocolate peanuts: Brazilian recipe for home made chocolate covered peanuts. Pure coding pleasure. ] |
||||
|
||||
![]() DirectXXX Member since: 9/24/2002 From: Lahore, Pakistan |
||||
|
|
||||
| Thanx for the article. I already learned how to use tinyxml to parse xml files but my aproach was not as good as described in article....The real thing I am waiting for is the game object model. |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
| Great article, I have a noob question I wonder if anyone could answer this. What advantage has an XML/Scripting have over just using a script langugae ? For example using this system compared to using Lua with Luabind to create objects that would represent game entities |
||||
|
||||
![]() evolutional Moderator - Alternative Game Libraries Member since: 1/25/2002 From: Leeds, United Kingdom |
||||
|
|
||||
Quote: Hi, thanks for your interest. In my eyes it depends on the context of your development environment. I prefer to keep logic and data separate from each other in my development, meaning that for me Xml makes perfect sense as a data definition language. You could achieve both at the same time using Lua if you wished as Lua has data defintion properties, however I wished to present a different paradigm, showing that Xml (which is traditionally used in regular applications) has potential to be used in games. I also wished to cater for those that don't use Lua in their development environment. As it stands, there's pros and cons to both methods - I simply wished to present a viable (and flexible) alternative that serves to introduce Xml to game developers that hadn't cosidered it as well as combining it with a script language to show an alternaive and interesting use for the technology. |
||||
|
||||
![]() Big_Bear_Scot Member since: 5/18/2004 From: Uddingston, United Kingdom |
||||
|
|
||||
Quote: Thanks for the answer, I have been thinking about using XML for data definition, but I did not think about combining it with a scripting language. It will be interesting to see how you combine scripting and XML. I am also looking forward to seeing how you use javascript you don't see many articles on using javascript in games. In fact I can't even think of one. |
||||
|
||||
![]() BiGF00T Member since: 2/28/2004 From: Germany |
||||
|
|
||||
| w00t, xml is very nice. i always used crappy selfmade file formats to load my data from. now i'm able to load xml files which are >> my files :) your article helped me a lot. it was simple and well written. i hope you'll finish the 2nd part soon... |
||||
|
||||
![]() tris Member since: 1/9/2004 From: Brisbane, Australia |
||||
|
|
||||
| Great article Oli, when can we expect to see the next part in the series? |
||||
|
||||
![]() brwarner Member since: 12/3/2005 From: Toronto, Canada |
||||
|
|
||||
| what file do i have to include to access tixmlelement and other structures mentioned in the article?? |
||||
|
||||
![]() Will F Member since: 4/10/2005 From: Carmel valley, CA, United States |
||||
|
|
||||
The loadXmlLevels function in the article has the potential to leak memory:int jsiGame::loadXmlLevels(const char *filename) { TiXmlDocument *xmlDoc = new TiXmlDocument(filename); if (!xmlDoc->LoadFile()) { // Fatal error, cannot load return 0; } // Delete our allocated document and return success ;) delete xmlDoc; return 1; } If you reach the fatal error the memory allocated for xmlDoc will never be freed. The relevant section should look like this if (!xmlDoc->LoadFile())
{
// Fatal error, cannot load
delete xmlDoc;
return 0;
}[Edited by - Will F on October 6, 2006 7:26:33 PM] |
||||
|
||||
![]() kevinlynx Member since: 5/18/2006 From: Shanghai, China |
||||
|
|
||||
| But where is the part II ? I need to read the part II ? Does anybody know ? |
||||
|
||||
All times are ET (US)![]() |
Last Thread Next Thread ![]() |
|