Animated twitter visualization ready for your projects
Screenshot of the twitter reader
One of my first ever posts on this blog was regarding twitter integration in AS3. I later refined the program and used it for my Merry Twitsmas animated card which read all the tweets using a #dearsanta hashtag. I then used the same code and idea to build a custom Christmas card for the company I am working for.
I have had a lot of fun with this project that actually started off as my first AS3 project and I would like to share the source code. I have all the classes commented, stripped out some of the unnecessary bits and pieces and generally tried to make it into a neat little package. This version can be seen here, and while it features tweets that fly across the screen it can be easily adapted for them to stay on the screen and build a scene like in the Twitsmas star scape.
The project consists of a fla file, which has no code in it, only a few assets. There is a main class a Twitter class that is used to draw the content from Twitter and a Tweet class for each of the tweets that includes all the information relevant for each tweet. So feel free to download and use the full source code for the Twitter AS3 Reader. If anyone makes anything cool with it let me know and I’ll post it.
A little christmas themed twitter visualization that I cooked up. You can change the twitter hash tag that is being searched by changing the ?key=dearsanta at the end of the URL string. In this way it can be used for any language (you could put index.html?key=buonnatale to make it italian for example).
I used the very cool Snow class from FlashAndMath.com and for the background I was a bit lazy and modified a desktop background from Vladstudio. Yeah I am a bit busy these days, sorry Vlad
Anyways, I hope you all like it. When I get time I will try to do a little tutorial on the Twitter API which I used to bring in all the tweets.
Recently at work I was asked to create a project in AS3 with some very specific requirements. The idea is to create a series of number generators to help users choose Lotto numbers. There will be various generators, each using different methods of finding numbers, based on inputs from users. The special needs part is that the algorithms and functions involved in finding the numbers need to be done in Javascript. The reason is because we will be creating these generators in different environments and platforms, across Facebook and mobile, and it will be nice to have just one set of methods to call.
I haven’t had the need to call a javascript function from flash in quite a while, so I was pleasantly surprised to find the ExternalInterface class. More so when I saw how robust it was and easy to use. Here is the Actionscript for getting an array of unique lotto numbers from a range of 1-90. It is very clean and simple.
1
2
3
4
5
6
7
import flash.external.ExternalInterfacevar numbers:Numbers = newArray(); //The array we will store the numbers in//Set the numbers array to the return value of the getLottoNumbers//javascript function. We pass a variable of 7, the number of numbers we want.
numbers = ExternalInterface.call("getLottoNumbers", "7");
//PRIMARY FUNCTIONfunction getLottoNumbers(num){var numStore =new Array();
numStore = getNumStore(90);//Get a store of the numbers 1-90var numbers =new Array();for(var i=0; i<num; i++)//Get random numbers from array and remove them{var tempNum;
tempNum = randomXToY(1, numStore.length-1);//Choose random index
numbers[i]= numStore[tempNum];
numStore.splice(tempNum,1);//Remove the number from array}return numbers;//Return the array}// FUNCTIONS USED BY THE PRIMARY FUNCTION// Function to get random number between min and maxfunction randomXToY(minVal,maxVal,floatVal){var randVal = minVal+(Math.random()*(maxVal-minVal));returntypeof floatVal=='undefined'?Math.round(randVal):randVal.toFixed(floatVal);}// Returns an array full of the numbers to pick fromfunction getNumStore(maxNum){var tempArray =new Array();for(var i =1; i<= maxNum; i++){
tempArray[i]= i;}return tempArray;}
The only other thing to note is that for the Flash to be able to access the javascript, it has to be included in the html file the holds the flash.
It can be very easy to get carried away with the fun part of the game and forget all the usability stuff like the menu and game shell (the chrome on the outside of the play area). I want to very quickly discuss a bit of structure for our display list and create a simple intro animation followed by a menu (just a play button for now).
Using sprites as layers in AS3
Organising the display list
A great feature of AS3 is the display list. As we go along, we may want to add new objects to the physics engine, or bring up a pop up menu mid game. In order to make this all run smoothly, so that the right objects are always on top of each other, we are going to create blank Sprites for each of our layers now. Then when we add objects throughout the game, we can just add them to the right Sprite.
Within the class definition, before any functions, we create our layers and instantiate our Intro object (just a movieclip in the library with limited code inside)
1
2
3
4
5
6
7
8
9
// Sprite containing the background objectsprivatevar bgObjects:Sprite = newSprite();// Sprite containing the physics world and graphicsprivatevar m_sprite:Sprite = newSprite();// Sprite containing the gameShellprivatevar gameShell:Sprite = newSprite();//The game introductionprivatevar intro:MovieClip = new Intro();
So basically you can think of the first three Sprites as being a bit like photoshop layers or groups. Alone they will have no visual presence, but instead will have other sprites added to them. The last object is an instance of a movieclip in the Flash library and has a timeline, a button and a tiny bit of code.
1
2
3
4
5
6
7
8
publicfunction TileGame(){//... other code for the cameaddChild(bgObjects);addChild(m_sprite);addChild(gameShell);}
This is just a part of the instantiating function of our main class and demonstrates each of our empty Sprites being added to the display list. The last to be added will be considered to be on top of the one before. In theory, we may want to leave gameShell only to the interface of the game and make another “layer” for menus, but we can always do that later on if we feel things become complex enough to justify it.
1
2
3
4
5
6
7
8
9
10
11
12
13
privatefunction setup(e:Event=null):void{//Intro is added and setup
gameShell.addChild(intro);
intro.addEventListener(Event.ENTER_FRAME, findEnd);//Background is added and setupvar background1:Sprite = new Background();
background1.y = 100;
background1.x = 150;
background1.blendMode = BlendMode.MULTIPLY;
bgObjects.addChild(background1);}
Above, not only can you see our intro being added to the gameShell layer, but you can also see a Background MovieClip being instantiated and positioned within the bgObjects layer. Also take note of the event listener we added to the intro, as you will see next, this is designed to detect when the intro ends.
By simply detecting the end of the intro MovieClip, we make dealing with the Intro much cleaner. We don’t have to pass variables back and forth between our main class and this imported MovieClip. We can do whatever we want in the Intro, and when we are done, just let the end of the clip play out. This function above will detect when the timeline ends, remove the intro, and continue the program.
With this system in place, making our button work in the Intro MovieClip is as easy as putting a stop() command in before the end of the timeline, and a play() command on the button.
I would like to break away from games and physics engines for this post to demonstrate something new that I learned last week at work. I had to use the new YouTube AS3 API to import video hosted on YouTube, into the clients site. They didn’t want the standard YouTube player interface, but something a lot simpler and styled to the website.
The API is very easy to use, you don’t even need to import custom scripts into your code. All you need to do is load in the chrome less player from YouTube’s site with Flash’s Loader Class and you will then have access to all the functions and properties of the player. The most important thing to note though, is that since this is AS3, you need to the YouTube domain security clearance in order to be able to communicate between your swf, and the one you load in from YouTube. Let’s take a look at the code.
package{//The usual importsimportflash.display.*;importflash.net.URLRequest;importflash.events.*;//Security to allow communication with the YouTube swfimportflash.system.Security;//A great Tween package that I use all the timeimport com.greensock.TweenLite;publicclass YouTubeVideo extendsflash.display.MovieClip{privatevar _player:Object;//The playerprivatevar _loader:Loader;//The loader to get the player//Three buttons: Pause, Play and Fullscreenprivatevar _fullButton:SimpleButton;privatevar _pauseButton:SimpleButton;privatevar _playButton:SimpleButton;privatevar _controls:Sprite;//A sprite to hold the controlsprivatevar _blank:Sprite;//A sprite to act as a detect area for the controlspublicfunction YouTubeVideo(){//Allow coms between our swf and YouTube'sSecurity.allowDomain("www.youtube.com");//Load in the chromeless YouTube player swf
_loader = newLoader();
_loader.contentLoaderInfo.addEventListener(Event.INIT, onLoaderInit);
_loader.load(newURLRequest("http://www.youtube.com/apiplayer?version=3"));}privatefunction onLoaderInit(event:Event):void{addChild(_loader);//Add the loader to the display list
_player = _loader.content;//Load the content of the loader into a player object//Set up a bunch of event listeners on the player to handle any changes
_player.addEventListener("onReady", onPlayerReady);
_player.addEventListener("onError", onPlayerError);
_player.addEventListener("onStateChange", onPlayerStateChange);
_player.addEventListener("onPlaybackQualityChange", onVideoPlaybackQualityChange);}privatefunction onPlayerReady(event:Event):void{// Once this event has been dispatched by the player, we can use// cueVideoById, loadVideoById, cueVideoByUrl and loadVideoByUrl// to load a particular YouTube video.
_player.loadVideoById("YOUTUBE_ID",0,"default");// Set appropriate player dimensions for your application
_player.setSize(533,300);//Add controls to the display list
_controls = newSprite();addChild(_controls);//This invisible sprite gives us an area of interaction for the controls//which we can use to fade out the controls when the mouse leaves.
_blank = new Blank();
_blank.width = 485;
_blank.height = 240;
_blank.x = 30;
_blank.y = 30;
_controls.addChild(_blank);//Create the three buttons we want for the interface and add//mouse click event listeners to them
_fullButton = new FullScreenButton();
_fullButton.addEventListener(MouseEvent.CLICK, onFullScreenClick);
_fullButton.x = 40;
_fullButton.y = 240;
_controls.addChild(_fullButton);
_pauseButton = new PauseButton();
_pauseButton.addEventListener(MouseEvent.CLICK, onPauseClick);
_pauseButton.x = 240;
_pauseButton.y = 120;
_controls.addChild(_pauseButton);//Play button isn't added to the display list as the video is already playing//and so only the pause button can be seen
_playButton = new PlayButton();
_playButton.addEventListener(MouseEvent.CLICK, onPlayClick);
_playButton.x = 240;
_playButton.y = 120;//Events to detect if the mouse is hovering over the controls
_controls.addEventListener(MouseEvent.ROLL_OUT, onMouseOut);
_controls.addEventListener(MouseEvent.ROLL_OVER, onMouseOver);}privatefunction onMouseOver(e:MouseEvent):void{//When the mouse is over the controls, fade them up using the//custom Tween class we already imported
TweenLite.to(_playButton,.6,{alpha:1});
TweenLite.to(_pauseButton,.6,{alpha:1});
TweenLite.to(_fullButton,.6,{alpha:1});}privatefunction onMouseOut(e:MouseEvent):void{//And fade them down when the mouse leaves
TweenLite.to(_playButton,.6,{alpha:0});
TweenLite.to(_pauseButton,.6,{alpha:0});
TweenLite.to(_fullButton,.6,{alpha:0});}privatefunction onFullScreenClick(e:MouseEvent):void{//This function toggles the fullscreen on and offif(this.stage.displayState == StageDisplayState.FULL_SCREEN){this.stage.displayState=StageDisplayState.NORMAL;//Actions for going to normal mode from full screen}else{this.stage.displayState=StageDisplayState.FULL_SCREEN;//Actions for going to full screen mode, for example,//masks will need to be turned off in full screen mode.}}privatefunction onPauseClick(e:MouseEvent):void{//When pause is clicked, we pause the video and swap//the pause button for the play button.
_player.pauseVideo();
_controls.removeChild(_pauseButton);
_controls.addChild(_playButton);}privatefunction onPlayClick(e:MouseEvent):void{//When play is clicked, we play the video and swap//the play button for the pause button.
_player.playVideo();
_controls.addChild(_pauseButton);
_controls.removeChild(_playButton);}privatefunction onPlayerStateChange(event:Event):void{//Test if the video is finished, and turn the play button on if trueif(_player.getPlayerState() == 0){
_controls.removeChild(_pauseButton);
_controls.addChild(_playButton);}}}}
While I am sure you can learn all the details you need from the documentation page for the YouTube AS3 API, I will quickly describe a few of the functions. As you can see, pausing and playing the video is as easy as calling the playVideo() and pauseVideo() functions. You can seek to any point in the timeline usingseekTo(seconds:Number, allowSeekAhead:Boolean) and set the volume with setVolume(volume:Number). Starting to get the picture? It is very straight forward and offers a great alternative to hosting your own videos.
Just a quick post to share something that I am going to be experimenting with over the next few weeks (whenever I get a chance). It is a website called PlayerIO which hosts all sorts of services for Flash game developers for free. They will host your databases for games scoreboards, they’ll store your files, but more interestingly you can get your own access to a server capable of executing custom server side game logic. In short, since instances of Flash can’t communicate with each other by themselves, they give you the server capable of sending and receiving messages between them. Setting up and maintaining a server like this by your self would be a costly and time consuming venture, so this is really very good news.
This is gonna require some C#
The only downside could be the fact that the server only communicates in full programming languages like C#. I think, this is kinda cool though, as it gives us all an incentive to learn a bit. Luckily, it is very similar to AS3, and depending on the complexity of the game, you can get away with only a little C# just to pass the messages back and forth between the Flash clients.
An idea for a turn based Petanque game
Luckily for us, there is a very comprehensive AS3/C# tutorial already on the site. I am going to be experimenting with this site and trying to make a turn based Pétanque style game with my physics engine. I can create terrain and obstacles with my engine, and then give the players turns to take shots. Each turn the player chooses an angle and velocity of shot to try to get as close as possible to the goal (in the case of Petanque it is the Jack).
In my last post I showed off round one of the design for the game. While I am sure that the design will go through many rounds of changes before it is ready, now it a good time to explore a method for getting those graphics into the game engine. I am going to start with the static objects, as they are the simplest to implement. Because the static objects don’t move once they are placed I can simply place the graphic in the right place when creating the physics object.
Separating code from design
The library of objects
Because this is the first encounter with any type of graphics, I want to make sure I do it right. I want to create a clear line between the world of the flash designer and the world of the flash developer. Obviously using Flex would create this division for me, but within flash alone, I find it helpful to think of the Library as the designer’s territory. In this way, we can still keep the stage clean of anything (design or code).
I have created a library of 2 types of static block and 2 types of ramp. Each has a name beginning with an underscore which I do to distinguish export objects from the others. They have also been given export names which basically attaches a blank class to the movieclip. In this way, the objects can be instantiated with some pretty simple code.
1
2
var block1:Sprite = new BlockType1();// Create a block as a SpriteaddChild(block1);//Add the block to the display list
Let’s make this a bit more object oriented
At the moment, all of our object creation is in the TileGame.as class. What would be better, instead of a messy block of code in the TileGame.as class every time an object needs to be created, would be a call to an external class. Ultimately, we want to be able to create our objects with pieces of code like this.
124
125
126
127
128
129
130
131
case"1"://When we want to create a blockvar block:Block = new Block(levelB, i, j, m_physScale,1, STD_FRICTION, STD_RESTITUTION,this);
m_sprite.addChild(block);break;case"2"://When we want to create a rampvar rampa:Ramp = new Ramp(levelB, i, j, m_physScale,1, STD_FRICTION, STD_RESTITUTION,this);
m_sprite.addChild(rampa);break;
Isn’t that a lot simpler than the big block of code we had before. Basically, we are creating a new Block or Ramp object, and passing the necessary variables through the object instantiation. To better understand these variables, lets have a look inside the block class, which is defined in the seperate actionscript file Block.as.
publicclass Block extendsSprite{privatevar body:b2Body;//The body we want to attach the block toprivatevar base:Object;//The base object or root of this objectprivatevar row:int;//The row this tile is to be placed atprivatevar column:int;//The column this tile is to placed atprivatevar m_physScale:int;//The physics scale of the worldprivatevartype:int;//The type of block. This is used for placing different graphics.privatevar bFriction:Number;//The friction of the blockprivatevar bRes:Number;//The bounce of the blockpublicfunction Block(bd:b2Body, r:int,c:int, p:int, t:int, f:Number, res:Number, bs:Object){//Here we attribute the received values to our object
body = bd;
base = bs;
row = r;
column = c;
m_physScale = p;
bFriction = f;
bRes = res;type = t;//Now we begin the function that builds the block
addBlock();}
And now to have a quick look at the addBlock class. I won’t spend time commenting the Box2d code as we have examined this before. More important is the part of the code that deals with the graphics. In order to deal with multiple block with different graphics, a switch statement is used after the physics of the block has been created. This saves time creating different block classes when the only thing that changes is the graphics.
publicfunction addBlock():void{var wallFd:b2FixtureDef = new b2FixtureDef();var wallSd:b2PolygonShape= new b2PolygonShape();var wallBd:b2BodyDef = new b2BodyDef();
wallBd.position.Set(column/ m_physScale*30, row/ m_physScale*30);
wallBd.type = b2Body.b2_staticBody;
wallFd.friction = bFriction;
wallFd.restitution = bRes;
wallFd.shape = wallSd;//Create an object with the name of the object and a reference to the rootvar dataObj:Object = newObject();
dataObj.name = "BLOCK";
dataObj.base = base;//This object is stored in the Fixture Definition userData object
wallFd.userData = dataObj;
wallSd.SetAsBox(15/ m_physScale, 15 / m_physScale);//Dimensions of the block
body = base.m_world.CreateBody(wallBd);//Add the body to the physics world
body.CreateFixture(wallFd);var xyPos:b2Vec2 = body.GetPosition();//Gets the postion of the physics objectthis.x = xyPos.x*30;//Converts the physics position into normal pixels so when...this.y = xyPos.y*30;//...we place our object it will already be in the right place.switch(type)//Remember that type was a value passed in at the start{case1://This value decided what graphics the block usesvar block1:Sprite = new BlockType1();//Instantiate a BlockType1 as a spriteaddChild(block1);//Add the block to the Display Listbreak;case2://This is a different style of blockvar block2:Sprite = new BlockType2();addChild(block2);break;}}
And that is all there is to it. When these objects are instantiated, they will have both physical and graphical presence. Obviously this won’t work with dynamic objects as we only set the position of the graphic one time at the object creation. I won’t bother explaining the Ramp.as file as it is the same idea as the Block class. The only difference is that because different ramps face different ways, I had to include the structural code in a switch statement. Feel free to check out the full source code or just a simple version for editing levels in XML.
The Result
Note: The Box2d decoding graphics are still present behind our new graphics. If these were turned off now, you wouldn’t be able to see the moving objects.
Last post we looked at restarting the level and I spoke about how the same code could be used to also kill the player and return them to the start of the level, as well as progress the player to the next level when they reach their goal.
To setup the game engine to have goals and multiple levels, we need to refer back to some previous posts. First up, we need to make sure our XML has multiple levels included. In the post dealing with the XML data of the game, you can see that our code is already setup to parse multiple levels. All of the level data is fed into an array of objects found inside our Levels Object declared in TileGame.as.
54
privatevar L:Levels = new Levels("levels.xml");
This object can be used to refer to any particular level by number.
1
2
3
4
5
6
var lvlArray:Array = L.levels[0].data;var lvlArray:Array = L.levels[1].data;var lvlArray:Array = L.levels[2].data;//Here the number is replaced by a variable to hold the current levelvar lvlArray:Array = L.levels[lvlCurrent-1].data;
The next thing we need is a goal for the player to reach. When want this object to be able to detect collisions, but not be effected by them. Luckily, Box2d has just such an object type. They are called sensors, and they are fixed shapes that register contact from moving objects. Let have a look at the code needed to add a goal sensor into the game engine. In the XML, the goal object will be represented by a capital “G”.
case"G"://Standard Box2d code for setting up a circle with 30 radiusvar goalBd:b2BodyDef = new b2BodyDef();
goalBd.position.Set(j/ m_physScale*30, i/ m_physScale*30);var goalSd:b2CircleShape=new b2CircleShape(14/ m_physScale);var goalFd:b2FixtureDef = new b2FixtureDef();//To be able to refer to the goal we need to give it a name.//We put this name into an object in the userData of the Fixture//definition in case we want to add other information to it later.//I have also stored the object's base to I can refer to it later.
dataObj = newObject();
dataObj.name = "GOAL";
dataObj.base = this;
goalFd.userData = dataObj;//No density or friction as the object doesn't interact physically
goalFd.shape=goalSd;
goalFd.isSensor=true;//This is where we define the goal as a Sensor
levelB = m_world.CreateBody(goalBd);
levelB.CreateFixture(goalFd);break;
Before we move on, I should point out that I have also added names to both the player object and the dynamic block objects. They are called “PLAYER” and “DBLOCK” respectively.
The Contact Listener
Now that we have a sensor to listen for, we need to create a class to listen for contacts, and take actions depending on which objects have been contacted. For full documentation of the the b2ContactListener class click here.
When we use the b2ContactListener class, we don’t actually create an instance of it, we create a new class that extends the original. We will create a new class in CustomCL.as that has all of the properties of b2ContactListener, with some custom code just for the game engine. Let’s take a look at that class in detail.
package{import Box2D.Dynamics.*;import Box2D.Collision.*;import Box2D.Collision.Shapes.*;import Box2D.Dynamics.Joints.*;import Box2D.Dynamics.Contacts.*;import Box2D.Common.*;//Note this class extends the b2ContactListenerpublicclass CustomCL extends b2ContactListener
{//We want to over ride the BeginContact function which is//called every time two objects make contact
override publicfunction BeginContact(contact:b2Contact):void{//Every contact involves two objects only, we refer to the//objects by as fixtures within the contact listenervar fixtureA:b2Fixture=contact.GetFixtureA();var fixtureB:b2Fixture=contact.GetFixtureB();var base:Object = newObject();var userDataA:Object = newObject();var userDataB:Object = newObject();//We only want to test for sensor contacts to save CPU timeif(fixtureB.IsSensor()|| fixtureA.IsSensor()){//Get the user data from the fixtures
userDataA = fixtureA.GetUserData();
userDataB = fixtureB.GetUserData();//The base of each object has been stored in the user data
base = userDataA.base;//The following switch statement handles all interactionsswitch(userDataA.name){case"PLAYER":switch(userDataB.name){//If the player contacts the goal then//increase the level and initiate a resetcase"GOAL":
base.lvlCurrent++;
base.reset = true;break;}break;case"DBLOCK":switch(userDataB.name){//If a block contacts the goal then just initiate//a reset, forcing the player to start againcase"GOAL":
base.reset = true;break;}break;}}}}}
While it may look silly using switch statements to for such a simple logic tree, I want this script to have room to grow. With some more complicate elements, such as enemies, boost tiles and other interactions, it will need to be robust.
Also note that the goal is always the fixtureB. I need to do some more research, but I think this is because it is static, I am not sure what the result would be if it could also move.
Source files
Here is the source code, and here is a simpler version where you can edit levels, but not the original source code.
For a while, I have wanted to have a reset button for this game. Often, I will get the player stuck in some position, or lose him all together off the map, and will have to restart the flash file to restart. What I realised, is that by building a reset button, I would also be creating the code I need to progress levels, and to send the player back to the start if he dies.
Because the game uses a combination of physics objects and normal movieclips, we need to be able to clear the entire level of both of these before we can rebuild another level over the top. Another important thing to remember, is that the game is constantly running cycles of the physics engine. You can’t remove objects mid cycle, so we will need to find the right place to stop and check if the reset button has been hit.
Let’s look at the code
First off, we setup reset and the current level as global variables in TileGame.as.
function key_pressed(event:KeyboardEvent):void{if(event.keyCode == 37){
rightKeyDown = true;}if(event.keyCode == 39){
leftKeyDown = true;}if(event.keyCode == 38){
upKeyDown = true;}if(event.keyCode == 40){
downKeyDown = true;}//The new reset key is a lowercase "r"if(event.keyCode == 82){reset = true;}}
So now we have function that sets the reset variable to true every time the “r” key is pressed. But how do we translate that into actually resetting the level? As I said before, we need to make sure we do our resetting in the right place. We need to do it at the start of a physics cycle to make sure that no physics calculations are happening when we start destroying the level. So we check for a level reset at the start of the run() function which basically runs the entire engine.
324
325
326
327
328
329
330
331
332
privatefunction run(e:Event):void{if(reset){
destroyLvl();}// The rest of the function updates physics etc etc}
Below is a detailed look at the destroyLvl() function. Before destroying any objects though, the game engine needs to be stopped. By removing the ENTER_FRAME event listener, the game no longer calls the run() function, and we can begin deleting the objects that make up the level. We do not need to restart this listener, as that is done at the end of the createLvl() function which we call at the end.
privatefunction destroyLvl(){//Temporarily stops the game runningremoveEventListener(Event.ENTER_FRAME, run);//Put the value back to false for when the game starts againreset = false;//Gets a list of every physics object createdvar node:b2Body = m_world.GetBodyList();//Loops through each object and destroys itwhile(node){varb:b2Body = node;
node = node.GetNext();
m_world.DestroyBody(b);}//Loops through every sprite in the level and destroys itwhile(m_sprite.numChildren){
m_sprite.removeChildAt(0);}//Recreate the level from scratch//Note: null needed because at the moment, createLvl is an //event function but this will be fixed in later versions
createLvl(null);}
We can also use this destroyLvl() function to progress to the next level, all you need to do is increase the lvlCurrent variable before you change the reset variable to true. For an example, see below.
1
2
lvlCurrent++;reset = true;
When our level gets created, we use the lvlCurrent variable to tell it which level to build from our Levels array.
81
82
83
84
85
86
privatefunction createLvl(e:Event):void{//The -1 is needed as arrays always start at 0var lvlArray:Array = L.levels[lvlCurrent-1].data;//... createLvl function continues as normal
Next post we will look into object collision detection within the Box2d physics engine, and how it can be used to create game goals, enemies and other effects.
Source files
Here is the source code, and here is a simpler version where you can edit levels, but not the original source code.
The game engine (remember to double click inside the box to play)
Don’t forget to try the “r” key to reset the level
Hello again, I am back from my holiday and am now living near Milan Italy. The last post I made showed a lot of improvements to the game engine, but I did not have time to describe the additions in detail. Today I will be describing in more detail the new camera movement being used in the game Please refer to the last post for game source files.
The reason I had to write a smarter camera into the code is because the old camera would follow the player no matter where it went. If the player is near the edge of the screen, or the whole level fits within the screen, we don’t want the camera to follow. When the camera should and shouldn’t follow can be a little confusing to put into code, so lets look at it in a diagram first.
Camera Movement Diagram
The rules of camera movement
In this diagram, X equals the screen or stage width, and Y is the height. When the player’s X position is more than half of the stage width, the camera begins to follow. If however, the player becomes close to the other side of the level (when the player’s X position is greater than the level width minus half the stage width) the camera ceases to follow again.
Visually, when the player’s position is in the lightest grey areas, the camera doesn’t follow at all. When the player is in the medium grey areas, the camera follows the X or Y movement of the player, and when the player is in the darkest grey area, the camera follows both the X and Y movement of the player. Whew! I knew it would be difficult to explain in words. Let’s have a look at it in the code.
The following extract of code is found in the TileGame.as file at line 269 of last weeks source code files.
//Please note: m_sprite is basically a container for the whole level//and WORLD_OFFSET is a constant used to adjust the level positioning.privatefunction moveWorld(){var vect:b2Vec2 = player.GetPosition();//Sprite position in Vect format//This statement deals with the player X positionif(m_sprite.width>stage.stageWidth){//If X position is away from the edges of the levelif(vect.x*30<(m_sprite.width-stage.stageWidth/2)&& vect.x*30>stage.stageWidth/2){//Notice there is no camera to move but the//sprite containing the level which moves
m_sprite.x = -(vect.x*30)+(stage.stageWidth/2);}//If the X position goes near the right edgeelseif(vect.x*30>= (m_sprite.width-stage.stageWidth/2)){//Set the camera to hold on the right edge
m_sprite.x = -(m_sprite.width-stage.stageWidth);}//If the X position is close to the left edgeelse{//Set the camera to hold on the left edge
m_sprite.x = 0;}//An offset constant is added to place the camera exactly
m_sprite.x = m_sprite.x+WORLD_OFFSET;}//We repeat the process with the Y positionif(m_sprite.height>stage.stageHeight){if(vect.y*30<(m_sprite.height-stage.stageHeight/2)&& vect.y*30>stage.stageHeight/2){
m_sprite.y = -(vect.y*30)+(stage.stageHeight/2);}elseif(vect.y*30>= (m_sprite.height-stage.stageHeight/2)){
m_sprite.y = -(m_sprite.height-stage.stageHeight);}else{
m_sprite.y = 0;}
m_sprite.y = m_sprite.y+WORLD_OFFSET;}}
As you can see in the flash example from last week, we now have a camera that moves with the player, but will not pan so far as to expose the world beyond the edge of the level. See you soon for more details on game goals and creating multiple levels.