The .zip file also contains the compiled "MonkeyGame-debug.apk" binary that can be run in the emulator. There is an HTML5 build in the folder "game.build". To test the game run the MonkeyGame.html in the Chrome browser.
Introduction
There are several RAD cross-platform game development tools/engines that have support for Android on x86 as listed here http://software.intel.com/en-us/blogs/2012/03/13/game-engines-for-android
We will be using Monkey game engine to build a new, NDK-based Android game for IntelĀ® architecture based devices. It has the advantage of having pre-compiled NativeGL shared libraries for Android on ARM and x86 architectures or we would have to use the NDK toolchain to re-compile them. You can find these in the "jni" folder in the accompanying source. The Android packager includes both the x86 and ARM shared libraries in the .apk and during deployment, installs the necessary libs depending on whether the target device is ARM or x86.
The generated .apk is therefore cross-platform compatible and doesn't need a native re-compile for the two different architectures.
Background
Memory Game is a variation on the classic game where you uncover cards two at a time to find matching pairs. When all matching pairs have been uncovered the game is over. The objective is to expose all pairs using the least number of moves. A partial deck of playing cards is used in this variation of the game.
Game Features:
- 24 cards for increased difficulty
- Animated cards with sound and particle effects
- Tween based front end
Technical Features:
- Game state class
- Collision class
- Procedural font generation class
- Autofit class using hard coded virtual resolution for scaling to differing actual device aspect ratios and resolutions - very important since Android device fragmentation is a major issue
- Tween class
- Particle engine class
- Highscore class
- Timer class
I chose this game as a pedagogic tool for this article because it contains all essential game elements and clearly demonstrates the real-time nature of the game loop and the design principles to be used. It is a 2D game because, for modern touchscreen based mobile devices, interacting with 2D sprites provides for a more intuitive interface.
Technical Information
Some requirements for rapid prototyping which the engine has:
- Fast Debug - Compile - Run cycles can be executed in any standards compliant HTML5 browser.
- The game loop is well defined with
OnCreate()
, OnUpdate()
, and OnRender()
methods. - The scripting language is object oriented. It has basic language constructs for sprite manipulation and game data structures. Automatic garbage collection.
Using the code
The GameState
variable cycles the state machine through its various stages. It is initialized at start to the STATE_MENU
. This variable also controls the update and rendering sections of code.
Method SetState (state:Int)
GameState = state
Select GameState
Case STATE_MENU
//The initial menu layer
Case STATE_PLAYING
//The game layer
Case STATE_PAUSED
//The game layer paused
Default
Print "ERROR: Unknown game state!"
End
End
Let's check for input and update game state:
In the OnUpdate()
Method we call the Card[i].Update()
Method of the Collision class. Depending on whether the pointer collides with the card sprite, it returns the visible or hidden state of the card. If two cards have been clicked we increment the tries count.
Case STATE_PLAYING
//check for two card clicks. show cards that were clicked by hiding corresponding cardback
For Local i : Int = 0 Until NUM_CARDS
Card[i].Update()
If Card[i].Pressed = True And Card[i].Visible = True And click < 2 Then
Card[i].Visible = False
savecard[click] = i
click = click + 1
If click = 2 Then
slowdown = Millisecs()
turns = turns + 1 //increment tries after two cards have been clicked
End If
PlaySound boom
End If
Next
If a matching pair is found we leave those cards uncovered else if the pair doesn't match we make the corresponding cardbacks visible and hide the cards again. The delay of 1.5 seconds permits both cards to be visible long enough for the player to see that they don't match.
If click = 2 Then
If coinplace[savecard[0]] = coinplace[savecard[1]] Then //a matching pair is found
click =0
Else
If Millisecs()-slowdown > slowlimit Then // wait before covering cards again
Card[savecard[0]].Visible = True
Card[savecard[1]].Visible = True
click =0 // reset to accept clicks on next two cards
Endif
Endif
End If
Local drawncard:Bool = False
For Local i : Int = 0 Until NUM_CARDS
If Card[i].Visible = True Then
drawncard = True
End If
Next
If drawncard = False Then Restart() // If no cards are left to uncover End game
When none of the cards will have a backcard covering it because the collision class returns backcard visible state as False, drawncard
Boolean will change state to False and the game has ended.
Shake up those cards:
A game without animations isn't much fun. Lets make the eye candy. The int variable n
does the magic. n
= -1*n
toggles n
between +1 and -1. The DrawImage()
Method has rotation and scaling parameters. We adjust the card sprite rotation between +1 degree and -1 degree to create the vibrating card effect.
//refactored because the renderscreen method appeared to work in the pause state, and halts
// game update, except for cards vibrating
Method RenderScreen()
Cls
// for a resolution of 800x600 render cards in 6 columns and 4 rows, starting at
// x offset 33 pixels and y offset 30 pixels
For Local i:Int = 0 Until NUM_CARDS
DrawImage coins[coinplace[i]], 33+((i Mod 6)*100), 30 +(Int(i/6)*140)
Next
//as n toggle between +-1, the card sprites oscillate(animation) between +-1 degree
//if collision check returns visible attribute show cardback else hide it
For Local i : Int = 0 Until NUM_CARDS
If Card[i].Visible = True
n = -1*n
DrawImage (card, Card[i].PositionX, Card[i].PositionY, n*1.0, 1.0, 1.0)
Else
DrawImage (cardclick, Card[i].PositionX, Card[i].PositionY, 0, 1.0, 1.0)
End If
Next
DrawImage (sidebar, 620, 0)
DrawImage pointer.image, pointer.x, pointer.y
font.Draw( turns, 710, 290, 3, 3, 0.5, 1.0 ) //render the procedural font for number of tries
End
The code in the download is heavily commented and requires little explanation, however class members and methods are detailed below.
MyGame
Class :
OnCreate()
Method : Instantiate game objects - images, sounds, members and variables.
OnUpdate()
Method : Poll / Interrupt check for mouse/ touchscreen/ keyboard input and change game states and object states accordingly.
OnRender()
Method : Render graphics depending on game state being Menu, Playing or Paused and the state attributes of game objects.
Collision
Class : Takes touch/ mouse co-ordinates and colliding sprites and returns a visible or hidden attribute for the corresponding sprite object.
Font
Class : This is a procedurally generated font. Tween
Class: Tweens sprite objects, easing them in or out using different equationsParticle
Class: Spawns particles on touch and "deletes" them after the particle life expiresHighscore
Class: Sorts high scores in ascending or descending orderTimer
Class: For delays and synchronization Autofit
Class : Provides a hard coded "virtual" resolution and scales the graphics to different screen resolutions (aspect ratios) without causing distortion along x,y screen space co-ordinate axes.
Black borders will appear to the left/ right or top/ bottom if
- The physical device aspect ratio is different from the "virtual" aspect ratio.
- The screen orientation is changed.
Issues Faced
For mobile devices with less horsepower I attempted to implement a particle engine because the integrated particle class is a bit heavy on resources. The compiler didn't like me creating a list of sprite objects at a particular point in the game loop! But I'm sure there's a workaround to it, given time. Finally implemented the particle class. The particle sprites are statically created in the OnCreate()
method. Instead of destroying sprite objects after the particle life expires we "remove" them by swapping them dynamically in an array.
I also sorely missed an integrated debugger.
Development and Testing
Initial development/ debugging was done in the browser for fast turnaround times.
Device testing for scaling and actual run time performance was done on a 1024x600, 7" Android tablet.
Final testing was on the x86 AVD in the Android emulator.
Although the HTML5 compile was smooth, when testing on the Android target, a null object exception was thrown when returning handles to graphics objects from the collision class, but a suitable workaround was implemented.
Screenshot - game running on x86 AVD in the Android emulator
Credits
My thanks to all the guys on the Monkey coder forums who were always ready to help out when issues popped up.
Wrapping up
The source code is there for you to play with, and I hope you can take it further in your own games on the x86 Android platform. If any clarification or explanations are required please ask. Also, if you find anything that needs correcting do let me know.
You can play around with the particle engine by changing parameters and use different tweens to see how they work.
History
10/11/2012
1.1.0
- Added highscores and timer class
8/11/2012
1.0.1
- Added tween class - the front end looks snazzier now with the start button easing into place
- Added particle engine class - clicking on a card spawns particles
5/11/2012
MemoryGame 1.0.0
To Do
- Online global high scores
- Avoid feature creep