Skip to content
Jan 19 2010 / alecmce

RobotLegsPong Part 1 – Overview

Introduction

RobotLegsPong attempts to combine RobotLegs application framework with the as3signals event model and a Frame-Ticker (as well as a pinch of ASUnit unit-testing) to create a ‘game’.

It is an open-source project, available on GitHub as an example of using RobotLegs together with these other technologies.

Source Code: github/alecmce/RobotLegsPong

RobotLegsPong is not a very good game; it is playable only in the loosest sense. Bats require to be dragged in order to move (and there is no computer-logic to play against), so you could not really play against an opponent. The purpose of the exercise was to see what structure a game like Pong would take within the RobotLegs framework.

The purpose of this article is to describe the particulars of the technologies and why I chose them. In a follow-up article, I will look in more detail how RobotLegsPong is wired up.

Why I Chose These Technologies

RobotLegs

The RobotLegs framework is a lightweight, flexible application framework that allows coders to spend less time wiring up their application, and more time working on its functionality.

Frameworks are often criticised because they force developers to write code in a particular way, which might be inappropriate for a particular context. I have used Cairngorm, Pure MVC, as well as some proprietary frameworks, and have found that all of them do some things well, and do other things clumsily. So far I have not found this problem with RobotLegs, because most of the time it gets out of the way and lets you code; in fact, it does more than that: it reduces the amount of code you have to write.

as3signals

RobotLegs comes with an application event bus, but I have decided to circumvent that and use as3signals instead. as3signals allows you to define an object which acts as an event dispatcher within an application.

I like as3signals because describing an event as a Signal object allows my event model to be described through interfaces. as3signals is supposed to be fast too. Then again, interfaces are slow. The relative speeds of the approaches is interesting, but not a significant factor in my decision making process.

Frame-Ticker

The frame-ticker is a simple idea: rather than creating multiple enter-frame event listeners, each creating event objects, create one enter-frame listener and a main game loop iterating through those methods that need to be called per-iteration.

This implementation came out of a discussion on Twitter that I was involved in on the fringes; I wrote about it in a previous blog post.

ASUnit

Unit testing enables developers to test portions of their code in isolation, so that they can be confident that every method works exactly as the developer intended. They also represent a secondary form of documentation-in-code; if you want to know how I meant a method to work, reading the unit test will tell you almost everything you need to know (including, if a good test is missing, what I didn’t consider).

That said, I have not unit tested every method; in this example I have only unit-tested the geometric logic for the bats. This is bad practice, however under time constraints I think that it is most important to unit test what is complex, mathematical or ambiguous, since this is where behaviour is most likely to fall down.

[Inject]

The ‘trick’ to using RobotLegs is that you do not need to inject dependent objects into classes when you contruct them; this is done automatically. This code:

public class Dependee {}

public class MyExample
{
	public var dependee:Dependee;

	public function MyExample(dependee:Dependee)
	{
		this.dependee = dependee;
	}
}

becomes this code:

public class Dependee {}

public class MyExample
{
	[Inject]
	public var dependee:Dependee;
}

The metadata tag [Inject] has the effect of automatically injecting the dependee into the class when it is constructed. This isn’t much on the face of it, but it is enough. It feels to me as though a quarter of my code is actually just wiring up classes so that they each have access to the classes that they need to function, or passing objects around methods. The [Inject] tag lets me shift all of that wiring into the RobotLegs framework.

Context

There is a little more to the RobotLegs story however; how should RobotLegs react when it comes across an [Inject] tag? It could construct a new Dependee object, or it could be that I want only one Dependee object to be injected wherever the [Inject] tag is found. This is where the Context class becomes meaningful.

In the PongContext, different classes are wired up in different ways. Their definitions are defined by adding injector mappings and the mediatorMap mappings in the startup method.

public class PongContext extends Context
{
	override public function startup():void
	{
		...
		injector.mapSingleton(DragMechanism);
		injector.mapSingletonOf(Geometry, GeometryModel);
		injector.mapClass(BatGeometry, BatGeometryModel);
		...
	}
}

injector.mapSingleton

injector.mapSingleton(DragMechanism)

mapSingleton indicates that only one instance of the class entered should be constructed. In this case, just one DragMechanism will be created, and injected whenever this is found:

[Inject]
public var mechanism:DragMechanism;

injector.mapSingletonOf

injector.mapSingletonOf(Geometry, GeometryModel);

mapSingletonOf extends the mapSingleton idea to interfaces and their implementing classes. In this case the Geometry interface is mapped to the GeometryModel class. The injection is called as follows:

[Inject]
public var model:Geometry;

No reference to the GeometryModel is needed in any class other than the PongContext. This allows a very pure use of interfaces across the application.

injector.mapClass

injector.mapClass(BatGeometry, BatGeometryModel)

injector.mapClass works a little differently; whenever a BatGeometry interface is flagged to be injected, a new instance of BatGeometryModel is constructed and injected into the class, so the following code is used to create the injection:

Each BatMediator needs its own BatGeometry object. By using the mapClass method, multiple BatGeometryModel objects will be constructed, one for each time the following code is found:

[Inject]
public var model:BatGeometry;

Mediators and Components

public class PongContext extends Context
{
	override public function startup():void
	{
		...
		mediatorMap.mapView(Background, BackgroundMediator);

		addChild(new Background());
	}
}

mediatorMap references a slightly different aspect of the application: your view objects. The mediatorMap allows users to define associations between display objects and Mediators. These two parts comprise the View aspect of the standard MVC pattern.

The DisplayObject – in this case Background – is the asset that you want to be visible on the stage. In my pong example this is essentially just a Sprite, though it might just as well have been imported as a graphical asset in a SWC.

The Mediator is associated with the DisplayObject so that when an instance of the DisplayObject is created, the corresponding Mediator is also constructed. The BackgroundMediator looks something like this:

public class BackgroundMediator extends Mediator
{
	[Inject]
	public var view:Background;

	override public function onRegister():void
	{
		...
	}
}

The DisplayObject Background is injected into the BackgroundMediator when it is constructed. The DisplayObject can then be manipulated. It is an elegant solution that separates display object from UI logic.

addChild(new Background());

is all that is needed to wire the entire DisplayObject/Mediator combination.

RobotLegs & Tickers

The RobotLegs structure is particularly useful for wiring up a Ticker-based application because one ticker can be mapped as a singleton, and injected to all those methods that need to iterate every frame:

public class PongContext extends Context
{
	override public function startup():void
	{
		...
		injector.mapSingletonOf(Ticker, EnterFrameTicker);
		...
	}
}

RobotLegs with as3signals

I really like the style of event dispatching that as3signals provides, but my implementation demands that if one class requires another class then it must flag it to be injected, and then the signal wired up in a method flagged as [PostConstruct] (see GeometryModel). This doesn’t feel like the right solution.

Integrating as3signals with RobotLegs is beyond the scope of this article. It looks like Joel Hooks and Robert Penner are making inroads into this problem through as3signals’ Google Group. I look forward to seeing what they come up with.

  • Pingback: Tweets that mention RobotLegsPong Part 1 – Overview at AlecMcE.com -- Topsy.com

  • Anonymous

    What’s this about AS3 interfaces being slow? I’ve never heard that, and Google didn’t immediately enlighten.

    • http://alecmce.com Alec McEachran

      That was a throw away line wasn’t it? I don’t have evidence to back me up right now, but I am pretty sure I can get it. I’ll look into it this evening. I am certain that in the past, when looking for optimisations, removing interfaces has led to a marginal speed improvement.

      If this is not the case, no longer the case, or I am having a memory failure then I can only apologise. It is one of those things that I have taken for granted for a long while.

      Thanks for picking me up on it shovemedia.

    • Anonymous

      I covered interfaces in my article on all sorts of function calls. Calling through an interface was ~50% slower than calls through classes.

      • http://alecmce.com Alec McEachran

        Thanks Jackson. I could have sworn that this was true. I’m glad you can back this up – it saves me a few hours this evening with Grant Skinner’s Performance Harness!

  • shovemedia

    What's this about AS3 interfaces being slow? I've never heard that, and Google didn't immediately enlighten.

  • http://alecmce.com alecmce

    That was a throw away line wasn't it? I don't have evidence to back me up right now, but I am pretty sure I can get it. I'll look into it this evening. I am certain that in the past, when looking for optimisations, removing interfaces has led to a marginal speed improvement.

    If this is not the case, no longer the case, or I am having a memory failure then I can only apologise. It is one of those things that I have taken for granted for a long while.

    Thanks for picking me up on it shovemedia.

  • jacksondunstan

    I covered interfaces in my article on all sorts of function calls. Calling through an interface was ~50% slower than calls through classes.

  • http://alecmce.com alecmce

    Thanks Jackson. I could have sworn that this was true. I'm glad you can back this up – it saves me a few hours this evening with Grant Skinner's Performance Harness!

  • http://twitter.com/robpenner Robert Penner

    Where’s Part 2? =)

    • http://alecmce.com Alec McEachran

      Ha, never got ’round to it. In my defence, I did just move to the USA. I have a draft of some stuff, I’ll get it written eventually! :-)

  • http://twitter.com/robpenner robpenner

    Where's Part 2? =)

  • http://alecmce.com alecmce

    Ha, never got 'round to it. In my defence, I did just move to the USA. I have a draft of some stuff, I'll get it written eventually! :-)

  • Pingback: Weekly Digest for March 23rd

  • Tim

    Hi, I know you posted this some time ago. I’m very interested in how to run the tests when using robotlegs. Sorry to be thick, but I can’t seem to get the tests to pass. I created a test runner .fla and can’t seem to get the source paths etc. working. Is there some trick to running the tests when using this type of setup? Do I have to be using some IDE like Flex or Eclipse? Following all of the AsUnit examples works when I setup a simple project, but in projects that are similar to this one, I’m having no luck.

    Any advice would really be appreciated.

    Thanks

    Tim

    • http://alecmce.com Anonymous

      Hi Tim,

      I haven’t ever run the tests through Flash, I use FDT and it’s certainly easier to setup tests to run that way. However, it should certainly be possible to run the tests through Flash. You will need to set the document class of an empty FLA to PongTestRunner.as and ensure that the three SWCs in the lib folder are added to your library path. Have a go, if you’re still having trouble ping me back and I’ll give it a go

  • Tim

    Hi, I know you posted this some time ago. I’m very interested in how to run the tests when using robotlegs. Sorry to be thick, but I can’t seem to get the tests to pass. I created a test runner .fla and can’t seem to get the source paths etc. working. Is there some trick to running the tests when using this type of setup? Do I have to be using some IDE like Flex or Eclipse? Following all of the AsUnit examples works when I setup a simple project, but in projects that are similar to this one, I’m having no luck.

    Any advice would really be appreciated.

    Thanks

    Tim

    • http://alecmce.com Alec McEachran

      Hi Tim,

      I haven’t ever run the tests through Flash, I use FDT and it’s certainly easier to setup tests to run that way. However, it should certainly be possible to run the tests through Flash. You will need to set the document class of an empty FLA to PongTestRunner.as and ensure that the three SWCs in the lib folder are added to your library path. Have a go, if you’re still having trouble ping me back and I’ll give it a go