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

 

In this part we are going to look at how to add animations to a TileMap in LibGDX.  Along the way we are going to look at using Properties a very important part of using Tile maps, as properties contain your games “data”.  First let’s take a look at setting properties in Tiled.  I am adding a second Tileset to my existing map called “Water” using the following graphic I download from Google Images.  Tiled doesn’t support animations, so we are going to hack support in using properties.

 

WaterTiles

 

You can get more details on working with Tiled here.

 

Properties are set in the Tile, not the Cell in TileEd.  Load the above image as a new Tileset in Tiled named Water.  We are working with just 3 of the water tiles:

Te1

 

Right click on a tile and select Tile Properties...

Te2

Now we want to set a property “Water Frame” and give it the value of 1.

Te3

 

Now repeat for the next two water tiles, with the values 2 and 3 respectively.

 

In addition to Tile properties, you can also set properties at the Layer and Map level.  Properties are just a name value pair of strings when imported into LibGDX.  Let’s take a look at that process now.

 

Save your tiled map and add it to the assets folder of your project.  Also add all the various texture maps used for tiles.  Let’s look at some ( heavily commented ) code that builds on our earlier tile map example:

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.*;

import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;

import java.util.*;

 

public class TiledTest extends ApplicationAdapter implements InputProcessor {

    Texture img;

    TiledMap tiledMap;

    OrthographicCamera camera;

    TiledMapRenderer tiledMapRenderer;

    SpriteBatch sb;

    Texture texture;

    Sprite sprite;

    ArrayList<TiledMapTileLayer.Cell> waterCellsInScene;

    Map<String,TiledMapTile> waterTiles;

    floatelapsedSinceAnimation = 0.0f;

 

    @Override public void create () {

        float w = Gdx.graphics.getWidth();

        float h = Gdx.graphics.getHeight();

 

        camera = new OrthographicCamera();

        camera.setToOrtho(false,w,h);

        // Position the camera over 100pixels and up 400 to capture more interesting part of map

        camera.translate(100,400);

        camera.update();

        //Load our tile map

        tiledMap = new TmxMapLoader().load("MyCrappyMap.tmx");

 

        tiledMapRenderer = new OrthogonalTiledMapRenderer(tiledMap);

        Gdx.input.setInputProcessor(this);

 

        // We created a second set of tiles for Water animations

        // For the record, this is bad for performance, use a single tileset if you can help it

        // Get a reference to the tileset named "Water"

        TiledMapTileSet tileset =  tiledMap.getTileSets().getTileSet("Water");

 

 

        // Now we are going to loop through all of the tiles in the Water tileset

        // and get any TiledMapTile with the property "WaterFrame" set

        // We then store it in a map with the frame as the key and the Tile as the value

        waterTiles = new HashMap<String,TiledMapTile>();

        for(TiledMapTile tile:tileset){

             Object property = tile.getProperties().get("WaterFrame");

            if(property != null)

                waterTiles.put((String)property,tile);

        }

 

        // Now we want to get a reference to every single cell ( Tile instance ) in the map

        // that refers to a water cell.  Loop through the entire world, checking if a cell's tile

        // contains the WaterFrame property.  If it does, add to the waterCellsInScene array

        // Note, this only pays attention to the very first layer of tiles.

        // If you want to support animation across multiple layers you will have to loop through each

        waterCellsInScene = new ArrayList<TiledMapTileLayer.Cell>();

        TiledMapTileLayer layer = (TiledMapTileLayer) tiledMap.getLayers().get(0);

        for(int x = 0; x < layer.getWidth();x++){

            for(int y = 0; y < layer.getHeight();y++){

                TiledMapTileLayer.Cell cell = layer.getCell(x,y);

                Object property = cell.getTile().getProperties().get("WaterFrame");

                if(property != null){

                    waterCellsInScene.add(cell);

                }

            }

        }

    }

 

    @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();

 

        // Wait for half a second to elapse then call updateWaterAnimations

        // This could certainly be handled using an Action if you are using Scene2D

        elapsedSinceAnimation += Gdx.graphics.getDeltaTime();

        if(elapsedSinceAnimation > 0.5f){

            updateWaterAnimations();

            elapsedSinceAnimation = 0.0f;

        }

    }

 

    // This is the function called every half a second to update the animated water tiles

    // Loop through all of the cells containing water.  Find the current frame and increment it

    // then update the cell's tile accordingly

    // NOTE!  This code depends on WaterFrame values being sequential in Tiled

    private void updateWaterAnimations(){

        for(TiledMapTileLayer.Cell cell : waterCellsInScene){

            String property = (String) cell.getTile().getProperties().get("WaterFrame");

            Integer currentAnimationFrame = Integer.parseInt(property);

 

            currentAnimationFrame++;

            if(currentAnimationFrame > waterTiles.size())

                currentAnimationFrame = 1;

 

            TiledMapTile newTile = waterTiles.get(currentAnimationFrame.toString());

            cell.setTile(newTile);

        }

    }

 

    @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;

    }

}

Basically what we do is load our map, then we loop through the “Water” tile set and grab a reference to any tile marked as a Waterframe.  We then perform the same action, looping through all of the cells in our map ( on the first layer! ) and if the cells tile has a reference to a tile that has the Waterframe property defined.  Then every half a second we update each cell to the next available frame of animation, or loop back to the first frame if none are available.

 

Now if you run this code, voila!  Animated water:

Te4

 

In this case we manually updated tiles in the map.  LibGDX does however present another option.  They have recently added an Animated tile class.  That said, this functionality is very new so warning ,there be dragons.  In fact, I didn’t find a single implementation online, so this may in fact be the first!

 

Here is a code example using AnimatedTiledMapTile to achieve the same effect:

 

package com.gamefromscratch;

 

import com.badlogic.gdx.ApplicationAdapter;

import com.badlogic.gdx.Gdx;

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.*;

import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;

import com.badlogic.gdx.maps.tiled.tiles.AnimatedTiledMapTile;

import com.badlogic.gdx.maps.tiled.tiles.StaticTiledMapTile;

import com.badlogic.gdx.utils.Array;

 

public class TiledTest extends ApplicationAdapter{

    Texture img;

    TiledMap tiledMap;

    OrthographicCamera camera;

    TiledMapRenderer tiledMapRenderer;

    SpriteBatch sb;

    Texture texture;

    Sprite sprite;

    Array<AnimatedTiledMapTile> waterTilesInScene;

    Array<StaticTiledMapTile> waterTiles;

    floatelapsedSinceAnimation = 0.0f;

 

    @Override public void create () {

        float w = Gdx.graphics.getWidth();

        float h = Gdx.graphics.getHeight();

 

        camera = new OrthographicCamera();

        camera.setToOrtho(false,w,h);

        camera.translate(100,400);

        camera.update();

        tiledMap = new TmxMapLoader().load("MyCrappyMap.tmx");

 

        tiledMapRenderer = new OrthogonalTiledMapRenderer(tiledMap);

 

        TiledMapTileSet tileset =  tiledMap.getTileSets().getTileSet("Water");

 

        waterTiles = new Array<StaticTiledMapTile>();

        for(TiledMapTile tile:tileset){

            Object property = tile.getProperties().get("WaterFrame");

            if(property != null) {

                waterTiles.add(new StaticTiledMapTile(tile.getTextureRegion()));

            }

        }

 

        waterTilesInScene = new Array<AnimatedTiledMapTile>();

 

        TiledMapTileLayer layer = (TiledMapTileLayer) tiledMap.getLayers().get(0);

        for(int x = 0; x < layer.getWidth();x++){

            for(int y = 0; y < layer.getHeight();y++){

                TiledMapTileLayer.Cell cell = layer.getCell(x,y);

                Object property = cell.getTile().getProperties().get("WaterFrame");

                if(property != null){

                    cell.setTile(new AnimatedTiledMapTile(0.5f,waterTiles));

                }

            }

        }

 

    }

 

    @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();

    }

}

 

Ultimately the logic is very similar.  Here however we actually replace the tile type of for each water instance we find in our map with a AnimatedTiledMapTile.  It is passed an interval to update ( 0.5 second again ) as well as an array of tiles to use as part of the animation.  The logic is basically identical, you just have slightly less control and no longer have to handle the updating on your own!


16. June 2014

 

In the previous tutorial part we looked at working with a single Sprite.  Reality is, very few games are composed of singular sprites.  UI’s are made up of a number of different sprites, animations are composed of several different frames, each composed of a single image.  Loading hundreds of individual textures is not good for performance.  A very common solution is to use a texture atlas ( or sprite sheet ).  Fortunately Xcode make it extremely easy.

 

We are going to use the same sprite we did in the previous tutorial, you can download it here.  As you can see, it’s actually a zip file containing a number of png images:

Ta1

 

Extract the zip file and rename it jet.atlas.

Now in Xcode, in Project Navigator, right click your project and select Add to Project

Ta2

 

Select the directory ( not the files ) and click add.  Defaults should be ok, but make sure it’s set to copy.

Ta3

 

And you are done.  The following code:

import SpriteKit

 

class GameScene: SKScene {

    

    override func didMoveToView(view: SKView) {

        var sprite = SKSpriteNode(imageNamed:"sprite4")

        sprite.xScale = 4

        sprite.yScale = 4

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

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

        self.addChild(sprite)

    }

    

}

 

Will load the sprite from the Atlas that had the file name “sprite4.png”.  NOTE HOWEVER, if you add an image named sprite4 to your project, it will be loaded instead of the Texture Atlas.

 

So, what exactly did Xcode do?  We behind the scenes, it went ahead an combined all of your images together into a single OpenGL optimized image, and created a reference file telling SpriteKit how to access it.  Let’s take a look at the results.

 

First, build your game.  Select Product->Build For->Running

Ta4

 

You should now have a Products folder in your project.  Right click the .app file for your project and select Show in Finder:

Ta5

 

Now right click the .app file and select Show Package Contents:

Ta6

 

Navigate into contents->resources->jet.atlasc and you will see two files, one is an image, the other a plist.  Let’s look at the image first:

 

Jet 1

 

That’s out images smash together in a power of 2 friendly texture that your GPU can handle efficiently.  The plist file:

Ta7

 

This plist shows SpriteKit how to access each individual image within the larger image.  To you however the process is pretty transparent.  Basically group all common images together in a directory.  Give the directory a .atlas name and add it to your project, then access each image as you would normally.

 

Sometimes however you may want to access the TextureAtlas itself.  You can do that as well, let’s take a look at how:

import SpriteKit

 

class GameScene: SKScene {

    let textureAtlas = SKTextureAtlas(named:"jet.atlas")

    var currentTexture:Int = 1;

    

    override func didMoveToView(view: SKView) {

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

        

        let sprite=SKSpriteNode(texture:textureAtlas.textureNamed("sprite1"))

        sprite.xScale = 8

        sprite.yScale = 8

        self.addChild(sprite)

    }

    

    override func keyDown(theEvent: NSEvent!) {

        // On any key press move to the next texture

        var sprite = self.scene.children[0] asSKSpriteNode

 

        switch currentTexture {

            case 1:

                sprite.texture = textureAtlas.textureNamed("sprite2")

            case 2:

                sprite.texture = textureAtlas.textureNamed("sprite3")

            case 3:

                sprite.texture = textureAtlas.textureNamed("sprite1")

            default:

                break

        }

        ++currentTexture

        if(currentTexture > 3) {

            currentTexture = 1

        }

    }

}

 

Now run it, and each time you press a key it will advance to the next frame.

 

 

 

First let me stop right here and make one thing perfectly clear… THIS IS NOT HOW YOU DO ANIMATION! :)  We will cover animation later. This was just a small code segment to illustrate how to access a TextureAtlas directly.  

 

As you can see, it’s as simple a matter as creating an SKTextureAtlas and passing in the file name.  You can then access each individual SKTexture within the atlas using textureNamed passing in the file name you used for the original image in the atlas directory.  As you can see, you do not need to pass in the file extension.  Here we see a Swift switch statement in action.  Switch statements are important to Swift and quite capable.  You can switch on Ints like we have done here, but you can also use strings. It is tempting to use the sprite name here, but an important thing to realize is the SKSprite name is NOT the same as the SKTexture name.  Unless you manually name the sprite, it will be nil.  Unlike C++, case statements in Swift do not fall through, so you do not need to provide a break statement for each case.  However, there are two caveats to be aware of.  First, every single possible value must be handled.  If you don’t want to handle every possible value you can provide a default handler which will catch everything else.  However each case ( even default ) must contain at least one executable statement, thus the break in default.  This only scratches the surface of switch… you can also specify multiple values in a case using commas, provide ranges using the .. and … operators, etc.

 

That’s it for TextureAtlases, on to the next part!


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


13. June 2014

CocoonJS just released a new version.  CocoonJS is a technology that allows you to bundle HTML applications into a native application for mobile app store deployment.  This release contains:

 

Canvas+

  • Canvas+ now handles correctly HTML5/Web exportations from Construct 2. Get more infohere.
  • Added implementation for the FontFace CSS style. Fonts can now be downloaded from a remote URL defined in the FontFace attribute. Get more info here.
  • Improvements in DOM support
    • Make the window.document readOnly.
    • Added window.pageXOffset and window.pageYOffset properties.
    • Added document.defaultView property.
  • XHR Improvements
    • Make "text/plain;charset=UTF-8" the default content-type.
    • Allow XHR responses to be saved to disk a new cocoonSetOutputFile extension. Get more info here.
  • Page pageLoaded / pageFailed are correctly called now.
  • Fixed Audio System deadlock when alcOpenDevice fails.
  • New (and correct) device orientation handling. Get more info here.

Rendering

  • Ensure the renderer is always resumed after loading a url.
  • Improved WebGL compatibility with renderTargets.
  • Fixed some problems in renderbufferStorage arguments, which caused an incomplete framebuffer status.

Extensions

App

  • Added new methods:
    • onSuspended: notification when the application is suspended.
    • existsPath: checking if a file exists in the filesystem.
    • setTextCacheSize: text rendering is cached inside CocoonJS. This method controls the size of that cache.

Ads

  • Fixed problem with iOS 6 and iAd interstitials

WebView+

  • Added check to avoid null pointer exceptions when destroying the webview
  • Implemented webView setNetworkAvailable method, which triggers 'online' event on the JavaScript side
  • Fixed viewport size problem which happened in some devices
  • Fixed some missing resources bugs

Cloud Compiler

  • Added support for Cordova 3.3 and 3.4.

Launcher

  • Some small improvements to the Android launcher UI.
  • Improve download/unzip times in Android launcher.
  • Make orientation handling behave the same as in the browser. Some existing games that depend on gyroscope/orientation might need some tweaking because of this.
  • Fixed crash if device orientation changes during game launch.

Known Bugs

  • Ads with admob and mopub aren't handled correctly, and may not appear. If more ad networks are configured in mopub, those will be served.  
  • Samsung Galaxy Tab devices crash due to SIGILL. We have already found the issue related to V8 and this specific hardware/processor and are working hard to try to solve it asap.

We are working on all of these bugs in order to solve them and create a new release asap. Sorry for the inconveniences.

Important: You can always revert the version of the launcher and the compiler you're using. Get more info here.

 

If you think Cocoon sounds a heck of a lot like PhoneGAP/Cordova, well, it is.  Cocoon have put together this comparison guide.  Basically the biggest difference is speed.


10. June 2014

 

When I did the first ( and currently only ) part of my Swift with SpriteKit tutorial series I ran into a crash problem with the default game template.  In Googling around and looking at the Mac password protected developer forums, I notice I am by no means alone.  Even a comment in the earlier tutorial part mentioned encountering the problem and said it was tied to Mac OS 10.9.  I can confirm on my 10.10 install, everything works correctly, for me it is only in 10.9 that I have a problem.

 

It’s pretty simple to re-create, when running Mac OS 10.9, create a simple project using the game template and Swift as your language ( the Objective-C template works just fine ), like this one:

 

BOOM

 

Then when you run it:

BOOM2

 

EXC_BAD_ACCESS Code=1 address=0x10.  

 

Fortunately I have discovered a very simple fix.  In Xcode, open up the generated sks file, GameScene.sks:

Boom3

 

Make sure the Utilities panel is visible:

BOOM4

 

Now from the Object library, drag an Empty Node to the editor window, like so:

BOOM5

 

And now, if you run the game it should work:

BOOM6

 

My guess is the scene file included in the Swift Spritekit template is corrupted.  By adding a node and saving it, it seems to fix the corruption.  By the way, you can delete the empty node now and it will continue to work.

 

Hope that helps some of you, at least till Apple fix the bug.

 

 

 

 

 


GFS On YouTube

See More Tutorials on DevGa.me!

Month List