Welcome, please login or register

PushButton Engine: Table of Contents » XML Level Format

XML Level Format

The PushButton Engine uses XML to store data about a game's objects. This chapter describes the format the engine expects when instantiating objects from XML. Loading XML object descriptions and instantiating them is handled by the TemplateManager class.

XML Format Overview

The easiest way to explain PushButton Engine's XML serialization format is to go through an example. Later sections provide a reference to specific details of the format. Here is an example level with explanatory comments:

   <!-- Root tag - indicates version in case there are breaking changes  in the format. The root tag contains one or more entities, templates or groups.-->
   <things version="1">
   
      <!-- Only one entity of a given name can exist in the world at a time. The TemplateManager will generate warnings if you instantiate them more than once. -->
      <entity name="PlatformSpriteSheet">
      
         <!-- Entities and templates contain components, which are named and have a specified type. -->
         <component type="com.pblabs.rendering2DSpriteSheetComponent" name="SpriteSheet">
            
            <!-- Fields on the component are dealt with using our normal  serializer. In this case we are assigning a string to  ImageFilename on the SpriteSheetComponent. -->
            <ImageFilename>../Assets/Images/platform.png</ImageFilename>
         </component>
      </entity>
      
      <!-- Another sprite sheet - just the same as the above one, but with a  different entity name and file. -->
      <entity name="DudeSpriteSheet">
         <component type="com.pblabs.rendering2DSpriteSheetComponent" name="SpriteSheet">
            <ImageFilename>../Assets/Images/guy.png</ImageFilename>
         </component>
      </entity>
      
      <!-- Templates are like entities in every respect except two. First, they can be instantiated more than once w/o warnings. Second, they can be referenced via the template attribute on an entity, which is explained later. -->
      <template name="Platform">
      
         <component type="com.pblabs.rendering2DSpriteRenderComponent" name="Render">
            <!-- Parent and SpriteSheet are typed fields. You can assign a direct reference to another component either on this entity or another using the componentReference attribute. -->
            
            <!-- Look up a named entity called Scene, find the first component that matches the type of Parent, and assign it. -->
            <Parent componentReference="Scene"/>
            
            <!-- Same for the SpriteSheet field, which happens to want a SpriteSheetComponent. -->
            <SpriteSheet componentReference="PlatformSpriteSheet"/>

            <!-- Although PositionReference, RotationReference, and SizeReference are all of type PropertyReference, PropertyReference implements ISerializable and does its own custom serialization - in this case so that you can specify a string property reference. -->
            <PositionReference>@Spatial.position</PositionReference>
            <RotationReference>@Spatial.rotation</RotationReference>
            <SizeReference>@Spatial.size</SizeReference>
         </component>
         <component type="com.pblabs.box2D.Box2DSpatialComponent" name="Spatial">
            <Manager componentReference="Box2D"/>
            <!-- CollisionType is of type ObjectType, and also implements ISerializable, in this case to allow you to provide a list of object types. -->
            <CollisionType>
               <Type>Platform</Type>
            </CollisionType>
            <CollidesWithTypes>
               <Type>Dude</Type>
            </CollidesWithTypes>
            
            <!-- Size is of type Point. The serializer lets you set fields by nesting tags. In this case <Size><x>1</x></Size is equivalent to saying Size.x = 1; in ActionScript. -->
            <Size>
               <x>256</x>
               <y>64</y>
            </Size>
            
            <!-- Booleans support named values (true/false). -->
            <CanMove>false</CanMove>
            <CanRotate>false</CanRotate>
            <CanSleep>true</CanSleep>
            
            <!--CollisionShapes is an array... -->
            <CollisionShapes>
               <!-- But you can specify the type of each item in the array. Here
                    we use the feature in order to distinguish between different
                    collision shapes. -->
               <_ type="com.pblabs.box2D.PolygonCollisionShape">
                  <!-- Vertices is also an array. In this case we can specify  the type of the items to put into it. In addition, we are using <_>. The underscore alone tells the serializer to put the value contained in it at the end of the array. -->
                       
                  <!-- Underscore can also escape numerical values, which are not valid XML tag names. You can'd to <3>, you have to do <_3>. If you want to do an underscore, do <__>. -->
                  <Vertices childType="flash.geom.Point">
                     <_><x>-1</x><y>-1</y></_>
                     <_><x>1</x><y>-1</y></_>
                     <_><x>1</x><y>1</y></_>
                     <_><x>-1</x><y>1</y></_>
                  </Vertices>
               </_>
            </CollisionShapes>
         </component>
      </template>
      
      <entity name="Scene">
         <component type="com.pblabs.rendering2DScene2DComponent" name="Scene">
            <Position>
               <x>400</x>
               <y>300</y>
            </Position>
         </component>
      </entity>
      
      <entity name="Box2D">
         <component type="com.pblabs.box2D.Box2DManagerComponent" name="Manager">
         </component>
         <component type="com.pblabs.box2D.Box2DDebugComponent" name="Debug">
            <Scene componentReference="Scene"/>
         </component>
      </entity>
      
      <entity name="Dude">
         <component type="com.pblabs.rendering2DSpriteRenderComponent" name="Render">
            <!-- In addition to componentReference, you can specify componentName, which indicates a specific component to reference. -->
                 
            <!-- If you use componentName alone, it will reference that component on the owning entity. -->
            <Parent componentReference="Scene" componentName="Scene"/>
            <SpriteSheet componentReference="DudeSpriteSheet"/>
            <PositionReference>@Spatial.position</PositionReference>
            <RotationReference>@Spatial.rotation</RotationReference>
            <SizeReference>@Spatial.size</SizeReference>
            <TrackWithCamera>true</TrackWithCamera>
         </component>
         <component type="com.pblabs.box2D.Box2DSpatialComponent" name="Spatial">
            <Manager componentReference="Box2D"/>
            <CollisionType>
               <Type>Dude</Type>
            </CollisionType>
            <CollidesWithTypes>
               <Type>Platform</Type>
            </CollidesWithTypes>
            <Position>
               <x>400</x>
               <y>100</y>
            </Position>
            <Size>
               <x>64</x>
               <y>74</y>
            </Size>
            <CanRotate>false</CanRotate>
            <CanSleep>false</CanSleep>
            <CollisionShapes>
               <_ type="com.pblabs.box2D.CircleCollisionShape">
                  <Friction>0</Friction>
                  <Radius>0.5</Radius>
                  <Offset><x>0</x><y>0.5</y></Offset>
               </_>
            </CollisionShapes>
         </component>
         <component type="com.com.pblabs.stupidSampleGame.DudeController" name="Controller">
            <VelocityReference>@Spatial.LinearVelocity</VelocityReference>
            <!-- Input Map Example -->
            <Input>
               <GoLeft>LEFT</GoLeft>
               <GoRight>RIGHT</GoRight>
               <Jump>UP</Jump>
            </Input>
         </component>
      </entity>
      
      <!-- Often you want one entity to be a clone of another with minor  modifications. You can do this via the template attribute. Everything on Platform is loaded into the entity, then the information in the Platform1 entity is applied on top of it. -->
      <entity name="Platform1" template="Platform">
         <!-- Notice that when we reference fields for an existing component 
              (from the template) we omit the type attribute. -->
         <component name="Spatial">
            <Position>
               <x>94</x>
               <y>450</y>
            </Position>
         </component>
      </entity>
      
      <entity name="Platform2" template="Platform">
         <component name="Spatial">
            <Position>
               <x>400</x>
               <y>500</y>
            </Position>
         </component>
      </entity>
      
      <entity name="Platform3" template="Platform">
         <component name="Spatial">
            <Position>
               <x>706</x>
               <y>450</y>
            </Position>
         </component>
      </entity>
      
      <!-- Groups are lists of templates/entities, so that you can instantiate them all as a single group. TemplateManager.InstantiateGroup returns an array of all the objects that were created. -->
      <group name="Managers">
         <!-- Group tags contain one or more objectReference or groupReference tags. These reference groups or entities/templates by name, indicating what is part of the group. Things can be part of any number of groups. -->
         <objectReference name="Scene"/>
         <objectReference name="Box2D"/>
      </group>
      
      <group name="SpriteSheets">
         <objectReference name="DudeSpriteSheet"/>
         <objectReference name="PlatformSpriteSheet"/>
      </group>
      
      <group name="Objects">
         <objectReference name="Dude"/>
         <objectReference name="Platform1"/>
         <objectReference name="Platform2"/>
         <objectReference name="Platform3"/>
      </group>
      
      <group name="Everything">
         <!-- This is how you reference a group from inside another group. -->
         <groupReference name="Managers"/>
         <groupReference name="SpriteSheets"/>
         <groupReference name="Objects"/>
      </group>
      
   </things>   
   

Basic Structure

There are three types of objects that can be instantiated from XML using the engine's TemplateManager class. These are entity, template, and group. Each of these can appear any number of times in a single XML file, in any order. They are all contained in the same parent tag that is the root of the XML document.

When an XML file is loaded, everything inside the root tag is added to the TemplateManager for later instantiation. Keep in mind, merely loading a description of an object does not actually create that object. It must be instantiated manually with the TemplateManager.

Entities

The entity tag contains the XML for an IEntity object. An IEntity is essentially a named container for a collection of components. So, the entity tag supports a single child tag, appropriately named component. Any number of component tags can be added to an entity tag, with each one describing a different component.

The name of the entity is set using the name attribute. Additionally, a template attribute is supported. The template attribute should be the name given to an XML object described with a template tag. The deserializer handles templates by first deserializing the new IEntity with the description contained in the template, then deserializing it again with the description contained in the entity. Essentially, this allows a template object to describe a common set of components for the IEntity, and the entity to overwrite or add additional components to the IEnity. The template attribute is optional.

Templates

A template is constructed in identical fashion to an entity, including the optional template tag. The only difference between a template and an entity, once instantiated, is that a template is not registered with the NameManager class, whereas an entity is. The reason for this is templates are designed to be instantiated multiple times, for things like bullets or respawning enemies, whereas an entity is designed to be instantiated once.

Entities cannot be used as templates in XML, however, so any object that is being used as a base description must be created as a template. Templates can be derived from other templates, though, with no limit on how many derivation levels there are.

Components

The component tag goes inside of either a template or entity tag and describes a component that should be added to that template or entity when it is instantiated.

The component tag has two attributes, both of which are required in the normal case (the other case will be described below). The first attribute is type, which specifies the fully qualified class of the component. So, for instance, a component with class DudeController in the namespace com.pblabs.stupidSampleGame would have its type attribute set to "com.com.pblabs.stupidSampleGame.DudeController".

The second attribute is name. This specifies the name to assign to the component when it is instantiated. A component's name must be unique across all components on the same entity as it is used to lookup the component on an entity.

The tags inside of a component correspond exactly to the properties of that component. If a component has a property Position, then that property's value can be set with a child tag called Position. More information on this is in the General Serialization section.

Component tags also have the capability of overwriting an existing component rather than instantiating an entirely new one. This would happen when templates are being used. If an entity derives from a template with a component named "MyComponent", that entity can also have a component with name "MyComponent". Instead of creating another instance, the existing component on the template is looked up and its properties are replaced with anything defined by the child component. In this case, the child component should not have a class attribute.

Groups

A group is the third type of object that can be described in XML. It is essentially just a list of other templates, entities, or groups that will all be instantiated when the group is instantiated. For example, if you define a group with two children "MyFirstEntity" and "MySecondEntity", instantiating the group will instantiate both of those entities.

The list of objects to instantiate can contain two different tags. The first is objectReference, which is used for both templates and entities. The second is groupReference, which is used for referencing other groups.

A group has no effect on the actual game once instantiated. It is merely a convenience for instantiating several objects at once.

General Serialization

Component tags can contain any number of child tags that correspond to their properties as defined in code. If a component has a public property named "MyProperty", it's value can be set to 7 by adding <MyProperty>7</MyProperty> as a child of the component tag.

If a component has a class property that has child properties itself, these can be set as well. For example, to set the value of a Point property called Position, use the XML:

   <Position>
      <x>10</x>
      <y>25</y>
   </Position>
   

Additionally, classes can contain child classes as well, to an infinite depth, using the same method.

By default, the deserialization process will automatically infer the type of the class to create based on the type specified in your code. You can override this behavior using the type attribute. This would allow you to instantiate a subclass of a class that was expected. The type attribute requires a fully qualified class name.

Class References

Properties that store references to IEntity objects or IEntityComponent objects can also be resolved in the XML. This is done with the entityReference, entityName, and objectReference attributes.

The nameReference attribute is used to assign a reference to an existing IEntity to the property. So, it must be the name of an entity that has already been instantiated. The property type should be IEntity and is resolved before onAdd is called on any components.

objectReference works the same way as nameReference, but instead of looking up an existing object, it will instantiate it from a named template instead.

The entityName attribute also takes the name of an existing entity, similar to the nameReference. The correct component is retrieved from the entity by inferring the type of component the property expects and looking up a component of that type from the found entity. An additional attribute can be specified to look up a specific component on the entity. This is the componentName attribute and should be set to the name of the component to lookup.

Arrays and Dictionaries

Arrays and Dictionaries are handled in almost the same way by the deserializer. The only difference is Arrays can use default tags to push objects onto them, whereas Dictionaries require a key.

The type of object that is added to a Dictionary or Array is specified by the childType attribute. This attribute value is the default type that will be used when instantiating child tags. Each child tag can optionally include the type attribute to override the type specified by the childType attribute.

The tag for each child corresponds to the key in the dictionary or array that the object will be added to. The handling of this key value has some special rules, however. To begin with, if the key has a leading underscore, it is automatically removed. This is to allow for keys that are just numbers as the XML specification does not allow numbers to be tags. If the key consists of just an underscore, the instantiated class is simply pushed onto the back of the array. For dictionaries, a single underscore is considered invalid.

Custom Serialization

The XML format for every class is as described in the General Serialization section, unless the class implements the ISerializable interface. If this is the case, the format can be anything, since it is defined by the specific ISerializable implementation. The built in classes that implement this are PropertyReference, ObjectType, and InputMap.

PropertyReference fields can be treated exactly like strings. The string value is that is set in XML is set as the value of the PropertyReference.

ObjectType fields can be treated like an array of strings. Each of the child tag values is added as a named type to the ObjectType field.

The InputMap class can be treated exactly like a dictionary. The keys should be the name of a binding on the input map, and the value should be the name of the key to use for that binding.

Loading a Level File

To load a level file you use the LevelManager. It's job is to manage the loading and unloading of level files. It also provides an interface to add and remove file and group references. You can also override the default file loading and unloading methods to customize this behavior.

Loading via MXML

There are two MXML tags available for prepping levels and groups for use with the LevelManager. LevelFileReference is used to associate level files with level numbers and GroupReference maps groups to level numbers.

To add the level file shown above in MXML we would do the following:

   <mx:Canvas xmlns:mx="http://www.adobe.com.2006/mxml" xmlns:pb="PBLabs.Engine.MXML.*">
      <pb:LevelFileReference filename="..Assets/Levels/level.pbelevel" level="0" id="myLevelReference" />
      <pb:GroupReference name="Managers" level="0" id="managerGroup" />
      <pb:GroupReference name="SpriteSheets" level="0" id="spriteSheetsGroup" />
      <pb:GroupReference name="Objects" level="0" id="objectsGroup" />
      <pb:GroupReference name="Everything" level="0" id="everythingGroup" />
   </mx:Canvas>
   

This will ensure that all four groups get their objects instantiated when LevelManager loads the level and has it's Start method called. The 'level' attribute shared by both classes is simply an index used to define sets of groups or files to have loaded.

   // Load level 0 and start game.
   LevelManager.Instance.LoadLevel(0);
   LevelManager.Instance.Start();
   

At this point, level 0 is loaded, the objects have been created and the game can begin. LevelManager also dispatches events based on the loading and unloading of level files.To be notified that a level has finished loading you would do the following:

   // Add a listener to respond to level loaded event
   LevelManager.Instance.addEventListener(LevelEvent.LEVEL_LOADED_EVENT, levelLoadedHandler, false, 0, true);
   // Load level 0 and start game.
   LevelManager.Instance.LoadLevel(0);
   LevelManager.Instance.Start();

   private function levelLoadedHandler(event:LevelEvent):void
   {
      // level is loaded!
   }
   

Loading via ActionScript

As one would expect, the process of loading level files in ActionScript is very similar to doing so in MXML:

   // Add file and group references
   LevelManager.Instance.AddFileReference(0, "../Assets/Levels/level.pbelevel");
   LevelManager.Instance.AddGroupReference(0, "Managers");
   LevelManager.Instance.AddGroupReference(0, "SpriteSheets");
   LevelManager.Instance.AddGroupReference(0, "Objects");
   LevelManager.Instance.AddGroupReference(0, "Everything");

   // Load level 0 and start game.
   LoadManager.Instance.LoadLevel(0);
   LoadManager.Instance.Start();
   

You'll notice the method arguments reflect the 'name' and 'level' attributes mentioned above.

Other Uses for XML

This chapter has described the XML format assuming level files and the TemplateManager are being used to load and instantiate the XML data. This does not have to be the case. The Serialization class provides all the functionality necessary to instantiate any class from an XML description, or serialize any existing class to an XML description. The format outlined in this chapter still applies, but the Serialization class is used directly, rather than loading level files with the TemplateManager.

Complete Tag List

  • things: The root tag.
  • template: A child of the root tag, specifying that the contained XML is a template definition.
  • entity: A child of the root tag, specifying that the contained XML is an entity definition.
  • group: A child of the root tag, specifying that the contained XML is a group definition.
  • component: A child of the template or entity tag, specifying that the contained XML is a component definition.
  • objectReference: A child of the group tag, specifying the name of a template or object to instantiate with the group.
  • groupReference: A child of the group tag, specifying the name of a group to instantiate with the parent group.

Complete Attribute List

  • name:
    • On template tags, it specifies the name of the template.
    • On entity tags, it specifies both the name of the entity in XML, and the name of the object that is instantiated from it.
    • On group tags, it specifies the name of the group.
    • On component tags, it specifies the name of the component once registered with its owning IEntity.
    • On objectReference tags, it specifies the name of the template or entity to reference.
    • On groupReference tags, it specifies the name of the group to reference.
  • template: Exists on a template or entity tag and specifies the name of a template to inherit component definitions from.
  • nameReference: Specifies the name of an entity to lookup and set on the tag.
  • objectReference: Specifies the name of a template or entity to instantiate and set on the tag.
  • entityName: Specifies the name of a template or entity to lookup and search for a compatible component to set on the tag.
  • w: Specifies the name of a component to reference. If used without a componentReference attribute, the component is looked up on the same entity that this attribute is a part of.
  • type:
    • On a component, specifies the fully qualified type to use to instantiate the component.
    • On a field, specifies the type to use when instantiating the field for the parent tag.
  • childType: Specifies the type to use when instantiating objects to add to an array or dictionary.
  • filename: For fields that hold resources, this lets you specify a file path. The field must be of a compatible type to the type of the resource as specified to the resource manager. The resource will be loaded and assigned to the field.