More Quick Start

Include tags

Although at the moment you've only got a single resource, eventually you're going to have lots of different resources. Keeping them within one file can be messy, so it's often best to split them into different files. To include one resource file from another, use the 'include' tag:

 
<resources>
  <include path="tanks.xml" />
</resources>
 

Remember that the 'tanks.xml' file should also have a top-level tag.

Parent resources

Often in games we want to define a basic enemy (or item, or behaviour, etc.) and have several variants of this with minor tweeks. Parent resources mean you don't have to duplicate the common values between these resources. For example, if we had a basic tank:

 
class Tank extends AutoResource
{
  float moveSpeed;
  float rotateSpeed;
  float health;
  float damage;
  String image;
}
 

We can define a base tank and several variants on it:

 
  <TankTemplate name="basic.tank"
    moveSpeed="3.0"
    turnSpeed="2.0"
    health="100"
    damage="10"
    image="basicTank.png"
  />
 
  <TankTemplate name="fast.tank" parent="basic.tank"
    moveSpeed="10"
    image="fastTank.png"
  />
 
  <TankTemplate name="heavy.tank" parent="basic.tank"
    health="200"
    damage="30"
    image="heavyTank.png"
  />
 

Parent resources must be defined before they are referenced in a child resource. For best results they should be in the same file as all child resources (so that parent and children get reparsed at the same time).

Decoders

Writing your own resource types is great, but sometimes you want to use a 3rd party type (such as java.awt.Point) which you can't modify yourself. Instead you can write a decoder to add Point support.

Decoders are classes with a static 'decode' method which accepts a String (or xml Element) and returns the decoded object:

 
// Xml attribute decoder
public static Type decode(String str)
 
// Xml element decoder
public static Point decode(org.w3c.dom.Element e)
 

'Type' is not a generic parameter but the concrete type you wish to decode. A simple java.awt.Point decoder would look like this:

 
package yourgame;
 
import java.awt.Point;
 
public class PointDecoder
{
	/** Decodes xml attributes into java.awt.Point objects of the form "(x, y)"
	 *  eg. <ResourceType name="bob" position="(10, 20)" />
	 */
	public static Point decode(String str)
	{
		str = str.trim();
 
		if (str.charAt(0) != '(')
			return null;
 
		if (str.charAt(str.length()-1) != ')')
			return null;
 
		str = str.substring(1, str.length()-1);
 
		String[] t = str.split(",");
		if (t.length == 2)
		{
			int x = Integer.parseInt(t[0].trim());
			int y = Integer.parseInt(t[1].trim());
 
			return new Point(x, y);
		}
		else
			return null;
	}
}
 

Once you've written your decoder, you need to register it in your resources xml with the 'decoder' tag:

 
  <decoder class="yourgame.PointDecoder" />
 

Element decoders work the same way, except that they accept an 'Element' argument, and the syntax to use them in a resource is different:

 
  <ResourceType name="bob">
    <bounds> <!-- member variable name -->
      <Rectangle x="1" y="2" width="3" height="4" />  <!-- Element to decode -->
    </bounds>
  </ResourceType>
 

Background Loading

So far we've used ResourcePool.parse() and ResourceHandle.forceCreate(), which will work just fine but are blocking calls and do all of their work in the main thread. For non-blocking behaviour (for example, to display an animation while loading) usage is slightly different.

Instead of ResourcePool.parse(), use ResourcePool.backgroundParse to parse in a background thread:

 
ResourcePool resources; = Resources.createPool("Data", Resources.DecoderGroup.CORE, true);
resources.backgroundParse("resources.xml");
 

ResourceHandle.forceCreate forces a resource to be fully created straight away, however if it is not currently created this will block as any remaining creation is done. Instead you can request a resource be created in a background thread with ResourcePool.requestCreate():

 
  // At init
  ResourceHandle<TankTemplate> tankHandle;
  tankHandle = resources.requestCreate(TankTemplate.class, "player.tank");
 
  // Every frame
  if (tankHandle.getState() == ResourceHandle.State.CREATED)
    System.out.println("player.tank ready for use");
 

One method is to requestCreate() all resources required for the next stage/map, and show a loading screen until they all report that they are fully created before continuing.

4 comments

4 Responses to “More Quick Start”

  1. Tony Sparks Says:

    You should include a call back function to the ResourcePool. This call back could determine the loading progress.

  2. JC Says:

    That does sound like a good idea. Have you any particular ideas what kind of API you have in mind for that?

    Figuring out a percentage could be tricky though, since there’s no way of determining up-front exactly how much data there is to load (since resource files can include other resource files).

  3. Tony Sparks Says:

    You must be using some sort of XML API under the hood, which should allow for counting nodes. Although like you had mentioned these nodes could link to other files, in which case they can be treated as one unit of work (granted a large unit of work).

    I haven’t looked to deeply into the API, but something like this would be nice:

    interface ProgressListener {
    void onLoaded(ProgressEvent e);
    // this could replace the polling of if (tankHandle.getState() == ResourceHandle.State.CREATED)
    void onCompleted(ProgressEvent e);
    }

    class ProgressEvent {
    ResourceHandle handle;
    long timeToLoadResource;
    String nextResourceToLoad;

    }

    ProgressListener listener = new MyProgressListener() {
    void onLoaded(ProgressEvent e) {
    MenuState m = stateManager.getCurrentState();
    m.updateStatus(“Finished loading: ” + e.getHandle() );
    m.updateStatus(“Loading: ” + e.getNextResourceName() );
    }

    void onCompleted(ProgressEvent e) {
    stateManager.changeState(InGameState.class);
    }
    };
    ResourcePool resources; = Resources.createPool(“Data”, Resources.DecoderGroup.CORE, true);
    resources.backgroundParse(“resources.xml”, listener);

  4. Steel Plume Says:

    Excellent! decoder is just what I need to support multiple OpenGL engines like JMonkey/Ardor3D/Xilith3D/Java3d.

    P.S.
    What xml attributes, like “parent”, are reserved for your fw?

Leave a Reply