Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon
13. June 2014

 

As you can imagine by the name “SpriteKit”, Sprites are a pretty core part of creating a game using SpriteKit.  We are going to continue building on the minimal application we created in the previous part.  I want to point out, this isn’t the recommended way of working with SpriteKit, it is instead the simplest way.  In a proper application we would be more data driven and store our data in SKS files instead of simply adding them to the project.  This is something we will cover later on.  First lets jump right in with code.

 

We are going to replace the the class GameScene we created in the last tutorial.  In SpriteKit, the fundamentals of your game are organized into SKScene objects.  For now we only have one.  Let’s look:

 

import SpriteKit

 

class GameScene: SKScene {

        let sprite = SKSpriteNode(imageNamed: "sprite1.png")

    

    override func didMoveToView(view: SKView) {

        sprite.anchorPoint = CGPoint(x:0.5,y:0.5)

        sprite.xScale = 4

        sprite.yScale = 4

        self.addChild(sprite)

    }

    

    override func mouseDown(theEvent: NSEvent!) {

        self.sprite.position = CGPoint(x:theEvent.locationInWindow.x,y:theEvent.locationInWindow.y)

    }

}

 

We add the sprite “sprite1.png” to our project directory, simply drag and drop it from Finder.  The sprite(s) ill be using are from the zip file available here.  When you run this code, click anywhere and you should see:

 

Sd1

 

Where ever you click the mouse, the sprite will be drawn.

One immediate change you will notice in this code is sprite was moved out of didMoveToView and made a member variable.  This allows us to access sprite in different functions ( although we could retrieve the sprite from the Scene, something we will see later ). In Swift there are only two main ways of declaring a variable, let and var.  var is a variable meaning it’s value can change.  Using let on the other hand you are declaring a the the value cannot change, this is the same as a const in other languages.  As we touched on briefly in the last part, a let declared value can be assigned later using the ? postfix operator.  In this case, it will have the value of nil at initialization, unless one is specifically given like in the code we just did.  One thing you may notice is, unlike C++, C# and Java, Swift currently has no access modifiers.  In other words all variables are publicly available ( there are no private, internal, protected, etc modifiers available ).  Apparently this is only temporary and will be changed in the language later.  This personally seems like a very odd thing not to have in a language from day one.

Since we set the sprite’s anchor to the middle (0.5,0.5), the sprite will be centred to your mouse cursor.  As you can see we added a mouseDown event handler.  This class is available because SKScene inherits UIResponder, this is how you handle I/O events in your scene.  The only other new aspect to this code is:

        sprite.xScale = 4

        sprite.yScale = 4

 

 

This code causes the sprite to be scaled by a factor of 4x.  We do this simply because our source sprite was only 64x64 pixels, making it really really tiny in an empty scene!  As you can see, scaling sprites in SpriteKit is extremely easy.

 

The structure of a SpriteKit game is actually quite simple.  Your SKScene contains a graph of SKNodes, of which SKSpriteNode is one.  There are others too including SKVideoNode, SKLabelNode, SKShapeNode, SKEmitterNode and SKEffectNode.  Even SKScene itself is a SKNode, which is how all the magic happens.  Let’s take a quick look at an SKLabelNode in action.

 

import SpriteKit

 

class GameScene: SKScene {

    

    override func didMoveToView(view: SKView) {

        var label = SKLabelNode();

        label.text = "Hello World"

        label.fontSize = 128

        label.position = CGPoint(x:0,y:0)

        view.scene!.anchorPoint = CGPoint(x: 0.5,y: 0.5)

        self.addChild(label)

    }

    

}

Which predictably enough gives you:

Sd2

 

These nodes however can be parented to make hierarchies of nodes.  Take for example a combination of the two we’ve seen, our sprite node with a text label parented to it.

import SpriteKit

 

class GameScene: SKScene {

    

    override func didMoveToView(view: SKView) {

        view.scene!.anchorPoint = CGPoint(x: 0.5,y: 0.5)

        

        var sprite = SKSpriteNode(imageNamed:"sprite1.png")

        sprite.position = CGPoint(x:100,y:0);

        sprite.xScale = 4.0

        sprite.yScale = 4.0

        

        var label = SKLabelNode();

        label.text = "Jet Sprite"

        label.fontSize = 12

        label.position = CGPoint(x:0,y: 15)

        label.fontColor = NSColor.redColor()

        label.alpha = 0.5

 

        

        sprite.addChild(label)

        

        self.addChild(sprite)

    }

    

}

 

And when you run it:

Sd3

 

There are a few things to notice here.  Each Node get’s its default coordinates from it’s parents.  Since the jet sprite is parented to the scene and the scene’s anchor is set to the middle of the screen, when we position the screen 100 pixels to the right, that’s 100 pixels to the right of the centre of the screen.  Additionally, the text label is positioned relative to the sprite, so it’s positioning is relative to the sprite.  Another thing you might notice is the text is blurry as hell.  That is because the label is inheriting the scaling from it’s parent, the sprite.  As you can see you compose your scene by creating a hierarchy of various types of nodes.  Now if we were to transform the parent sprite, all the transformations will apply to the child nodes.

 

The following example shows how transforming a parent node effects all child nodes.  Spoilers, it also shows you how to Update a Scene… we will cover this in more detail later, so don’t pay too much attention to the man behind the curtain.

import SpriteKit

 

class GameScene: SKScene {

    

    var sprite = SKSpriteNode(imageNamed:"sprite1.png")

    override func didMoveToView(view: SKView) {

        view.scene!.anchorPoint = CGPoint(x: 0.5,y: 0.5)

        

        sprite.position = CGPoint(x:0,y:0);

        sprite.xScale = 8.0

        sprite.yScale = 8.0

        

        var label = SKLabelNode();

        label.text = "Jet Sprite"

        label.fontSize = 12

        label.position = CGPoint(x:0,y: 15)

        label.fontColor = NSColor.redColor()

        label.alpha = 0.5

 

        

        sprite.addChild(label)

        

        self.addChild(sprite)

    }

    

    override func update(currentTime: NSTimeInterval) {

        if(sprite.yScale > 0) {

            sprite.yScale -= 0.1

            sprite.xScale -= 0.1

        }

        else {

            sprite.xScale = 8.0

            sprite.yScale = 8.0

        }

    }

    

}

 

Now if we run this code:

 

 

Each time update() is called, the sprite is reduced in scaling until it disappears, at which point it’s zoomed back to 8x scaling.  As you can see, the child labelNode is scaled as well automatically.

 

Notice how until this point if we wanted to access our sprite across functions we had to make it a member variable?  As I said earlier, there is another option here, you name your nodes and retrieve them later using that name.  Like so:

 

import SpriteKit

 

class GameScene: SKScene {

    

 

    override func didMoveToView(view: SKView) {

        view.scene!.anchorPoint = CGPoint(x: 0.5,y: 0.5)

        var sprite = SKSpriteNode(imageNamed:"sprite1.png")

        

        sprite.name = "MyJetSprite"

        sprite.position = CGPoint(x:0,y:0);

        sprite.xScale = 4.0

        sprite.yScale = 4.0

        

        self.addChild(sprite)

    }

    

    override func update(currentTime: NSTimeInterval) {

        var sprite = self.childNodeWithName("MyJetSprite");

        if(sprite != nil){

            if(sprite.yScale > 0) {

                sprite.yScale -= 0.1

                sprite.xScale -= 0.1

            }

            else {

                sprite.xScale = 8.0

                sprite.yScale = 8.0

            }

        }

    }

    

}

You can perform some pretty advanced searches, such as searching recursively through the tree by prefixing your name with “//“.  You can also search for patterns and receive multiple results.  We will look at this in more details later.

 

This part is starting to get a bit long so I am going to stop now.  The next part will look at more efficient ways of using Sprites, such as using an Atlas, as well as look at basic animation and whatever else I think to cover!

 

Continue to Part 3


9. June 2014

 

 

I’ve done a number of these walk through type tutorials using many different languages/libraries and there is one common traffic trend.  People LOVE reading about graphics.  In every single example the post I do about graphics always seems to draw the most traffic.  I guess we just love drawing stuff on screen.  Now for the good part, Phaser is good at it and makes it really all quite easy.

 

Loading and adding a sprite

 

Back in the previous post I actually jumped the gun a bit and showed preloading and rendering sprites.  Since so many people jump ahead straight to the graphics post, I’ll review the process.

 

/// <reference path="phaser.d.ts"/>
class SimpleGame {
    game: Phaser.Game;
    titleScreenImage: Phaser.Sprite;

    constructor() {
        this.game = new Phaser.Game(800, 600, Phaser.AUTO, 'content', { create: this.create, preload: this.preload });
    }
    preload() {
        this.game.load.image("title", "TitleScreen.png");
    }
    create() {
        this.titleScreenImage = this.game.add.sprite(0, 0, "title");
    }
}

window.onload = () => {
    var game = new SimpleGame();
};

 

The key concepts to be aware of here is preloading assets using game.load methods, where you pass in the filename as well as a unique string key that you will use to access the asset.  Then in create you can see this in action, where we add a sprite to the game using game.add.sprite, using the key “title” to access it.  In this case our “sprite” was a full screen image.  Now let’s look at how you can work with a sprite, this time using a slightly smaller image.

Working with sprites

 

For this section I am going to work with this sprite ( created in this tutorial series ):

jet

 

Add it to your project’s root directory.  In my case I’ve called it jet.png.  Using the above code, simply replace “TitleScreen.png” with “jet.png” and “title” with “jet” and you should see:

 

image

 

 

As you can see, our sprite is drawn at the top left corner of the screen.  That is because the value (0,0) in Phaser refers to the top left corner of the screen by default.  Let’s instead center our sprite using the following code:

 

/// <reference path="phaser.d.ts"/>
class SimpleGame {
    game: Phaser.Game;
    jetSprite: Phaser.Sprite;

    constructor() {
        this.game = new Phaser.Game(800, 600, Phaser.AUTO, 'content', { create: this.create, preload: this.preload });
    }
    preload() {
        var loader = this.game.load.image("jet", "jet.png");
    }
    create() {
        var image = <Phaser.Image>this.game.cache.getImage("jet");
        
        this.jetSprite = this.game.add.sprite(
            this.game.width / 2 - image.width / 2,
            this.game.height / 2 - image.height / 2,
            "jet");
    }
}

window.onload = () => {
    var game = new SimpleGame();
};

 

Run this code and:

image

 

We are now nicely centered, both to the window and sprite.

We have one major challenge with centering the image.  Until the sprite is created, it doesn’t have a width or height.  However, when you create the sprite you can set it’s position.  Of course it would be possible to create then move the sprite but that is hackish and could have unintended graphical problems.  Instead we can get the image we loaded using this.game.cache.getImage() then access the images dimensions.  One line of code might stand out for you here:

 

var image = <Phaser.Image>this.game.cache.getImage("jet");

 

This is TypeScript’s way of typecasting.  If you’ve worked in Java, C# or C++ you’ve no doubt encountered typescasting.  If your experience was mostly in JavaScript ( a mostly typeless language ), this might be new to you.  Basically what you are saying is “we promise the value returned by getImage() is of the type <Phaser.Image>, so make image a Phaser.Image”.  If you try to access a value or method in image that doesn’t exist in Phaser.Image, TypeScript will give you an error.

 

Positioning items in Phaser

 

When using a sprite, by default, transformations happen relative to the top left corner of the sprite.  This is why we had to subtract half to the width and height of the sprite when positioning it in the center of the screen.  Otherwise the top left corner of the sprite would be centered to the screen like this:

image

 

Sometimes however you would rather transform the sprite relative to a different point, commonly the very middle or occasionally the bottom left corner.   Fortunately there is an option for this, the anchor.  The anchor tells Phaser where to draw your Sprite relative to.  Here we set the anchor to the center of the sprite then draw it at (0,0) like so:

 

    create() {
        var image = <Phaser.Image>this.game.cache.getImage("jet");
        
        this.jetSprite = this.game.add.sprite(
            this.game.width / 2 - image.width / 2,
            this.game.height / 2 - image.height / 2,
            "jet");

        this.jetSprite.anchor.set(0.5,0.0)
        this.jetSprite.position.x = this.jetSprite.position.y = 0.0;
    }

 

And the result:

image

As you can see, draw calls for the sprite now position relative to it’s center.  Positioning sprites relative to their center is incredibly handy when it comes to rotation, while anchoring at the bottom is useful for platformers where you are aligning the sprite’s feet to the ground.  What you chose is entirely up to you.  The values passed in to anchor might be a bit confusing, as they are normalized, meaning they go from 0 to 1.  The values are all relative to the sprite itself, while (0,0) is the top left corner of the sprite, while (1,1) is the bottom right corner.  (1,0) would be the bottom left, while (0,1) would be the top right.

 

There is one important thing to be aware of here.  Anchor works relative to the source image, not the sprite itself.  Therefore if you intend to scale your sprites, instead of using anchor, you are going to want to use pivot instead.  (Until recently pivot was broken, but it appears to work now).  Pivot sets the center point of the sprite, not the image that composes the sprite.  Setting the pivot looks like this:

        this.jetSprite.pivot.x = this.jetSprite.width / 2;
        this.jetSprite.pivot.y = this.jetSprite.height / 2;

 

Again, you don’t have to set the anchor at all, but it can be useful.  Unlike anchor, pivot uses relative pixel coordinates within the sprite itself.  Therefore the mid-point is at (width/2,height/2).  Once again, (0,0) is the top left corner.

 

Simple Graphics

 

Sometimes you just want to draw primitive graphics on screen… lines, circles, boxes, that kind of stuff.  Fortunately Phaser has that built in as well in the form of the Graphics object.

 

/// <reference path="phaser.d.ts"/>
class SimpleGame {
    game: Phaser.Game;
    jetSprite: Phaser.Sprite;

    constructor() {
        this.game = new Phaser.Game(800, 600, Phaser.AUTO, 'content', { create: this.create, preload: this.preload });
    }
    preload() {
        var loader = this.game.load.image("jet", "jet.png");
    }
    create() {
        // Add a graphics object to our game
        var graphics = this.game.add.graphics(0, 0);

        // Create an array to hold the points that make up our triangle
        var points: Phaser.Point[] = [];
        // Add 4 Point objects to it
        points.push(new Phaser.Point());
        points.push(new Phaser.Point());
        points.push(new Phaser.Point());

        // Position one top left, top right and botto mmiddle
        points[0].x = 0;
        points[0].y = 0;

        points[1].x = this.game.width;
        points[1].y = 0;

        points[2].x = this.game.width/2;
        points[2].y = this.game.height;

        // set fill color to red in HEX form.  The following is equal to 256 red, 0 green and 0 blue.  
        // Do at 50 % alpha, meaning half transparent
        graphics.beginFill(0xff0000, 0.5);
        
        // Finally draw the triangle, false indicates not to cull ( remove unseen values )
        graphics.drawTriangle(points, false);

        // Now change colour to green and 100% opacity/alpha
        graphics.beginFill(0x00ff00, 1.0);

        // Draw circle about screen's center, with 200 pixels radius
        graphics.drawCircle(this.game.width / 2, this.game.height / 2, 200);
        
    }
}

window.onload = () => {
    var game = new SimpleGame();
};

 

The code is pretty heavily commented so should be self explanatory.  When you run it you should see:

image

 

 

A look behind the scenes

 

Let’s take a quick look at how graphics drawing works in Phaser.  That involves going back to this line:

this.game = new Phaser.Game(800, 600, Phaser.AUTO, 'content', {});

Here you are passing in a lot of important information.  First (and second) are the resolution of your game.  Next is the type of Renderer that Phaser should use.  We mentioned this briefly in the prior tutorial.  You have the option of WEBGL or Canvas rendering ( or headless, which means no rendering at all and is used for server side programming ).  Which you chose depends heavily on the device you are supporting.  For example, currently no iOS devices support WebGL and only the most recent version of Internet Explorer work.  By selecting AUTO you let Phaser decide based on the device you are running on.  Finally ‘content’ is the HTML ID of the DIV to render our game in.

 

You may notice scattered throughout Phaser’s code/documentation are references to PIXI.  Pixi.js is a popular WebGL 2D renderer that is able to fallback on Canvas rendering when WebGL is unavailable.  Pixi is the renderer that Phaser uses, so you will occasionally see Pixi classes inside Phaser code.

 

There is one final thing to cover about graphics before moving on, full screen and handling multiple resolutions.

 

Going Full Screen

 

Now let’s take a look at an application that can go full screen:

 

/// <reference path="phaser.d.ts"/>
class SimpleGame {
    game: Phaser.Game;
    jetSprite: Phaser.Sprite;

    constructor() {
        this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', { create: this.create, preload: this.preload });
    }
    preload() {
        var loader = this.game.load.image("jet", "jet.png");
    }

    // This function is called when a full screen request comes in
    onGoFullScreen() {
        // tell Phaser how you want it to handle scaling when you go full screen
        this.game.scale.fullScreenScaleMode = Phaser.ScaleManager.EXACT_FIT;
        // and this causes it to actually do it
        this.game.scale.refresh();
    }
    goFullScreen() {

    }
    create() {
        var image = <Phaser.Image>this.game.cache.getImage("jet");

        // Draw the jet image centered to the screen
        this.jetSprite = this.game.add.sprite(
            this.game.width / 2 - image.width / 2,
            this.game.height / 2 - image.height / 2,
            "jet");

        // Set background to white to make effect clearer
        this.game.stage.backgroundColor = 0xffffff;

        // Add a function that will get called when the game goes fullscreen
        this.game.scale.enterFullScreen.add(SimpleGame.prototype.onGoFullScreen, this);

        // Now add a function that will get called when user taps screen.
        // Function declared inline using arrow (=>) function expression
        // Simply calls startFullScreen().  True specifies you want anti aliasing.
        // Unfortunately you can only make full screen requests in desktop browsers in event handlers
        this.game.input.onTap.add(
            () => { this.game.scale.startFullScreen(true); },
            this);
    }

}

window.onload = () => {
    var game = new SimpleGame();
};

 

The comments cover most of what’s going on, but I thought I would touch on a couple things in the above example.  First you cant simply request to go fullScreen in Desktop browsers for security reasons.  This means your game can’t simply start in full screen, you need to make the call to startFullScreen() inside an event handler.  Most commonly this will be in the form of a “Click here for FullScreen” button or link.

 

Next is the ()=> syntax, known in TypeScript as an arrow function expression (if you’ve used C#, this syntax is going to look awfully familiar to you!) and is something that should be coming in the next JavaScript version (ECMAScript 6).  It is simply a more compact form of a function expression ( no need for the word function ) that is automatically scoped to “this”.  You could have created a function like onGoFullScreen like we did for enterFullScreen.  ( Coincidentally we could have also handled enterFullScreen using an arrow function.

 

The last thing to look at is the scale mode.  In this example we used Phaser.ScaleManager.EXACT_FIT, which scales the scene up to match your resolution.  There are two other options, SHOW_ALL and NO_SCALE.  Here is the result of running the code above using each setting:

 

Phaser.ScaleManager.EXACT_FIT

EXACT_FIT

 

Phaser.ScaleManager.NO_SCALE

NO_SCALE

 

Phaser.ScaleManager.SHOW_ALL 

SHOW_ALL

 

If you have an HDTV, you can think about them this way.  EXACT_FIT is the same as Stretch mode.  It scales the scene to use as much of the screen as possible, but can result in some distortion.  NO_SCALE does nothing, it simply shows the scene un-altered, centered to the screen.  SHOW_ALL is about the equivalent of Letterbox.  Basically it fits as well as it can while maintaining the aspect ration of your original scene. 

 

Don’t worry, that’s not it for graphics, we have all kinds of things coming up…  spritesheets, effects, particles, animation, etc…  That’s just it for the foundations.

 

Programming


29. May 2014

 

 

One thing every single game has in common is a game loop.  That said, it’s not always under your control!  Today we are going to look at how the game loop is implemented in the Phaser HTML5 game engine.

 

Pretty much every single video game ever created follows the same basic program flow:

 

Program Starts

Check Input

Update World

Draw scene

Program Exits

 

Of course this is a massive simplification ignoring things like updating physics simulations, multiple threads, streaming of assets, etc… but the basic flow is there in every game.  The three indented process, Input, Update and Draw are performed over and over in a loop, thus “game loop”.  In Phaser there is no exception, but the way it’s handled is a bit different.

 

If you’ve done any JavaScript game development before you’ve no doubt encountered requestAnimationFrame or if using an older browser setTimeout JavaScript functions.  Both perform basically the same task, they call a function once an interval, such as every 30th of a second if your game is set to run at 30fps.  This is the very heart of most JavaScript games and Phaser is no exception.  You the end developer don’t have to care about such low level aspacts though as this functionality is taken care of in the class Phaser.RequestAnimationFrame and is automatically created by Phaser.Game.  If you want to see the actual game loop driving your game though, I suppose this code snippet from RequestAnimationFrame.js is it:

 

    updateRAF: function () {

        this.game.update(Date.now());

        this._timeOutID = window.requestAnimationFrame(this._onLoop);

    }

 

As you can see, its basically just calling Game’s update() over and over.  Now if we take a look at the source code for update in Game it all becomes clear:

 

update: function (time) {

    this.time.update(time);

    if (!this._paused && !this.pendingStep) {
        if (this.stepping) {
            this.pendingStep = true;
        }

        this.debug.preUpdate();
        this.physics.preUpdate();
        this.state.preUpdate();
        this.plugins.preUpdate();
        this.stage.preUpdate();

        this.state.update();
        this.stage.update();
        this.tweens.update();
        this.sound.update();
        this.input.update();
        this.physics.update();
        this.particles.update();
        this.plugins.update();

        this.stage.postUpdate();
        this.plugins.postUpdate();
    }
    else {
        this.state.pauseUpdate();
        // this.input.update();
        this.debug.preUpdate();
    }

    if (this.renderType != Phaser.HEADLESS) {
        this.renderer.render(this.stage);
        this.plugins.render();
        this.state.render();
        this.plugins.postRender();
    }

}

 

So there is your traditional game loop, just nicely tucked away.  So then, where then does your code fit in all of this?  Remember back in the Hello World post when we created a Game instance we past in a “State” object implementing create and passed in the function this.create to be called, like so:

this.game = new Phaser.Game(800, 600, Phaser.AUTO, 'content', { create: this.create });

 

Well, that’s how we do it.  A State object has a number of functions that will be called, in this case we provide an implementation for create, now lets look at a slightly more complicated example:

 

class SimpleGame {

    constructor() {
        this.game = new Phaser.Game(800, 600, Phaser.CANVAS, 'content', {
            create: this.create, update: this.update,
        render: this.render});
    }

    game: Phaser.Game;
    textValue: Phaser.Text;
    updateCount: number;

    create() {
        var style = { font: "65px Arial", fill: "#ff0000", align: "center" };
        this.textValue = this.game.add.text(0, 0, "0", style);
        this.updateCount = 0;
    }

    update() {
        this.textValue.text = (this.updateCount++).toString();
    }

    render() {
        this.game.debug.text("This is drawn in render()", 0, 80);
    }
}

window.onload = () => {
    var game = new SimpleGame();
};

 

Here is the code running:

 

 

In this example the State object we are passing in to the Phaser.Game constructor implements create, update and render.  Create will be called once, predictably enough on creation.  Here we create a red text object like we did in the Hello World example.  This time however we keep a reference to it.  We also add a counter variable updateCount.  Each frame update() will be called, we simply increment the counter value and assign this to out text object.  Finally in render we draw our text using game.debug.text().  Phaser provides a number of convenient debug methods for dumping information on screen, either as text or graphically.  These functions however are *NOT* optimized and should not be used in production!

 

So as you can see, update works pretty much like you would expect, but instead of your game controlling the loop you implement methods in a State object that will be called by the Phaser engine.

 

Let’s look at a slightly more complex example that will probably make State objects make a bit more sense.  This is a two screen game, first there is a title sreen shown that when clicked then moves to the game state, which is the same as the above demo.  Let’s jump in with code:

 

module GameFromScratch {
    export class TitleScreenState extends Phaser.State {
        game: Phaser.Game;
        constructor() {
            super();
        }
        titleScreenImage: Phaser.Sprite;

        preload() {
            this.load.image("title", "TitleScreen.png");
        }
        create() {
            this.titleScreenImage = this.add.sprite(0, 0, "title");
            this.input.onTap.addOnce(this.titleClicked,this); // <-- that um, this is extremely important
        }
        titleClicked (){
            this.game.state.start("GameRunningState");
        }
    }

    export class GameRunningState extends Phaser.State {
        constructor() {
            super();
        }
        textValue: Phaser.Text;
        updateCount: number;

        create() {
            var style = { font: "65px Arial", fill: "#ff0000", align: "center" };
            this.textValue = this.game.add.text(0, 0, "0", style);
            this.updateCount = 0;
        }

        update() {
            this.textValue.text = (this.updateCount++).toString();
        }

        render() {
            this.game.debug.text("This is drawn in render()", 0, 80);
        }
    }

    export class SimpleGame {
        game: Phaser.Game;

        constructor() {
            this.game = new Phaser.Game(800, 600, Phaser.WEBGL, 'content');

            this.game.state.add("GameRunningState", GameRunningState, false);
            this.game.state.add("TitleScreenState", TitleScreenState, false);
            this.game.state.start("TitleScreenState", true, true);
        }

    }
}

window.onload = () => {
    var game = new GameFromScratch.SimpleGame();
};

 

And when you run it you see ( click to proceed ):

 

One thing to be aware of right away is this example should probably be split across 3 files not in a single one.  I kept them together to make following along easier.

 

Here, instead of creating a state object inline we declare two of them.  Here we are using the TypeScript ability extend to create Phaser.State derived objects as inheritance really isn’t part of JavaScript. Let’s take a quick look at what this code does, starting with SimpleGame.

 

Here instead of providing a State object to the Phaser.Game constructor inline ( in { } form ) we register 2 different states using game.state.add().  The first value is the string value we will access this state using, the second is the state itself while the final value is if you want to start the state while adding it.  Finally after adding each state we start one calling game.stat.start and pass in the key value of the state.  Yes, we could have simply added true when we added the TitleScreenState, but I think doing it long form is clearer.

 

Once our TitleScreenState starts, in preload it loads an image, then in create it adds the image as a sprite.  Finally it adds a tap handler that will be called when the screen is tapped.  Image loading, sprites and input will all be covered later.  One very important thing to understand here though is the this parameter passed in to onTap.addOnce.  The second value is the context that titleClicked will be called in.  In other words, the value of “this” within titleClicked is determined by the value you pass here.  This is one of the warts of JavaScript IMHO and I wished TypeScript fixed it, although it appears it hasn’t.  The importance here is, if you don’t pass the context into the Signal (onTap) then the called function (titleClicked) wont have access to itself!  You will then get an error that this.game is undefined.  Finally when titleClicked is called we launch the GameRunningState like we did earlier.  GameRunningState is basically just the functionality from our earlier example split out into Phaser.State form.

 

As you can see, Phaser.State objects allow you to logically split up your game functionality.  Of course you could just implement a single inline state like we did earlier and ignore them from this point on.

 

Programming


4. May 2014

 

Last year the Torque2D was open sourced under an MIT license by GarageGames.  Torque2D is a cross platform 2D game engine.  Originally it only targeted Windows, OSX and iOS.  Now fortunately support for Android, Web and Linux have been added with the release of version 3.0.

 

From the release notes, here is what’s new:

What’s New in 3.0


The following is a summary of the major changes and additions:


Android, Linux, and Web Support

The major focus of this release brings several additional platform publishing options for your game or application. T2D truly delivers on cross platform support, making it easy with one codebase to reach many different audiences on many different devices.

64-bit Applications

In addition to the standard 32-bit support we’ve always had, the engine can now compile as a native 64-bit application for platforms that support it.

Named Image Cells and Frames

Until now, image frames have always been referenced using a numerical index. Now, developers have the opportunity to give explicit ImageAsset cells specific names. Those names can then be used with all SceneObject derived classes that consume ImageAssets.

<ImageAsset 

  1.     AssetName="soldierWalk"
  2.     ImageFile="@assetFile=soldierWalk.png">   
  3.     <ImageAsset.Cells> 
  4.         <Cell RegionName="walk1" Offset="2 2" Width="103" Height="66"/> 
  5.         <Cell RegionName="walk2" Offset="179 100" Width="37" Height="35"/> 
  6.         <Cell RegionName="walk3" Offset="2 70" Width="65" Height="31"/> 
  7.         <Cell RegionName="walk4" Offset="161 137" Width="36" Height="41"/>         
  8.     </ImageAsset.Cells> 
  9. </ImageAsset> 
  10. <AnimationAsset 
  11.     AssetName="soldierWalkAnim"
  12.     Image="@asset=ToyAssets:soldierWalk 
  13.     NamedAnimationFrames="walk1 walk2 walk3 walk4"
  14.     AnimationTime="1" /> 

Skeletal Animation

We are pleased to announce that an initial implementation for skeletal animation is now available. The first format supported comes from the animation program Spine.
t2dtutorials.com/img/SpineToy.png
Leap Motion Controller

The Leap Motion controller takes input from hands, fingers, and small tools that are moved just above its surface and provides their absolute position and rotation through the Leap SDK. Torque 2D offers built in integration and now includes a demo toy to show off how it works.
t2dtutorials.com/img/LeapToy.png
Xbox 360 Controller

Gamepad support has always been part of the engine, but now the necessary code to use an Xbox 360 controller with T2D has been added.

JSON Format for TAML

Joining XML and binary, developers can how serialize their game objects and scenes in the JSON format, with the engine being able to read-in or write-out this format as easily as the others.

 

  1. "Sprite": { 
  2. "SceneLayer": "31", 
  3. "Size": "100 75", 
  4. "BodyType": "Static", 
  5. "BlendColor": "SlateGray", 
  6. "Image": "@asset=ToyAssets:highlightBackground", 
  7. "Frame": "0"
  8.     } 

ConsoleMethodWithDocs

A major undertaking in the codebase, the vast majority of TorqueScript binding methods and global functions have moved into their own header files. This makes the engine code much easier to read and allowed us to further integrate Doxygen support for a comprehensive reference guide available in the wiki.

ScriptObject Behaviors

Behavior support was added to the ScriptObject class. If you enjoy writing behaviors, now there is an engine class outside of the Scene and SceneObjects which can use them.

Polar Coordinate Changes

One of the few changes made that would break any of your existing scripts - polar coordinates have been standardized throughout the engine to follow the typical polar axis convention where 0 degrees is horizontal and to the right.

New Sandbox Toys

A few more toys were added to the Sandbox, giving new users even more practical examples to learn from. These include AudioToy, AngleToy, LeapToy, and SpineToy.
t2dtutorials.com/img/AudioToy.png

Change List


Below is a list of all changes that have been made to the development branch of the Torque2D repository since the 2.0 release and have now been pushed to the master branch and tagged as Torque 2D 3.0.
TAML supports reading and writing in JSON format
Batch renderer now works exclusively with triangles instead of quads
Fixed SceneObject.applyForce and applyLinearImpulse
Added signum convenience function (PR #56)
New Gui Object: GuiGridCtrl (PR #9)
Added missing TorqueScript methods to ParticleAsset/Emitter/Player, renamed a few static fields for consistency (breaking change) (PR #68)
Fixed ImageAsset typo - image width and height were swapped (PR #70)
All picking modes for CompositeSprite are now functional (PR #69)
TextureManager fix - replaced call to use expandPath (PR #83)
ParticleAsset creation fix - now writes out correctly to TAML (PR #84)
ParticlePlayer play and stop functions now correctly pause and unpause emitters (PR #85)
Added functions to get the explict width and height of cells to ImageAsset (PR #88)
Fixed AABB size in ImageFont (PR #97)
Fixed TAML bug with ImageFont (PR #99)
Fixed TAML bug with ShapeVector (PR #104)
Added Mac OSX 10.6 support (PR #101)
Added linear interpolation and smooth step functions, SceneWindow now uses these (PR #95)
OpenAL streaming audio fix (PR #106)
OpenAL pause and unpause audio fix (PR #115)
OpenAL shutdown fix (PR #114)
PointForceController getPosition definition fix (PR #110)
SimXMLDocument crash fix (PR #113)
Replaced most ConsoleMethod (and functions) with ConsoleMethodWithDocs for easier Doxygen reading, moved most console methods into their own ScriptBinding.h files. (PR #116)
Behavior support for ScriptObject (PR #121)
Angle/polar coordinate issue fixes (breaking change), AngleToy added to Sandbox (PR #123)
Input bug fix for OSX (PR #125)
Fix assert conditional in consoleNamespace.cc (PR #126)
Android platform support added
Xbox 360 controller support (PR #136)
Leap Motion controller support, LeapToy added to Sandbox (PR #137)
Added missing Mutex to StringTable::lookupn (PR #127)
Asset support for named cells and frames (PR #139)
Bugfix for SceneObject::getContacts (PR #142)
Fixed deleting a clone of an object breaking the original object (PR #143)
Fixed bug that prevented scene controllers from being read by TAML (PR #145)
Initial implementation of Spine support, SpineToy added to Sandbox (PR #146)
Doxygen file cleanup (PR #148)
Fixed some Xcode compiler warnings (PR #150)
64-bit platform support (PR #154)
Fixed GUI object namespace issue (PR #152)
Fixed crash when executing FileDialogs (PR #153)
Linux platform support (PR #138)
AudioToy added to the Sandbox
Exposed method CompositeSprite::getSpriteLogicalPosition in TorqueScript (PR #155)
Emscripten platform support (PR #157)
Fixed TAML binary writer crash (PR #158)
Fixed compile errors and warnings resulting from the Xcode 5.1 update (PR #159)
Windows platform fixes (PR #168)
SkeletonObject now respects scene layer depth settings (PR #171)
Named cell and frame improvements (PR #172)
Fixed iOS7 bug with audio not playing (PR #175)
Arm64 support (PR #176)
Removed PVRTexTool files (PR #177)
Android platform fixes (PR #174)
Visual Studio 2013 solution files added (PR #182)
Behavior Connection bug fixes (PR #183)

 

Torque2D is available on Github.

 

I’ve personally never checked out Torque2D, one of the biggest drawbacks was the limited platform support, which has been solved by this release.  My other major caveat is their use of a proprietary scripting language, something I am generally not a fan off.

News


1. May 2014

 

In the prior tutorial we got a simple top down tile map working.  Now we want to add some character to it, literally.  Depending on your needs this can be laughably simple or somewhat complex.  Let’s take a look at a basic example.  This is building on our previous code example and if you’ve worked through the prior tutorials in this series, there is nothing new here yet.

 

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TiledMapRenderer;
import com.badlogic.gdx.maps.tiled.TmxMapLoader;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;

public class TiledTest extends ApplicationAdapter implements InputProcessor {
    Texture img;
    TiledMap tiledMap;
    OrthographicCamera camera;
    TiledMapRenderer tiledMapRenderer;
    SpriteBatch sb;
    Texture texture;
    Sprite sprite;
    
    @Override public void create () {
        float w = Gdx.graphics.getWidth();
        float h = Gdx.graphics.getHeight();

        camera = new OrthographicCamera();
        camera.setToOrtho(false,w,h);
        camera.update();
        tiledMap = new TmxMapLoader().load("MyCrappyMap.tmx");
        tiledMapRenderer = new OrthogonalTiledMapRenderer(tiledMap);
        Gdx.input.setInputProcessor(this);

        sb = new SpriteBatch();
        texture = new Texture(Gdx.files.internal("pik.png"));
        sprite = new Sprite(texture);
    }

    @Override public void render () {
        Gdx.gl.glClearColor(1, 0, 0, 1);
        Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        camera.update();
        tiledMapRenderer.setView(camera);
        tiledMapRenderer.render();
        sb.begin();
        sprite.draw(sb);
        sb.end();
    }

    @Override public boolean keyDown(int keycode) {
        return false;
    }

    @Override public boolean keyUp(int keycode) {
        if(keycode == Input.Keys.LEFT)
            camera.translate(-32,0);
        if(keycode == Input.Keys.RIGHT)
            camera.translate(32,0);
        if(keycode == Input.Keys.UP)
            camera.translate(0,-32);
        if(keycode == Input.Keys.DOWN)
            camera.translate(0,32);
        if(keycode == Input.Keys.NUM_1)
            tiledMap.getLayers().get(0).setVisible(!tiledMap.getLayers().get(0).isVisible());
        if(keycode == Input.Keys.NUM_2)
            tiledMap.getLayers().get(1).setVisible(!tiledMap.getLayers().get(1).isVisible());
        return false;
    }

    @Override public boolean keyTyped(char character) {

        return false;
    }

    @Override public boolean touchDown(int screenX, int screenY, int pointer, int button) {
        return false;
    }

    @Override public boolean touchUp(int screenX, int screenY, int pointer, int button) {
        return false;
    }

    @Override public boolean touchDragged(int screenX, int screenY, int pointer) {
        return false;
    }

    @Override public boolean mouseMoved(int screenX, int screenY) {
        return false;
    }

    @Override public boolean scrolled(int amount) {
        return false;
    }
}

 

 

So what exactly did we do?  Well first off, we added this sprite to the project in the android/asset folder.

pik

It might look somewhat familiar… 

In create() we allocate a sprite batch, load our texture and create a sprite with it.  Finally in render() after the map is drawn, we simply draw our sprite to the batch.  Order is of critical importance.  One other thing I should probably point out… I don’t dispose() of anything, so this code leaks like mad…  Now if you run the code you see:

image

 

So far so good! 

 

First problem we’ve got here is, how do we position our sprite within the world?  In this case I am simply drawing at the origin, but how would we go about positioning the sprite in the tile a user clicks?  Let’s go ahead and see!

 

Add the following to the touchDown handler:

public boolean touchDown(int screenX, int screenY, int pointer, int button) {
    Vector3 clickCoordinates = new Vector3(screenX,screenY,0);
    Vector3 position = camera.unproject(clickCoordinates);
    sprite.setPosition(position.x, position.y);
    return true;
}

 

Now we need to make a small change to our sprite batch.  We need to apply our camera transformations to it, so when we scroll the screen around using the arrow keys, our sprite stays put.  This is easily accomplished in render():

public void render () {
    Gdx.gl.glClearColor(1, 0, 0, 1);
    Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    camera.update();
    tiledMapRenderer.setView(camera);
    tiledMapRenderer.render();
    sb.setProjectionMatrix(camera.combined);
    sb.begin();
    sprite.draw(sb);
    sb.end();
}

 

Now when you run it, the sprite will draw where you click in the world:

image

 

There’s a small possible problem here… what if you wanted your sprite to be BEHIND those rocks, or items in the higher level layers? 

 

There are a couple of ways to do this.  First you could actually create a layer for player sprites.  You could simply create a layer in Tiled that you kept empty and add your sprite into it.  Here instead I am going to do so programmatically.  Let me make something perfectly clear, this is most certainly *NOT* how you would do it, but it does give an interesting bit of insight into how tiles and layers are composed.

public void create () {
    float w = Gdx.graphics.getWidth();
    float h = Gdx.graphics.getHeight();

    camera = new OrthographicCamera();
    camera.setToOrtho(false,w,h);
    camera.update();
    tiledMap = new TmxMapLoader().load("MyCrappyMap.tmx");
    tiledMapRenderer = new OrthogonalTiledMapRenderer(tiledMap);
    Gdx.input.setInputProcessor(this);

    sb = new SpriteBatch();
    texture = new Texture(Gdx.files.internal("pik.png"));
    sprite = new Sprite(texture);

    
    // Get the width and height of our maps
    // Then halve it, as our sprites are 64x64 not 32x32 that our map is made of
    int mapWidth = tiledMap.getProperties().get("width",Integer.class)/2;
    int mapHeight = tiledMap.getProperties().get("height",Integer.class)/2;

    // Create a new map layer
    TiledMapTileLayer tileLayer = new TiledMapTileLayer(mapWidth,mapHeight,64,64);
    
    // Create a cell(tile) to add to the layer
    TiledMapTileLayer.Cell cell = new TiledMapTileLayer.Cell();
    
    // The sprite/tilesheet behind our new layer is a single image (our sprite)
    // Create a TextureRegion that is the entire size of our texture
    TextureRegion textureRegion = new TextureRegion(texture,64,64);
    
    // Now set the graphic for our cell to our newly created region
    cell.setTile(new StaticTiledMapTile(textureRegion));
    
    // Now set the cell at position 4,10 ( 8,20 in map coordinates ).  This is the position of a tree
    // Relative to 0,0 in our map which is the bottom left corner
    tileLayer.setCell(4,10,cell);

    // Ok, I admit, this part is a gross hack. 
    // Get the current top most layer from the map and store it
    MapLayer tempLayer = tiledMap.getLayers().get(tiledMap.getLayers().getCount()-1);
    // Now remove it
    tiledMap.getLayers().remove(tiledMap.getLayers().getCount()-1);
    // Now add our newly created layer
    tiledMap.getLayers().add(tileLayer);
    // Now add it back, now our new layer is not the top most one.
    tiledMap.getLayers().add(tempLayer);
}

 

And look, we are behind a tree!

image

 

 

The comments explain it all really.  We basically are programmatically creating a new Tile Layer within the map and positioning it below the top most layer.  You would never do it this way for a sprite for several reasons.  First you will only be able to move in cell by cell chunks ( aka, 64 pixels at a time ).  In certain maps that makes sense, like old school Ultima 4 type role playing games.  Next, to move you actually will have to remove the tile from it’s current location and add it to the new one.  Finally, it’s probably pretty slow to boot.  However if you want to dynamically populate a layer, say with power ups or things you create programmatically instead of using the level editor, this is how you can do it.

 

What about when dealing with the players sprite and moving it only a few pixels at a time?  Well that’s slightly more complicated.

 

One option you have, especially if you want to use the Sprite class, is to extend the map rendering class, in this case OrthogonalTiledMapRenderer then override the render method and render your sprite(s).  Here is a simple example:

package com.gamefromscratch;

import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.maps.MapLayer;
import com.badlogic.gdx.maps.MapObject;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TiledMapTileLayer;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;

import java.util.ArrayList;
import java.util.List;

public class OrthogonalTiledMapRendererWithSprites extends OrthogonalTiledMapRenderer {
    private Sprite sprite;
    private List<Sprite> sprites;
    private int drawSpritesAfterLayer = 1;

    public OrthogonalTiledMapRendererWithSprites(TiledMap map) {
        super(map);
        sprites = new ArrayList<Sprite>();
    }

    public void addSprite(Sprite sprite){
        sprites.add(sprite);
    }

    @Override
    public void render() {
        beginRender();
        int currentLayer = 0;
        for (MapLayer layer : map.getLayers()) {
            if (layer.isVisible()) {
                if (layer instanceof TiledMapTileLayer) {
                    renderTileLayer((TiledMapTileLayer)layer);
                    currentLayer++;
                    if(currentLayer == drawSpritesAfterLayer){
                        for(Sprite sprite : sprites)
                            sprite.draw(this.getSpriteBatch());
                    }
                } else {
                    for (MapObject object : layer.getObjects()) {
                        renderObject(object);
                    }
                }
            }
        }
        endRender();
    }
}

 

Now we simply use it in place of the other TileMapRenderer.  Not now that we no longer need a SpriteBatch, since we are using the map renderer’s.  Here’s the updated code:

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TmxMapLoader;
import com.badlogic.gdx.math.Vector3;

public class TiledTest extends ApplicationAdapter implements InputProcessor {
    Texture img;
    TiledMap tiledMap;
    OrthographicCamera camera;
    OrthogonalTiledMapRendererWithSprites tiledMapRenderer;
    Texture texture;
    Sprite sprite;
    
    @Override
    public void create () {
        float w = Gdx.graphics.getWidth();
        float h = Gdx.graphics.getHeight();

        camera = new OrthographicCamera();
        camera.setToOrtho(false,w,h);
        camera.update();

        texture = new Texture(Gdx.files.internal("pik.png"));
        sprite = new Sprite(texture);

        tiledMap = new TmxMapLoader().load("MyCrappyMap.tmx");
        tiledMapRenderer = new OrthogonalTiledMapRendererWithSprites(tiledMap);
        tiledMapRenderer.addSprite(sprite);
        Gdx.input.setInputProcessor(this);
    }

    @Override
    public void render () {
        Gdx.gl.glClearColor(1, 0, 0, 1);
        Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        camera.update();
        tiledMapRenderer.setView(camera);
        tiledMapRenderer.render();
    }

    @Override
    public boolean keyDown(int keycode) {
        return false;
    }

    @Override
    public boolean keyUp(int keycode) {
        if(keycode == Input.Keys.LEFT)
            camera.translate(-32,0);
        if(keycode == Input.Keys.RIGHT)
            camera.translate(32,0);
        if(keycode == Input.Keys.UP)
            camera.translate(0,-32);
        if(keycode == Input.Keys.DOWN)
            camera.translate(0,32);
        if(keycode == Input.Keys.NUM_1)
            tiledMap.getLayers().get(0).setVisible(!tiledMap.getLayers().get(0).isVisible());
        if(keycode == Input.Keys.NUM_2)
            tiledMap.getLayers().get(1).setVisible(!tiledMap.getLayers().get(1).isVisible());
        return false;
    }

    @Override
    public boolean keyTyped(char character) {

        return false;
    }

    @Override
    public boolean touchDown(int screenX, int screenY, int pointer, int button) {
        Vector3 clickCoordinates = new Vector3(screenX,screenY,0);
        Vector3 position = camera.unproject(clickCoordinates);
        sprite.setPosition(position.x, position.y);
        return true;
    }

    @Override
    public boolean touchUp(int screenX, int screenY, int pointer, int button) {
        return false;
    }

    @Override
    public boolean touchDragged(int screenX, int screenY, int pointer) {
        return false;
    }

    @Override
    public boolean mouseMoved(int screenX, int screenY) {
        return false;
    }

    @Override
    public boolean scrolled(int amount) {
        return false;
    }
}

 

Now when you run it, where you click the player sprite will be positioned correctly and behind objects:

image

.

Under this system however, you just treat your sprite normally.  Update it’s position and it’s updated in the game world.

 

There is one other possible way to support this functionality, that is a bit of a hybrid of both approaches.  Like the earlier example, we add a new layer in to the map, in this case we are going to do it in Tiled instead of programmatically.

 

In Tiled, create a new object layer and position it like so:

image

 

Now we are going to access it using the MapObject class, specifically the TextureMapObject ( a somewhat confusingly named class… ).  Once again this requires a custom TileRenderer implementation, this time overriding the renderObject class, like so:

package com.gamefromscratch;

import com.badlogic.gdx.maps.MapObject;
import com.badlogic.gdx.maps.objects.TextureMapObject;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;

public class OrthogonalTiledMapRendererWithSprites extends OrthogonalTiledMapRenderer {

    public OrthogonalTiledMapRendererWithSprites(TiledMap map) {
        super(map);
    }

    @Override
    public void renderObject(MapObject object) {
        if(object instanceof TextureMapObject) {
            TextureMapObject textureObj = (TextureMapObject) object;
                spriteBatch.draw(textureObj.getTextureRegion(), textureObj.getX(), textureObj.getY());
        }
    }
}

 

renderObject() is called to render the various well… MapObjects in the tilemap.  The default implementation is empty, so we need to provide one.  Here we simply check to see if our object is a TextureMapObject and if it is we draw it using the spriteBatch, much like before.

 

A side effect is we no longer use the Sprite class for positioning.  Instead we create a TextureRegion again and control position via the TextureMapObject setX/setY methods.  Let’s take a look at the result on our main application:

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.maps.MapLayer;
import com.badlogic.gdx.maps.objects.TextureMapObject;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TiledMapRenderer;
import com.badlogic.gdx.maps.tiled.TmxMapLoader;

import com.badlogic.gdx.math.Vector3;

public class TiledTest extends ApplicationAdapter implements InputProcessor {
    Texture img;
    TiledMap tiledMap;
    OrthographicCamera camera;
    TiledMapRenderer tiledMapRenderer;
    SpriteBatch sb;
    Texture texture;
    Sprite sprite;
    MapLayer objectLayer;

    TextureRegion textureRegion;

    @Override
    public void create () {
        float w = Gdx.graphics.getWidth();
        float h = Gdx.graphics.getHeight();

        camera = new OrthographicCamera();
        camera.setToOrtho(false,w,h);
        camera.update();
        tiledMap = new TmxMapLoader().load("MyCrappyMap.tmx");
        tiledMapRenderer = new OrthogonalTiledMapRendererWithSprites(tiledMap);
        Gdx.input.setInputProcessor(this);
        texture = new Texture(Gdx.files.internal("pik.png"));

        objectLayer = tiledMap.getLayers().get("objects");
        textureRegion = new TextureRegion(texture,64,64);

        TextureMapObject tmo = new TextureMapObject(textureRegion);
        tmo.setX(0);
        tmo.setY(0);
        objectLayer.getObjects().add(tmo);
    }

    @Override
    public void render () {
        Gdx.gl.glClearColor(1, 0, 0, 1);
        Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        camera.update();
        tiledMapRenderer.setView(camera);
        tiledMapRenderer.render();
    }

    @Override
    public boolean keyDown(int keycode) {
        return false;
    }

    @Override
    public boolean keyUp(int keycode) {
        if(keycode == Input.Keys.LEFT)
            camera.translate(-32,0);
        if(keycode == Input.Keys.RIGHT)
            camera.translate(32,0);
        if(keycode == Input.Keys.UP)
            camera.translate(0,-32);
        if(keycode == Input.Keys.DOWN)
            camera.translate(0,32);
        if(keycode == Input.Keys.NUM_1)
            tiledMap.getLayers().get(0).setVisible(!tiledMap.getLayers().get(0).isVisible());
        if(keycode == Input.Keys.NUM_2)
            tiledMap.getLayers().get(1).setVisible(!tiledMap.getLayers().get(1).isVisible());
        return false;
    }

    @Override
    public boolean keyTyped(char character) {

        return false;
    }

    @Override
    public boolean touchDown(int screenX, int screenY, int pointer, int button) {
        Vector3 clickCoordinates = new Vector3(screenX,screenY,0);
        Vector3 position = camera.unproject(clickCoordinates);
        TextureMapObject character = (TextureMapObject)tiledMap.getLayers().get("objects").getObjects().get(0);
        character.setX((float)position.x);
        character.setY((float)position.y);
        return true;
    }

    @Override
    public boolean touchUp(int screenX, int screenY, int pointer, int button) {
        return false;
    }

    @Override
    public boolean touchDragged(int screenX, int screenY, int pointer) {
        return false;
    }

    @Override
    public boolean mouseMoved(int screenX, int screenY) {
        return false;
    }

    @Override
    public boolean scrolled(int amount) {
        return false;
    }
}

 

It’s mostly the same, except of course the different renderer, the fact we no longer use a Sprite object and in touchDown() we locate the TextureMapObject in the “objects” layer and update its positions.

 

 

Which method you use is up to you, your personal preference and the requirements of your game.  Myself, I find the second to be probably the most clean.  Of course if your sprite doesn’t need to care about tile depth at all, you can ignore all of this and just use SpriteBatch over top and save yourself many headaches!

Programming


GFS On YouTube

See More Tutorials on DevGa.me!

Month List