Create a Facebook Graph API App in Flash: Displaying Facebook Info – Tuts+ Premium
In this mini-series, exclusive to Tuts+ Premium members, you’ll learn how to use Flash to build a Facebook Graph API application that can create slideshows for your public pages. The final part shows you how to actually retrieve and display the information from Facebook.
Premium Preview
This application allows you to select a Facebook album or event list from one of your public pages and turn it into a slideshow for your page tab. When the users enter your page, they will see photos from your chosen album, with your photo title and description, or event name, date and invites (for the event tab).
In the previous part of this series, we created the event slider generator. In this part we’ll make the actual slider for the images (and later, for the events).
You’ll need to be logged in to Facebook in order to see this demo: https://apps.facebook.com/activetuts_tabmaker/
Read the Full Tutorial
Premium members can access the full tutorial right away!
If you’re not yet a Premium member, you can still read the first few steps for free.
Tuts+ Premium Membership
We run a Premium membership system which periodically gives members access to extra tutorials, like this one, from across the whole Tuts+ network. If you’re a Premium member, you can log in and read the tutorial. If you’re not a member, you can of course join today!
Also, don’t forget to follow @envatoactive on twitter, circle us on Google+, like us on Facebook, and grab the Activetuts+ RSS Feed to stay up to date with the latest tutorials and articles.
View full post on Activetuts+


In this mini-series, exclusive to Tuts+ Premium members, you’ll learn how to use Flash to build a Facebook Graph API application that can create slideshows for your public pages. The final part shows you how to actually retrieve and display the information from Facebook.
Premium Preview
Click to try the app on Facebook.
In the previous part of this series, we created the event slider generator. In this part we’ll make the actual slider for the images (and later, for the events).
You’ll need to be logged in to Facebook in order to see this demo: https://apps.facebook.com/activetuts_tabmaker/
Read the Full Tutorial
Premium members can access the full tutorial right away!
If you’re not yet a Premium member, you can still read the first few steps for free.
Tuts+ Premium Membership
We run a Premium membership system which periodically gives members access to extra tutorials, like this one, from across the whole Tuts+ network. If you’re a Premium member, you can log in and read the tutorial. If you’re not a member, you can of course join today!
Also, don’t forget to follow @envatoactive on twitter, circle us on Google+, like us on Facebook, and grab the Activetuts+ RSS Feed to stay up to date with the latest tutorials and articles.
Earlier today, Wilson Lim showed you how to create a gravity-based game called Flux. In this Workshop, Matt Porter critiques Flux, explaining what it would take to turn that simple demo into a real game.
Play the Game
Use the left and right arrow keys to manoeuvre your ship, the up and down arrow keys to increase or reduce the size of the magnetic field it produces, and the space bar to reverse the polarity. Collect the white crystals to increase your fuel supply – but avoid the red ones, because they use it up. Don’t hit a rock, or it’s game over.
Please bear in mind that this demo was created specifically for a tutorial, rather than for release on a Flash portal, so it’s naturally less polished than games we’ve featured in past critiques.
Introduction
Flux is a barebones game. It has one core mechanic, and very basic controls. That being said, even the most basic aspects of a game can require careful balance. With a few small tweaks, we can make something that’s simple and frustrating into something that might not necessarily be a blast, but will be a much smoother and more enjoyable experience.
The core aim of Flux is to sustain energy for your ship by collecting white crystals, while at the same time avoiding red crystals. In addition, you must also avoid rocks, which end your game immediately upon contact. To mix things up a bit, the ship is equipped with a magnet of sorts, that can attract or deflect the negative crystals, but has no effect on the rocks of instant doom.
You can move your ship horizontally, but not vertically, using the left and right arrow keys. The last bit of spice in this otherwise currently bland game is that you can increase or decrease your magnet’s area of effect, by pressing the up and down arrow keys respectively.
So there you have it, that’s Flux. While there’s very little to this game, we’re about to break it down into more than the average eyes see, and come up with some simple, yet game changing, alterations.
Getting Started
The first and most immediate flaw in Flux is that there isn’t really a title screen.
Flux’s starting screen
There’s no instructions, no place to check high score, no title, no music… you’re just tossed onto a bland screen with a Start Game button. Pretty bad start, but okay, let’s move on.
Upon pressing the button, we now see our ship, and some objects in our face. Let’s just assume that we know what to do right off, and that there is a rock (instant death if it hits us) coming right at us (that’ll happen when positions are 100% random). We quickly get our hands onto the keyboard (we started using the mouse, and there were no instructions, so we’re not actually prepared for this), and attempt to dodge the rock.
Oh no, our ship isn’t moving! Does WASD not work? Well, actually, they don’t work (which is a horrible decision, as WASD and arrow keys should always both be supported where possible) – but believe it or not, that’s not our issue either.
The real issue is that the game area doesn’t have focus. We actually need to click the player area to gain control, even though we just pressed the Start Game button. Looks like we just blew up from an oncoming rock. So here we are, literally five seconds into the game, and all we’ve done is see a bland title screen and die from absolute unfairness. Congratulations, you’ve just lost the majority of all your players right off, and most of them have down-voted your game if it’s being played on a major portal such as Newgrounds or Kongregate.
A likely response from a Newgrounds gamer
While the above is an absolute mess, it’s actually quite easily fixed.
Pixel Purge‘s title screen, for comparison
First off, we add a simple title screen. The quality can range greatly, but at the very least, we need a title, a play button, and something related to scores – whether it’s a list already on screen, or a button to let us view them all.
Next, we make sure that the play area has keyboard focus after the Start Game button is pressed. If this were my game, I would then choose to display the controls of the game to the player through pictures and text, and not just a wall of text. For good measure, I’d save a variable to a shared object that says the player has seen the controls, and I would never display them at the start of a game again (unless a “delete all data” button was pressed).
Now that the player has a good initial first impression, with a decent menu, and controls shown to them, we’d start the game. Here I would make sure that the game didn’t actually start until the player moved the ship. This would accomplish two things: first, we’d know that they have their hands on the keyboard and are ready to play; and second, they wouldn’t die from a randomly generated flying space rock within the first two seconds of the game.
Nothing that I just listed above is hard, but it’s just changed that poor initial first impression of this game to “this has potential, let’s see what happens”.
Gameplay
Now that we’ve taken care of analyzing the first impression, let’s focus a bit on the actual gameplay.
Flux’s play screen
The first thing I noticed when playing Flux, is that the movement was extremely stiff, and felt very amateur. It’s obvious that the movement is locked to a set speed, and that there’s no acceleration or easing. If we were to add some of that then not only would the game feel better and look more smooth, but our control would be far more precise, making for a much more enjoyable experience.
The magnet mechanic is actually pretty neat, but it’s a bit rough around the edges. The first thing I noticed, is that there are no restrictions on how big or small the attract radius can be. This allows us to go negative, and eventually appear to be positive again, but the magnet won’t actually do anything. This could leave various impressions on the player, from them being confused to them thinking the game looks unprofessional, so it’s important that we put restrictions in place.
One More Time
Assuming that players actually have fun with the game the way it is (admittedly doubtful beyond the first play or two), we still have some fixing up to do. When the player dies, they aren’t really left with any incentive to play again. The game over screen should be used as an opportunity to tempt the player to play again – and simply having a button to let them do so is not what I mean.
Flux’s game over screen
In this case, the game over screen should still show the player’s score, but also their all-time best score. If their previous play was their best, this should be related to the player both visually and audibly.
Pixel Purge’s game over screen, for comparison
If we wanted to take things even further, we’d toss in Facebook / Twitter buttons, and allow the player to brag about their accomplishment with friends.
What Else?
As I’ve mentioned, even with the critical flaws fixed, and the controls a bit polished, the game is still a bit boring. While there are hundreds if not thousands of directions in which to take a game this basic, I’ll just focus on some of the simpler yet still effective changes.
The first addition I would add is powerups. We’ve already got collision code we use for collecting crystals, so adding powerups is not that hard from a technical standpoint. For example, we could add a powerup that turns every red crystal into a white one. With a cool visual effect, and an awesome sound effect, grabbing one of these would be quite rewarding.
The next thing I would do, is add crystals of different sizes, where the bigger the crystal, the harder it is to attract or repel. Give the larger crystals a higher score value, and a more rewarding sound, and you’ve now got some substance to your game. You’ll find players act extra risky around instant-kill death rocks, just so they can attempt to pull in a giant white crystal.
Speaking of instant-kill death rocks, why not add a way to get rid of them? With the simple press of a button (say, the X key), let the player shoot directly ahead – for a cost. To make the game more interesting, firing your weapon would require some of your energy, so it would have to be used sparingly. We could also add a powerup that explodes all rocks on the screen, or that makes the player invincible so that they can hit them without worry for a limited time.
If I really wanted to add a level of complexity to the game, I’d make it so that all crystals gained would be stored as currency, and after each game (or level), the player could spend those points on upgrading stats. These stats could range from maximum magnet radius, to increased energy regeneration from white crystals; the possibilities are nearly endless.
All of the biggest remaining issues are related to polish, and as many developers will tell you, this can take a long time to get right.
Some of the more major aspects of polish that are needed are as follows:
Conclusion
As you can see, it didn’t take too much to really rip this game apart, and then point out some simple but effective ways to fix it up.
There are enormous benefits to being able to fix up such a basic game. In reality, everything that Flux has is no larger or more complex than one individual portion of any other larger game. If we look at game development like this, we then see the benefit of being able to fix up such a simple game. We’re essentially able to view our bigger projects as small pieces, and we can then polish each piece up individually until it’s a smooth and enjoyable experience on its own. In the end, we’ll be left with a full-fledged game, comprised of many small, extremely well built pieces.
What do you think should be added to Flux? Post your suggestions in the comments – or you could even follow the tutorial and make the changes yourself…
In this tutorial, I’ll explain the major steps and workflow for creating a simple space survival game, based on the gravity mechanic explained in a previous tutorial. This game is written in AS3 using FlashDevelop.
Play the Game
Use the left and right arrow keys to manoeuvre your ship, the up and down arrow keys to increase or reduce the size of the magnetic field it produces, and the space bar to reverse the polarity. Collect the white crystals to increase your fuel supply – but avoid the red ones, because they use it up. Don’t hit a rock, or it’s game over!
In this tutorial, we won’t actually create the full game displayed above; we’ll just get started on it, by making a very simple version with primitive graphics and just one type of object. However, by the end, you should have learned enough to be able to add the other features yourself!
The game itself is very simple in its current state – take a look at this critique for tips on how you can take it from a simple demo to a full game!
Let’s Get Started!
Set up a new AS3 project in FlashDevelop, and set its dimensions to 550x600px.
package { [SWF(width = "550", height = "600")] public class Main extends Sprite { } }Step 1: Identifying the Game Objects
There are six objects in particle that you can identify from playing the game above:
Of course you can add in any other object to make the game more interactive or add a new feature. For this tutorial we’ll just make
Step 2: The
EnergyClassFrom the objects we identified, four of them actually work in exactly the same way: by falling from top to bottom.
They are:
In this tutorial, we’re only going to make the “energy supply” objects, out of the four above. So let’s begin by creating these objects and making them fall down, with a random spawning position and speed.
Start by creating an
Energyclass:package { import flash.display.MovieClip; import flash.events.Event; public class Energy extends MovieClip { private var rSpeed:Number = 0; public function Energy(speed:Number) { graphics.beginFill(0x321312); graphics.drawCircle(0, 0 , 8); rSpeed = speed; } // we will call this every frame public function move():void { this.y += rSpeed; //rotation speed is linked to moving speed this.rotation += rSpeed / 8; } } }Step 3: The
GameScreenClassThis class will eventually control most of the aspects of our game, including the player movement and the game loop.
Create the class:
package { public class GameScreen { public function GameScreen() { } } }That’s all we need for now.
Step 4: Update The Main Class
We’ll now create an instance of
GameScreenwithinMain:package { import flash.display.Sprite; import flash.events.Event; [SWF(width = "550", height = "600")] public class Main extends Sprite { private var game:GameScreen; public function Main():void { // don't display a yellow rectangle on the screen at startup stage.stageFocusRect = false; game = new GameScreen(); addChild(game); // give keyboard focus to the game screen immediately stage.focus = game; } } }Why bother? Well, this way, it’ll be easier to add extra screens later if we want to (like a preloader, a title screen, a game over screen…).
Step 5: Introducing a Manager Class
To avoid the
GameScreenclass becoming too much of a mess, we’ll use separate classes to manage each object.Each manager class will contain all the functions that relate to, and interact with, a particular object. Here’s the
EnergyManagerclass:package { import flash.display.MovieClip; public class EnergyManager { // this Vector will store all instances of the Energy class private var energyList:Vector.<Energy> private var gameScreen:GameScreen; public function EnergyManager(gs:GameScreen) { gameScreen = gs; energyList = new Vector.<Energy>; } } }Note that we require a reference to the GameScreen to be passed to the constructor, and we store this reference in a private variable. We also set up a Vector to store references to all the energy objects.
So far the class contain no other functions; we will add them in later.
Step 6: Creating Energy
Add the below function for creating energy, this is just a function; we will call the function later from
GameScreenClass:public function createEnergy(number:int):void { var energy:Energy; for (var i:int = 0; i < number; i++) { energy = new Energy(4); gameScreen.addEnergyToScreen(energy); energyList.push(energy); energy.x = Calculation.generateRandomValue(30, 520); energy.y = Calculation.generateRandomValue( -150, -20); } }We create a new energy supply with a speed of 4, add it to the display list (via the GameScreen), add it to the Vector of all energy objects that we just created, and set its position to a random point within certain bounds.
The
Calculation.generateRandomValue(#, #)is a static function we haven’t written yet, so let’s do that now. Create a new class calledCalculationand add this function:public static function generateRandomValue(min:Number, max:Number):Number { var randomValue:Number = min + (Math.random() * (max - min)); return randomValue; }This function will generate a random number between the two values passed to it. For more information on how it works, see this Quick Tip. Since this is a static function, we don’t need to create an instance of
Calculationin order to call it.Now, what’s that
addEnergyToScreen()function? We haven’t defined that yet, so let’s do it now. Add this toGameScreen:public function addEnergyToScreen(energy:Energy):void { addChild(energy); }It just adds the passed instance of energy to the display list. Let’s also make a corresponding function to remove a given energy object from the screen:
public function removeEnergyFromScreen(energy:Energy):void { if (energy.parent == this) { removeChild(energy); } }Step 7: Spawning Energy
Let’s set a timer that defines the interval for each spawning. This code goes in
GameScreen‘s constructor function:So, every three seconds, the timer will call
spawnEnergy(). Let’s write that function now:private function spawnEnergy(e:TimerEvent):void { energyM.createEnergy(4); // create 4 energies }Step 8: Creating Player
Let’s use another, bigger circle to represent the player. Feel free to import an image to use instead:
public function Player() { graphics.beginFill(0x7ebff1); graphics.drawCircle(0, 0, 20);Add this code to
GameScreento add the player to the screen:So far we should have a few energy supplies falling few seconds, and the player appearing in the middle of the screen:
Step 9: Moving the Player
There are basically two ways to apply movement:
true. In each frame update, “moving right” istrue, we increase the object’s x-value.Using direct update each frame– when the right arrow key is pressed, an object is told to move right immediately, by increasing its x-value.The second method does not lead to smooth movement when the key is continuously pressed, but the first method does – so we shall use the first method.
There are three simple steps to doing this:
addEventListener(Event.ENTER_FRAME, update); addEventListener(KeyboardEvent.KEY_DOWN, KeyDownHandler); addEventListener(KeyboardEvent.KEY_UP, KeyUpHandler); } private function KeyDownHandler(e:KeyboardEvent):void { if (e.keyCode == Keyboard.RIGHT) { moveRight = true; } if (e.keyCode == Keyboard.LEFT) { moveLeft = true; } if (e.keyCode == Keyboard.SPACE) { if (isGravityPushing == true) { isGravityPushing = false; } else { isGravityPushing = true; } } } private function KeyUpHandler(e:KeyboardEvent):void { if (e.keyCode == Keyboard.RIGHT) { moveRight = false; } if (e.keyCode == Keyboard.LEFT) { moveLeft = false; } }Don’t forget to first create a function listen from the enter frame event, “updating” :
//call this function every frame private function update(e:Event):void if (moveRight == true) { player.x += 6; } if (moveLeft == true) { player.x -= 6; }Keep the player within the bounds of the screen:
if (player.x >= 525) { moveRight = false; } if (player.x <= 20) { moveLeft = false; }Here’s how all that looks, in place:
package { import flash.display.MovieClip; import flash.events.Event; import flash.events.TimerEvent; import flash.ui.Keyboard; import flash.utils.Timer; import flash.events.KeyboardEvent; public class GameScreen { public var player:Player; private var energyM:EnergyManager; private var moveRight:Boolean = false; private var moveLeft:Boolean = false; private var isGravityPushing:Boolean = true; private var returnedPower:int = 0; private var scoreText:Text; private var totalScore:int=0; private var score:Text; public function GameScreen() { scoreText = new Text("Score :"); addChild(scoreText); energyM = new EnergyManager; var spawnTimer:Timer = new Timer(3000, 0); spawnTimer.addEventListener(TimerEvent.TIMER, spawnEnergy); spawnTimer.start(); player = new Player; addChild(player); player.x = 275; player.y = 450; addEventListener(Event.ENTER_FRAME, update); addEventListener(KeyboardEvent.KEY_DOWN, KeyDownHandler); addEventListener(KeyboardEvent.KEY_UP, KeyUpHandler); } private function KeyDownHandler(e:KeyboardEvent):void { if (e.keyCode == Keyboard.RIGHT) { moveRight = true; } if (e.keyCode == Keyboard.LEFT) { moveLeft = true; } if (e.keyCode == Keyboard.SPACE) { if (isGravityPushing == true) { isGravityPushing = false; }else if (isGravityPushing == false) { isGravityPushing = true; } } } private function KeyUpHandler(e:KeyboardEvent):void { if (e.keyCode == Keyboard.RIGHT) { moveRight = false; } if (e.keyCode == Keyboard.LEFT) { moveLeft = false; } } private function update(e:Event):void { if (player.x >= 525) { moveRight = false; } if (player.x <= 20) { moveLeft = false; } if (moveRight == true) { player.x += 6; } if (moveLeft == true) { player.x -= 6; } } } }Step 10: Move the Energy Supplies
At the moment, the energy supplies are spawning but not moving. We’ll use the
GameScreen.update()function to make them move, since it runs every frame.Add this code to
GameScreen.update():Now of course we need to make the
EnergyManager.moveAll()function, so add this toEnergyManager.as:public function moveAll():void { for (var i:int = 0; i < energyList.length; i++) { var energyS:Energy = energyList[i]; energyS.move(); } }Step 10: Collision Detection
We will need to check for collisions between each energy object and the player. (If you develop the game further, you’ll need to check this for asteroids and energy consumers, but not for stars.)
The best place to handle these checks is inside the
EnergyManager, triggered every frame by theGameScreen.One thing to consider: the collision checks will be between two circles, so
hitTestObject()is not ideal. Instead, we’ll be using the method explained in this tutorial.We can write the function as below:
public function checkCollision(p:Player):int { // energy transferred due to collision var energyTransfer:int = 0; for (var i:int = 0; i < energyList.length; i++) { var energyS:Energy = energyList[i]; var newX:Number = p.x - energyS.x; var newY:Number = p.y - energyS.y; var distance:Number = Math.sqrt(newX * newX + newY * newY); if (distance <= 28) { gameScreen.removeEnergyFromScreen(energyS); energyList.splice(i, 1); // for this simple game, we'll always transfer 1 unit // but you could alter this based on speed of collision // or any other factor energyTransfer = 1; } } return energyTransfer; }EnergySis short for Energy Supply.You could alter Line 51 to
energyTransfer += 1, to allow the player to absorb more than one energy object at once. It’s up to you – try it out and see how it affects the game.Step 11: Call Collision Detection Routine
We need to check for collisions every frame, so we should call the function we just wrote from
GameScreen.update().First, we need to create an integer variable to store the energy transfer value from the collision detection function. We’ll use this value for increasing the ship’s energy and adding to the player’s score.
Step 12: Newton’s Law of Gravitation
Before we go into creating the game mechanic for the ‘Push’ and ‘Pull’ function of the ship, I would like to introduce the physics concept on which the mechanic is based.
The idea is to attract the object towards the player by means of a force. Newton’s Law of Universal Gravitation gives us a great (and simple) mathematical formula we can use for this, where the force is of course the gravitational force:
G is just a number, and we can set it to whatever we like. Similarly, we can set the masses of each object in the game to any values that we like. Gravity occurs across infinite distances, but in our game, we’ll have a cut-off point (denoted by the white circle in the demo from the start of the tutorial).
The two most important things to note about this formula are:
Step 13: Revising Math Concepts
Before we start coding the game mechanics for the ‘Push’ and ‘Pull’ function, let’s be clear on what we want it to do:
Essentially, we want A (the player) to exert a certain force on B (a crystal), and move B towards A based on that force.
We should revise a few concepts:
Math.atan2(B.y - A.y, B.x - A.x).B.x += (Force*Math.cos(angle));B.y += (Force*Math.sin(angle));For more information, see the tutorials Gravity in Action and Trigonometry for Flash Game Developers.
Step 14: Implementing Push and Pull
Based on the previous explanation, we can come up with an outline for our code that attracts each crystal to the ship:
Sample Code:
public function gravityPull(p:Player): void { for (var i:int = 0; i < energyList.length; i++) { var energyS:Energy = energyList[i]; var nX:Number = (p.x - energyS.x); var nY:Number = (p.y - energyS.y); var angle:Number = Math.atan2(nY, nX); var r:Number = Math.sqrt(nX * nX + nY * nY); if (r <= 250) { var f:Number = (4 * 50 * 10) / (r * r); energyS.x += f * Math.cos(angle); energyS.y += f * Math.sin(angle); } } }Here’s a timelapse showing how this looks:
Note that the energy moves faster the closer it gets to the ship, thanks to the r-squared term.
We can implement the pushing function just by making the force negative:
public function gravityPull(p:Player): void { for (var i:int = 0; i < energyList.length; i++) { var energyS:Energy = energyList[i]; var nX:Number = (p.x - energyS.x); var nY:Number = (p.y - energyS.y); var angle:Number = Math.atan2(nY, nX); var r:Number = Math.sqrt(nX * nX + nY * nY); if (r <= 250) { var f:Number = (4 * 50 * 10) / (r * r); energyS.x -= f * Math.cos(angle); energyS.y -= f * Math.sin(angle); } } }Here the object moves more slowly as it gets further away from the player, since the force gets weaker.
Step 15: Apply the Mechanic
Of course that you will need this function to be run each frame by
GameScreen– but before that, we will need to use a Boolean function to toggle between the two functions:We are going to use true for ‘Push’ and false for ‘Pull’.
Inside
KeyDownHandler():if (e.keyCode == Keyboard.SPACE) { if (isGravityPushing == true) { isGravityPushing = false; } else if (isGravityPushing == false) { isGravityPushing = true; } }Afterwards, you will have to check the Boolean each frame. Add this to
update():if (isGravityPushing == true) { energyM.gravityPull(player); } if (isGravityPushing == false) { energyM.gravityPush(player); }Step 16: Modification
You might find that the movement doesn’t look so nice. This could be because the force is not quite ideal, or because of the r-squared term.
I’d like to alter the formula like so:
As you can see, I’ve reduced the value of “G” to 0.8, and changed the force to depend simply on the distance between the objects, rather than the distance squared.
Try it out and see if you enjoy the change. You can always alter it however you like.
Step 17: The Text Class
We will need to show some text on the screen, for showing the score and the ship’s remaining power.
For this purpose, we’ll build a new class,
Text:package { import flash.display.MovieClip; import flash.text.TextField; import flash.events.Event; import flash.text.TextFormat; import flash.text.TextFormatAlign; public class Text extends MovieClip { public var _scoreText:TextField= new TextField(); public function Text(string:String) { var myScoreFormat:TextFormat = new TextFormat(); //Format changeable myScoreFormat.size = 24; myScoreFormat.align = TextFormatAlign.LEFT; myScoreFormat.color = (0x131313); _scoreText.defaultTextFormat = myScoreFormat; _scoreText.text = string; addChild(_scoreText); } public function updateText(string:String) { _scoreText.text = string; } } }It’s very simple; it’s basically a MovieClip with a text field inside.
Step 18: Adding Power for Player
To give the game some challenge, we’ll make the ship’s power get used up slowly, so that the player has to collect energy objects in order to recharge.
To make the ship’s power appear on the ship itself, we can simply add an instance of
Textto the ship object’s display list.Declare these variables within the
Shipclass:We’ll need to keep the amount of power (both stored and displayed) updated every frame, so add this new function to
Player:First, in the constructor:
// add a new text object if it doesn't already exist if (!powerText) { powerText = new Text(String(int(totalPower))); addChild(powerText); powerText.x -= 20; //Adjust position powerText.y -= 16; }And then…
public function updatePower():void { // fps = 24, so this makes power decrease by 1/sec totalPower -= 1 / 24; powerText.updateText(String(int(totalPower))); }The power will decrease every frame by 1/24th of a unit, meaning it’ll decrease by one full unit every second.
We need to make this run every frame, so add this line to
GameScreen.update():Step 19: Make Energy Increase Power
When the ship collides with an energy object, we want it to increase its power.
In
GameScreen.update(), add the highlighted line:Remember you can alter how much power is returned in the
EnergyManager.checkCollision()function.Step 20: Setting Up the Score
Again, we will need the text class. This time, we’ll display “Score” and then the value.
Here, we will need three more variables:
Declare these in
GameScreenclass:In the constructor, add this code:
scoreText = new Text("Score :"); addChild(scoreText); score = new Text(String(totalScore)); addChild(score); score.x = scoreText.x + 100; //Positioning it beside the "Score : " Text. score.y += 2;Now, in the
update()function, add this:That’s it – we’ve created a basic version of the above game!
Take a look (you may need to reload the page):
Extra Features and Polishing
Space Background
Maybe you would also like a background with an embedded image and stars. Add this to your
Mainclass:Now create the
Starclass:package assets { import flash.display.MovieClip; import flash.events.Event; public class Star extends MovieClip { private var speed:Number; public function Star(alpha:Number, size:Number, speed1:Number) { graphics.beginFill(0xCCCCCC); graphics.drawCircle(0, 0, size); speed = speed1; } // make sure you call this every frame private function moveDown():void { this.y += speed; if (this.y >= 600) { this.y = 0; } } } }In the
Main()constructor, add this to create the stars:for (var i:int = 0; i < numOfStars; i++) { createStars(); }Here’s the actual
createStars()function:private function createStars():void { var star:Star = new Star( Math.random(), Calculations.getRandomValue(1, 2), Calculations.getRandomValue(2, 5) ); //random alpha, size and speed addChild(star); star.x = Calculations.getRandomValue(0, 550); star.y = Calculations.getRandomValue(0, 600); }With random alpha, size, position, and speed, a pseudo-3D background can be generated.
Range indicator
A range indicator circle can be made by simply creating another circle and adding it to the ship’s display list, just like how you added the power indicator text. Make sure the circle is centred on the ship, and has a radius equal to the ship’s push/pull range.
Add transparancy (alpha value) to the circle with the below code:
Try adding extra controls that make the range increase or decrease when the up and down arrow keys are pressed.
Conclusion
I hope you enjoyed this tutorial! Please do leave your comments.
Next: Read this critique for a guide to taking Flux from a simple demo to a full game!
There are several methods used to produce menus within Unity, the main two being the built in GUI system and using GameObjects with Colliders that respond to interactions with the mouse. Unity’s GUI system can be tricky to work with so we’re going to use the GameObject approach which I think is also a bit more fun for what we’re trying to achieve here.
Final Result Preview
Please view the full post to see the Unity content.
Step 1: Determine Your Game Resolution
Before designing a menu you should always determine what resolution you are going to serve it to.
Open the Player settings via the top menu, Edit > Project Settings > Player and enter your default screen width and height values into the inspector. I chose to leave mine as the default 600x450px as shown below.
You then need to adjust the size of your Game view from the default "Free Aspect" to "Web (600 x 450)", else you could be positioning your menu items off the screen.
Step 2: Choosing a Menu Background
As you will have seen in the preview, our menu scene is going to have our game environment in the background so that when you click Play you enter seamlessly into the game.
To do this you need to position your player somewhere in the scene where you like the background and round up the Y rotation value. This is so it’s easier to remember and to replicate later, so that the transition can be seamless from the menu into the game.
Let’s now get on with the creation of the menu scene!
Step 3: Creating the Menu Scene
Make sure your scene is saved and is called "game" – you’ll see why later.
Select the game scene within the Project view and duplicate it using Ctrl/Command + D, then rename the copy to "menu" and double-click it to open it.
Note: You can confirm which scene is open by checking the top of the screen, it should say something like "Unity – menu.unity – ProjectName – Web Player / Stand Alone". You don’t want to start deleting parts accidently from your game scene!
Now select and delete the GUI and PickupSpawnPoints GameObjects from the Hierarchy. Expand the "Player" and drag the "Main Camera" so that it’s no longer a child of the Player, then delete the Player.
Next, select the terrain and remove the Terrain Collider Component by right-clicking and selecting Remove Component.
Finally, select the "Main Camera" and remove the MouseLook Component by right-clicking and selecting Remove Component.
If you run the game now nothing should happen and you shouldn’t be able to move at all. If you can move or rotate then redo the above steps.
Step 4: Adjusting the Build Settings
Currently when you build or play your game the only level included within that build is the "game" scene. This means that the menu scene will never appear. So that we can test our menu, we’ll adjust the build settings now.
From the top menu select File > Build Settings and drag the menu scene from your Project View into the Build Settings’ "Scenes In Build" list as shown below
(Make sure you rearrange the scenes to put "menu.unity" at the top, so that it’s the scene that’s loaded first when the game is played.)
Perfect!
Step 5: Adding the Play Button
We’re going to use 3D Text for our menu, so go ahead and create a 3D Text GameObject via the top menu: GameObject > Create Other > 3D Text, then rename it "PlayBT". Set the Y rotation of the PlayBT text to match the Y rotation value of your Main Camera so that it’s facing directly at it and therefore easily readable.
With the PlayBT selected, change the Text Mesh text property from Hello World to "Play", reduce the Character Size to 0.1 and increase the Font Size to 160 to make the text crisper.
Note: If you want to use a font other than the default, either select the font before creating the 3D Text or drag the Font onto the 3DText’s TextMesh’s Font property and then drag the Fonts "Font Material" onto the Mesh Renderers Materials list, overwriting the existing Font Material. Quite a hassle I know!
Finally, add a Box Collider via the top menu: Component > Physics > Box Collider. Resize the Box Collider to fit the text if it doesn’t fit it nicely.
At this point in the tutorial you really need to have both the Scene and Game Views open at the same time since you are now going to move the PlayBT within the Scene View so that it’s centred within your Game View as shown below. I recommend first positioning it horizontally using a top down view and then revert to using a perspective view to position it vertically using the axis handles.
So that’s our Play button all set up. Now let’s make it play!
Step 6: The Mouse Handler Script
Create a new JavaScript script within your scripts folder, rename it "MenuMouseHandler" and add it as a Component of the PlayBT GameObject by either dragging in directly onto PlayBT or by selecting PlayBT and dragging the script onto it’s Inspector.
Replace the default code with the following:
/** Mouse Down Event Handler */ function OnMouseDown() { // if we clicked the play button if (this.name == "PlayBT") { // load the game Application.LoadLevel("game"); } }We’re using the MonoBehaviour OnMouseDown(…) function, invoked every time the BoxCollider is clicked by the mouse. We check whether the button clicked is called "PlayBT", and if so we use Application.LoadLevel(…) to load the "game" scene.
Enough talk – go run it and watch your game come to life when you click Play!
Note: If you click Play and have found yourself with a build settings error, don’t fret; you just need to check your build settings – revisit Step 4.
Step 7: Ending the Game
So the menu to start the game is great but the game technically never ends since when the timer runs out nothing happens… let’s fix that now.
Open the CountdownTimer script and at the bottom of the
Tick()function add the following line:Application.LoadLevel("menu");Now re-run your game and when the timer runs out you’ll be taken back to the menu! Easy Peasy!
There we go – a basic menu added to our game. Now let’s make it a little more user friendly with a help screen to explain how to play.
Step 8: Adding the Help Button
The help button is identical to the PlayBT in practically every way, so go ahead and duplicate the PlayBT, rename it to HelpBT and position it below the Play button. Adjust the text property to say "Help" rather than "Play" and perhaps make the Font Size a little smaller as shown below – I used 100.
Now open the MenuMouseHandler script and add the following
else ifblock to your existingifstatement.// if we clicked the help button else if (this.name == "HelpBT") { // rotate the camera to view the help "page" }If you check the preview you’ll see that when you click Help the camera rotates around to show the help menu. So, how do we do that?
Step 9: God Save iTween
Our camera rotation can all be done nice and cleanly in one line, thanks to iTween. Without iTween life wouldn’t be nearly as fun. As the name may give away it’s a tweening engine, built for Unity. It’s also free.
Go ahead and open iTween within the Unity Asset store, then click Download/Import and import it all into your project. Once it’s imported you need to move the iTween/Plugins directory into the root of your Assets folder.
You’re now all set to tween your life away!
Step 10: Rotating the Camera
Grab a piece of paper and a pen (or open a blank document) and make a note of your Main Camera’s Y rotation value, as circled below.
Within the scene view rotate the camera around in whichever direction you like around the Y axis so that the Play and Help text are out of view and so that you’ve got a decent background for your help page. You can see mine below: I rotated from -152 to -8.
Return to your MenuMouseHandler script and add the following line within the
else ifstatement:We use Camera.main to retrieve the main camera (defined by the "MainCamera" tag) from the scene and use iTween.RotateTo(…) to rotate the camera to a specific angle – in my case
-8.(You need to replace the
-8within the above line with your camera’s current rotation.)Now go back to your scene and return your camera back to its original rotation that you wrote down at the start of this section, so that it’s facing the PlayBT. Run your game, click Help and the camera should spin around. Woo!
Note: If you get an error about iTween not existing then you haven’t imported it properly – revisit Step 9.
Now let’s build our Help page.
Step 11: Building the Help Page
Rotate your camera back to the Y rotation of your help page – in my case
-8.Now we’re going to add a little explanation text as well as some more text to explain the different pickups and their scores. Finally, we’ll add a Back button to return to the main menu. You can arrange your page in whatever way you wish so feel free to get creative. Here we go…
Duplicate the HelpBT, rename it HelpHeader, set its rotation to that of your camera, change the Anchor value to "upper center" and reduce the Font Size – I used 60.
Next, copy and paste the below paragraph into the
textproperty:"Collect as many Cubes as you can within the time limit.
Watch out though, they change over time!
Note: It’s worth noting that you can’t type multiline text into the text property; you have to type it in another program and then copy and paste it since pressing enter/return assigns the field.
Finally remove the Box Collider and MenuMouseHandler Components within the Inspector since this text won’t need to be clickable. Hopefully you end up with something like this:
Now drag a pickup prefab into the scene and position it on the screen. I put mine as shown below.
Next, duplicate the HelpHeader, rename it to HelpPowerups, change the Anchor to "upper-left" and copy and paste the below paragraph into the text field.
"Green: + 2 Points
Pink: +/- Random Points
Blue: Random Speed Boost"
Reposition it so you have something like the below.
All that’s left now is to add a Back button to return to the main menu.
Step 12: The Help Page Back Button
Duplicate the HelpBT, rename it BackBT, change its text to "Back", set its rotation to that of your camera and use the Scene View to reposition it within the Game View. I placed mine in the bottom corner as shown here:
Now we just need to update our MenuMouseHandler script to handle mouse clicks from the BackBT as well. Add the following
else ifblock to the bottom of theOnMouseDown()ifstatements:// if we clicked the Back button else if (this.name == "BackBT") { // rotate the camera to view the menu "page" iTween.RotateTo(Camera.main.gameObject, Vector3(0, -152, 0), 1.0); }This is nearly identical to the previous iTween statement, the only difference being the angle the camera is rotated to –
-152in my case. You need to change this number to the original Y rotation of your camera was (the one you wrote down, remember?)Now all you need to do it set your camera back to its original rotation – the value you just added to the iTween statement – so that it’s facing the main menu again.
Run the game and your camera should spin round to reveal the help page and spin back round to the main menu. Congratulations, you’ve finished!
Conclusion
I hope you’ve enjoyed this Getting Started with Unity Session!
In this part we’ve covered using GameObjects as menu items and the incredibly powerful tweening library, iTween.
If you want an extra challenge, try using iTween to change the text colour on MouseOver and then back again on MouseExit. (You’ll find a list of Mouse handlers on this page.)
Or add an iTween CameraFade and then fade it out when the timer runs out, then load then menu – rather than abruptly ending the game. You could then delay the call to
Application.LoadLevel(...)usingyield WaitForSeconds(...).Let me know how you get on in the comments!
We’ve tackled drawing curves, and finding their quadratic and cubic roots, as well as handy applications for using quadratic roots within games. Now, as promised, we’ll look at applications for finding cubic roots, as well as curves’ gradients and normals, like making objects bounce off curved surfaces. Let’s go!
Example
Let’s take a look one practical use of this math:
In this demo, the “ship” bounces off the edges of the SWF and the curve. The yellow dot represents the closest point to the ship that lies on the curve. You can adjust the shape of the curve by dragging the red dots, and adjust the movement of the ship using the arrow keys.
Step 1: Shortest Distance to a Curve
Let’s consider the scenario where a point is located near a quadratic curve. How do you calculate the shortest distance between the point and the curve?
Well, let’s start with Pythagoras’s Theorem.
\[
Let\ the\ point\ be\ (x_p,\ y_p)\\
and\ call\ the\ closest\ point\ on\ the\ curve\ (x_c,\ y_c)\\
Then:\\
z^2 = x^2 + y^2\\
z^2 = (x_c-x_p)^2 + (y_c-y_p)^2\\
Given\ y_c=ax_c^2 + bx_c + c,\\
z^2 = (x_c-x_p)^2 + [(ax_c^2 + bx_c + c) -y_p]^2
\]
You can see that we have substituted \(y_c\) with the quadratic equation. At a glance, we can see the highest power is 4. Thus, we have a quartic equation. All we need to do is to find a minimum point in this equation to give us the shortest distance from a point to a quadratic curve.
But before that, we’ll need to understand gradients on a curve…
Step 2: Gradient of a Curve
Before we look at the problem of minimizing a quartic equation, let’s try to understand gradients of a curve. A straight line has only one gradient. But a quadratic curve’s gradient depends on which point on the curve we refer to. Check out the Flash presentation below:
Drag the red dots around to change the quadratic curve. You can also play with the slider’s handle to change the position of blue dot along x. As the blue dot changes, so will the gradient drawn.
Step 3: Gradient Through Calculus
This is where calculus will come in handy. You may have guessed that differentiating a quadratic equation would give you the gradient of the curve.
\[
f(x) = ax^2+bx+c\\
\frac{df(x)}{dx} = 2ax+b
\]
So \(\frac{df(x)}{dx}\) is the gradient of a quadratic curve, and it’s dependant on the \(x\) coordinate. Well, good thing we’ve got a method to handle this:
diff1(x:Number)will return the value after a single differentiation.To draw the gradient, we’ll need an equation to represent the line, \(y=mx+c\). The coordinate of the blue point \((x_p, y_p)\) will be substituted into the \(x\) and \(y\), and the gradient of the line found through differentiation will go into \(m\). Thus the y-intercept of line, \(c\) can be calculated through some algebra work.
Check out the AS3:
Step 4: Coordinate Systems
Always bear in mind of the inverted y-axis of Flash coordinate space as shown in the image below. At first glance, the diagram on right may seem like a negative gradient – but due to the inverted y-axis, it’s actually a positive gradient.
The same goes for the minimum point as indicated below. Because of the inverted y-axis, the minimum point in Cartesian coordinate space (at (0,0)) looks like a maximum in Flash coordinate space. But by referring to the location of origin in Flash coordinate space relative to the quadratic curve, it’s actually a minimum point.
Step 5: Rate of Change for Gradient
Now let’s say I’m interested in finding the lowest point on a curve – how do I proceed? Check out the image below (both figures are in the same coordinate space).
In order to get the minimum point, we’ll just equate \(\frac{df(x)}{dx} = 0\), since by definition we’re looking for the point where the gradient is zero. But as shown above, it turns out that the maximum point on a curve also satisfies this condition. So how do we discriminate between these two cases?
Let’s try differentiation of the second degree. It’ll give us the rate of change of the gradient.
\[
\frac{df(x)}{dx} = 2ax+b\\
\frac{df^2(x)}{dx^2} = 2a
\]
I’ll explain with reference to the image below (drawn in Cartesian coordinate space). We can see that, as we increment along the x-axis, the gradient changes from negative to positive. So the rate of change should be a positive value.
We can also see that when \(\frac{df^2(x)}{dx^2}\) is positive, there’s a minimum point on the curve. Conversely if the rate is negative, a maximum point is present.
Step 6: Back to the Problem
Now we are ready to solve the problem presented in Step 1. Recall the quartic equation (where the highest degree is 4) we arrived at:
\[
z^2 = (x_c-x_p)^2 + [(ax_c^2 + bx_c + c) -y_p]^2
\]
The same quartic equation, plotted
Remember, we are interested to find the minimum point on this curve, because the corresponding point on the original quadratic curve will be the point that’s at the minimum distance from the red dot.
So, let’s differentiate the quartic function to get to gradient of this curve, and then equate the gradient of this quartic function to zero. You will see that the gradient is actually a cubic function. I’ll refer interested readers to Wolfram’s page; for this tutorial, I’ll just pluck the result of their algebra workings:
\[
\frac{d(z^2)}{dx}=
2(x_c-x_p) + 2(ax_c^2 + bx_c + c - y_p)(2ax_c+b)\\
\frac{d(z^2)}{dx}= 2a^2(x_c)^3+3ab(x_c)^2+(b^2+2ac-2ay_p+1)(x_c)+(bc-by_p-x_p)\\
Equate\ gradient\ to\ 0\\
\frac{d(z^2)}{dx}=0\\
2a^2(x_c)^3+3ab(x_c)^2+(b^2+2ac-2ay_p+1)(x_c)+(bc-by_p-x_p)=0\\
Compare\ with\ cubic\ equation\\
Ax^3+Bx^2+Cx+D=0\\
A = 2a^2\\
B=3ab\\
C=b^2+2ac-2ay_p+1\\
D=bc-by_p-x_p
\]
Solve for the roots of this (rather messy) cubic function and we’ll arrive at the coordinates of the three blue points as indicated above.
Next, how do we filter our results for the minimum point? Recall from the previous step that a minimum point has a rate of change that’s positive. To get this rate of change, differentiate the cubic function that represents gradient. If the rate of change for the given blue point is positive, it’s one of the minimum points. To get the minimum point, the one that we’re interested in, choose the point with the highest rate of change.
Step 7: Sample of Output
So here’s a sample implementation of the idea explained above. You can drag the red dots around to customise your quadratic curve. The blue dot can also be dragged. As you move the blue dot, the yellow one will be repositioned so that the distance between the blue and yellow dots will be minimum among all points on the curve.
As you interact with the Flash presentation, there may be times where three yellow dots appear all at once. Two of these, faded out, refer to the roots obtained from the calculation but rejected because they are not the closest points on the curve to the blue dot.
Step 8: ActionScript Implementation
So here’s the ActionScript implementation of the above. You can find the full script in
Demo2.as.First of all, we’ll have to draw the quadratic curve. Note that the matrix
m2will be referred to for further calculation.private function redraw_quadratic_curve():void { var cmd:Vector.<int> = new Vector.<int>; var coord:Vector.<Number> = new Vector.<Number>; //redraw curve; m1 = new Matrix3d( curve_points[0].x * curve_points[0].x, curve_points[0].x, 1, 0, curve_points[1].x * curve_points[1].x, curve_points[1].x, 1, 0, curve_points[2].x * curve_points[2].x, curve_points[2].x, 1, 0, 0,0,0,1 ); m2 = new Matrix3d( curve_points[0].y, 0, 0, 0, curve_points[1].y, 0, 0, 0, curve_points[2].y, 0, 0, 0, 0,0,0,1 ) m1.invert(); m2.append(m1); quadratic_equation.define(m2.n11, m2.n21, m2.n31); for (var i:int = 0; i < stage.stageWidth; i+=2) { if (i == 0) cmd.push(1); else cmd.push(2); coord.push(i, quadratic_equation.fx_of(i)); } graphics.clear(); graphics.lineStyle(1); graphics.drawPath(cmd, coord); }And here’s the one that implements the mathematical concept explained.
c1refers to a point randomly positioned on stage.private function recalculate_distance():void { var a:Number = m2.n11; var b:Number = m2.n21; var c:Number = m2.n31; /*f(x) = Ax^3 + Bx^2 +Cx + D */ var A:Number = 2*a*a var B:Number = 3*b*a var C:Number = b*b + 2*c*a - 2*a*c1.y +1 var D:Number = c * b - b * c1.y - c1.x quartic_gradient = new EqCubic(); quartic_gradient.define(A, B, C, D); quartic_gradient.calcRoots(); roots = quartic_gradient.roots_R; var chosen:Number = roots[0]; if (!isNaN(roots[1]) && !isNaN(roots[2])) { //calculate gradient and rate of gradient of all real roots var quartic_rate:Vector.<Number> = new Vector.<Number>; for (var i:int = 0; i < roots.length; i++) { if (!isNaN(roots[i])) quartic_rate.push(quartic_gradient.diff1(roots[i])); else roots.splice(i, 1); } //select the root that will produce the shortest distance for (var j:int = 1; j < roots.length; j++) { //the rate that corresponds with the root must be the highest positive value //because that will correspond with the minimum point if (quartic_rate[j] > quartic_rate[j - 1]) { chosen = roots[j]; } } //position the extra roots in demo position_extras(); } else { //remove the extra roots in demo kill_extras(); } intersec_points[0].x = chosen intersec_points[0].y = quadratic_equation.fx_of(chosen); }Step 9: Example: Collision Detection
Let’s make use of this concept to detect the overlap between a circle and a curve.
The idea is simple: if the distance between the the blue dot and the yellow dot is less than blue dot’s radius, we have a collision. Check out the demo below. The interactive items are the red dots (to control the curve) and the blue dot. If the blue dot is colliding with the curve, it will fade out a little.
Step 10: ActionScript Implementation
Well, the code is quite simple. Check out the full source in
CollisionDetection.as.graphics.moveTo(intersec_points[0].x, intersec_points[0].y); graphics.lineTo(c1.x, c1.y); var distance:Number= Math2.Pythagoras( intersec_points[0].x, intersec_points[0].y, c1.x, c1.y) if (distance < c1.radius) c1.alpha = 0.5; else c1.alpha = 1.0; t.text = distance.toPrecision(3);Step 11: Bouncing Off the Curve
So now that we know when collision will occur, let’s try to program some collision response. How about bouncing off the surface? Check out the Flash presentation below.
You can see the ship (triangle shape), is surrounded by a circle (translucent blue). Once the circle collides with the curve, the ship will bounce off the surface.
Step 12: Controlling the Ship
Here’s the ActionScript to control the ship.
public function CollisionDetection2() { /** * Instantiation of ship & its blue-ish circular area */ ship = new Triangle(); addChild(ship); ship.x = Math.random() * stage.stageWidth; ship.y = stage.stageHeight * 0.8; c1 = new Circle(0x0000ff, 15); addChild(c1); c1.alpha = 0.2; /** * Ship's velocity */ velo = new Vector2D(0, -1); updateShip(); stage.addEventListener(KeyboardEvent.KEY_DOWN, handleKey); stage.addEventListener(KeyboardEvent.KEY_UP, handleKey); stage.addEventListener(Event.EXIT_FRAME, handleEnterFrame); /** * The curve and the calculations */ quadratic_equation = new EqQuadratic(); curve_points = new Vector.<Circle>; populate(curve_points, 0xff0000, 3); intersec_points = new Vector.<Circle>; populate(intersec_points, 0xffff00, 3, false); redraw_quadratic_curve(); } private function handleKey(e:KeyboardEvent):void { if (e.type == "keyDown") { if (e.keyCode == Keyboard.UP) isUp = true; else if (e.keyCode == Keyboard.DOWN) isDown = true; if (e.keyCode == Keyboard.LEFT) isLeft = true; else if (e.keyCode == Keyboard.RIGHT) isRight = true; } if (e.type == "keyUp") { if (e.keyCode == Keyboard.UP) isUp = false; else if (e.keyCode == Keyboard.DOWN) isDown = false; if (e.keyCode == Keyboard.LEFT) isLeft = false; else if (e.keyCode == Keyboard.RIGHT) isRight = false; } } private function handleEnterFrame(e:Event):void { /** * Control the magnitude */ if (isUp) velo.setMagnitude(Math.min(velo.getMagnitude()+0.2, 3)); else if(isDown) velo.setMagnitude(Math.max(velo.getMagnitude()-0.2, 1)); /** * Control the direction */ if (isRight) velo.setAngle(velo.getAngle() + 0.03); else if (isLeft) velo.setAngle(velo.getAngle() - 0.03); recalculate_distance(); if (distance < c1.radius) bounce(); updateShip(); } /** * Update ship's position, orientation and it's area (the blue-ish circle) */ private function updateShip():void { ship.x += velo.x; ship.y += velo.y; ship.rotation = Math2.degreeOf(velo.getAngle()); c1.x = ship.x; c1.y = ship.y; if (ship.x > stage.stageWidth || ship.x < 0) velo.x *= -1; if (ship.y > stage.stageHeight || ship.y < 0) velo.y *= -1; }You can see that the keyboard controls are updating flags to indicate whether the left, up, right, or down keys are being pressed. These flags will be captured by the enterframe event handler and update the magnitude and direction of the ship.
Step 13: Calculating the Reflection Vector
I’ve already covered the vector calculation of reflection vector in this post. Here, I shall just cover how to obtain the normal vector from gradient.
\[
\frac{df(x)}{dx}=gradient\\
line\ gradient=\frac{y}{x}\\
Assume\ gradient=0.5\\
y=0.5\\
x=1\\
Vector\ of\ left\ normal=
\begin{bmatrix}-1 \\0.5\end{bmatrix}\\
Vector\ of\ right\ normal=
\begin{bmatrix}1 \\-0.5\end{bmatrix}
\]
Step 14: ActionScript Implementation
So the ActionScript below will implement the mathematical concept explained in the previous step. Check out the highlighted lines:
private function bounce():void { var gradient:Number = quadratic_equation.diff1(intersec_points[0].x); var grad_vec:Vector2D = new Vector2D(1, gradient); var left_norm:Vector2D = grad_vec.getNormal(false); var right_norm:Vector2D = grad_vec.getNormal(); var chosen_vec:Vector2D; if (velo.dotProduct(left_norm) > 0) chosen_vec = left_norm else chosen_vec = right_norm var chosen_unit:Vector2D = chosen_vec.normalise(); var proj:Number = velo.dotProduct(chosen_unit); chosen_unit.scale(-2*proj); velo = velo.add(chosen_unit); }Conclusion
Well, thanks for your time! If you’ve found this useful, or have any questions, do leave some comments.
This week, Matt Porter critiques Jelly Escape, a popular new platformer developed by TawStudio Entertainment. Platformers are a dime a dozen on Flash portals, so let’s see what this one brings to the table…
Play the Game
Play the game at full size over on TawStudio’s site!
Overview
The web has always had an abundance of platformer games, thanks to Flash. Unfortunately, most of them lack any kind of serious polish. Fortunately, Jelly Escape isn’t one of them; it’s an incredibly well built platformer, and it’s clear that the developers did their homework and studied industry hit and obvious inspiration: Super Meat Boy.
Gameplay
Jelly Escape’s main focus is simple, quick, addicting platforming, and achieves this quite effectively. The game has a number of tricks up its sleeve to engage the player beyond the first few minutes, and the overall level of polish means players are likely to stick with the game long enough to get to that point.
The most immediate, and engaging aspect of the game to me, is the retro feel. While there has been a surplus of retro-inspired games since the introduction of great Flash game frameworks such as Flixel and FlashPunk, most don’t take that retro-magic to the same level.
As soon as I saw the opening animation where quarters were being popped into an arcade machine, I was hooked. Even if you’re not a hardcore fan of classic gaming, the added story is a very welcome addition, and is sure to please most.
Level Select
The second thing I noticed and was impressed with in Jelly Escape was the sheer number of levels the game had to offer. As soon as you start the game, you can see that there at least 60 – that’s a lot of levels! These are split into four sections of fifteen levels each, so I was pretty convinced that variety wouldn’t be an issue.
While I do love the sheer number of levels offered, I do think all great platformers should have a world map of sorts since, to me, exploration is one of my favorite aspects of gaming. Not only is arranging 60 levels as boxes with numbers a tad boring, it straight up tells you how many levels there are, and doesn’t leave any mystery for the player. I personally love unlocking a new world when I think a game is about to be complete, and this setup just doesn’t allow for that.
Another possible negative aspect of telling the player how many levels there are, is that they may find themselves slightly bored part way through your game and, knowing they’re nowhere near the end, just stop playing. If they thought that they might be nearly finished, they might just stick around another few levels to finish it up. During this time they may become bored and leave anyways, complete the game, or they might just hit your next big game mechanic that adds a breath of fresh air into their gaming experience, keeping them around that much longer.
There are a few levels that can be unlocked through your actions…
A proper compromise to having a full blown world map, versus a simple menu setup, would be to unlock new sets of levels upon the completion of a previous set. Jelly Escape has four sets, but perhaps only one needs to be seen at first; the unlocking of the others could have been used to spice up the player’s experience. Even if my love for exploration isn’t shared by all, it’s important as a developer to understand the various experiences that any gamer enjoys, so that you can be sure to fit in as many positive experiences as your game’s design will allow, and please as large an audience as possible.
Rewards and Stats
One of the strongest points of Jelly Escape is how much – and how often – the player is rewarded. Some of these rewards are given by playing the game well, while others are a bit more creative and are rewarded for completing actions such as following the developers on Twitter and Facebook.
While I’m generally against gimmicks, the unlockables are purely bonus material, and truly don’t take away from the game if you pass them up – so no harm done. It’s actually a great move on the developers part, and I’ll probably use it myself in future games, as I like their implementation.
In addition to the unlockables, the game also keeps track of a ton of stats, such as your best times and lowest death count for each level. Small features like this make for great replay value, and add a lot to the game.
Like many games of this style, Jelly Escape keeps track of how many of a certain collectible you get in each level. This information is shown on the level select screen, and is easily to understand. Unlike certain games, this rating actually matters. Far too many games will give you a “3 star rating” system of sorts, just for the hell of it, with no real incentive for the player to go back and master levels, but Jelly Escape offers two incentives to collect the energy bolts.
The first, which is my favorite, is that you can find characters locked up behind a gate within select levels (as shown on the level select screen), and if you’ve collected enough of the energy collectibles, you can set them free. Once free, you can use their appearance as your jelly’s skin, which is a pretty awesome addition, especially since some of the skins are based off of characters from other popular games.
The second, is that collecting enough bolts will unlock additional challenge levels. I played a few of these myself, and they definitely had an “unlockable level” kind of feel, much like the “secret” zone found in Super Mario World. Between these two awesome incentives, I definitely felt encouraged to replay levels, rather than to just rush through the game.
Level Design
Level design is a strong point of Jelly Escape, as it should be with any platformer. There aren’t too many mechanics in the game, but what’s there is used very well.
I never found the level design to be too repetitive or bland, and was actually impressed with the overall diversity. In addition, the difficulty curve seemed to be rather balanced, as few levels stood out as too hard, but it did feel like the difficulty was slowly climbing. To make things even better, levels are littered with checkpoints so that death isn’t overly punishing: an excellent addition that makes this game far more friendly with its casual target market. Plus, I never encountered any levels that were strictly tedious and not fun.
While the level design is quite well done, some core design choices of the game do take away from the overall fun factor. The game has a rather zoomed out feel, because all of the levels are presented within a single screen. While this seems to be accepted among many platformers, I feel that a more zoomed in view, with a camera for scrolling, is the better approach. To me, this allows for more precise movement, and gives a hint of exploration during gameplay, as you can’t see everything at once.
On the other hand, the game design often requires the levels to fit in a single screen – for example, falling through the bottom of the level simply brings you back to the top, and some levels are designed around this – so perhaps this is just a matter of my preferences getting in the way.
I will note that Super Meat Boy isn’t limited to a single screen however, and I feel that if it had been, it would greatly deter from the experience. Perhaps this was a deliberate design choice, perhaps scrolling levels were simply a technical complexity the developers didn’t want to mess with; either way, I still feel a more zoomed-in experience would benefit the game’s design.
Graphics
The graphics in Jelly Escape are retro, but kick things up with with massive attention to small details in visual effects. Touching walls covers them with your jelly’s slime, and spits out particle effects. Dying makes the edges of the screen tweak out. There’s a neat lighting effect in place where you can see better around your character, and collecting energy within a level increases this effect significantly.
The game is also very animation complete, something that is often lacking in indie platformers. The player is excellently animated, energy collectibles are animated, springs are animated; everything that should have an animation, does. All menus are very clean, and all menu items have a hover effect, which makes for a very polished game. While you may think that menus are nearly worthless in a game as simple as a platformer, retaining a polished and positive image consistently throughout the project is extremely important. If you’re not willing to polish your audio-mute button, don’t bother polishing your enemy animations, as a lack of consistency breaks the entire image.
All in all, the graphical quality of this greatly excels because of the extreme attention to small details. No single effect is a game changer on its own, but together, they make this game look ten times better than it would without them.
Audio
While the audio is nothing amazing in Jelly Escape, it’s still quite well done. I find the music very fitting to the somewhat dark and ominous atmosphere, and I personally feel it’s a very good fit.
As was the case with animations and effects for grpahics, Jelly Escape is extremely complete in the audio department. There’s sounds for literally everything. Jumping has a sound, landing has a sound, enemies hitting walls, opening gates, hitting buttons, dying, hovering over menu items; everything in the entire game has a sound, as it should be.
To further add polish, the toggle music and sound buttons are pretty awesome looking too. Each is its own small audio visualizer, which I found very neat. This is most definitely not needed, but if I noticed it, others did too, so take note of the positive effects such features can have on a players mode, and how it can effect their perception of an author’s hard work.
Conclusion
Jelly Escape may have its flaws, but I haven’t played a web platformer this polished and addicting since MoneySeize, and those were great (albeit somewhat frustrating) times.
Yikes!
Jelly Escape doesn’t take many shortcuts. A ton of unlockables, a huge array of levels, and badges on Kongregate make the addicting qualities of the game shine brighter than ever.
I definitely plan on revisiting the game to finish up those last 30 levels or so, as I’m a sucker for platformers; how far did you play before losing interest?
In this Premium series, you’ll learn how to use Flash to build a Facebook Graph API application that can create slideshows for your public pages. This third part covers generating a code to load the events from a Facebook Page.
Premium Preview
Click to try the app on Facebook.
In the second part, we created the embed code for the album (basically finding out the album ID from which we load the photos). This time, we are going to use the Graph API to get a list of a page’s events, based on its Page ID.
You’ll need to be logged in to Facebook in order to see this demo: https://apps.facebook.com/activetuts_tabmaker/
Read the Full Tutorial
Premium members can access the full tutorial right away!
If you’re not yet a Premium member, you can still read the first few steps for free.
Tuts+ Premium Membership
We run a Premium membership system which periodically gives members access to extra tutorials, like this one, from across the whole Tuts+ network. If you’re a Premium member, you can log in and read the tutorial. If you’re not a member, you can of course join today!
Also, don’t forget to follow @envatoactive on twitter, circle us on Google+, like us on Facebook, and grab the Activetuts+ RSS Feed to stay up to date with the latest tutorials and articles.
In the first tutorial of this series, we took a look at drawing curves using equations and AS3. Now, we’re going to tackle solving those equations to find the roots of a curve – that is, the places where the curve crosses a given straight line. We can use this to predict collisions with curved surfaces, and to avoid “tunnelling” in Flash games.
Step 1: Quadratic Roots
First, time for some quick math revision. In this tutorial, we’ll just accept and apply the methods we’ll use, but interested readers can refer to Wikipedia’s page on quadratic equations for information about the mathematical deriviations.
So \(f(x)\) is a quadratic function. If \(f(x)\) is equivalent to 0, \(x\) can be obtained by this formula:
\[Given\ f(x)\ = \ ax^2+bx+c,\ \]
\[f(x)\ =\ 0,\ x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a} \]
\(b^2 – 4ac\) is called the discriminant of the formula. If the discriminant is negative, the square root of the discriminant will produce imaginary roots, which we can’t plot. Conversely, if the discriminat is positive, you will have real number roots and you’ll be able to plot them onto the screen.
Step 2: Visualising Quadratic Roots
So what are roots? Well in our context they are nothing more than intersection points between the quadratic curve and a line. For example, suppose we are interested to find the intersection point(s) of the following set of equations:
\(
f(x)\ = \ ax^2+bx+c \\
g(x)\ = \ 0
\)
This is a typical scenario of looking for the intersection point(s) between a quadratic curve and the x-axis (because the x-axis is the line where
y==0). Since by definition the intersection point(s) are shared by \(f(x)\) and \(g(x)\), we can conclude that \(f(x) = g(x)\) for the values ofxthat we are looking for.It’s then a trivial operation where you just substitute the functions and then apply the formula from Step 1 to obtain the roots. Now there are several possibilities we can anticipate as shown below.
(As you can see, “imaginary roots” means, for our purposes, that the curve doesn’t ever cross the x-axis.)
Now let’s consider the case where \(g(x)\) is more than just a mundane horizontal line. Let’s say it’s a slanted line, \(g(x)\ =\ mx\ +\ d\). Now when we equate both functions, we’ll need to do a little precalculation before the formula can be effectively applied.
\[
ax^2\ +\ bx + c\ =\ mx\ +\ d\\
ax^2\ +\ (\ b\ - m)\ x + (c\ -\ d)\ =\ 0
\]
I’ve included an interactive Flash presentation below so feel free to drag the red and blue dots. Yellow dots indicate the intersection points. You may need to position the curve and line to intersect each other in order for the yellow dots to appear.
Step 3: Plotting This With ActionScript
The full script can be found in
Demo1.as; here I’ll just explain a crucial extract of the code. Let’s look at the AS3 for drawing the curve and line:private function redraw():void { var cmd:Vector.<int> = new Vector.<int>; var coord:Vector.<Number> = new Vector.<Number>; //redraw curve; m1 = new Matrix3d( curve_points[0].x * curve_points[0].x, curve_points[0].x, 1, 0, curve_points[1].x * curve_points[1].x, curve_points[1].x, 1, 0, curve_points[2].x * curve_points[2].x, curve_points[2].x, 1, 0, 0,0,0,1 ); m2 = new Matrix3d( curve_points[0].y, 0, 0, 0, curve_points[1].y, 0, 0, 0, curve_points[2].y, 0, 0, 0, 0,0,0,1 ) m1.invert(); m2.append(m1); quadratic_equation.define(m2.n11, m2.n21, m2.n31); for (var i:int = 0; i < stage.stageWidth; i+=2) { if (i == 0) cmd.push(1); else cmd.push(2); coord.push(i, quadratic_equation.fx_of(i)); } //draw line n1 = new Matrix(); n1.a = line_points[0].x; n1.c = 1; n1.b = line_points[1].x; n1.d = 1; n2 = new Matrix(); n2.a = line_points[0].y; n2.c = 0; n2.b = line_points[1].y; n2.d = 0; n1.invert(); n2.concat(n1); var x:Number = stage.stageWidth //y = mx + c cmd.push(1); coord.push(0, n2.a * 0 + n2.b); cmd.push(2); coord.push(x, n2.a * x + n2.b); graphics.clear(); graphics.lineStyle(1); graphics.drawPath(cmd, coord); }The bulk of ActionScript for drawing the curve from line 80~104 is largely borrowed from the previous tutorial, so I shall just explain a little about the code for drawing a line.
In the Flash presentation above, there are two interactive blue dots. Each of these has coordinates, and with both dots, a line is formed. Since both dots lie on the same line, they share a common slope and y-intercept to form one general line equation:
\[
y\ =\ mx\ +\ d,\\
m\ =\ slope,\ d\ =\ y-intercept
\]
We can use a little algebra to solve for the two unknowns, \(m\) and \(d\). Given the coordinates of the two blue dots as \((x_1,\ y_1)\) and \((x_2,\ y_2)\):
\(
y_1 = mx_1 + d\\
y_2 = mx_2 + d\\
\)
\(
\begin{bmatrix}y_1 \\y_2\end{bmatrix} =
\begin{bmatrix}x_1 & 1\\x_2 & 1\end{bmatrix}
\begin{bmatrix}m \\d\end{bmatrix} \\
\)
\(
\begin{bmatrix}x_1 & 1\\x_2 & 1\end{bmatrix}^{-1}
\begin{bmatrix}y_1 \\y_2\end{bmatrix} =
\begin{bmatrix}x_1 & 1\\x_2 & 1\end{bmatrix}^{-1}
\begin{bmatrix}x_1 & 1\\x_2 & 1\end{bmatrix}
\begin{bmatrix}m \\d\end{bmatrix} \\
\)
\(
\begin{bmatrix}x_1 & 1\\x_2 & 1\end{bmatrix}^{-1}
\begin{bmatrix}y_1 \\y_2\end{bmatrix} =
I
\begin{bmatrix}m \\d\end{bmatrix}
\)
(Note that a matrix with superscripted -1 refers to inverse of that matrix.)
So using this, \(m\) and \(d\) are calculated. We can can now draw the line by joining the coordinates \((0, y_3)\) and \((stage.stageWidth, y_4)\). How do you find \(y_3\) and \(y_4\)? Well now that \(m\), \(x\) and \(d\) are known, we can simply put all these values into the line general equation,
\(y\ =\ mx\ +\ d\)
..to get those \(y\)s.
Step 4: Calculate the Quadratic Roots
To calculate the position of the intersecting points, we shall use the formula from Step 1. This is done in
EqQuadratic.asas the functions shown below:/**Read-only * Discriminant of equation */ public function get discriminant():Number { //B*B-4*A*C return _B * _B - 4 * _A * _C; } /** * Performs calculation to obtain roots */ public function calcRoots():void { var disc:Number = this.discriminant //handle imaginary roots if (disc < 0) { disc *= -1; var component_real:Number = -_B / (2 * _A); var component_imaginary:Number = Math.sqrt(disc) / (2 * _A); _root_i[0] = (component_real + "+ i" + component_imaginary).toString(); _root_i[1] = (component_real + "- i" + component_imaginary).toString(); } //handle real roots else { var sqrt:Number = Math.sqrt(disc); _root_R[0] = ( -_B + sqrt) / (2 * _A); _root_R[1] = ( -_B - sqrt) / (2 * _A); } }Further details of
EqQuadratic.as:Step 5: Plotting This With ActionScript
An example of utilising this
EqQuadratic.asis inDemo1.as. After the initiation ofEqQuadratic, we shall use it to calculate the roots. Then, after validating the presence of real roots, we’ll use them to plot the yellow dots.Now the roots refer to only the \(x\) component of the coordinates. To obtain the \(y\)s, guess what? Again, we put the values of \(m\), \(d\) (calculated earlier in Step 3) and \(x\) (from the roots) into the line general equation, \(y\ =\ mx\ +\ d\). Check out the corresponding code in line 135 and 136.
private function recalculate_reposition():void { quadratic_equation.define(m2.n11, m2.n21 - n2.a, m2.n31 - n2.b); quadratic_equation.calcRoots(); var roots:Vector.<Number> = quadratic_equation.roots_R; if (!isNaN(roots[0]) && !isNaN(roots[1])) { intersec_points[0].x = roots[0]; intersec_points[0].y = n2.a * roots[0] + n2.b intersec_points[1].x = roots[1]; intersec_points[1].y = n2.a * roots[1] + n2.b } else { intersec_points[0].x = -100; intersec_points[0].y = -100; intersec_points[1].x = -100; intersec_points[1].y = -100; } }Step 6: Cubic Roots
Cubic roots, not surprisingly, are the intersection points between a cubic curve and a line. But a cubic curve is a little different to a quadratic curve, and in this respect the possibilities for where intersections could be located are different.
The image below shows a cubic curve intersecting with the x-axis:
Again, here’s a little Flash presentation for you to experiment with. Red and blue dots can be dragged while the yellow ones just indicate the intersection points.
Step 7: General Formula for Cubic Roots
The general formula to find a cubic curve was discovered by Cardano. Although I’m enticed to elaborate on the details, I’ll just point interested readers to the following links:
Anyway, the
EqCubic.asclass implements this formula to resolve roots of cubic functions along with other mathematical utility functions. Generally all the attributes and methods forEqCubic.asfollow the desciption as tabled in Step 4, because both classesEqQuadratic.asandEqCubic.asimplement one common interface,IEquation.as, except for the details listed below.defineroots_R,root_iStep 8: Plotting This With ActionScript
Here’s the Actionscript implementation for the Flash presentation from Step 5. The full code is in
Demo3.as.private function redraw():void { var cmd:Vector.<int> = new Vector.<int>; var coord:Vector.<Number> = new Vector.<Number>; //redraw curve; m1 = new Matrix3d( curve_points[0].x * curve_points[0].x * curve_points[0].x, curve_points[0].x * curve_points[0].x, curve_points[0].x, 1, curve_points[1].x * curve_points[1].x * curve_points[1].x, curve_points[1].x * curve_points[1].x, curve_points[1].x, 1, curve_points[2].x * curve_points[2].x * curve_points[2].x, curve_points[2].x * curve_points[2].x, curve_points[2].x, 1, curve_points[3].x * curve_points[3].x * curve_points[3].x, curve_points[3].x * curve_points[3].x, curve_points[3].x, 1 ); m2 = new Matrix3d( curve_points[0].y, 0, 0, 0, curve_points[1].y, 0, 0, 0, curve_points[2].y, 0, 0, 0, curve_points[3].y, 0, 0, 0 ) m1.invert(); m2.append(m1); cubic_equation.define(m2.n11, m2.n21, m2.n31, m2.n41); for (var i:int = 0; i < stage.stageWidth; i+=2) { if (i == 0) cmd.push(1); else cmd.push(2); coord.push(i, cubic_equation.fx_of(i)); } //draw line n1 = new Matrix(); n1.a = line_points[0].x; n1.c = 1; n1.b = line_points[1].x; n1.d = 1; n2 = new Matrix(); n2.a = line_points[0].y; n2.c = 0; n2.b = line_points[1].y; n2.d = 0; n1.invert(); n2.concat(n1); var x:Number = stage.stageWidth //y = mx + c cmd.push(1); coord.push(0, n2.a * 0 + n2.b); cmd.push(2); coord.push(x, n2.a * x + n2.b); graphics.clear(); graphics.lineStyle(1); graphics.drawPath(cmd, coord); }Again, the ActionScript commands to draw a cubic curve are exactly the same as explained in my previous article, whereas Actionscript commands to draw the line are already explained in Step 3 of this one.
Now let’s move on to calculating and positioning the cubic roots:
private function recalculate_reposition():void { cubic_equation.define(m2.n11, m2.n21 , m2.n31 - n2.a, m2.n41 - n2.b); cubic_equation.calcRoots(); var roots:Vector.<Number> = cubic_equation.roots_R; for (var i:int = 0; i < roots.length; i++) { if (!isNaN(roots[i])) { intersec_points[i].x = roots[i]; intersec_points[i].y = n2.a * roots[i] + n2.b } else { intersec_points[i].x = -100; intersec_points[i].y = -100; } } }After instantiating
cubic_equationin the constructor we proceed on to define its coefficients, calculate the roots, and store the roots in a variable.One little note on the roots: there are a maximum of three real roots for a cubic equation, but not all real roots are present in all situation as some roots may be imaginary. So what happens when there’s only one real root, for example? Well, one of the array of roots called from
cubic_equation.roots_Rwill be a real number, while all the others will be Not a Number (NaN). Check out the highlighted ActionScript for this.Step 9: Predicting Where an Object Will Collide With Curved Surface
A great application of calculating roots is projecting a collision point onto curved surface, as show below. Use the left and right arrow keys to steer the moving ship, and press up to accelerate. You will notice that collision points which would have happened in the past are slightly dimmed.
Step 10: Implementation
The idea is similar to that in my tutorial about predicting collision points. However, instead of colliding with a straight line, we’re now using a curved line. Let’s check out the code.
The snippet below is called every frame:
private function update(e:Event):void { //Steering left and right if (control == 1) velo = velo.rotate(Math2.radianOf(-5)); else if (control == 2) velo = velo.rotate(Math2.radianOf(5)); //manipulating velocity var currVelo:Number = velo.getMagnitude(); if (increase == 0) { currVelo -= 0.5; currVelo = Math.max(currVelo, 1); //lower bound for velocity } else if (increase == 1) { currVelo += 0.5; currVelo = Math.min(currVelo, 5); //upper bound for velocity } velo.setMagnitude(currVelo); //update velocity ship.x += velo.x; ship.y += velo.y; ship.rotation = Math2.degreeOf(velo.getAngle()); //reflect when ship is out of stage if (ship.x <0 || ship.x > stage.stageWidth) velo.x *= -1; if (ship.y <0 || ship.y > stage.stageHeight) velo.y *= -1; redraw(); recalculate(); }The core code lies in
redrawandrecalculate. Let’s first see what’s inredraw. It’s the same one we had been using in previous demos. One little note on drawing the line. We saw in previous demos that two dots are needed to draw get the equation. Well, here we only have one ship. So to get the second point, just add the ship’s velocity to its current position. I’ve highlighted the code for convenience.private function redraw():void { var cmd:Vector.<int> = new Vector.<int>; var coord:Vector.<Number> = new Vector.<Number>; //redraw curve; m1 = new Matrix3d( w1.x * w1.x, w1.x, 1, 0, w2.x * w2.x, w2.x, 1, 0, w3.x * w3.x, w3.x, 1, 0, 0,0,0,1 ); m2 = new Matrix3d( w1.y, 0, 0, 0, w2.y, 0, 0, 0, w3.y, 0, 0, 0, 0,0,0,1 ) m1.invert(); m2.append(m1); quadratic_equation.define(m2.n11, m2.n21, m2.n31); minX = Math.min(w1.x, w2.x, w3.x); maxX = Math.max(w1.x, w2.x, w3.x); for (var i:int = minX; i < maxX; i+=2) { if (i == minX) cmd.push(1); else cmd.push(2); coord.push(i, quadratic_equation.fx_of(i)); } n1 = new Matrix(); n1.a = ship.x; n1.c = 1; n1.b = ship.x + velo.x; n1.d = 1; n2 = new Matrix(); n2.a = ship.y; n2.c = 0; n2.b = ship.y + velo.y; n2.d = 0; n1.invert(); n2.concat(n1); var x:Number = stage.stageWidth //y = mx + c cmd.push(1); coord.push(0, n2.a * 0 + n2.b); cmd.push(2); coord.push(x, n2.a * x + n2.b); graphics.clear(); graphics.lineStyle(1); graphics.drawPath(cmd, coord); }Now for
recalculate, I’ve done a little vector calculation to check whether the point is behind or in front of the ship. Check out the highlighted code:private function recalculate():void { quadratic_equation.define(m2.n11, m2.n21 - n2.a, m2.n31 - n2.b); quadratic_equation.calcRoots(); var roots:Vector.<Number> = quadratic_equation.roots_R; for (var i:int = 0; i < roots.length; i++) { var reposition:Sprite = getChildByName("c" + i) as Sprite //conditions: //real root, value of x within the range if (!isNaN(roots[i]) && roots[i] > minX && roots[i] < maxX) { reposition.x = roots[i]; reposition.y = n2.a * roots[i] + n2.b; //discriminating between future and already happened collision point var vec:Vector2D = new Vector2D(reposition.x - ship.x, reposition.y - ship.y); if (velo.dotProduct(vec) < 0) reposition.alpha = 0.4; else reposition.alpha = 1 } else { reposition.x = -100; reposition.y = -100; } } }Step 11: Time-Based Collision Detection
Another great application is not quite as obvious as the first. To perform a more accurate collision detection, instead of basing our conclusion on the distance between two objects, we’ll uae their time to impact. Why? Because “tunneling” can happen if we use distance based collision detection:
Consider a collision detection algorithm that’s based on distance for two circles. Of the four frames shown, only frame 2.15 successfully detected collision between two circles. Why? Because the current distance between the gray and the red circles’ centers is less than the sum of both circles’ radii.
(Readers interested on more details on this topic can refer to this article.)
\[distance\ between\ circles\ < \ radius_{red}\ +\ radius_{gray}\]
The problem is caused by how Flash proceeds by one discrete frame at a time, which means that only frames 1, 2, and 3 will be successfully captured, and not the moments in between thos snapshots of time. Now the gray and red circles did not collide in these frames according to a distance-based calculation, so the red circle tunnels right through the gray one!
To fix this, we need a way to see the collision that occurred between frames 2 and 3. We need to calculate the time to impact between two circles. For example, once we check that time to impact is less than 1 frame at frame 2, this means that once Flash proceeds 1 frame forward collision or even tunneling will have definitely had taken place.
\[if\ time\ to\ impact,\ t,\ fulfills\ 0\ < \ t\ <\ 1,\ tunneling\ will\ happen\ next\ frame\]
The question is, how do we calculate this time?
Step 12: Precalculations
I’ll try to show my method as simply as possible.
Given the scenario above, the two gray and red circles are currently located at \((x_{gray},\ y_{gray})\) and \((x_{red},\ y_{red})\). They are moving at \(v_{gray}\) and \(v_{red}\) respectively, and set on a collision path. We are interested to calculate the time taken, \(t\), for them to reach positions \((x’_{gray},\ y’_{gray})\) and \((x’_{red},\ y’_{red})\), indicated by the translucent gray and red circles, where the collision occurred.
\[
displacement_{future} = displacement_{present}+velocity*time\\
x'_{gray}=x_{gray}+v_{gray_x}*t\ ...(eq.\ 1)\\
y'_{gray}=y_{gray}+v_{gray_y}*t\ ...(eq.\ 2)\\
x'_{red}=x_{red}+v_{red_x}*t\ ...(eq.\ 3)\\
y'_{red}=y_{red}+v_{red_y}*t\ ...(eq.\ 4)
\]
Note that I’ve derived the horizontal and vertical components of \(v_{gray}\) into \(v_{gray_x}\) and \(v_{gray_y}\). Same goes with velocity of the red circle; check out this Quick Tip to get to know how these components are derived.
We still lack one relationship to bind all these equations together. Let’s see the image below.
The other relationship goes back to Pythagoras. When both circles meet, the distance between both centers is exactly \(rad_{gray}\) plus \(rad_{red}\).
\[
Pythagoras'\ Theorem,\ z^2=x^2+y^2\\
(rad_{gray}+rad_{red})^2=(x'_{gray}-x'_{red})^2+(y'_{gray}-y'_{red})^2\ ...(eq.\ 5)\\
\]
This is where you substitute equations 1~4 into equation 5. I understand its quite daunting mathematically, so I’ve separated it out into Step 13. Feel free to skip it to arrive at the result at Step 14.
Step 13 (Optional): Mathematical Rigour
First, we establish the following identities.
\[
Identity,\\
(a+b)^2=a^2+2ab+b^2\ ...(id.\ 1)\\
(a-b)^2=a^2-2ab+b^2\ ...(id.\ 2)\\
\]
At all times, bear in mind that all mathematical symbols represent a constant except for time, \(t\), which is the subject of interest.
\(x_{gray},\ v_{gray_x},\ y_{red}, \) and so on are all defined in the scenario.
Next, we’ll try to break our problem down term by term:
\[
(rad_{gray}+rad_{red})^2=(x'_{gray}-x'_{red})^2+(y'_{gray}-y'_{red})^2\\
Consider\ term\ (x'_{gray}-x'_{red})^2\ and\ utilising\ id.\ 2\\
(x'_{gray}-x'_{red})^2 = (x'_{gray})^2-2(x'_{gray})(x'_{red})+(x'_{red})^2\\
\]
\[
Consider\ term\ (x'_{gray})^2\\
(x'_{gray})^2\\
=(x_{gray}+v_{gray_x}*t)^2,\ utilise\ id.\ 1\\
=(x_{gray})^2+2(x_{gray})(v_{gray_x}*t)+(v_{gray_x}*t)^2
\]
\[
Consider\ term\ -2(x'_{gray})(x'_{red})\\
-2(x'_{gray})(x'_{red})\\
=-2(x_{gray}+v_{gray_x}*t)(x_{red}+v_{red_x}*t)\\
=-2[(x_{gray})(x_{red})+(x_{gray})(v_{red_x}*t)+(v_{gray_x}*t)(x_{red})+(v_{gray_x}*t)(v_{red_x}*t)]\\
=-2(x_{gray})(x_{red})-2(x_{gray})(v_{red_x}*t)-2(v_{gray_x}*t)(x_{red})-2(v_{gray_x}*t)(v_{red_x}*t)
\]
\[
Consider\ term\ (x'_{red})^2\\
(x'_{red})^2\\
=(x_{red}+v_{red_x}*t)^2,\ utilise\ id.\ 1\\
=(x_{red})^2+2(x_{red})(v_{red_x}*t)+(v_{red_x}*t)^2
\]
Now at a glance, we can easily see the highest power of \(t\) is 2. So we have ourselves a quadratic equation. Let’s collect all the coefficients contributed by these three terms according to their powers.
Let’s analyse the coefficients with \(t^2\) and \(t^0\).
\[
(v_{gray_x})^2-2(v_{gray_x})(v_{red_x})+(v_{red_x})^2,\ recall\ id.\ 2\\
(v_{gray_x})^2-2(v_{gray_x})(v_{red_x})+(v_{red_x})^2 = (v_{gray_x}-v_{red_x})^2
\]
\[
(x_{gray})^2-2(x_{gray})(x_{red})+(x_{red})^2,\ recall\ id.\ 2\\
(x_{gray})^2-2(x_{gray})(x_{red})+(x_{red})^2 = (x_{gray}-x_{red})^2
\]
And that of \(t\).
\[
Simplify\\
a=(x_{gray}),\ b=(v_{gray_x})\\
c=(v_{red_x}),\ d=(x_{red})\\
2ab-2ac-2bd+2dc\\
=2[ab-ac-bd+dc]\\
=2[a(b-c)-d(b-c)]\\
=2[(b-c)(a-d)]\\
Resubstitute\\
2[(b-c)(a-d)] = 2(v_{gray_x}-v_{red_x})(x_{gray}-x_{red})
\]
Let’s summarise in term of \((x’_{gray}-x’_{red})^2\)
\[
(x'_{gray}-x'_{red})^2\\
=(v_{gray_x}-v_{red_x})^2*t^2+2(v_{gray_x}-v_{red_x})(x_{gray}-x_{red})*t +(x_{gray}-x_{red})^2
\]
Note that this only caters for one term in \(eq.\ 5\). We’ll need to perform the same process for another term \((y’_{gray}-y’_{red})^2\). Since they have the same algebraic form, the result should also be the same.
\[
(y'_{gray}-y'_{red})^2\\
=(v_{gray_y}-v_{red_y})^2*t^2+2(v_{gray_y}-v_{red_y})(y_{gray}-y_{red})*t +(y_{gray}-y_{red})^2
\]
Thus after rearrangement in terms of \(t\), \(eq.\ 5\) should be as follow.
\[
(rad_{gray}+rad_{red})^2=(x'_{gray}-x'_{red})^2+(y'_{gray}-y'_{red})^2\\
p=v_{gray_x}-v_{red_x}\\
q=x_{gray}-x_{red}\\
r=v_{gray_y}-v_{red_y}\\
s=y_{gray}-y_{red}\\
(p^2+r^2)*t^2+2(pq+rs)*t+(q^2+s^2-(rad_{gray}+rad_{red})^2) = 0
\]
Step 14: The Result
So from the previous step, through rigourous algebra we arrived at the following formula:
\[
p=v_{gray_x}-v_{red_x}\\
q=x_{gray}-x_{red}\\
r=v_{gray_y}-v_{red_y}\\
s=y_{gray}-y_{red}\\
(p^2+r^2)*t^2+2(pq+rs)*t+(q^2+s^2-(rad_{gray}+rad_{red})^2) = 0
\]
Now this is one huge quadratic formula. We’ll try to group the coefficients into that accepted by
EqQuadratic. Compare the two forms:\[
ax^2+bx+c = 0\\
(p^2+r^2)*t^2+2(pq+rs)*t+(q^2+s^2-(rad_{gray}+rad_{red})^2) = 0\\
a = p^2+r^2)\\
b = 2(pq+rs)\\
c = (q^2+s^2-(rad_{gray}+rad_{red})^2)
\]
Step 15: Sample Implementation
So here’s a Flash presentation to demonstrate the idea. You will see two particles on the stage, one gray and the other red. Both are connected to an arrow, indicating the magnitude and direction of velocity.
To alter the velocity of the particles, press:
Finally, to toggle visibility of the arrows, press "V"
Step 16: A Note on the Quadratic Roots
There are two roots to the quadratic equation. In this context, we are interested in the real roots. However if the two particles are not set on collision path (both paths are parallel to each other), then imaginary roots will be produced instead of real roots. In this case, both real roots will remain
NaN.If both particles are set on a collision path, we will get two real roots. But what do these two roots represent?
Recall in Step 12 that we made used of Pythagoras’s Theorem to tie \((x’_{gray},\ y’_{gray})\) and \((x’_{red},\ y’_{red})\) together into an equation. Well, there are two situations where the distance between two circles’ centers are exactly the sum of both radii: one before collision and one after collision. Take a look at this image:
So which one do we choose? Obviously the first because we’re not interested in the instance after collision. So we should always choose the lesser value of both roots and evaluate it. If the value is positive and less than 1, a collision will happen during the next frame. If the value is negative, the collision happened in the past.
Step 17: The ActionScript Explained
Let’s look at the Actionscript implemented for this example. First, the variables.
Then the calculation of roots. You may want to cross check the following ActionScript with the variables above.
Here’s how you should interpret the roots:
//if no real roots are available, then they are on not on collision path if (isNaN(roots[0]) && isNaN(roots[1])) { t.text = "Particles not on collision path." } else { var time:Number = Math.min(roots[0], roots[1]) var int_time:int = time * 1000; time = int_time / 1000; t.text = "Frames to impact: "+time.toString() + "\n"; if(time>1) t.appendText("Particles are closing...") else if (time > 0 && time < 1) t.appendText("Particles WILL collide next frame!") else if (time < 0) t.appendText("Collision had already happened."); }Conclusion
So now we’ve studied quadratic and cubic roots in ActionScript, as well as taking a close look at two examples of how we can use the quadratic roots.
Thanks for taking the time to read this tutorial. Do leave a comment if you see other applications of quadratic roots (or any errors!)
It’s been about 6 months since we launched our much improved New Tuts+ Premium complete with courses and eBooks. In that time we’ve added 97 hours of video training, 36 eBooks and of course our premium tutorial library has continued to grow and is rapidly apporaching 1000 tutorials, including lots of fantastic Activetuts+ content. Now, for a limited time only, thanks to PayPal we’re offering anyone who joins Tuts+ Premium with a yearly membership via the PayPal service, a $50 cash back to their PayPal account. It’s a good way to get an even better deal on Tuts+ Premium yearly subscriptions!
Join Tuts+ Premium
If you’re serious about skilling up, head over to Tuts+ Premium and invest in yourself and your development! We’re constantly adding more content and it’s only getting better and better, so buying a yearly subscription is worth it. Plus we have a money back guarantee in the first month, so if you discover it’s not for you, you can always bail out without charge (and of course without the $50 cash!) If you enjoy our free Tuts+, you’ll definitely find Tuts+ Premium a valuable edition.
Read the terms below and Join Tuts+ Premium today!
Terms – read these!
Disclaimer:PayPal is not responsible for the promotion offered by Envato and you should read the Terms & Conditions below. The PayPal service is provided by PayPal Australia Pty Limited (ABN 93 111 195 389) which holds an Australian Financial Services Licence, number 304962. Before deciding to sign-up for or use the PayPal service you should consider the Combined Financial Services Guide and Product Disclosure Statement, available at http://www.paypal.com.au.
Upgrades
If you are an existing monthly Tuts+ Premium member, and interested in upgrading to a yearly membership to take advantage of this offer, be sure to cancel your existing monthly subscription via PayPal. Also, thank you for being a member!!
Kongregate is one of the largest Flash game portals on the net, and has its own API that can be integrated into your games (for which Kongregate even rewards you financially). In this tutorial, I’ll show you how to implement the Kongregate API into your games, and also go into detail about what the API is capable of and why you should use it.
(Note that this tutorial assumes you already have a Kongregate account; if you don’t, then please create one now.)
Final Result Preview
Let’s take a look at what the API enables us to do:
Badges
Mass Messages
High Scores
There’s another big reason to implement the API…
Step 1: Let’s Get Motivated
Before we dive into the technical aspects of implementing the Kongregate API, let’s get ourselves hyped up a little bit, and make sure that we actually want to implement it.
There are many reasons to implement the API, but to most developers, nothing speaks louder than money, and there’s plenty of that involved. When you upload your game to Kongregate, you automatically earn 25% of all ad revenue generated by your game’s page.
It gets better; if you implement their “Statistics & Challenges API”, you’ll receive an additional 10%! Finally, if your game is exclusive to Kongregate, or sponsored by them, you receive an additional 15%. This gives you the opportunity to earn up to 50% of the ad revenue for your game on Kongregate. If you’re wondering how much that is, check out some of my personal stats:
Step 2: Setting Up Our Work Environment
For this tutorial, we’ll be using FlashDevelop, a free (and amazing) open source editor for developers. We’ll be doing everything in simple .as files, so if you’d like to follow along using the Flash IDE, you shouldn’t have any trouble. If you’d like to use FlashDevelop and are unfamiliar with it, check out this excellent FlashDevelop beginner guide to get you started on what I would consider the best AS3 editor out there.
To begin, open FlashDevelop, go to the Project tab, and select “New Project”. From here, select “AS3 Project with Pre-Loader”. Alternatively, you can grab the
Preloader.asandMain.asfiles from the source download, and simply follow along.Your file should be a barebones
Main.asfile, like this:package { import flash.display.Sprite; import flash.events.Event; /** * ... * @author Your Name */ [Frame(factoryClass = "Preloader")] public class Main extends Sprite { public function Main():void { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); // entry point } } }Nothing above should be new to you; if it is, all you need to know is that this file is the entry point for our program, this is where it all begins. If you compile this with FlashDevelop, you should get a blank white screen, with no compiler errors.
Step 3: Let’s Get Connected
Before we dive into all the cool features of the API, we need to make sure that we have the API up and running.
Unlike many sponsor APIs, the Kongregate API isn’t a standalone set of files that we need to compile with our project. The API is actually stored on the Kongregate server, and we load it in at runtime. There’s a number of ways to do this in your projects, but for the sake of this tutorial, we’ll simply connect within our
Main.as, and a save a reference to it there.To start, copy the following code into our
Main.asfile just below the existing imports:The above are just a few simple imports that will allow us to use the necessary classes for loading in the Kongregate API.
Next, we’ll add a variable to store our reference to the Kongregate API. Go ahead and add the following right above the constructor of our Main.as file.
Notice that the data type of our kongregate variable is
*. If you’re unfamiliar with this, we’re simply telling the compiler that thekongregatevariable will accept any data type, much like a wild card.(Also, note that in a real game you’d want to store your reference to the API somewhere that your entire project has access to, such as a
public static const. This reference is needed so that you can use the API from anywhere in your project, for any purpose, rather than just in theMain.asfile when we first start up.)This next piece of code will be contained in a custom function by the name of
initKongregateAPI(). This isn’t actually necessary, but I prefer to encapsulate ideas when writing code, as it helps keep the code readable and easy to work with.Go ahead and add this function below the
initfunction inMain.as.private function initKongregateAPI():void { // Pull the API path from the FlashVars var paramObj:Object = LoaderInfo(root.loaderInfo).parameters; // The API path. The "shadow" API will load if testing locally. var apiPath:String = paramObj.kongregate_api_path || "http://www.kongregate.com/flash/API_AS3_Local.swf"; // Allow the API access to this SWF Security.allowDomain(apiPath); // Load the API var request:URLRequest = new URLRequest(apiPath); var loader:Loader = new Loader(); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete); loader.load(request); this.addChild(loader); }While that may look like a lot of code, it’s really not much, and with the comments, it’s quite easy to follow.
As you’ll see, in the first part we’re creating a variable to store the API path from the FlashVars (if you don’t know what these are, look them up really quick, they’re a great thing to understand).
In the second chunk, we determine whether the SWF is on the Kongregate website or running locally, and we assign the proper information to the
apiPathvariable.Next, we give the API access to the SWF, with a simple
security.allowDomaincall, where we pass in theapiPathas the parameter.We then create a new
URLRequestobject, which gets theapiPathpassed in to the constructor, a newLoaderobject, and add an event listener for the loader which will callloadCompletewhen done.Last, we call
loader.loadand pass in our request (the newly createdURLRequestobject, which contains theapiPathof the Kongregate API). If you understand what just happened, great; if not, don’t sweat it, as you won’t have to touch this again.Don’t Forget to Call It!
Now that the initKongregateAPI function is created, and contains all of the connection code, we should probably make sure this function actually gets called! Simply go back to the
initfunction that ourMain.asfile already contained, and add a function call toinitKongregateAPIafter the line that “entry point” line, like so,private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); // entry point initKongregateAPI(); }Finally, we need to add that
loadCompletefunction, to be called when theCOMPLETEevent is fired from our previous code. Add this function below theinitKongregateAPIfunction inMain.as.// This function is called when loading is complete private function loadComplete(event:Event):void { // Save Kongregate API reference kongregate = event.target.content; // Connect to the back-end kongregate.services.connect(); // You can now access the API via: // kongregate.services // kongregate.user // kongregate.scores // kongregate.stats // etc... }The above code is super simple; let’s go over it. We start off by saving a reference to the Kongregate API. As you can see, we’re referencing the Kongregate API through the passed in event parameter, via
event.target.content– simple enough.Now all we have to do is connect, and our SWF is good to go. As you can see, we connect by calling the
kongregate.services.connectmethod. No arguments required.While that may have seemed like a lot of work, it really wasn’t. I simply went over the connection process in a lot of detail, so that you can understand how we’re actually gaining access to the API, rather than just having that access and using it. Now that you understand it, you can use all the above as boilerplate code.
Step 4: The Statistics & Challenges API
Now that we’re connected, we can take a look at the most important feature of the entire API: the Statistics & Challenges portion. This API sends player related stats to the Kongregate server, which enables a number of things.
First off, this is the most basic way to allow users to compete for high scores on Kongregate. With the API integrated, the Kongregate side bar will include a new tab labeled “ACHIEVEMENTS”, which can be found next to the “CHAT” tab. Players can view leaderboards for any stat you send to the servers, which could be anything from a basic high score to the total number of enemies defeated.
The second and far more important use, is to allow Kongregate to use the stats you submit to create “badges” for your games. Badges are a central part of the Kongregate user experience, and are much like the achievement systems on platforms such as Xbox LIVE.
The best part about having badges added to your game is that your game will become featured for a short duration, greatly increasing the number of views you get, and thus greatly increasing your ad revenue. Even after your game is out of the spotlight, all badged games on Kongregate continue to receive increased views over normal games, which gives you an excellent long tail revenue stream.
Note that badges aren’t added by developers, but are instead created by the Kongregate staff. You’ll need a high rated game to get selected, but you’ll also need the API to be set up – so let’s get that half the battle out of the way!
Step 5: Back-End Preparation
To actually use the stats that we’ll be sending, Kongregate first requires us to let their server know what information it should be prepared to receive from us.
To do this, we simply go to the Statistics page for our game on the Kongregate website. This can be found in the “Edit Game” page, or by adding
/statisticsto the end of your game’s URL (for example, http://www.kongregate.com/games/EpicShadow/pixel-purge/statistics). For this tutorial, we’ll simply upload our test SWF as the “game”.Before adding any stats to the Statistics & Challenges API, we need to first understand the four types of stats that can be used, and the rules that they are bound to. Values must be positive, and must be whole integers. The four types are as follows:
Knowing which of the above types you need to use for each of your game stats is extremely important, so make sure you familiarize yourself with the above list. You’ll obviously want a list of stats that you want your game to send to Kongregate, so make sure that you have those prepared before you dive into the next step when actually submitting a game.
For the sake of this tutorial, we’ll simply use the following stats:
Once your list is prepared, head to the Statistics page of your game, and input the required data. Once the back-end work is done on the Statistics page, the game will be ready to submit data to the Kongregate server.
Step 6: How to Send Stats
To actually send data to the server, we simply call the “submit” function, which looks like this:
As you can see, the function takes two parameters:
statNameis the name of your stat. It’s very important that the String passed is identical (case sensitive) to the name of the stat you listed in the previous step when prepping the server to handle your stats.valueis the actual numeric value to be passed. Even though the data type is Number, remember that your value must be a positive, whole integer.To call this function in your game, simply do the following:
Even though we had four different types of stats we could send, this function only sends the value; the server itself will look at the information we provided in the previous step to determine how to treat the incoming data. It’s as simple as that; now we know how to send data to the server.
Step 7: Prepping Our Project to Send Stats
Now that we’ve prepped the back-end on the Kongregate website, and we now know how to send data, let’s give this project a go.
The first thing we need to do is add some code to our project to actually send our stats. Since the easiest thing to track is mouse input, I’ve chosen mouse-related stats. As you saw in our previous step, I chose Max Clicks, Total Clicks, and Last X.
Max Clicks will be the high score for how many times we click in a single game, to demonstrate the Max type; Total Clicks will be the grand total of all clicks we’ve done, to demonstrate the Add type; and Last X will be the x-position of our most recent click, to demonstrate the Replace type.
To track our Mouse clicks, we’ll need import the
MouseEventclass. Go back to Main.as, and add the following to your imports:Now we’re going to need to add a variable for our Max Clicks stat, to keep track of the total number of clicks per game session. Right below where we added the
kongregatereference variable (of data type*), add the following:We’re going to need an Event Listener to listen for our clicks, so we’ll add that now. In the
initfunction, right below the call toinitKongregateAPI, add the following:As you can see in the above code, the function called whenever the event it fired is called
clicked. Let’s go ahead and create that function. Add the following below yourloadCompletefunction:private function clicked(event:Event):void { maxClicks++; kongregate.stats.submit("Total Clicks", 1); kongregate.stats.submit("Max Clicks", maxClicks); kongregate.stats.submit("Last X", mouseX); }All we’re doing here is incrementing the
maxClicksvariable by1, and then submitting all the required information to the Kongregate server. This will add 1 to the Total Clicks stat, send the currentmaxClicksvariable to the server, which will then determine if it’s higher than the previous value and replace it if so, and send the x-position of our previous click, which will automatically replace the previous value.Our SWF may just be a blank screen, but a lot is going on, and we’re about to see it in action. Make sure you compile the project before moving on.
Step 8: Testing Our Project
Now it’s time to actually upload our project, and see it in action.
Go back to the Kongregate website, head to your game page, and upload the final version of our project. Once you’ve uploaded the project, you’ll be brought to a preview screen, where we can test our project before publishing it. To save the Kongregate staff a lot of time and energy, do everyone a favor and do not press publish on your test SWF. (If you’re working on a real game, go ahead, but for the sake of this tutorial we will not be publishing this project.)
Once you’re on the test page, give the game a few clicks. Refresh the page, and you should now see that there is a “HIGH SCORES” tab next to the “CHAT” and “GAME” tab. If you’ve done everything correctly up to this point, you should have a drop down box that currently reads “Last X” that also contains “Max Clicks” and “Total Clicks”. Note that clicking quickly will result in innacurate stats, as the server can’t keep up with all the requests, so click slowly for best results. This is why I advised earlier that you send large batches of data upon death, or level completion, when possible.
Well, there you go: you’ve now got the most important portion of the Kongregate API up and running. If your project isn’t working at this point, make sure that your Kongregate back-end stat names are typed exactly as they are in your submit function – case-sensitive – as that’s usually the problem.
You can also find the completed code in the final files folder in the source download, so compare your code to that if you’re still having issues.
Step 9: Mass Message Communications
Ever released a game, then later, really wanted to reach out to all of your fans? Well, with the Kongregate API, you can do just that – at least, for all of your Kongregate fans.
There are some restrictions on who can do this, but these restrictions are very much in the best interest of both developers and players. In order to qualify, your game must receive a 3.75 rating or higher, and have at least 10k gameplays.
You can send an “active players” message at most once every seven days. These messages will be sent to any players who have played the game at least three times ever, and at least once within the last ten days.
You can send an “inactive players” message at most once every 30 days. These messages will be received by any players who have played at least five times total, but not within the last ten days.
There are many of reasons to send these messages to your players, such as alerting them of bug fixes, or perhaps informing them of an upcoming sequel. Regardless of what you use this for, it’s an incredibly useful system that can really help you leverage your fans.
For more information, see this guide.
So Much More…
While we’ve covered a lot in this tutorial, the Kongregate API is capable of doing much more. Unfortuantely, I’d have to write a small book to go over all of the features, especially those that are useful for web based MMOs. If you’re interested in checking out what other features the API has to offer, I recommend checking out the Kongregate Developers Center for more.
Now that you know how to get the Kongregate API up and running, I strongly encourage you to add it to all of your future games; if you’re lucky, you might even get some badges, and that’s when the real fun begins.