Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon

26. January 2012

 

 

You may remember me discussing PlayN in previous posts, it’s a Google run cross-platform, open source game library.  Previously the most missing feature was iOS support and frankly that feature is still missing, but there is light at the end of the tunnel!

 

 

PlayN developer Michael Bayne recently made a post announcing he had successfully got the Peas demo running on an iPhone4. IMG_0935Without a doubt this progress is a “very good thing”, as iOS support is easily the most important missing feature of PlayN today.

 

 

Michael’s progress:

 

I got side tracked by trying to get things working on a real device.
The simulator runs the Mono VM in JIT mode (though with various
restrictions in place to ensure that you don't do anything that's
incompatible with AOT compilation), but actually doing AOT compilation
enforces substantially more restrictions. I had to "refactor" IKVM to
contain no references whatsoever to System.Reflection.Emit even if
they were never called. I also bumped into a Mono compiler bug and
spent some time digging into the internals of IKVM and mcs (the Mono
compiler) so that I could come up with a work-around and file a
sensible bug report. It turned out to have already been fixed in trunk
(which made my investigations that much more perplexing), but since
MonoTouch is commercial software, I was necessarily working with the
latest beta release, not trunk; annoying!

Performance of the Pea Physics demo is not stellar on an iPhone 4
(it's quite reasonable, it's just not silky smooth 60 FPS with twenty
or thirty peas moving on screen). It's pretty comparable to what I've
see on actual Android devices. Depending on what Box2D's interfaces
are like, there's a possibility that it could be improved by writing
an interface-compatible implementation of Box2D directly in C#. C#
supports value types, and in a physics simulator, being able to store
your Vec2s and Matrix3s directly inline, rather than separately on the
heap, can substantially improve cache performance. That said, the
Box2D implementation, as is, is not very data-oriented. Rewriting it
to store all of the entity geometry in big flat arrays and to perform
its calculations by iterating over those arrays, rather than following
a zillion pointers through the heap, would probably help a lot on
every platform.

Now that I've got things running on an actual device, I'll go back to
finishing up the platform implementation. IOSCanvas is substantially
done. IOSStorage is done (built on Sqlite). IOSTouch (and IOSPointer)
are done. I need to implement the font and text rendering bits of
IOSGraphics/IOSCanvas. Then IOSSound, IOSNet and other niggling bits.
I'm not sure what I'll do about IOSKeyboard. I'd kind of like an
additional interface for requesting a string of text from the user,
which would allow the keyboard to pop up in "edit a line of text" mode
so that the user can use the standard copy/paste and click with
magnification text navigation. Having the app respond to key events
and attempt to implement text entry directly is a massively bad idea
(on any mobile device), IMO.

 

 

Excellent news and great work Michael! 

 

So for the people looking to see if PlayN works with iOS, the answer is no, but it’s damned close!

Programming

26. January 2012

 

 

OK, so I may just be late to the party on this CtotW, as this product is currently one of the darlings of the programming world but I ignored it completely until now.  What is it I ignored so completely and now am rather enamored of?

 

Node.js

 

 

What exactly is Node?  Well basically they ripped the V8 Engine ( yeah, it’s actually called that ) that powers the Javascript engine in Google Chrome and instead used it for creatinglogo-light server side applications, like you would traditionally make using ASP.Net or JSP.  In addition to providing a server side Javascript implementation, they have implemented a number of modules ( in C++ ) to handle many common tasks, from creating an HTTP server to cryptography.  You can of course create your own add-ons in C++.  Of course, as with all things Google, the build process is a bit convoluted and poorly documented, especially for Windows based developers.

 

 

So, why exactly have I ignored Node until now?  Frankly, I hate developing in Javascript, or at least I thought I did.  Reality is, I hate developing in Javascript for browsers!  Once you move yourself out of the browser, it becomes a much more pleasant experience!

 

 

 

What exactly makes Node so enticing?  You can make light weight, simple, scalable and asynchronous servers with absolutely no other software required.  Simple run node.exe “yourAppName” and you have a running server, no need to install Java or configure a web server.  Consider the following Hello World, a completely functional web server in just a few lines of code:

 

var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }).listen(8124, "127.0.0.1");

 

Pretty simple eh?

 

 

There are a few downsides though.  Since Node.exe runs as a single process, so will your application.  This means you are tied to a single core per instance of node.  Also, tooling support is a bit lacking.  I tried out the excellent (in concept ) Cloud9 online IDE but simply put, it didn’t work.  I really hope this changes soon, as a web based IDE sounds about perfect.  I am currently trying out WebStorm, but have formed no opinion yet.  Book support is quite limited as well, with Node Web Development and Node: Up and Running: Scalable Server-Side Code with JavaScript  being the only two published books at the moment, although a number of others are in the works.  No doubt as Node matures, so will the educational and tooling support available for it.

 

 

I am going to do a quick test of using Node.js as a simple game server for an SFML based game.  I will update here accordingly when (if) that is complete.

Cool Thing of the Week

19. January 2012

 

 

 

Our first Blender reference guide chapter has just been put online.  Think of it as a mash-up between a quick reference guide and a video tutorial.  We will cover off the most commonly performed actions in Blender with instructions on how to perform them via hotkey or using the mouse, as well as an accompanying image showing the action in, um… action.  We hope to bridge the gap between regular written instruction and video tutorial.  So for those that like moving at their own pace like you would with a written page, but with the clarity of video, hopefully this guide is perfect for you.

 

 

Obviously if you’ve been using Blender for a while, this will be of very little use to you, but if BorderSelectyou are just starting out, I hope you find it helpful.  If you have not, hopefully these will be of use.  Here for example, is the graphic illustrating the (B)order selection tool.  Click any image for a more detailed version.

 

 

We have many other pages in the work, the next one specifically covers the editing process, but any and all suggestions are welcome!  Feel we’ve glossed over an important command in a certain category, let us know!

 

 

Here is the first release though, covering selection and camera manipulation.  Consider everything to be a WIP, subject to multiple changes.  There are also some tutorials in the works, and they are going to assume you are familiar with everything in the guide.  As we bring more sections online, we will be improving the navigation of course.

Art

15. January 2012

 

 

I work on a multi-monitor setup and until now I have been using Blender on a single monitor.  Sometimes though, like when texturing, having a multi monitor setup is ideal.  I had always thought Blender offered no support for working across multiple monitors, how wrong I was!  Not only can any window be duplicated to its own window, it is laughably simple to do!

 

 

image

You know that little tab thing in the top corner of each Blender window you use for splitting and merging Blender windows?  ( If not, check this out. )

 

Well, all you need to do is left click and drag while holding down shift and it will duplicate that viewport into it’s own window.

 

 

 

 

As you can see ( after doing this a couple times ), each looks like a completely different instance of Blender to Windows:

image

 

 

However, if you look in Task Manager, Blender is still a single process:

 

image

 

 

You can now split blender into as many windows, across as many monitors as you want.  Here is Blender split out into 4 separate windows, while the original window is on the bottom left.

 

image

 

 

Changes in one window will immediately be reflected in all the other Windows.  NOTE! Closing the original window will close all the torn off windows, but closing a torn off window will do nothing ( more than close that window ).

 

It’s simple, as simple as a Left Click-Shift-Drag, but it will completely change the way I work!

 

Know doubt the majority of you knew this little feature, I certainly didn’t.  Hopefully it proves as enlightening to some of you as it was for me!

Art

14. January 2012

 

Alright, first off this post has almost nothing to do with, well anything.  I just discovered this and frankly I think it’s damned cool! Although it has absolutely nothing to do with making games, who doesn’t like Lego?

 

I have a Safari online book subscription ( which I love by the way ) so when they add new books I am often exposed to things I was previously unaware of, but this one added today was unexpected!  They just added Badass Lego Guns!

 

Ok, a book about making guns in Lego, that is kinda cool.  A book about making 6 guns in Lego, that’s a bit cooler.  A book about making 6 guns that work in Lego, that’s just epic.  ( Actually, it’s 4 guns, a crossbow and a switchblade )

 

 

The book goes step by step in creating each of the guns, starts with an over view of each gun, then a bill of materials of LEGO pieces you will need.  The rest of each chapter is then basically IKEA style directions on how to assemble the gun ( or crossbow).

 

Obviously, the don’t fire bullets, but instead use ( what else! ) LEGO as projectiles and elastics as the propellant.  Some of these guns though have the look like even being LEGO and elastic powered, they could cause some serious carnage…  Consider the gun featured on the cover, the Warbeast… Yeah, it’s fully automatic and capable of a theoretical 1200 rounds per second!

 

imageimage

 

 

Amazon’s got it for 20$ or of course, as I mentioned earlier, it was recently released on Safari Books Online if you happen to be a subscriber.  Now I just need to come up with a few hundred dollars worth of LEGO!  Wonder how to buy technic LEGO is these days?

Totally Off Topic

Month List

Popular Comments

LibGDX Tutorial 11: Tiled Maps Part 2: Adding a sprite and dealing with layers
Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon


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

blog comments powered by Disqus

Month List

Popular Comments