A Simple Game Engine Using Random – taterboy
December 1st, 2008 | Filed under: ActionScript 3, Flash, Flex, Games, Tutorials
This sample is not a single solution but an approached I used in developing Whacky Voting - a politically smashing game. It uses random with some strict rules to get rid of sequences that some players can easily remember, yet keeping game play consistent from one game to the next.
This demo starts with a configuration section, used to set up how we want the game to act. The engine it self could be organized a little better to be more transportable. It is commented pretty well though, so it should be easy enough to understand the approach as well as practical usage. Random is used to shuffle all of the objects that appear on screen as well as what container the objects are associated with.
Configuration:
We use config data for each game so that we can easily tweak game-play without hacking our code later. We can use arrays, xml or other data sources. Plan ahead and build config options into your game as it is developed.
This array designates slots or containers to hold each appearing object. Use this to limit how many objects appear at a time and where they are placed on screen. this can represent MovieClips or other classes.
var objHolderArr:Array = []; objHolderArr[0] = {obj:"object0",id:0,stat:false,timer:20,type:"",score:0,targ:null,xCoor:10}; objHolderArr[1] = {obj:"object1",id:1,stat:false,timer:20,type:"",score:0,targ:null,xCoor:90}; objHolderArr[2] = {obj:"object2",id:2,stat:false,timer:20,type:"",score:0,targ:null,xCoor:170}; objHolderArr[3] = {obj:"object3",id:3,stat:false,timer:20,type:"",score:0,targ:null,xCoor:250}; objHolderArr[4] = {obj:"object4",id:4,stat:false,timer:20,type:"",score:0,targ:null,xCoor:330}; objHolderArr[5] = {obj:"object5",id:5,stat:false,timer:20,type:"",score:0,targ:null,xCoor:410}; |
Next is an array of objects to appear, this is to control what type of objects appear and to make sure they are nicely distributed through out each level. You may have different scores, wilds and/or bonus score objects that need to get incorporated into the object list, but using random by it self will not allow them to be distributed appropriately for consistent game play.
var objSequenceArr:Array = []; objSequenceArr[0] = {obj:"type0", count:20,score:100}; objSequenceArr[1] = {obj:"type1", count:15, score:150}; objSequenceArr[2] = {obj:"type2", count:5, score:500}; objSequenceArr[3] = {obj:"type3", count:5, score:1000}; objSequenceArr[4] = {obj:"null", count:5, score:0}; //timer increment for the engine to trigger an event var timeInc:int = 500; // integer to keep track of increment of game engine var arrLoc:int = 0; // random key var randomKey:int = 1000; // time for each object to stay visible; var delay:int = 4; |
It is good to set game status variables to allow games controls to be a little more intuitive. Sometimes I use a gameStatus string variable and a string to denote the current status. That way I can get more mileage out of one variable.
var gameOver:Boolean; //or var gameStatus:String = "_play"; |
Build Config Data for Engine:
The current level array built from config data above. The method then adds to the level array all the objects that we want to appear during the game.
var levelArr:Array = []; function buildConfigData():void{ for(var p:String in objSequenceArr){ var loopCnt:int = objSequenceArr[p].count; for(var i:uint = 0; i < loopCnt; i++){ var randNum:int = randomNumber(randomKey); levelArr.push({obj:objSequenceArr[p].obj, score:objSequenceArr[p].score, key:randNum}); } } levelArr.sortOn("key",Array.NUMERIC || Array.DESCENDING); } buildConfigData(); |
Last, we sort the list so that it is shuffled which gives us the first random effect. All the objects are placed in the array in order of the sequence array, but we added a random number as the key to sort the array with. Now all of the objects will be randomly placed throughout the array and we will always have the right amount of objects for consistent game play.
Game Engine:
Now that we have our data, we are ready to start the engine.
1. Pull first random number
2. Loop until random number is not a slot that is already taken. We add a break and status variable to stop the loop if we can not find an available object. On a break, we will wait until the next TimerEvent and try again.
3. We have an active slot that is not already taken, we set it’s status to true and show and configure our object.
4. Use this timer event to also run our Active Checker Method below. The more you can piggy-back off of single timers and EnterFrame events the better.
var timer:Timer = new Timer(timeInc,0); timer.addEventListener(TimerEvent.TIMER, timerHandler); timer.start(); function timerHandler(ev:TimerEvent):void{ //1. var randSlot:int = randomNumber(objHolderArr.length); //2. if(arrLoc < levelArr.length){ if(levelArr[arrLoc].obj != "null"){ var breakCnt:int var breakStatus:Boolean; while(objHolderArr[randSlot].stat == true){ randSlot = randomNumber(objHolderArr.length); breakCnt ++; if(breakCnt >= 100){ breakStatus = true; break; } } if(!breakStatus){ //3. var slotObj:Object = objHolderArr[randSlot]; slotObj.stat = true; slotObj.timer = delay; slotObj.score = levelArr[arrLoc].score; slotObj.type = levelArr[arrLoc].type; //******* add code here to initiate your visual objects. var tempBO:ballObj = new ballObj(); tempBO.x = slotObj.xCoor; tempBO.y = 20; tempBO.objIDStr = arrLoc.toString(); tempBO.objScoreStr = levelArr[arrLoc].score; tempBO.objTypeStr = levelArr[arrLoc].obj; addChild(tempBO); //********* end visual code here. slotObj.targ = tempBO; activeObjArr.push(slotObj); // we advance the array location variable. arrLoc ++; } } else{ arrLoc ++; } } //4. activeChecker(); } |
After each object is initiated, we need to give them an amount of time to be active. This is the activeObject array, we will track the time, if the user does not score with the object then it will be removed by the timer.
1. Loop through the active array to decrease each object’s timer.
2. If the timer is run out, remove the object from the array and reset the object’s status.
var activeObjArr:Array = []; function activeChecker():void{ for(var p:String in activeObjArr){ //1. activeObjArr[p].timer --; //2. if(activeObjArr[p].timer <= 0){ activeObjArr[p].stat = false; removeChild(activeObjArr[p].targ); activeObjArr.splice(int(p),1); if(arrLoc >= levelArr.length && activeObjArr.length == 0){ //game is over. gameOver = true; timer.stop(); btn.enabled = true; btn.alpha = 1; } } } } |
Here is where you can place your event handlers for a user interactive with an object. We have to remove and reset the object, shown above and add the objects score to the main score.
We will also need a method for when the game clears the last array object.
Utilities:
method to pull random numbers just so I do not need to write this same code more than once.
function randomNumber(limit:int):int{ var randNum:int = Math.random()*limit; return randNum; } |
Controls:
A restart control availble when game is over
btn.addEventListener(MouseEvent.CLICK,clickHandler); btn.enabled = false; btn.alpha = 0.2; function clickHandler(ev:MouseEvent):void{ if(gameOver){ // rebuild data to show off shuffling. levelArr = []; buildConfigData(); //reset game controls and game engine. arrLoc = 0; timer.start(); gameOver = false; btn.enabled = false; btn.alpha = 0.2; } } |
The Ball Object:
The ball object is a simple class that displays the data for each object. You would normally place a button or some other way to capture user interaction and send it back to the engine to initiate scores and feedback.
package{ import flash.display.MovieClip; import flash.text.TextField; import flash.events.Event; public class ballObj extends MovieClip{ public var objIDStr:String = ""; public var objTypeStr:String = ""; public var objScoreStr:String = ""; public function ballObj():void{ init(); stop(); } private function init():void{ this.addEventListener(Event.ADDED,addedHandler); } private function addedHandler(ev:Event):void{ objID.text = objIDStr; objScore.text = objScoreStr; objType.text = objTypeStr; } } } |
|
RSS feed for comments on this post. TrackBack URL
I’m really very thankful for your help. I still have some problem that require your explanation. Firstly, if i only have an item for my game, do i still need to do the random part? If my game include clicking on the to have the score recorded, do i put the code in the As file or the fla file?As for the part building config data for engine i really do not understand. Really my apologise because i’m quite a newbie to AS3. Even my AS2 isn’t that good either. Once again, really thankful of your help.
Comment by June — December 2, 2008 @ 9:41 pm
Sorry about this. My last question. do i put all the code in the fla and AS file, it will work?Because i would like to solve it out if ii could do the ading of the function clicking of the bug myself. Thank you. Because when i put it in it will have errors. thank you and sorry about it
Comment by June — December 2, 2008 @ 9:49 pm
If you only have one bug, you may need the random stuff, there is nothing to randomize except for where and when it shows up. If you do not need it, skip it.
The configuration code works for this sample and may not work for your game, but the concept is still valid. The idea is to have controls built into your code, so that you can tweak things during testing. A simple example would be a speed variable.
Say you test the game and it is too fast or too slow, instead of searching through the code and finding all the places that you hard coded the speed, you could have one speed variable. By changing one number, you can effect how the game plays until it feels right. As the games get more complex, you can add more configuration hooks into your code. But do not let this hold you up. It does not stop the game from working, it is a good practice, but not essential.
In the sample, I can add remove, objects, changes scores, control how long they stay on screen all in the config section of the code. try messing with the delay and timeInc variables.
For the clicking event. In this example code you could add it into this area
//******* add code here to initiate your visual objects.
var tempBO:ballObj = new ballObj();
tempBO.x = slotObj.xCoor;
tempBO.y = 20;
tempBO.objIDStr = arrLoc.toString();
tempBO.objScoreStr = levelArr[arrLoc].score;
tempBO.objTypeStr = levelArr[arrLoc].obj;
addChild(tempBO);
just add a line.
tempBO.addEventListener(MouseEvent.CLICK, clickHandler);
Then add a function called clickhandler.
function clickhandler(ev:MosueEvent)
{
//handle click event
}
If there are issues with the click event not firing consistently when clicking on the bug, then you may need to add a button into the Ball Object (action script file),
(You will have to import any classes for the button or SimpleButton) Add an event listener to the button, then in the handler add this line. dispatchEvent(new Event(”BUG_EVENT”,true);
Then back in the fla add a listener, addEventListener(”BUG_EVENT”,bugHandler); then add a function called bugHandler,
Sorry if I am throwing too much info at you each time, if you need more specific answers it really helps to see some code or have more details for the game play. If you want to share that with me offline, use the contact link at that top of the page and I will get back to you through email.
Comment by taterboy — December 2, 2008 @ 11:47 pm
I re-read your first comment from the other post, then did some code updates, you can download a new source file here.
http://www.taterboy.com/blog/downloads/GameRandom2.zip
1. Added more to the objHolderArr, so it is a three row/six column grid. This is a visual gird that is easier for us visual people to understand.
2. Added the button into the ball Object with the dispatch event code, this is really the best way to handle the click.
3. Added some score code and the code to remove the ball when it is clicked. It is now almost a fully functioning game.
You will notice that code to remove the ball is almost an exact copy of most of the code from the activeChecker function. Normally I would make a new function with just the code that is the same and call it from both places. this will help if there is a bug or an update to keep the code clean. Again, this is nice to have, a best practice but will not make or break the game.
I know, just get it to work for now, you can clean up the code later, nuff said.
Good luck.
Comment by taterboy — December 3, 2008 @ 11:06 am