11th Mar 2011

Tip #5 – Configure your games in real-time

How often have you been faced with the situation where-by you’ll test your game. Then need to tweak the value of one or more variables (perhaps sometimes only by a small amount) and then test the game again to see if it had the desired effect. If you are like me you’ll end up repeating this process perhaps several hundred times over the course of development.

There is an easier way – and although it won’t work for ALL genres, it could save you some precious time in the long run.

In-game variable editor

The tip is simply to build a panel into your game that allows you to edit key variables on the fly.

Here is a screen shot of such a panel in our game Chickaboom:

The grey panel at the bottom contains controls allowing us to modify variables in real-time, such as:

The quantity of birds in the level

The speed at which they move

The size of them

The maximum size gum bubble you can blow

The number of birds you need to pop to complete the level

The amount of air with which you have to blow the gum bubbles

There is also a Text Area window and a button to restart the level with the new settings. Each time you modify something it generates AS3 source code into the TextArea, which we then cut and paste into the level data for the game.

The panel is draggable, to stop it obscuring the game, and can be hidden and displayed with a single key press.

Minimal Comps to the rescue

Creating the panel was extremely easy. Rather than mess with the complexities of Flash UI components, we turned to the ever expanding set of Minimal Components released by Keith Peters over at Bit101.

Minimal Comps comes as a source package or an SWC, and has enough documentation to get you started quickly.

Here is another debug panel, this time from a game called Kingdums (work-in-progress title!) which is being built with Flixel:

This panel is slightly different in that it’s mostly used to display debug information about the game. In this case the tile you are currently over, and the actions of the AI players. While I could (and do!) trace this to the Output panel in FlashDevelop as well, it means that when Ilija tests the game (which he does by just running the swf) he gets to see the log too, which he’d otherwise not be able to do without compiling it via FlashDevelop for himself.

There are two values at the bottom which can be tweaked in real-time to alter how aggressively the AI plays.

Here is the full code from Kingdums that creates the panel seen above:

package { import com.bit101.components.Label; import com.bit101.components.NumericStepper; import com.bit101.components.PushButton; import com.bit101.components.TextArea; import com.bit101.components.Window; import com.bit101.components.Text; import flash.events.Event; import org.flixel.FlxState; public class DebugPanel extends Window { public var gridX:Label; public var gridY:Label; public var gridIndex:Label; public var owner:Label; public var village:Label; public var soldiers:Label; public var mountain:Label; public var castle:Label; public var traceLog:TextArea; private var player2AttackPercentage:NumericStepper; private var player3AttackPercentage:NumericStepper; public function DebugPanel() { super(null, 310, 410, "Kingdumbs"); this.width = 320; this.height = 220; gridIndex = new Label(this, 8, 8, "Grid Index:"); gridX = new Label(this, 100, 8, "Grid X:"); gridY = new Label(this, 200, 8, "Grid Y:"); owner = new Label(this, 8, 24, "Owner:"); soldiers = new Label(this, 100, 24, "Soldiers:"); castle = new Label(this, 200, 24, "Castle:"); traceLog = new TextArea(this, 8, 48, ""); traceLog.width = 300; traceLog.height = 100; traceLog.selectable = true; var p2l:Label = new Label(this, 8, 154, "P2 attack if troops >"); player2AttackPercentage = new NumericStepper(this, 120, 154, updateGame); player2AttackPercentage.value = Registry.player2.soldierDiffToAttack; player2AttackPercentage.step = 0.1; player2AttackPercentage.maximum = 4; player2AttackPercentage.minimum = 0.1; var p3l:Label = new Label(this, 8, 174, "P3 attack if troops >"); player3AttackPercentage = new NumericStepper(this, 120, 174, updateGame); player3AttackPercentage.value = Registry.player3.soldierDiffToAttack; player3AttackPercentage.step = 0.1; player3AttackPercentage.maximum = 4; player3AttackPercentage.minimum = 0.1; } public function log(t:String):void { traceLog.text = traceLog.text.concat(t + " "); } private function updateGame(e:Event):void { Registry.player2.soldierDiffToAttack = player2AttackPercentage.value; Registry.player3.soldierDiffToAttack = player3AttackPercentage.value; } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 package { import com . bit101 . components . Label ; import com . bit101 . components . NumericStepper ; import com . bit101 . components . PushButton ; import com . bit101 . components . TextArea ; import com . bit101 . components . Window ; import com . bit101 . components . Text ; import flash.events . Event ; import org . flixel . FlxState ; public class DebugPanel extends Window { public var gridX : Label ; public var gridY : Label ; public var gridIndex : Label ; public var owner : Label ; public var village : Label ; public var soldiers : Label ; public var mountain : Label ; public var castle : Label ; public var traceLog : TextArea ; private var player2AttackPercentage : NumericStepper ; private var player3AttackPercentage : NumericStepper ; public function DebugPanel ( ) { super ( null , 310 , 410 , "Kingdumbs" ) ; this . width = 320 ; this . height = 220 ; gridIndex = new Label ( this , 8 , 8 , "Grid Index:" ) ; gridX = new Label ( this , 100 , 8 , "Grid X:" ) ; gridY = new Label ( this , 200 , 8 , "Grid Y:" ) ; owner = new Label ( this , 8 , 24 , "Owner:" ) ; soldiers = new Label ( this , 100 , 24 , "Soldiers:" ) ; castle = new Label ( this , 200 , 24 , "Castle:" ) ; traceLog = new TextArea ( this , 8 , 48 , "" ) ; traceLog . width = 300 ; traceLog . height = 100 ; traceLog . selectable = true ; var p2l : Label = new Label ( this , 8 , 154 , "P2 attack if troops >" ) ; player2AttackPercentage = new NumericStepper ( this , 120 , 154 , updateGame ) ; player2AttackPercentage . value = Registry . player2 . soldierDiffToAttack ; player2AttackPercentage . step = 0.1 ; player2AttackPercentage . maximum = 4 ; player2AttackPercentage . minimum = 0.1 ; var p3l : Label = new Label ( this , 8 , 174 , "P3 attack if troops >" ) ; player3AttackPercentage = new NumericStepper ( this , 120 , 174 , updateGame ) ; player3AttackPercentage . value = Registry . player3 . soldierDiffToAttack ; player3AttackPercentage . step = 0.1 ; player3AttackPercentage . maximum = 4 ; player3AttackPercentage . minimum = 0.1 ; } public function log ( t : String ) : void { traceLog . text = traceLog . text . concat ( t + " " ) ; } private function updateGame ( e : Event ) : void { Registry . player2 . soldierDiffToAttack = player2AttackPercentage . value ; Registry . player3 . soldierDiffToAttack = player3AttackPercentage . value ; } } }

I have this in the file DebugPanel.as, which is access via my Registry so is available from anywhere in the game.

You can see a function called updateGame. When one of the numeric steppers is changed, it alters the two player variables in real-time.

Here are some more examples of how I use it:

// Changes the title of the window (in this case it shows the round number) Registry.debugPanel.title = "Kingdums Round: " + Registry.round; // Updates the land details as the mouse changes the tile it's over Registry.debugPanel.owner.text = "Owner: " + land.owner; Registry.debugPanel.soldiers.text = "Soldiers: " + land.soldiers; Registry.debugPanel.castle.text = "Castle: " + land.castle; // Writes to the log window in the panel Registry.debugPanel.log("You have no adjacent land to this piece"); 1 2 3 4 5 6 7 8 9 10 11 12 // Changes the title of the window (in this case it shows the round number) Registry . debugPanel . title = "Kingdums Round: " + Registry . round ; // Updates the land details as the mouse changes the tile it's over Registry . debugPanel . owner . text = "Owner: " + land . owner ; Registry . debugPanel . soldiers . text = "Soldiers: " + land . soldiers ; Registry . debugPanel . castle . text = "Castle: " + land . castle ; // Writes to the log window in the panel Registry . debugPanel . log ( "You have no adjacent land to this piece" ) ;

I appreciate that these examples are mostly for the display of information, rather than variable tweaking. But I just wanted to show how quick and easy it was to get a panel like this thrown into your game. You can create all kinds of things with the Minimal Comps, from buttons, to select lists to sliders. Anything you need to display or modify almost certainly has a corresponding component.

Another thought is to make the panel quite generic. So if for example you are focused on tweaking bullets (speed, power, duration) then you could have 3 numeric steppers to alter those values. And when you are finished and happy your bullets feel exactly as you want them, you can re-use the steppers to alter another part of your game (rather than making the debug panel bigger and bigger)

You can also of course re-cycle your debug panel to future games.

Variable editor vs. game editor

I think it’s important to explain that this Tip is all about building a panel that allows you to edit key game values on the fly. It is something you use during build, then strip away when you’re finished. It’s a disposable tool. This is not the same thing as building a game editor, such as something that allows you to create new levels or maps, and is much more complex subject in its own right.

I’m not saying this concept is revolutionary. Not for a second. Games have had built-in editors for decades. But I did want to show that you can throw a variable editing panel into a game very easily, with minimal fuss. As long as you don’t get carried away it could save you hours of tweaking and fine-tuning as a result.

AS3 Game Object Editor

Updated: 22nd March 2011. After posting this article Damian Connolly went and created the AS3 Game Object Editor. Which uses the concepts outlined above but wraps it in a more generic package that can be applied to any class. In short you won’t need to create a custom panel per game. Give it a go over at: http://divillysausages.com/as3_game_object_editor

Visit the Flash Game Dev Tips Google Code Project page for downloads in this series.