Integrating Brass Monkey into Waste Invaders
Have you had a chance to check out the gaming.adobe.com site? If not, you’re missing some great showcase apps and resources for game developers working with Flash and AIR. One of these resources is a game called Waste Invaders which also has source code available. http://gaming.adobe.com/getstarted/
Waste Invaders is a great web and mobile example of a Stage3D top down airplane shooter game. It would be cool to be able to control the web version with a D-pad type controller on a mobile device. This is where Brass Monkey comes into play.
Quote from http://playbrassmonkey.com/ – “Your phone is the controller, your browser is the console”.
So what is Brass Monkey? It is an application that consumers download only once on their phones. When running the application on the phone it displays any applications that are on the same network that have registered themselves. The user is given a list of these applications and can select one to join. Once the consumer clicks to join a game/application the game itself then sends down to the consumers phone what the controller will look like. The developer of the application has a full range of capabilities to build out controllers for different devices and uses.
Actually playing the game will give you a better understanding of what I am talking about. Follow these steps to give it a try:
- First go to http://playbrassmonkey.com/ and download the mobile application to your specific mobile phone device. You can also look for Brass Monkey on mobile app stores.
- Launch the Brass Monkey application on your mobile phone.
- In a browser navigate to this URL: http://renaun.com/flash/wasteinvadersbm/
- In the Brass Monkey application you should see “Waste Invaders Flash” show up in the connections list. Click on this list item.
- Once connected the game will start on the browser and you should see a D-pad and red button on your phone. Start playing!
http://playbrassmonkey.com/developers is where you can get the SDK for HTML5, Unity, and Flash to build in support for you application. To give you a better idea of how this is done let me walk you through how I integrated Brass Monkey into Waste Invaders.
Overview
Brass Monkey handles all the connections, game matching issues, and getting the controller application to connect with your game. All you have to do is register your application with their service and send down the controller scheme(s) to the players. The controller scheme is basically a way to push down a visual UI that displays on the phone and returns input data. Mobile phones by default have a rich set of input mechanisms perfect for gaming, including the accelerometer, multi-touch input, compass, and even gyroscope. As a game developer what you are doing is asking the device to basically send different input data to your game and providing Brass Monkey with the UI to help give the data context when it comes back.
Think of it like a dumb terminal that just takes the raw input data and sends it to your game. The controller doesn’t really care if the touch points are on the left vs right part of the mobile device, or what that input means for your game. But by providing UI for the left D-pad and red button on the right I give the consumer some context of what I’ll do with that data once it comes to my game.
To sum it up all the magic happens in your game, so you have to think about how to handle the raw input data in your game in a meaningful way.
All the code referenced below is located on https://github.com/renaun/ActionScriptExamples/tree/master/Waste%20Invaders
Registering the Game with Brass Monkey
This part is quite simple. Using their API you create a BMApplication instance, setup the event handlers, and initiate your game. Here is the code:
//It may contain the hooks the portal uses to transfer players to your game.
brassmonkey=new BMApplication(parameters);
brassmonkey.addEventListener(DeviceEvent.DEVICE_CONNECTED, onDeviceConnected);
brassmonkey.addEventListener(DeviceEvent.DEVICE_LOADED, onDeviceReady);
brassmonkey.addEventListener(DeviceEvent.DEVICE_DISCONNECTED, onDeviceDisconnected);
brassmonkey.addEventListener(TouchEvent.TOUCHES_RECEIVED, touchHandler);
brassmonkey.addEventListener(DeviceEvent.XML_UPDATED, xmlUpdatedHandler);
//set the client
brassmonkey.client=this;
//Initiate. Then setup controls.
brassmonkey.initiate("Waste Invaders Flash", 3);
Building the Controller Scheme that is Sent to the Consumers Phone
In my game I wanted a left side D-pad to move the airplane around the Waste Invaders game and a red button on the right to be the fire button. Here is what it looks like on the phone:
How do you create this? Brass Monkey provides many ways to get the UI and a bit of logic to their Brass Monkey mobile application. But first its important to understand that Brass Monkey is basically serializing your UI (they call it AppScheme
and ControlScheme
) into XML and PNG’s. Think of it as a simplified HTML browser where you tell it all the images and positions of those images. Now there is more to it than just that. They try and provide hooks to make some simple things easier like simple toggle buttons to return on a specific callback function or handling the complexity around scaling for different devices. Also, they handle things like all the data coming from different devices, or having different control schemes for different players. The possibilities are basically endless.
Once you download the SDK you’ll find a bunch of examples to get you started using Flash Pro and its class linkage capabilities to make it easier to understand how the XML and images are mapped in the serialization process. I decided to go the AS3 route which is not fully supported at this point, but with some help from the Brass Monkey team I was able to find some workarounds. In either process you want to have unique class names and the UI display list on one MovieClip. In my case it was simply a movie clip with 3 images attached, D-pad, red button up, and red button down.
Here is what it looks like in AS3 (source found here):
{
import flash.display.DisplayObject;
import flash.display.MovieClip;
public class ControlSchemeMovieClip extends MovieClip
{
[Embed(source="assets/bm/dpad200.png")]
public var dpad200Asset:Class;
[Embed(source="assets/bm/redbutton200.png")]
public var redbutton200Asset:Class;
[Embed(source="assets/bm/redbutton200Down.png")]
public var redbutton200AssetDown:Class;
public function ControlSchemeMovieClip()
{
// Make our control scheme that is going to be parsed
var mc:MovieClip = new MovieClip();
mc.name = "main";
mc.graphics.beginFill(0x000000);
mc.graphics.drawRect(0, 0, 640, 440);
mc.graphics.endFill();
addChild(mc);
var b:DisplayObject = new dpad200Asset() as DisplayObject;
b.name = "dpad";
b.x = 0;
b.y = 120;
addChild(b);
// The "Down" part of this name is important for some BrassMonkey parsing magic
b = new redbutton200AssetDown() as DisplayObject;
b.x = 440;
b.y = 120;
addChild(b);
b = new redbutton200Asset() as DisplayObject;
b.name = "fireButton";
b.x = 440;
b.y = 120;
addChild(b);
}
}
}
You’ll notice that I give the main parts names with the .name
property. The Brass Monkey SDK uses these to map ID’s in their XML and function callbacks. I mentioned the toggle button magic, well if you have a class name like redbutton200Asset
with another same class name + prefix of Down (redbutton200AssetDown
) it will consider it a button and magically try and call a function on the client object passed to the BMApplication
instance. The function name that will be called is the .name
value of the first class; in this case I have to have a function called fireButton
on my client object.
To get this MovieClip into the Brass Monkey XML and image format they provide some helper parsing classes. First I need to define how big the MovieClip is on my side and the scale factor width and height. Then it knows how to scale and render it correctly on the different devices. Here is the code in BrassMonkeyManager that makes this happen:
var tc:ControlSchemeMovieClip = new ControlSchemeMovieClip(); // Create instance
StageScaler.LONG = CONTROL_WIDTH; // Scaler options
StageScaler.SHORT = CONTROL_HEIGHT;
controlScheme = BMControls.parseDynamicMovieClip(tc,false, true, "landscape", CONTROL_WIDTH, CONTROL_HEIGHT);
// Validate it and register to be sent to connected devices
brassmonkey.session.registry.validateAndAddControlXML(controlScheme.toString());
Pretty simple, I created an instance of my custom class and had BMControls
parse it out into the AppScheme
class controlScheme
.
Handling the Raw Input Data
I wanted the D-pad to move to the initial touch coordinate and then use the relative coordinates from the initial touch point to move the airplane around.
Lets talk about the code below, which demonstrates how the logic works for the left D-pad.
for (var i:int = 0; i < event.touches.touches.length; i++)
{
touch = event.touches.touches[i];
// A touch on the left side
if (!hasPressedDpad && touch.phase == TouchPhase.BEGAN && touch.x < CONTROL_WIDTH/2)
{
hasPressedDpad = true;
pressedDpadTouchID = touch.id;
dpadOriginalX = touch.x;
dpadOriginalY = touch.y;
if (safeToUpdate)
{
safeToUpdate = false;
BMImage(controlScheme.getChildByName("dpad")).rect.x = (touch.x-90)/CONTROL_WIDTH;
BMImage(controlScheme.getChildByName("dpad")).rect.y = (touch.y-90)/CONTROL_HEIGHT;
brassmonkey.session.updateControlScheme(brassmonkey.session.registry.getDevice( event.deviceId), controlScheme.pageToString(1));
}
}
else if (hasPressedDpad && pressedDpadTouchID == touch.id && touch.phase == TouchPhase.MOVED)
{
dpadDeltaX = touch.x - dpadOriginalX;
dpadDeltaY = touch.y - dpadOriginalY;
//trace("touch["+touch.id+"]" + touch.phase + ": " + touch.screenWidth+"/"+touch.screenHeight + " - " + touch.x+"/"+touch.y);
}
else if (hasPressedDpad && pressedDpadTouchID == touch.id && touch.phase == TouchPhase.ENDED)
{
hasPressedDpad = false;
pressedDpadTouchID = -1;
}
}
This logic is inside the function that handles all the touch events. Remember we are just getting raw input data and we have to do something with it inside the game. I first check to see if it’s a TouchPhase.BEGAN
event and if it’s on the left side of the controller. Any touch points on the right would be the red button in my case. I go ahead and setup some flags to make sure I’m tracking on data from the initial touch down event. I record the movements of the new events as deltas to use in the game logic. The last part you’ll notice is the safeToUpdate
section. This is where we send new coordinates to the controller of where to place the D-pad image.
Integration with Waste Invaders
Luckily the Waste Invaders game was created with good abstraction and it was easy to hook in the X/Y movement and firing logic into my BrassMonkeyManager class.
Here are some of the code highlights:
bmManager = new BrassMonkeyManager(loaderInfo.parameters, this);
...
// Provide hooks into the engine and vice versa, this is a lot of coupling but find for my demo
shootyEngine.brassMonkeyManager = bmManager;
bmManager.shootyEngine = shootyEngine;
And then in the ShootyEngine
:
if(isDesktop && isBrassMonkeyControlled)
{
// If pressed use deltas to move airplane
if (brassMonkeyManager.hasPressedDpad)
{
var d:Number = (brassMonkeyManager.dpadDeltaX/100) * 10;
if (player.targetPosition.x+d <= stage.stageWidth-80 && player.targetPosition.x+d >= 0)
player.targetPosition.x += d;
d = (brassMonkeyManager.dpadDeltaY/100) * 10;
if (player.targetPosition.y+d < stage.stageHeight-80 && player.targetPosition.y+d >= 0)
player.targetPosition.y += d;
}
}
And in the BrassMonkeyManager
I put the hook for toggling the fire button:
* BrassMonkey magic callback function for resources that show up as button
*/
public function fireButton(button:String):void
{
isFiring = !isFiring;
shootyEngine.changePlayerFiringState(isFiring);
}
Conclusion
The BrassMonkey SDK is actually quite easy to integrate into your games. The ControlScheme
took me a bit of time to wrap my head around, but after understanding what was going on it is pretty cool to have so many options and so much control of one app on the consumers devices without having to get users to download specific controller apps for each game I potentially would make. This definitely makes me think of controller companion apps in a whole new light.