PushButton Tutorial Series – Loading Levels

Google+ Pinterest LinkedIn Tumblr +

PLAY THE DEMO

DOWNLOAD THE SOURCE CODE

RETURN TO THE TUTORIAL INDEX

One of the differences between the component design system that is used by PushButton and the inheritance/object oriented design system typically used in ActionScript is that the creation of an entity is not done by a class. This logic could be housed in a class constructor, but there is little benefit in doing so. Way back in the first tutorial the decision was made to use the static class EntityFactory as a kind of database for the creation of entities. In this tutorial we will take the code from the static functions in the EntityFactory and move it into an XML file. This allows a Pushbutton level, or even a complete game, to be specified in an easily edited XML file.

In order for the PushButton deserialisation code to work we need to import all of the classes that are referenced by the XML file. This is done by creating a class that contains member variables of the types that are referenced in the XML file. We don’t need to instantiate any new objects, just create variables of the types that are going to be used.

References.as

public class References
{
 private var _scene2DComponent:com.pblabs.rendering2D.Scene2DComponent;
 private var _spriteRenderComponent:com.pblabs.rendering2D.SpriteRenderComponent;
 private var _spriteSheetComponent:com.pblabs.rendering2D.SpriteSheetComponent;
 private var _simpleSpatialComponent:com.pblabs.rendering2D.SimpleSpatialComponent;
 private var _basicSpatialManager2D:com.pblabs.rendering2D.BasicSpatialManager2D;
 private var _cellCountDivider:com.pblabs.rendering2D.CellCountDivider;
 private var _animationController:com.pblabs.rendering2D.AnimationController;
 private var _box2DDebugComponent:com.pblabs.box2D.Box2DDebugComponent;
 private var _box2DManagerComponent:com.pblabs.box2D.Box2DManagerComponent;
 private var _box2DSpatialComponent:com.pblabs.box2D.Box2DSpatialComponent;
 private var _polygonCollisionShape:com.pblabs.box2D.PolygonCollisionShape;
 private var _circleCollisionShape:com.pblabs.box2D.CircleCollisionShape;
 private var _animatorComponent:com.pblabs.animation.AnimatorComponent;
 private var _sceneView:com.pblabs.rendering2D.ui.SceneView;
}

The appComplete function in the MXML file is modified to create a new instance of the References class, and then load a level from an XML file called levels.xml. Along with the resources embedded in the Resources class, these few dozen lines of code are all that is required to load a PushButton level from an XML file.

Main.mxml

protected function appComplete():void
{
 Global.startup(this);
 
 new Resources();
 new References();
 
 LevelManager.instance.load("../media/levels.xml", 1);
}

The majority of the application now lies in the XML files. For the most part the code from the EntityFactory class ports straight across, but there are some subtleties to loading entities from XML files.

We will start by defining a template for an enemy entity. Templates are kind of like the functions from the EntityFactory class – a template itself is not an entity in the game, but is used to create one. The template contains all of the logic that is common to a type of entity, and the blanks are then filled in when an entity is created from the template. For our enemy entity this means all the logic to place, render and animate the entity is in the template, and the position of the entity is supplied later on when a new enemy entity is created.

Although the XML looks complicated, the code is actually very similar to the ActionScript we have been using to create the same entities. The root element for all PushButton XML files that create groups and entities (more on groups later) is call things. The version attribute defines the format of the PushButton XML.

Code

Templates are contained in a XML element called template.

Code

Individual components are defined in a XML element called component. The type attribute is the full ActionScript class name of the component, and the name attribute is the name of the component.

Code

Component properties are defined in individual elements. So the ActionScript code component.property = true translates to the child element true.

Code

Arrays and Dictionaries have an attribute called childType that defines the ActionScript class type that is contained in the collection. Here we have created an Array of Strings to assign to the collidesWithTypes property.

Code

The elements of an array are created in XML elements with an underscore followed by a number. This works around a XML limitation where the name of an element cannot be a number. Here we are setting the first element of the array to a String called “Player”.

Code

Next we assign an array of com.pblabs.box2D.CollisionShape objects to the collisionShapes property.

Code

The first element in this array will be a com.pblabs.box2D.PolygonCollisionShape object (as defined by the type attribute).

Code

We assign an array of Points to the vertices property.

Code

To the verticies Array we assign 4 new Point objects. Notice that because the type of the objects being assigned to the array is the same as the type that was assigned to the array childType attribute, the type attribute on the child elements can be left blank. The reason why the type of the child element being assigned to the collisionShapes Array above was specified was because it was different than the childType specified on the collection (the elements of the array were CollisionShapes, while the actual object being created was a PolygonCollisionShape).

Code

References to other components are made with an XML attributed called componentReference. Here we are setting the manager property to a component held in an entity called SpatialDB (this entity will create created in a separate XML file). The XML deserialisation code knows how to extract the required component by its type from the specified entity.

Code

Dictionaries are created in the same way that Arrays are. The only difference is that the elements containing the child elements are named after the dictionary keys rather than an underscore followed by a number. Here we set the “Idle” element in the animations dictionary to be a new instance of a AnimationControllerInfo class.

Code

This template XML file highlights pretty much all of the issues you will face creating an entity from an XML file: creating components, populating arrays and dictionaries with plain old ActionScript objects and referencing external components.

In the managers.xml file we create two entities, each one containing a spatial and scene “manager” component. In the ActionScript code these components were added to the same entity. Here we add each component to its own entity.

Code

Groups are how entities get loaded. Just defining an entity in an XML file won’t actually load it in the scene. Here we add the two manager entities to a group called Managers. It is this Managers group that will be referenced later on, and in doing so will create the entities added to the group.

Code

The SpriteSheets referenced by the enemy template are created much like the manager entities. The SpriteSheets are loaded into separate entities so they can be shared amongst entities. This means that multiple enemy entities share the same SpriteSheet, saving some memory.

Code

The level1.xml file is where we create some enemy entities, using the template created in the templates.xml file as a reference. By setting the template attribute in the entity element we define the entity being created as being based off that template. After that we supply the data needed to specialise each entity instance, that data being the position of the entity in this case.

Code

The three new enemy entities are bundled up in a group.

Code

At this point we have defines the entities that contain the manager components and SpriteSheets that will be shared by all the enemy entities. We have also defined a template for the enemies, and created three new instances of the enemy entiies based off that template. The final step is to bind up all of these entities into an individual level.

Level dinfiintions are held in a root element called LevelDescriptions.

Code

Each individual level is defined in an element called level. The index attribute defines a number that is used to reference the level. The name attribute defines a user friendly name to reference the level with.

Code

Each of the XML files that were created above are referenced in file elements. The filename specifies the location of the XML as if it were being referenced as a PushButton resource, meaning it has the same path as the embedded XML file in the Resources class.

Code

The groups defined in the XML files are referenced in group elements. The name attribute is set to the name of the group. This is how the individual entities are created. By referencing a group from a level definition, each of the entities in that group are created.

Code

So, as a quick overview, this is the process of loading a level from XML files:

  • Templates are created, providing a single place where the common components of an entity are defined.
  • Common resources, like SpriteSheets, are created in their own entities. This saves memory by providing a single resource which other entities then link to.
  • Specific entities are defined. Some are based on templates, and some that are only going to be created once, like manager entities, are defined individually.
  • All entities that need to be loaded are placed into groups.
  • The individual XML files, and the groups they define, are referenced and combined into a level by another level definition XML file.
  • The level definition XML file is loaded by ActionScript code. The groups referenced by the level are loaded, which in turn creates the entities referenced by the groups.
Share.

About Author

Leave A Reply