Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon
16. December 2014

 

Sorry… absolutely can’t resist that pun, no matter how obvious it is.  Anyways… the Godot Game Engine has been on my radar since they announced they were going open source at the beginning of the year.  Today they finally announced their 1.0 release today.godot

 

 

Never heard of Godot?  It’s a Unity-esque game engine, except powered by C++ and scripted using a Python-like scripting language.  It includes a surprising number of tools and most importantly, a complete game editor (pictured right).

 

Godot works in both 2D and 3D, with 2D being a first class citizen, not just 3D minus a D.  Godot runs on Windows, OSX and Linux.  Godot is able to target iOS, Android, Desktops, Googles’ NaCL, PlayStation3 and Vita, as well as HTML5 and Windows Phone coming soon.

 

You can read the complete feature list here.

 

You can browse available documentation here.

 

Godot is an open source project hosted on GitHub.

 

What do you think… are you interested in Godot, would you be interested in seeing GameFromScratch do some more in-depth coverage now that it’s reached such a milestone release?

News


8. December 2014

 

 

In this tutorial we are going to look at how to use Cameras ( and in the next, Viewports ) in LibGDX.  I will admit, I am a bit late in covering this topic, as I should have covered it much earlier in the series.  In fact, in some prior tutorials I actually made use of Cameras with little prior discussion.  Better late than never, no?

 

The first immediate question that comes to mind are probably “What’s a camera, what’s a viewport and how are they different?”.

 

Well, basically a camera is responsible for being the players “eye” into the game world.  It’s an analogy to the way video camera’s work in the real world.  A viewport represents how what the camera sees is displayed to the viewer.  Any easy way to think about this is to think about your HD cable or satellite box and your HD TV.  The video signal comes in to your box ( this is the camera ), this is the picture that is going to be displayed.  Then your TV devices how to display the signal that comes in.  For example, the box may send you a 480i image, or a 1080p image, and it’s your TV’s responsibility to decide how it’s displayed.  This is what a viewport does… takes an incoming image and adapts it to run best on the device it’s sent to it.  Sometime this means stretching the image, or displaying black bars or possibly doing nothing at all.

 

So, simple summary description…

  • Camera – eye in the scene, determines what the player can see, used by LibGDX to render the scene.
  • Viewport – controls how the render results from the camera are displayed to the user, be it with black bars, stretched or doing nothing at all.

 

In LibGDX there are two kinds of cameras, the PerspectiveCamera and the OrthographicCamera.  Both are very big words and somewhat scary, but neither needs to be.  First and foremost, if you are working on a 2D game, there is a 99.9% change you want an Orthographic camera, while if you are working in 3D, you most likely ( but not always ) want to use a Perspective camera.

 

Now, the difference between them.  A perspective camera tries to mimic the way the human eye sees the world ( instead of how the world actually works ).  To the human eye, as something gets further away the smaller it appears.  One of the easiest ways to illustrate the effect is to fire up Blender and view a Cube in both perspectives:

 

Perspective Rendered:

image

 

Orthographic Rendered:

image

 

When dealing with 3D, a Perspective camera looks much more like we expect in the real world.  However, when you are working in 2D, you are actually still in 3D but for the most part you are ignoring depth ( except for sprite ordering ).  In a 2D game, you don’t want objects to change size the further “into the screen” they are.

 

So, TL;DR version, if you are making a 2D game, you probably want Orthographic.  If you aren’t, you probably don’t.

 

Ok, enough talk, CODE time.

 

We are going to implement a simple Orthographic camera that pans around a single image that represents our game world.  For this demo I am going to use this 2048x1024 image (click it for the full resolution, non-squished version, or make your own):

TheWorld

 

Paint skills at their finest!  Now lets look at rendering this using a camera:

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

public class CameraDemo extends ApplicationAdapter {
   SpriteBatch batch;
   Texture img;
   OrthographicCamera camera;
   
   @Override
   public void create () {
      batch = new SpriteBatch();
      img = new Texture("TheWorld.png");
      camera = new OrthographicCamera(Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
   }

   @Override
   public void render () {
      Gdx.gl.glClearColor(1, 0, 0, 1);
      Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

      camera.update();
      batch.setProjectionMatrix(camera.combined);
      batch.begin();
      batch.draw(img, 0, 0);
      batch.end();
   }
}

The process is quite simple.  We simply create a camera, then in our render loop we call it’s update() method then set it as the projectionMatrix of our SpriteBatch.  If you are new to 3D ( fake 2D ), the ProjectionMatrix along with the View Matrix are matrix based multiplications responsible for transforming 3D data to 2D screen space.  Camera.combined returns the camera’s view and perspective matrixes multiplied together.  Essentially this process is what positions everything from the scene to your screen.

 

Now if we go ahead and run this code we see:

image

 

Hmmmm… that may not be what you were expecting.  So what exactly happened here?

 

Well, the camera is located at the position 0,0.  However, the camera’s lens is actually at it’s center.   The red you see in the above image are the portions of the scene that have nothing in it.  So in the above example if you want to start at the bottom left of your world you actually need to take the camera’s dimensions into account.  Like so:

      camera = new OrthographicCamera(Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
      camera.translate(camera.viewportWidth/2,camera.viewportHeight/2);

Now when you run it, the results are probably more along the lines you expected:

image

 

In the previous examples I actually did something you really don’t want to do in real life:

camera = new OrthographicCamera(Gdx.graphics.getWidth(),Gdx.graphics.getHeight());

 

I am setting the camera viewport to use the devices resolution, then later I am translating using pixels.  If you are working across multiple devices, this almost certainly isn’t the approach you want to take as so many different devices have different resolutions.  Instead what you normally want to do is work in world units of some form.  This is especially true if you are working with a physics engine like Box2D.

 

So, what’s a world unit?  The short answer is, whatever you want it to be!  The nice part is, regardless to what units you choose, it will behave the same across all devices with the same aspect ratio!  Aspect ration is the more important factor here.  The aspect ratio is the ratio of horizontal to vertical pixels.

 

Let’s take the above code and modify it slightly to no longer use pixel coordinates.  Instead we will define our world as 50 units wide by 25 tall.  We then are going to set our camera to be the worlds height and centered.  Finally we will hook it up so you can control the camera using arrow keys.  Let’s see the 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;

public class CameraDemo2 extends ApplicationAdapter implements InputProcessor {
   SpriteBatch batch;
   Sprite theWorld;
   OrthographicCamera camera;

   final float WORLD_WIDTH = 50;
   final float WORLD_HEIGHT = 25;

   @Override
   public void create () {
      batch = new SpriteBatch();
      theWorld = new Sprite(new Texture(Gdx.files.internal("TheWorld.png")));
      theWorld.setPosition(0,0);
      theWorld.setSize(50,25);

      float aspectRatio = (float)Gdx.graphics.getHeight()/(float)Gdx.graphics.getWidth();

      camera = new OrthographicCamera(25 * aspectRatio ,25);
      camera.position.set(WORLD_WIDTH/2,WORLD_HEIGHT/2,0);

      Gdx.input.setInputProcessor(this);
   }

   @Override
   public void render () {
      Gdx.gl.glClearColor(1, 0, 0, 1);
      Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

      camera.update();
      batch.setProjectionMatrix(camera.combined);
      batch.begin();
      theWorld.draw(batch);
      batch.end();
   }

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

   @Override
   public boolean keyDown(int keycode) {
      if(keycode == Input.Keys.RIGHT)
         camera.translate(1f,0f);
      if(keycode == Input.Keys.LEFT)
         camera.translate(-1f,0f);
      if(keycode == Input.Keys.UP)
         camera.translate(0f,1f);
      if(keycode == Input.Keys.DOWN)
         camera.translate(0f,-1f);

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

Now when you run this code:

image

 

Now let’s look at what happens when we change our application’s resolution.  Since we aren’t using pixels anymore, the results should be fairly smooth.

 

In case you forgot, you set the resolution in the platform specific Application class.  For iOS and Android, you cannot set the resolution.  For HTML and Desktop you can.  Setting the resolution on Desktop is a matter of editing DesktopLauncher, like so:

public class DesktopLauncher {
   public static void main (String[] arg) {
      LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
      config.width = 920;
      config.height = 480;
      new LwjglApplication(new CameraDemo2(), config);
   }
}

Here is the code running at 920x480

image

 

And here is 1280x400:

image

 

As you can see, the code updates so the results render in a pixel independent way.

 

However, and as you can see above, if your aspect ratios don’t stay the same, the results look massively different.  In the previous example you can see in the 1280x400 render, the results look squished and the world contains a great deal less content.  Obviously, the values I used to illustrate this point are pretty extreme.  Let’s instead use the two most common different aspect ratios, 16:9 ( most Android devices, many consoles running at 1080p ) and 4:3 ( the iPad and SD NTSC signals ):

 

16:9 results:

image

 

4:3 results:

image

 

While much closure, the results are still going to look rather awful.  There are two ways to deal with this, both have their merits.

 

First, is create a version for each major aspect ratio.  This actually makes a great deal of sense although it can be a pain in the butt.  The final results are the best, but you double your burden for art assets.  We will talk about managing multiple resolution art assets in a different tutorial at a later date.

 

Second, you pick a native aspect ratio for your game to run at, then use a viewport to manage the different aspect ratios, just like you use the aspect button on your TV.  Since this tutorial is getting pretty long, we will cover Viewports in the next section, so stay tuned!

 

Programming


29. October 2014

 

As part of the ongoing Guide to Creating a Game Entirely on an iPad series, we recently looked at a number of 2D raster and vector graphics programs available onidraw_large_icon iOS.  One of the programs that really stood out was iDraw, a vector drawing program.  I’ve actually used iDraw for a while now, going back to the concept art I did for the Blender tutorial series and I am certainly a fan.  Although both Inkscape (Desktop) and Inkpad (iPad) are available for free, I find both to be pretty unwieldy at times.  iDraw just hits that sweet spot between usability and functionality, at least for me.

 

In this video I take a more in-depth look at iDraw. 

 

In this video I show the functionality available in iDraw.  THe first half of the video I complete this game art tutorial that was written for Inkscape, so if you are familiar with Inkscape, you can see how iDraw works at the same task.  If you’ve got no experience with vector graphics at all, this video should certainly give you the basics.   The last half of the video is a tour of iDraw’s additional functionality.

 

 

 

If you have a Mac or iPad and are looking for a vector graphics solution, iDraw is certainly worth checking out.  iDraw is available for $8.99 on the AppStore for iPad, while it is also available (with almost identical functionality) on the Mac for $24.99.   If you are looking for a tutorial on creating vector graphics, or are looking for more details on using vector graphics for game dev, 2dgameartforprogrammers is a great resource you should check out.

 

… I still hate the name iDraw though.  I hate an irrational hatred for all iNaming iProducts, I think many others do too!  Get over the name though and you will find an excellent product.

 

Next up, I will look at using Vector graphics from a Codea code perspective, to see how it’s done and what the performance is like.  Stay tuned!

Art


28. October 2014

 

A question came up in a comment in the Scene2D part of the LibGDX tutorial series about re-using actions.  You very much can re-use actions, so I decided to do it in post form here.  This entire post is mostly just one large code sample.  It’s just easier to do it here than in comments.  For a greater context of what this code is doing, see the earlier linked tutorial section.

 

Here is a sample showing action re-use.

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationListener;
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.Texture;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.actions.MoveToAction;


public class Scenetest implements ApplicationListener, InputProcessor {


   public class MyActor extends Actor {
      Texture texture = new Texture(Gdx.files.internal("badlogic.jpg"));

      public MyActor(){
         setBounds(getX(),getY(),texture.getWidth(),texture.getHeight());
      }

      @Override
      public void draw(Batch batch, float alpha){
         batch.draw(texture,this.getX(),getY());
      }
   }

   
   private Stage stage;
   private MyActor myActor;
   MoveToAction moveToOrigin,moveToCenter;

   @Override
   public void create() {
      stage = new Stage();
      myActor = new MyActor();

      myActor.setPosition(Gdx.graphics.getWidth()/2 - myActor.getWidth()/2,
            Gdx.graphics.getHeight()/2 - myActor.getHeight()/2);
      
      moveToOrigin = new MoveToAction();
      moveToOrigin.setPosition(0f, 0f);
      moveToOrigin.setDuration(2f);
      

      moveToCenter = new MoveToAction();
      moveToCenter.setPosition(Gdx.graphics.getWidth()/2 - myActor.getWidth()/2,
            Gdx.graphics.getHeight()/2 - myActor.getHeight()/2);
      moveToCenter.setDuration(2f);

      myActor.addAction(moveToOrigin);
      stage.addActor(myActor);
      Gdx.input.setInputProcessor(this);
   }

   @Override
   public void dispose() {
   }

   @Override
   public void render() {
      Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
      stage.act(Gdx.graphics.getDeltaTime());
      stage.draw();
   }

   @Override
   public void resize(int width, int height) {
   }

   @Override
   public void pause() {
   }

   @Override
   public void resume() {
   }


   @Override
   public boolean keyDown(int keycode) {
      if(keycode == Input.Keys.NUM_1)
         if(myActor.getActions().contains(moveToOrigin,true)) {
            // this is "A" currently running action, do nothing
            // If you wanted you could restart the action which 
            // would cause the duration to start over, like so:
            moveToOrigin.restart();
            // This action will now have a 2 second tween between 
            // its current location and target
         }
         else {
            moveToOrigin.reset();
            myActor.addAction(moveToOrigin);
         }
      if(keycode == Input.Keys.NUM_2)
         if(myActor.getActions().contains(moveToCenter,true)) {
            // this is "A" currently running action so do nothing
         }
         else {
            moveToCenter.reset();
            myActor.addAction(moveToCenter);
         }
      return false;
   }

   @Override
   public boolean keyUp(int keycode) {
      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;
   }  
}

When you run this code, the graphic will start at the center of the screen and move towards the origin, with a total duration of 2 seconds.  If you press the 2 key, it will start an action to move back to the center of the screen.  Pressing 1 will move back to the origin.  Pressing 1 while a moveToOrigin is active will cause that action to restart, basically resetting the total duration back to 2 seconds again, just from your current position.

 

The only things to really be aware of here are that an Actor getActions() will only return actions that are currently run.  When the action is finished, it is removed from the Actor.  The other thing of note is, although you can reuse an Action, you will have to reset() it before you can add it again, or it will already be over.  If it is currently run, calling restart() has the same effect as resetting.

Programming


22. October 2014

 

 

In this tutorial we are going to look at loading and using Tiled TMX maps.  Tiled is a free, open sourced map editor, and TMX is the file format it outputs.  You basically use it to “paint” levels using one or more spritesheets containing tiles, which you then load and use in your game.

 

Here is Tiled in action, creating the simple map I am going to use in this example:

image

 

By the way, I downloaded the tile graphics from here.  Additionally, you can download the generated TMX file we will be using here.

 

I am not going to go into detail on using the Tiled editor.  I actually covered this earlier here.  For Phaser however, just be certain you export as either JSON or CSV format and you should be good to go.

 

Now let’s look at some code to load the tile map.

 

 

/// <reference path="phaser.d.ts"/>
class SimpleGame {
    game: Phaser.Game;
    map: Phaser.Tilemap;
    
    constructor() {
        this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', {
            create: this.create, preload:
            this.preload, render: this.render
        });
    }
    preload() {
        this.game.load.tilemap("ItsTheMap", "map.json", null, Phaser.Tilemap.TILED_JSON);
        this.game.load.image("Tiles", "castle_0.png");
    }
    render() {

    }
    create() {
        this.map = this.game.add.tilemap("ItsTheMap", 32, 32, 50, 20);
        this.map.addTilesetImage("castle_0", "Tiles");

        this.map.createLayer("Background").resizeWorld();
        this.map.createLayer("Midground");
        this.map.createLayer("Foreground");
        
        
        this.game.camera.x = this.map.layers[0].widthInPixels / 2;
        this.game.camera.y = 0;

        this.game.add.tween(this.game.camera).to({ x: 0 }, 3000).
to({ x: this.map.layers[0].widthInPixels }, 3000).loop().start(); } } window.onload = () => { var game = new SimpleGame(); };

 

And when you run it… assuming like me you are using Visual Studio 2013 you will probably see:

image

 

Hmmmm, that’s not good.  Is there something wrong with our tilemap?  Did we make a mistake?

 

Nope… welcome to the wonderful world of XHR requests.  This is a common problem you are going to encounter over and over again when dealing with loading assets from a web server.  If we jump into the debugger, we quickly get the root of the problem:

 

image

 

Let’s look closely at the return value in xhr.responseText:

Ohhh. it’s an IIS error message and the key line is:

The appropriate MIME map is not enabled for the Web site or application.

Ah…

 

See, Visual Studio ships with an embedded version of IIS called IIS Express, and frankly, IIS Express doesn’t have a clue what a JSON file is.  Let’s solve that now.  If you created a new TypeScript project in Visual Studio, it should have created a web.config file for you.  If it didn’t create one and enter the following contents:

<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
  </system.web>
  <system.webServer>
    <staticContent>
      <mimeMap fileExtension=".json" mimeType="application/json" />
    </staticContent>
  </system.webServer>
</configuration>

 

Now the code should run without error

I should take a moment to point out that this is an entirely Visual Studio specific solution.  However, this particular problem is by no means limited to IIS Express.  I documented a very similar problem when dealing with WebStorm’s integrated Chrome plugin.  If your loadJson call fails, this is most likely the reason why!  Well that or you typo’ed it. :)

 

Ok, assuming everything is configured right,now we should see:

 

 

By the way, you may have to click on the to get it to start rendering.

 

Most of the loading code should look pretty familiar by now, Phaser is remarkably consistent in its approach.  There are a few things to be aware of though from that code.  First, the order you create layers in is important.  In Tiled I created 3 layers of tiles.  A solid background layer named “background”, a middle layer with most of the tiles in it called “midground” then a detail layer for the topmost tiles named “foreground”.  Think of rendering tiles like putting stickers on a flat surface… the front most stickers will obscure the ones that are behind them.  The same is true for tiles.  There are other options in tiled for creating foreground and background layers, but I stuck with normal tile layers for ease.  Just no that more efficient options exist.

 

The next thing to be aware of is when I called addTilesetImage, that is the same image filename that I provided to Tiled.  It is important that you use the same graphics files and names between Tiled and your code.  The next thing worth noticing is the call to resizeWorld() I made when loading the first tiled layer.  This simply set’s the world’s dimensions to be the same size as the tile layer you specified.  Since all the tile layers are the same size, you could have called it on any of them.  Finally, we made a simple tween that pans the camera from one end of the level to the other and back.

 

There is more to touch on with tiles, but I will have to cover that in later post(s).

 

Programming


AppGameKit Studio

See More Tutorials on DevGa.me!

Month List