Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon

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 , , ,

17. April 2017

 

Up until this point in the HaxeFlixel Tutorial Series all of our examples have been confined to a single screen.  What happens however when your game world is larger than the screen?  Well, that’s where cameras come in.  In this tutorial we are going to look at creating a camera and navigating around the game world.  Cameras in HaxeFlixel are also capable of much more then this, including special effects and even split screen, all of which we will look at in this tutorial. 

There is one important concept to understand before we get too far.  The camera and the screen resolution are two completely different things.  The screen resolution, which we covered in this tutorial, determines the resolution and size of the applications window.  A camera on the other hand is a view into our game world.  They do not have to be the same, nor do they have to even use the same units.

In the example we are going to create I have a single large image to use as a background, an image many times larger than the screen, then another sprite to represent our player within the game world.  Let’s get the setup out of the way.  Don’t worry, I’ll do a full source listing later in the tutorial.

class PlayState extends FlxState
{
   var background:FlxSprite;
   var jet:FlxSprite;
   var blur:Bool = false;
   override public function create():Void
   {
      super.create();
      background = new FlxSprite();
      background.loadGraphic(AssetPaths.aerialcropped__jpg,false);

      jet = new FlxSprite();
      jet.loadGraphic(AssetPaths.jet__png);
      
      camera = new FlxCamera(0,0,640,480);

      camera.bgColor = FlxColor.TRANSPARENT;
      camera.setScrollBoundsRect(0,0,640,background.height);
      
      // Replace default camera with our newly created one
      FlxG.cameras.reset(camera);
      
      jet.angle = 90.0;
      jet.x = 320 - jet.width/2;
      jet.y = 0;

      camera.target = jet;
      add(background);
      add(jet);
   }
}

 

We run this game and get:

image

 

Nothing too exciting so far.  We setup and add our two sprites and most importantly create our FlxCamera object.  We create it with a top left position of 0,0 and size of 640x480.  We then set our camera as active with a call to FlxCamera.reset().  One important thing to be aware of, by default Flixel will create a camera for you in FlxG.camera that matches the size of your application’s resolution.  In this example there is no reason why we couldn’t just use the default created camera.  The other critical line of code to be aware of is

camera.target = jet;

This tells our camera to follow the FlxObject passed in, in this case our jet sprite.  The other key line here is

camera.setScrollBoundsRect(0,0,640,background.height);

This line sets the range that our camera will navigate.  As you will see in a moment when our jet sprite exceeds the bounds the camera will travel no further.  On that topic, let’s get some movement going and see what happens!

Add the following code to your project:

   override public function update(elapsed:Float):Void
   {
      super.update(elapsed);
      jet.y+= 300.0 * elapsed;
   }

 

And now when we run it (focus and press spacebar to begin):

 

As you can see, the camera smoothly follows the jet until the jet sprite exceeds the predefined bounds.  Neat.  So that’s the basics of a camera, but by no means all it can do.  Let’s look at adding zoom functionality next.  Add the following code to your update:

if(FlxG.keys.justReleased.UP)
   camera.zoom += 0.1;
if(FlxG.keys.justReleased.DOWN)
   if(camera.zoom > 1)
      camera.zoom -= 0.1;

Now when you press the update and down arrows:

zoomGifResized

 

You can also perform special effects, such as camera shake:

if(FlxG.keys.justReleased.SPACE)
   camera.shake(0.01,1);   

Resulting in:

Gif4Optimized

 

You can even apply special effect filters, such as blur:

if(FlxG.keys.justReleased.ESCAPE){
   blur = !blur;
   if(blur){
      var filter = new BlurFilter();
      camera.setFilters([filter]);
   }
   else {
      camera.setFilters([]);
   }
}

Resulting in:

GIF5

 

Now let’s look at a complete example.  In addition to all of the code above this one has a couple more features.  You can flash the screen red by pressing the A key.  Also when the jet goes off screen, the screen will fade to black then restart a few seconds later. 

package;

import flixel.FlxG;
import flixel.FlxSprite;
import flixel.FlxCamera;
import flixel.FlxState;
import flixel.util.FlxColor;
import flash.filters.BlurFilter;

class PlayState extends FlxState
{
   var background:FlxSprite;
   var jet:FlxSprite;
   var blur:Bool = false;
   var start:Bool = false;
   override public function create():Void
   {
      super.create();
      background = new FlxSprite();
      background.loadGraphic(AssetPaths.aerialcropped__jpg,false);

      jet = new FlxSprite();
      jet.loadGraphic(AssetPaths.jet__png);
      
      camera = new FlxCamera(0,0,640,480);

      camera.bgColor = FlxColor.TRANSPARENT;
      camera.setScrollBoundsRect(0,0,640,background.height);
      
      // Replace default camera with our newly created one
      FlxG.cameras.reset(camera);
      
      jet.angle = 90.0;
      jet.x = 320 - jet.width/2;
      jet.y = 0;

      camera.target = jet;
      add(background);
      add(jet);
   }



   override public function update(elapsed:Float):Void
   {
      super.update(elapsed);
      if(FlxG.keys.justReleased.SPACE)
         start = true;
         
      if(FlxG.keys.justReleased.UP)
         camera.zoom += 0.1;
      if(FlxG.keys.justReleased.DOWN)
         if(camera.zoom > 1)
            camera.zoom -= 0.1;

      if(FlxG.keys.justReleased.S)
         camera.shake(0.01,1);
      
      if(FlxG.keys.justReleased.ESCAPE){
         blur = !blur;
         if(blur){
            var filter = new BlurFilter();
            camera.setFilters([filter]);
         }
         else {
            camera.setFilters([]);
         }
      }              

      if(FlxG.keys.justReleased.A)
         camera.flash(flixel.util.FlxColor.RED,0.5);

      if(start){
         if(jet.y > background.height)
            camera.fade(FlxColor.BLACK,2.0,false, function(){
               FlxG.resetGame();
               });
         jet.y+= 300.0 * elapsed;
      }
   }
}

Click the window below and press Space to begin. S to shake, Up and Down arrows to zoom.  Unfortunately the Blur filter depends on the Flash target so does not work in the browser.

 

 

One final thing we are going to cover is supporting multiple cameras.  Let’s just jump in with a code example.

   override public function create():Void
   {
      super.create();
      background = new FlxSprite();
      background.loadGraphic(AssetPaths.aerialcropped__jpg,false);

      jet = new FlxSprite();
      jet.loadGraphic(AssetPaths.jet__png);
      
      var camera = new FlxCamera(0,0,320,480);

      camera.bgColor = FlxColor.TRANSPARENT;
      camera.setScrollBoundsRect(0,0,640,background.height);
      
      var camera2 = new FlxCamera(320,0,320,480);
      camera2.focusOn(new FlxPoint(320/2,480/2));

      // Replace default camera with our newly created one
      FlxG.cameras.reset(camera);
      FlxG.cameras.add(camera2);

      
      
      jet.angle = 90.0;
      jet.x = 320/2 - jet.width/2;
      jet.y = 0;

      camera.target = jet;
      add(background);
      add(jet);
   }

Now when run you see the screen is now split:

 

The second camera stays stationary while the camera on the left behaves exactly as the original did.

fin

 

The Video

Programming , , ,

11. April 2017

 

Articy:draft is a unique tool aimed at game designers for visually creating and organizing game content.  In many ways it is like a version of Visio created specifically for game developers handling such tasks as conversation trees, game flow, inventory, characters and more.  Additionally with the most recent release you can directly consume your data in the Unity game engine.  As the title suggests, articy:draft 2 SE is currently on sale for 95% off on the Bundle Stars website.  Available for less than $5 USD instead of the usual $200 price tag, this represents a pretty massive discount.  Act fast though, the sale ends on April 14th.

-------------------

EDIT – Be aware of the following limitation!

This version is for non-commercial use only. You can acquire an upgrade for commercial usage via Steam.

I am unable to determine the upgrade price for the commercial edition!

 

If you’ve never heard of articy:draft, be sure to watch the video below for more information!

GameDev News

11. April 2017

 

The Godot Engine is inching closer to the major 3.0 update and a recent progress report illustrated several new features that have been added to the engine.  If you are currently unaware of it, Godot is an open source fully featured 2D/3D game engine with a complete editing environment.  You can learn how to use Godot with our comprehensive tutorial series available here

Several new features have just been added, in varying degrees of completion, including:part_directed

  • Web Export an experimental exporter to WebAssembly and WebGL enabling you to publish your Godot game to run in browsers, although only the most current Firefox and Chrome browsers are currently supported
  • GDNative was recently announced as DLScript but was thankfully renamed to the much more expressive GDNative.  This provides a bridge between the Godot engine and native code without requiring any recompilation
  • New Particle System that works entirely on the GPU enabling a huge number of particles, many more than the current implementation.  The new system also allows tighter control, curve based tweaking, mesh based particles and more options for particle emitters, as seen in the image to the right.

Godot 3.0 alpha will be released soon.  If you are interested in playing with the new features, you will instead have to build Godot from the current master branch.  For details on how to build Godot using Visual Studio refer to this tutorial.

 

For more details on Godot 3.0, be sure to check the Godot engine blog post.

GameDev News

11. April 2017

 

Cocos Creator is a full game engine and editor powered by JavaScript created by the team behind the Cocos2d-x project.  If you want to learn more about the Cocos Creator project be sure to check out our hands on video.  Today Cocos Creator learned a new trick in the form of C++ and Lua support for creator alpha zero.  This doesn’t actually add C++ or Lua language support to Cocos Creator editor.  Instead it’s a pair of new features.  First Cocos Creator is now capable of exporting projects in .ccreator file format.  Second they have provided a reader enabling Lua and C++ Cocos2d-x projects to read and use ccreator files.  Essentially this enables Cocos Creator to be an editor for your C++/Lua Cocos games.  Loading the scene in C++ is simple as illustrated by this code:

#include "reader/CreatorReader.h"

void some_function()
{
    creator::CreatorReader* reader = creator::CreatorReader::createWithFilename("creator/CreatorSprites.ccreator");

    // will create the needed spritesheets + design resolution
    reader->setup();

    // get the scene graph
    Scene* scene = reader->getSceneGraph();

    // ...and use it
    Director::getInstance()->replaceScene(scene);
}

 

Details from the Cocos Creator forum:

What's C++ and lua support for creator

It includes two parts:

  • First, it is a Cocos Creator plugin that can export scenes generated by Cocos Creator into .ccreator files.
  • Second, it has a reader that can parse .ccreator files. The reader can be used in cocos2d-x c++/lua project.

Download

There are two ways to get the plugin

GameDev News

Month List

Popular Comments