Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon

30. May 2017


Back in Part One of our look at the PlayCanvas HTML5 3D game engine, we started creating our simple bowling game.  We already created our lane and pins.  Now it is time to continue on and create our bowling ball.  Attached to our bowling ball will also be the script the controls the majority of our game.  Let’s jump back in.


Creating the Bowling Ball

This time, instead of importing a model, we are simply going to use a built in procedurally generated mesh.  Simply right click the Root in the Hierarchy view, select New Entity then Sphere.

image

Scale the newly created ball down and position it accordingly.

image

Under the Entity properties, I renamed it from Sphere to BowlingBall.  Right now our ball doesn’t look all that great.  Let’s go ahead and make it a bit shiny.  Right click in the asset area, select New Asset->Material.

image

Select the newly created material from the assets tab.  In the Material panel, I rename it to BowlingBallMaterial.  What I want to do is make the bowling ball a bit shiny.  I am going to leave it white, but if you want to change the color, do so under the Diffuse tab.  Next head on down to the Specular tab.  I used the following settings

image

Finally we need to apply the material to our bowling ball.  In the Hierarchy view, select the BowlingBall entity, under the Model panel, select our newly created Material.

image

Our bowling ball also needs to have a Collision shape and Rigid Body attached to it.

image

image

Be sure to set the Collision object to a Sphere and size the radius to be only slightly larger than our bowling ball model.


Now we have all the pieces needed for our game.  The pins, the lane and the bowling ball.  Now all we need to do is add some logic to the game.  This is done by attaching scripts to entities within our game.  In this case we are going to attach the logic to our bowling ball.


Scripting

With our BowlingBall entity active, add a component and select Script.

image


Scroll down to the Script panel, select Add Script->New Script.

image

Enter a name for the script.

image

Now click Edit Script to launch the script editor.

image

Now enter the following script:

var BallScript = pc.createScript('ballScript');

BallScript.attributes.add('speed', {
    type: 'number',
    default: -8
});

BallScript.prototype.initialize = function() {
    this.entity.collision.on('collisionstart', this.onCollisionStart, this);
    this.app.keyboard.on('keydown', this.onKeyDown, this);
    
    var pins = this.app.root.findByTag("Pin");
    this.startingPositions = [];
    this.startingRotations = [];
    for(var i = 0; i < pins.length; i++){
        this.startingPositions.push(pins[i].getLocalPosition().clone());
        this.startingRotations.push(pins[i].getLocalRotation().clone());
    }
    
    this.ballPosition = this.app.root.findByName("BowlingBall").getLocalPosition().clone();
    this.ballMoving = false;
};

BallScript.prototype.onCollisionStart = function (result) {
    
    if (result.other.rigidbody) {
        if(result.other.name == "BowlingPin")
            this.entity.sound.play("Hit");
    }
};

BallScript.prototype.onKeyDown = function(e) {
    if(e.key == pc.KEY_SPACE || e.key == pc.KEY_W){
        this.entity.rigidbody.applyImpulse(0,0, this.speed);
        this.ballMoving = true;
    }
    if(e.key == pc.KEY_R){
        var pins = this.app.root.findByTag("pin");
        for(var i = 0; i < pins.length; i++){
                pins[i].rigidbody.teleport(this.startingPositions[i],this.startingRotations[i]);
                pins[i].rigidbody.linearVelocity = pc.Vec3.ZERO;
                pins[i].rigidbody.angularVelocity = pc.Vec3.ZERO;
        }
        // now the ball
        this.entity.rigidbody.teleport(this.ballPosition);
        this.entity.rigidbody.linearVelocity = pc.Vec3.ZERO;
        this.entity.rigidbody.angularVelocity = pc.Vec3.ZERO;
        this.ballMoving = false;
    }
    if(e.key == pc.KEY_B){
         // Just the ball
        this.entity.rigidbody.teleport(this.ballPosition);
        this.entity.rigidbody.linearVelocity = pc.Vec3.ZERO;
        this.entity.rigidbody.angularVelocity = pc.Vec3.ZERO;
        this.ballMoving = false;
    }
    if(e.key == pc.KEY_LEFT || e.key == pc.KEY_A){
        this.entity.rigidbody.applyImpulse(-0.2,0,0);
    }
    if(e.key == pc.KEY_RIGHT || e.key == pc.KEY_D){
        this.entity.rigidbody.applyImpulse(0.2,0,0);
    }    
};

// update code called every frame
BallScript.prototype.update = function(dt) {
};


One thing you may notice in the script above is the definition of the speed attribute.  This exposes this variable back to the editor, like so:

image


You may notice the line of code in the script for playing audio when we collide with a pin:

    if (result.other.rigidbody) {
        if(result.other.name == "BowlingPin")
            this.entity.sound.play("Hit");
    }

Now we need to add this audio asset to our bowling ball.  First add the audio file as an asset.  Simply click add asset –> Upload, then upload a WAV file.  Now we need to add a sound component to our BowlingBall entity. 

image


Now scroll down to Slot 1 and add the asset you just uploaded and be sure to set name to Hit.

image


And done.  A complete, rather simple but fully functioning 3D bowling game in just a few minutes.


The Video

Programming , , ,

25. May 2017


I featured the PlayCanvas engine a couple years ago in this Closer Look review.  PlayCanvas is a 3D HTML5 powered game engine with a full suite of editing tools.  In the time since that initial review, PlayCanvas has advanced a great deal, so I have decided to revisit the engine.  Much of the engine has remained the same as it was, just generally improved all around.  Therefore I have decided instead of another review, I will do a hands on tutorial showing how to create a simple game using PlayCanvas.  In this case, a bowling game.  For Patreon backers all the assets used to make this tutorial are now available including Blender files, FBX, textures and more.

There is a video version of this tutorial available here.


Spoiler alert... here is the “game” we are about to create in this tutorial.

Click the window to focus.

Press spacebar or W to throw bowling ball.

Press left | A and right | D to move the bowling ball.

Press R to reset the table.

Press B to reset just the ball.


Creating your Project

Alright, lets jump in and build this game.  First head on over to http://playcanvas.com.  Now you either need to create an account or log in to your existing account.

image


A free account is sufficient for what we are doing here.  Once your account is created and you’ve logged in, click the New button

image


Select  Blank Project:

image


Now name your game and hit Create.  Unless you have a premium account, you can’t make private projects.

image


This will open your project after it’s created.  Now head into the settings.

image

You can set the resolution, change the name, description, etc...  but what we want to do in this case is enable physics for our project.

image

Next scroll to the bottom right corner and click the save button.

image


Now we are ready to fire up the editor.  Scroll back up and locate the Editor button.

image


Creating your Game

Welcome to the default editor!

image

Now that we are in the editor, it’s time to start creating our game.  You will notice on the left there is the Hierarchy, this is your games scene graph.

image

We have no need of the Box or Plane, simply right click each and delete them.

Now let’s get our assets into the game.  In Blender I created the bowling lane and pin textured meshes, then exported them as FBX ready for importing.  Let’s import them for use into our game, along with the required textures.  Locate the Assets window, click the + Icon, select Upload then select your FBX file(s).  Repeat the process with any texture maps you require.

image

This process will create a .json for each of your 3D models.  It also creates an empty material for both.

image


Now let’s set the texture for each material.  Simply select the Material, then in the Material tab to your right, scroll down and select Diffuse.  Click ... then select your texture map from the asset pane, like so:

image


I also have a normal map for the bowling lane, I repeat the above process, but instead of Diffuse I select Normal:

image


Creating the Bowling Lane

Ok, now that we have our raw assets and our textures linked, lets start creating some game objects.  Simply drag the lane into the scene like so:

CreateEnt


You will notice this creates a new entity in our scene graph:

image


PlayCanvas takes the traditional Entity Component model that is rapidly becoming the norm in the world of game development. 

image

You will notice that we have a Transform component, which enables us to position our entity in the world.  It also automatically created a Model component for us.  Now we want to add some physics to the game.  Since this is the lane, it wont be moving, so we create it as a static physics object.  To accomplish this we need to add two components, one for collisions that define the shape of our object to the physics engine, the second a RigidBody that defines our physical properties.  Let’s create the Collision shape first.

First click the Add Component button:

image

Then select Collision

image


Select Box then set the dimensions to surround your mesh as tightly as possible (note that PlayCanvas is a Y-up game engine):

image

image


Now Add Component again, this time selecting Rigid Body.  The default value should be Static, which is what we want.

image

Static means the object will be part of the physics simulation, but wont be moved by it.  Friction determines how well other objects slide over this surface, while Restitution determines how objects “bounce” off of it.


That’s it!  Our lane is completed.  Now it’s on to creating the bowling pin object.


Creating the Bowling Pins

Now it’s time to create the bowling pin.  Follow the exact same process you did earlier, drag the .json file onto the scene.  Position and scale it appropriately for your scene.

image

Just like last time, we want to create a collision component for this object.  This time instead of a cube, a cylinder is a better fit.

image

image

We also need to create a rigid body to control physics for this entity.  This time we create our entity as Dynamic ( static means stationary, dynamic is fully simulated, while kinematic takes part in the simulation but expects the movement to come directly from code, not the simulation).

image

Finally we are going to set a tag under the Entity section so we can identify all pins via code.  In the Tags section, enter “Pin” then hit enter to add a tag.  You can add multiple tags to an entity, a quick easy way to create ad-hoc groups of objects as we will see in a moment.

image


We are simulating 5 pin bowling here, so we need 4 more of them.  In the Hierarchy review, right click the pin and select Duplicate (not copy!).  This creates a copy of the entity and all of it’s components.

image

Repeat this process 3 more times, then go ahead and position the pins in a V like so.

image

Obviously if you are a 10 pin fan, other than being a heathen, you simply have to duplicate and position 5 more pins.


This post is getting pretty long so I’ve decided to split it into two parts.  Click here to continue on to part two.

Programming , ,

8. May 2017

 

Ever needed to loop a background forever in your 2D game and wondered the best way to do it?  That’s exactly what we are going to look at today, with all the code samples done using HaxeFlixel, which I recently covered in this tutorial series if you wish to learn more.  One key aspect is to have a background that is seamless.  That is the ending edge and beginning edge need to blend seamlessly together.  For this example I am using this graphic (click for the full resolution version).

BloodRed

 

There are two approaches we are going to cover today.  The first one is the simplest, essentially we update our camera when we get within half a screens height of the edge and loop it to the bottom, like so:

CameraLoop0001-0060

 

Let’s take a look at the code.

import flixel.FlxG;
import flixel.FlxSprite;
import flixel.FlxState;
import flixel.FlxObject;

class PlayState extends FlxState
{
   var bg:FlxSprite;
   var camTarget:FlxObject;
   override public function create():Void
   {
      super.create();
      bg = new FlxSprite(0,0,AssetPaths.BloodRed__png);
   
      add(bg);

      camTarget = new FlxObject();
      camTarget.setPosition(FlxG.width/2, FlxG.height/2);
      FlxG.camera.target = camTarget;

      add(camTarget);
   }

   override public function update(elapsed:Float):Void
   {
      super.update(elapsed);
      camTarget.y+=25;

      // Move the camera to loop forever  
      if(camTarget.y >  bg.height - FlxG.height/2){
         camTarget.setPosition(FlxG.width/2, FlxG.height/2);
      }
   }
}

 

The downside to this approach is you also need to reset the Y coordinate of all the entities in your scene as your camera resets.

Another option is to use two instances of the background and as you approach the edge you raise the lower most version to the top and repeat forever.  Like so:

CameraLoop20001-0060

 

Here is the code for this approach.

import flixel.FlxG;
import flixel.FlxSprite;
import flixel.FlxState;
import flixel.FlxObject;

class PlayState extends FlxState
{
   var bg:FlxSprite;
   var bg2:FlxSprite;
   var camTarget:FlxObject;
   override public function create():Void
   {
      super.create();
      bg = new FlxSprite(0,0,AssetPaths.BloodRed__png);
      bg2 = new FlxSprite(0,bg.height,AssetPaths.BloodRed__png);
   
      add(bg);
      add(bg2);

      camTarget = new FlxObject();
      camTarget.setPosition(FlxG.width/2, FlxG.height/2);
      FlxG.camera.target = camTarget;

      add(camTarget);
   }

   override public function update(elapsed:Float):Void
   {
      super.update(elapsed);
      camTarget.y+=25;

      var greater,lesser;
      if(bg.y > bg2.y){
         greater = bg;
         lesser = bg2;
      }
      else{
         greater = bg2;
         lesser = bg;
      }
      if(camTarget.y > greater.y + bg.height - FlxG.height/2){
         trace("Flip");
         lesser.y = greater.y + bg.height;
      }

   }
}

 

This approach has the advantage of enabling you to keep coordinates consistent, but has the downside of requiring a second identical sprite.  Since the second sprite is simply an instance of the same texture though, the memory impact should be almost non-existent.

Programming , , ,

26. April 2017

 

Welcome back to our ongoing HaxeFlixel Tutorial Series, today we are going to cover playing sound and music in HaxeFlixel.  This title is a bit misleading, as they are both basically the same thing, they are all ultimately FlxSound objects.  The single biggest challenge you are going to have with HaxeFlixel audio is file formats, so let’s start there. 

The file format of your audio file depends entirely on the platform your code is going to run on.  On the Flash player platform, all of your audio files need to be in mp3 format.  However on other platforms the mp3 file format has a number of licensing issues, so they instead use Ogg Vorbis (ogg) files for long audio files, and WAV for shorter effects.  Why two different formats?  WAV files are much faster to load, but take up more system memory.  They are ideal for frequently used but shorter sound files.  Ogg files on the other hand are much more compressed, taking more CPU power to process but a great deal less space.  This makes them ideal for longer audio files, such as your game’s music.  In addition to the file format limitations, your sounds need to be encoded at 11025, 22050 or 44100kHz frequency.  If you need to switch encoding frequencies, Audacity is a great free program (you can learn more about here) that can also be used to convert between the various file formats.

Using a default project, HaxeFlixel will automatically create sound and music folders in your asset directory.  One thing you will notice in this example is when you start including multiple different audio files for different platforms, the warnings can become extremely irritating.  Hearing that your mp3 file isn't compatible with your platform, or that your WAV file wont work on Flash gets irritating quickly.  Thankfully we have the ability to filter our files based on platform.  In your Project.xml file, locate the Path Settings area and edit it like so:

   <!-- _____________________________ Path Settings ____________________________ -->

   <set name="BUILD_DIR" value="export" />
   <classpath name="source" />
   <!-- <assets path="assets" /> -->
   <assets path="assets/music" include="*.mp3" if="flash" />
   <assets path="assets/music" include="*.ogg|*.wav" unless="flash" />
   <assets path="assets/sounds" include="*.mp3" if="flash" />
   <assets path="assets/sounds" include="*.ogg|*.wav" unless="flash" />

This will cause Flash builds to only see mp3 files from the music and sound directories, while all other platforms will only see the ogg and wav format files.  Ok, now lets move on to some actual code.

Playing music and sound effects is trivial in HaxeFlixel, but once again platform becomes a factor.  In this case we are using conditional compilation to solve this problem, like so:

#if flash
   FlxG.sound.playMusic(AssetPaths.techno__mp3);   
   soundEffect = FlxG.sound.load(AssetPaths.gunshot__mp3);
#else
   FlxG.sound.playMusic(AssetPaths.techno__ogg);
   soundEffect = FlxG.sound.load(AssetPaths.gunshot__wav);  
#end

 

In this case the music file will play automatically, while the soundEffect needs to be triggered manually.  Let’s take a look at how to do that in our update() method.

if(FlxG.keys.justPressed.G)
   soundEffect.play(true);

The true parameter causes the sound effect to replace any running instances.  This will result in the sound starting from the beginning if you press the G key multiple times before a sound effect has finished playing. 

You also have a fair bit of control over sound playback.  The following code shows how to pause/resume sound as well as increasing and decreasing the volume.  Volume is a value that ranges from 0.0 to 1.0, with 0.0 being complete silence while 1.0 is maximum volume.

if(FlxG.keys.justPressed.P)
   if(FlxG.sound.music.active)
      FlxG.sound.music.pause();
   else
      FlxG.sound.music.resume();
if(FlxG.keys.justPressed.PLUS)
   FlxG.sound.changeVolume(0.1);
if(FlxG.keys.justPressed.MINUS)
   FlxG.sound.changeVolume(-0.1);

 

HaxeFlixel also supports positional audio.  Both by changing the X and Y position of a sound effect, or positioning it relative to another object.  The latter is the approach we are going to take here, creating a virtual ear that is centered to the screen.

soundX = FlxG.width/2;  
soundY = FlxG.height/2;
ear = new FlxObject();
ear.setPosition(FlxG.width/2, FlxG.height/2);   

Now we can relocate the sound relative to the ear and it will change accordingly.

 

There is also

if(FlxG.keys.justPressed.LEFT)
   soundX -= 25;
if(FlxG.keys.justPressed.RIGHT)
   soundX += 25;        
if(FlxG.keys.justPressed.UP)
   soundY -= 25;
if(FlxG.keys.justPressed.DOWN)
   soundY += 25;                 
FlxG.sound.music.proximity(soundX,soundY, ear,300,true);

It is also possible to tween audio, fading it in and out over time, like so:

if(FlxG.keys.justPressed.S)
   FlxG.sound.music.fadeOut(3.0);

You can also set up callback functions when a sound stops playing.  This can be used to easily create a music or sound effect management system.

FlxG.sound.music.onComplete = function() {
   // This will only ever fire if you hit L to turn looping off
   trace("Song ended");
}

 

Now let’s show the complete source listing, which also illustrates a couple other features such as looping, muting and pausing audio.

package;

import flixel.FlxG;
import flixel.FlxSprite;
import flixel.FlxState;
import flixel.system.FlxSound;
import flixel.FlxObject;

class PlayState extends FlxState
{
   var soundEffect:FlxSound;
   var soundX:Float;
   var soundY:Float;
   var ear:FlxObject;

   override public function create():Void
   {
      super.create();
      #if flash
         FlxG.sound.playMusic(AssetPaths.techno__mp3);   
         soundEffect = FlxG.sound.load(AssetPaths.gunshot__mp3);
      #else
         FlxG.sound.playMusic(AssetPaths.techno__ogg);
         soundEffect = FlxG.sound.load(AssetPaths.gunshot__wav);  
      #end

         soundX = FlxG.width/2;  
         soundY = FlxG.height/2;
         ear = new FlxObject();
         ear.setPosition(FlxG.width/2, FlxG.height/2);   

      FlxG.sound.music.onComplete = function() {
         // This will only ever fire if you hit L to turn looping off
         trace("Song ended");
      }

      FlxG.sound.muted = true;
   }

   override public function update(elapsed:Float):Void
   {
      super.update(elapsed);

if(FlxG.keys.justPressed.G)
   soundEffect.play(true);

      if(FlxG.keys.justPressed.P)
         if(FlxG.sound.music.active)
            FlxG.sound.music.pause();
         else
            FlxG.sound.music.resume();
      if(FlxG.keys.justPressed.PLUS)
         FlxG.sound.changeVolume(0.1);
      if(FlxG.keys.justPressed.MINUS)
         FlxG.sound.changeVolume(-0.1);

      if(FlxG.keys.justPressed.LEFT)
         soundX -= 25;
      if(FlxG.keys.justPressed.RIGHT)
         soundX += 25;        
      if(FlxG.keys.justPressed.UP)
         soundY -= 25;
      if(FlxG.keys.justPressed.DOWN)
         soundY += 25;                 
      FlxG.sound.music.proximity(soundX,soundY, ear,300,true);

      
      if(FlxG.keys.justPressed.L)
         FlxG.sound.music.looped = false;

      if(FlxG.keys.justPressed.S)
         FlxG.sound.music.fadeOut(3.0);

      if(FlxG.keys.justPressed.M)
         FlxG.sound.muted = !FlxG.sound.muted;
   }
}

 

The Video

Programming , , ,

21. April 2017

 

Earlier on in the ongoing HaxeFlixel Tutorial series we covered drawing sprites, a fairly critical concept to any 2D game.  Today we are going to take that one step further and look at the process of animating them.  If you look at the FlxSprite class you will notice there is a member animation which is a FlxAnimationController.  This is the class we are going to be focusing on today.  However in order to animate sprites, we need sprites to animate.  2D animation is a lot like those flipbook animations you may have drawn on the corner of books.  Like the following:

 

 

This is exactly what we are going to do here.  Except instead of multiple pages of animations, we are going to put all of our sprites in a single image file.  This one in fact.  (Click to view full size)

walk

 

At it’s full size this image is composed of 16 frames of our sprite, each one 512x384 in size.  When we create our FlxSprite, we use a slightly different setup than we did in the past.

      spritesheet = new FlxSprite();
      spritesheet.loadGraphic(AssetPaths.walk__png,true,512,384);

 

Here when we call loadGraphic we pass in the width and height of the rectangle within the larger image that represents our individual sprite.  Next we need to define our animation. 

      spritesheet.animation.add("walkRight",[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],5,true);

Here we are adding an animation with the name “walkRight”, then we pass in the individual frame indexes that represent this animation.  These represent the index of the sub-rectangle within our source image, starting at the top left, going across to the right and down a row.  Therefore in this case 0 is the animation frame at the top left corner, 1 would be the one to it’s right, 4 would be the first image on the second row, 15 would be the final image in the sequence, etc.  After the array of indices, we pass in the frame rate.  In this case we are saying to play this animation at a rate of 5 frames per second.

One cool thing we can do is actually flip the animation on the X or Y axis, enabling us to use the same images for a walkLeft animation, like so:

      spritesheet.animation.add("walkLeft",[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],5,true,true);

It’s the exact same parameters, except with a different name and we pass in true to the FlipX parameter.

 

After we have defined our animation(s) and add() it, we need to tell the game engine which one to play, like so:

      spritesheet.animation.play("walkRight");

 

Let’s put some code in the update() callback that flips the direction of animation when the player presses the spacebar.

      if(FlxG.keys.justReleased.SPACE)
         if(spritesheet.animation.name == "walkRight")
            spritesheet.animation.play("walkLeft");
         else
            spritesheet.animation.play("walkRight");

 

You can also easily alter the speed an animation plays after it has been loaded, like so:

      if(FlxG.keys.justReleased.UP)
         spritesheet.animation.curAnim.frameRate++;
      if(FlxG.keys.justReleased.DOWN)
         spritesheet.animation.curAnim.frameRate--;   

We also have the ability to stop a playing animation, like this code that does it when ESC is hit.

      if(FlxG.keys.justReleased.ESCAPE){
         spritesheet.animation.curAnim.curFrame = 0;
         spritesheet.animation.finish();
      }

You do not have to set the frame to the beginning, but it helps if you want to transition to another animation or restart the existing one.

The animation controller also has a pair of callback functions that can be called each frame of an animation, or when an animation ends.  Please note that in this case we created our animation to loop by default, so the callback will only be called if you hit the ESC key to stop the animation manually.  Or you turn looping off when you define the animation sequence.

      spritesheet.animation.callback = function(s:String, frame:Int, index:Int){
         trace("Animation:" + s + " -- frame: " + frame + " index: " + index);
      }

      spritesheet.animation.finishCallback = function(s:String){
         trace ("------------------------ " + s + " anim finished ------------------");
      }

 

Ok, let’s look at that as one complete example now.

package;

import flixel.FlxG;
import flixel.FlxSprite;
import flixel.FlxState;
import flixel.text.FlxText;
import flixel.util.FlxColor;

class PlayState extends FlxState
{

   var spritesheet:FlxSprite;
   var text:FlxText;
   override public function create():Void
   {
      super.create();

      this.bgColor = FlxColor.GRAY;
      text = new FlxText(0,0,300);
      add(text);

      spritesheet = new FlxSprite();
      spritesheet.loadGraphic(AssetPaths.walk__png,true,512,384);
      spritesheet.animation.add("walkRight",[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],5,true);
      spritesheet.animation.add("walkLeft",[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],5,true,true);
      add(spritesheet);

      spritesheet.animation.play("walkRight");
      

      spritesheet.animation.callback = function(s:String, frame:Int, index:Int){
         trace("Animation:" + s + " -- frame: " + frame + " index: " + index);
      }

      spritesheet.animation.finishCallback = function(s:String){
         trace ("------------------------ " + s + " anim finished ------------------");
      }
   }

   override public function update(elapsed:Float):Void
   {
      super.update(elapsed);
      if(FlxG.keys.justReleased.SPACE)
         if(spritesheet.animation.name == "walkRight")
            spritesheet.animation.play("walkLeft");
         else
            spritesheet.animation.play("walkRight");

      if(FlxG.keys.justReleased.UP)
         spritesheet.animation.curAnim.frameRate++;
      if(FlxG.keys.justReleased.DOWN)
         spritesheet.animation.curAnim.frameRate--;   

      if(FlxG.keys.justReleased.ESCAPE){
         spritesheet.animation.curAnim.curFrame = 0;
         spritesheet.animation.finish();
      }
      text.text = "Cur speed: " + spritesheet.animation.curAnim.frameRate;    
   }
}

 

Here is the results.  Press SPACE to flip direction, UP and DOWN to control animation speed and ESC to stop the animation all together.

 

The Video

[Coming Soon]

Programming , , ,

Month List

Popular Comments