This project is read-only.
DEngine 1.0 Documentation

Revision History
  • 04/03/2009: Initial documentation
  • 30/08/2009: Fixed defunct name of actor, obsolete information
_

DEngine

The DEngine library contains a class Engine which inherits from the XNA Game object. (Learn about the XNA component model here: http://msdn.microsoft.com/en-us/library/bb198548.aspx).

Tiles

Tiles are square, static objects which comprise the entire game board. They can be solid or non-solid so that actors can collide with them or merely be a backdrop. (This can be set in the app settings file).
By setting this flag and enabling/disabling physical simulation in the engine, a 2D game world can be constructed which uses gravity and collision detection (a platformer), or a top-down game which requires no physical simulation (an RTS). The Tile class (ultimately) inherits from the DrawableGameComponent XNA class.
The tile size is set in the app config file. Currently it is set to 64x64 by default. The DEditor will use this tile size to perform grid placement when you are creating a level.

Actors

Actors are dynamic objects which animate and typically move around the game board. In the DEngine, this is accomplished by the code in Actor.cs and Sprite.cs. A Sprite is a set of frames which can be set to animate at a certain speed and/or to loop. An Actor contains a set of Sprites which it will choose to display and animate at certain times according to the actor logic. These sprites are defined in the actor's XML file.
The idea of creating a game in the DEngine is to create your own actors which inherit from the Actor class. Then, you can write your own code (in the Update routine) to control the actor's behavior and set animations accordingly. Information about the game state, other actors, and game board geography is obtained from the DEngine.

A sample XML file for an actor is the following:

<?xml version="1.0" encoding="utf-8" ?>
<Actor name="Soldier" width="32" height="50" scale="0.6">
  <Sprite name="SoldierIdle" loop="false" speed="6">
    <Image name="soldier-south1.png" />
  </Sprite>
  <!-- N,E,S,W -->
  <Sprite name="SoldierSouth" loop="true" speed="12">
    <Image name="soldier-south1.png" />
    <Image name="soldier-south2.png" />
    <Image name="soldier-south3.png" />
    <Image name="soldier-south4.png" />
  </Sprite>
  <Sprite name="SoldierNorth" loop="true" speed="12">
    <Image name="soldier-north1.png" />
    <Image name="soldier-north2.png" />
    <Image name="soldier-north3.png" />
    <Image name="soldier-north4.png" />
  </Sprite>
  ...
</Actor>

This xml file is named ''Soldier.xml'' and is contained in a folder called ''Soldier'' in the ''Actors'' directory of the XNA content tree. The actor's images must be stored in an ''images'' subdirectory of the Soldier folder. So it looks like this:
  • Content
    • actors
      • Soldier
        • Soldier.xml
        • images
          • (image files...)
Technical comments: When the engine is initialized it will create its own internal instance of each tile/actor which it reads from the game content files. When an instance of that tile/actor is required in the game, the engine will create a copy of this internal instance and place it on the game board as required. This is so the game content does not need to be read from the disk when spawning an object.

Scene graph

The DEngine uses a scene graph courtesy of mdx4ever's SceneGraph tutorial http://www.ziggyware.com/readarticle.php?article_id=130&rowstart=0 (slightly adapted). This ensures there is an explicit render order of game board objects, instead of simply the order in which they are added to the game. The Tile and Actor objects actually inherit from the SceneNode class. The DEngine also contains GUI objects which are founded upon this scene graph.
The engine has two scene graphs: a scene graph with a camera, and a static scene graph. These are accessed through the Engine properties SceneGraph and StaticSceneGraph.
The SceneGraph's camera is accessed through the Engine. You can move the camera to an absolute point or relative to its current position using the Engine methods MoveCameraBy() and MoveCameraTo().

Collision Detection

Physical simulation of the in-game bodies is done using the http://www.codeplex.com/FarseerPhysics Farseer Physics Engine. Both tiles and actors can be physically simulated. These settings are set in the app config file.

GUI elements

The DEngine also contains some GUI elements such as a panel, button, scroll bar, scrollable list view, checkbox and progress bar. These can be added to the engine's scene graph just like a Tile or Actor.

Tile Terrain Transitions

I used a great article from Gamedev as a reference when implementing tile terrain transitions in the DEngine: http://www.gamedev.net/reference/articles/article934.asp
The article explains the use of a tile precedence order. This can be thought of generally as a height order. It establishes the order for which transition tiles are drawn over adjacent terrain. The game engine draws the lowest items in the precedence chain first and works upward, for each tile in the game board. An adjacent higher-precedence tile will trigger a draw of the appropriate edge/corner overlay tile over the lower precedence tile.
The important thing to note is that the transition tiles are transparent overlays. This drastically reduces the number of artwork needed since we need not create transition tiles for every terrain transition possible.
The DEngine uses an XML format to represent precedence and transition tiles. An example Transitions.xml file is shown below:

<?xml version="1.0"?>
<DTileTransitions>
  <Tile name="forest" file="forest.png">
    <Overlay position="n" file="forest-n.png" />
    <Overlay position="ne" file="forest-ne.png" />
    <Overlay position="e" file="forest-e.png" />
    <Overlay position="se" file="forest-se.png" />
    <Overlay position="s" file="forest-s.png" />
    <Overlay position="sw" file="forest-sw.png" />
    <Overlay position="w" file="forest-w.png" />
    <Overlay position="nw" file="forest-nw.png" />

    <Tile name="grass" file="grass.png">
      <Overlay position="n" file="grass-n.png" />
      <Overlay position="ne" file="grass-ne.png" />
      <Overlay position="e" file="grass-e.png" />
      <Overlay position="se" file="grass-se.png" />
      <Overlay position="s" file="grass-s.png" />
      <Overlay position="sw" file="grass-sw.png" />
      <Overlay position="w" file="grass-w.png" />
      <Overlay position="nw" file="grass-nw.png" />

      <Tile name="sea" file="sea.png"/>
    </Tile>
  </Tile>
</DTileTransitions>

This XML file defines three terrain types: sea, grass, and forest. The Tile elements are nested to establish the precedence order. The forest and grass tiles have Overlay elements defining the edge and corner transition tiles for that terrain type. The transitions file is located at Content/tiles/transitions/Transitions.xml.
This example file defines an overall height order from highest to lowest of: forest, grass, sea. A sea tile that is adjacent to a grass tile and a forest tile will have both a grass transition tile and a forest transition tile drawn upon it (in that order).
This tile precedence is loaded by the engine and applied to the map terrain when adding tiles a map. It is similar to the behavior of the Starcraft or Warcraft II map editor in which edge and corner transitions are automatically determined when altering terrain.

DEditor

The DEditor is a Windows forms application with an embedded XNA surface on a panel. It allows the creation of a level using tiles and placement of actors. The file is saved in an XML format. Currently it doesn't have too many features such as changing object properties once placed. The XML levels you create can be loaded by the Engine method LoadLevelFromXml() - these calls should be done from within your game code.

A sample XML level file looks like this:

<?xml version="1.0"?>
<DLevel>
  <Tile name="blueblock" x="1232" y="16" />
  <Tile name="blueblock" x="1232" y="16" />
  <Tile name="blueblock" x="1232" y="16" />
  <Tile name="blueblock" x="1264" y="16" />
  <Tile name="blueblock" x="1264" y="16" />
  <Tile name="blueblock" x="1296" y="16" />
  <Tile name="blueblock" x="1296" y="16" />
  <Tile name="blueblock" x="1296" y="16" />
  <Actor name="Soldier" x="270" y="277" />
</DLevel>

FactionsGame

The Factions game includes multi-unit pathfinding code, an ants system for enemy navigation, a minimap, and capable of playing (fairly) large maps.
The gameplay is currently near-nonexistant it is merely a tech demo! So, you can't yet build units. But units placed on the game board can be controlled and will fight with enemy units. The enemy will also attack you.
The important thing to understand about this game "stub" is that the class FactionsGame inherits from Engine (which in turn inherits from the XNA Game class). A game is constructed using the Engine functions to bring together levels, tiles and actors.
To implement your own actor in your game, you must inherit from the Actor class and implement the actor's behavior in the Update() method.You'll also need to override the Engine function AddTemplateActor(). You must implement code such as the following:

protected override void AddTemplateActor(Actor actor)
{
     if (actor.Name == "PlayerSpawn")
     {
          PlayerSpawn newActor = new PlayerSpawn(this);
          newActor.Scale = actor.Scale;
          newActor.Size = actor.Size;
          newActor.Sprites = actor.Sprites;
          actorTemplates.Add(newActor);
     }
     else if (actor.Name == "Soldier")
     {
          Soldier newActor = new Soldier(this);
          newActor.Scale = actor.Scale;
          newActor.Size = actor.Size;
          newActor.Sprites = actor.Sprites;
          actorTemplates.Add(newActor);
     }
     else
     {
          base.AddTemplateActor(actor);
     }
}

This is a really bad block of code, I know! (It should be a switch-case). But what's happening here is that when the engine loads the template actors, it will initialize them as your specialized actors.
In this code example, PlayerSpawn and Soldier are two classes that inherit from Actor. The string literals "Soldier" and "PlayerSpawn" are the names of the actor folders in the game content tree. When the engine starts it reads all the folders in the Actors content directory and examines the XML definition files within. If the xml file is valid, an Actor is initialized and added to the template actors list. However, this code will ensure that when the engine loads an actor named "Soldier" for example, it will be initialized as a Soldier class, with all your extra Soldier functionality - and not just an Actor.
The Update() routine in the Soldier class will invoke its game logic. This is where you tell it to move, attack, etc. You can also examine the game state through the Engine's SceneGraph.

Further Information

  • Check out the FactionsGame project included with the DEngine source code. You can take many examples of using the DEngine from this code.

Last edited Aug 30, 2009 at 1:33 AM by spamdog, version 13

Comments

No comments yet.