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

 

 

 

In the previous tutorial we looked at how to use a camera with LibGDX to abstract away resolution differences so you are no longer using pixel coordinates.  This however doesn’t really help you all that much if your aspect ratios are massively different.  Fortunately LibGDX implements the concepts of Viewports, which can be considered the coding equivalent of the Aspect button on your HDTV, controlling how non-native content is scaled to be displayed on your TV.

 

There are a number of different Viewports available:

 

Or of course you can inherit Viewport and create your own.

 

We are going to use the following image ( click for full resolution, no squished version ) taken from here.

Aspect

 

 

Here is the code for creating a FillViewport, which results in behavior very similar to using no Viewport at all:

 

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.utils.viewport.FillViewport;
import com.badlogic.gdx.utils.viewport.Viewport;

public class ViewportDemo extends ApplicationAdapter {
   SpriteBatch batch;
   Sprite aspectRatios;
   OrthographicCamera camera;
   Viewport viewport;

   @Override
   public void create () {
      batch = new SpriteBatch();
      aspectRatios = new Sprite(new Texture(Gdx.files.internal("Aspect.jpg")));
      aspectRatios.setPosition(0,0);
      aspectRatios.setSize(100,100);

      camera = new OrthographicCamera();
      viewport = new FillViewport(100,100,camera);
      viewport.apply();

      camera.position.set(camera.viewportWidth/2,camera.viewportHeight/2,0);
   }

   @Override
   public void render () {

      camera.update();
      Gdx.gl.glClearColor(1, 0, 0, 1);
      Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

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

   @Override
   public void dispose(){
      aspectRatios.getTexture().dispose();
   }

   @Override
   public void resize(int width, int height){
      viewport.update(width,height);
      camera.position.set(camera.viewportWidth/2,camera.viewportHeight/2,0);
   }
}

 

The code is pretty straight forward but has a few caveats to be aware of.  Most importantly, you absolutely need to update the viewport in the ApplicationAdapter’s resize method or the viewport will not work.  Standard process is to create your camera, then create the viewport passing in the viewport resolution as well as the camera to apply to.  For the technically minded, the Viewport ultimately manipulates the GL viewport behind the scenes.  Now let’s look at the various viewport options, we simply replace one line of code in each example:

 

Here are the results of various options running at 900x600 resolution:

 

No Camera or Viewport

image

 

 

viewport = new ExtendViewport(100,100,camera);

image

 

 

viewport = new FillViewport(100,100,camera);

image

 

viewport = new FitViewport(100,100,camera);

image

 

viewport = new StretchViewport(100,100,camera);

image

 

viewport = new ScreenViewport(camera);

image

 

 

The behavior of most of those viewports should be evident from the results but a few certainly require a bit of explanation.  The oddest result is most likely ScreenViewport.  When you use ScreenViewport you are simply saying “create a viewport the same size as the screen resolution”.  However, we also told our game that the sprite is 100x100 in size, but instead of being treated in our arbitrary camera units, this value is now relative to actual pixels, so as a result, our background is drawn as a 100 x 100 pixel rectangle.

 

You may also be wondering what the difference between Fill and Stretch is.  It isn’t obvious because our source image and our resolution are both widescreen.  When you run in a 4:3 aspect ratio, such as 1024x768 of the iPad, the results become more obvious:

 

FillViewport @ 1024x768

image

 

StetchViewport @ 1024x768

image

 

Fill will always fill the viewport, even if it means have to crop part of the image.  As you can see in the first image, the blue border at the top and bottom is not visible.  IMHO, this is the worst looking of all scaling options.

 

Stretch on the other hand, stretches the result to fit the screen width and height, however it also results in some distortion.  This mode is probably the easiest to implement, but the results depend heavily on your games art style.

 

 

Mapping To And From Camera to Screen Coordinates

 

One last quick thing to touch upon with Cameras and Viewports… how do you handle converting coordinates, such as touch or click events from our arbitrary camera coordinates to screen coordinates?  Fortunately it’s quite easy.

 

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
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.math.Vector3;
import com.badlogic.gdx.utils.viewport.StretchViewport;
import com.badlogic.gdx.utils.viewport.Viewport;

public class mouseproject extends ApplicationAdapter implements InputProcessor {
   SpriteBatch batch;
   Sprite aspectRatios;
   OrthographicCamera camera;
   Viewport viewport;

   @Override
   public void create () {
      batch = new SpriteBatch();
      aspectRatios = new Sprite(new Texture(Gdx.files.internal("Aspect.jpg")));
      aspectRatios.setPosition(0,0);
      aspectRatios.setSize(100,100);

      camera = new OrthographicCamera();
      viewport = new StretchViewport(100,100,camera);
      viewport.apply();

      camera.position.set(camera.viewportWidth/2,camera.viewportHeight/2,0);
      Gdx.input.setInputProcessor(this);
   }

   @Override
   public void render () {

      camera.update();
      Gdx.gl.glClearColor(1, 0, 0, 1);
      Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

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

   @Override
   public void dispose(){
      aspectRatios.getTexture().dispose();
   }

   @Override
   public void resize(int width, int height){
      viewport.update(width, height);
      camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
   }

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

      Gdx.app.log("Mouse Event","Click at " + screenX + "," + screenY);
      Vector3 worldCoordinates = camera.unproject(new Vector3(screenX,screenY,0));
      Gdx.app.log("Mouse Event","Projected at " + worldCoordinates.x + "," + worldCoordinates.y);
      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;
   }
}

 

It's simply a matter of using Camera.unproject to convert a screen coordinate to your world coordinate, and Camera.project to do the reverse.  There is more to Viewports and Cameras, but that covers enough to get you going in most cases.  Keep in mind, you don’t need to use either, but they both certainly make making a game that runs across devices a lot easier.

 

Programming


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


4. December 2014

 

The Phaser HTML5 game development library has been churning out the releases as of late, and just yesterday 2.2.1 was released.  Perhaps most impressively, much of the releases content came from members within the community, always a good sign with open source projects. 

 

The biggest new feature is the Scale Manager, which you can read about here or from this newly released e-book.  The ScaleManager is to help enable your code to run on devices with multiple different resolutions.  The biggest change is Phaser moved to a proper fixed-step game loop, fully decoupling logic and rendering.

 

The full (and massive) change log below:

 

New Features
  • Updated to Pixi v2.2.0 - see separate change log entry below.
  • Cache.getRenderTexture will retrieve a RenderTexture that is stored in the Phaser Cache. This method replaces Cache.getTexture which is now deprecated.
  • Cache.autoResolveURL is a new boolean (default false) that automatically builds a cached map of all loaded assets vs. their absolute URLs, for use with Cache.getURL and Cache.checkURL. Note that in 2.1.3 and earlier this was enabled by default, but has since been moved behind this property which needs to be set to true before you load any assets to enable.
  • You can now call Tween.to again on a Tween that has already completed. This will re-use the same tween, on the original object, without having to recreate the Tween again. This allows a single tween instance to be re-used multiple times, providing they are linked to the same object (thanks InsaneHero)
  • Phaser.Color.valueToColor converts a value: a "hex" string, a "CSS 'web' string", or a number - into red, green, blue, and alpha components (thanks @pnstickne #1264)
  • Stage.backgroundColor now supports CSS 'rgba' values, as well as hex strings and hex numbers (thanks @pnstickne #1234)
  • Pointer.addClickTrampoline now adds in support for click trampolines. These raise pointer events into click events, which are required internally for a few edge cases like IE11 full screen mode support, but are also useful if you know you specifically need a DOM click event from a pointer (thanks @pnstickne #1282)
  • Point.floor will Math.floor both the x and y values of the Point.
  • Point.ceil will Math.ceil both the x and y values of the Point.
  • ScaleManager.scaleSprite takes a Sprite or Image object and scales it to fit the given dimensions. Scaling happens proportionally without distortion to the sprites texture. The letterBox parameter controls if scaling will produce a letter-box effect or zoom the sprite until it fills the given values.
  • Phaser.DOM.getBounds is a cross-browser element.getBoundingClientRect method with optional cushion.
  • Phaser.DOM.calibrate is a private method that calibrates element coordinates for viewport checks.
  • Phaser.DOM.aspect gets the viewport aspect ratio (or the aspect ratio of an object or element)
  • Phaser.DOM.inViewport tests if the given DOM element is within the viewport, with an optional cushion parameter that allows you to specify a distance.
  • Phaser.DOM.viewportWidth returns the viewport width in pixels.
  • Phaser.DOM.viewportHeight returns the viewport height in pixels.
  • Phaser.DOM.documentWidth returns the document width in pixels.
  • Phaser.DOM.documentHeight returns the document height in pixels.
  • TilemapLayers have been given a decent performance boost on canvas with map shifting edge-redraw (thanks @pnstickne #1250)
  • A large refactor to how the internal game timers and physics calculations has been made. We've now swapped to using a fixed time step internally across Phaser, instead of the variable one we had before that caused glitchse on low-fps systems. Thanks to pjbaron for his help with all of these related changes.
  • We have separated the logic and render updates to permit slow motion and time slicing effects. We've fixed time calling to fix physics problems caused by variable time updates (i.e. collisions sometimes missing, objects tunneling, etc)
  • Once per frame calling for rendering and tweening to keep things as smooth as possible
  • Calculates a suggestedFps value (in multiples of 5 fps) based on a 2 second average of actual elapsed time values in the Time.update method. This is recalculated every 2 seconds so it could be used on a level-by-level basis if a game varies dramatically. I.e. if the fps rate consistently drops, you can adjust your game effects accordingly.
  • Game loop now tries to "catch up" frames if it is falling behind by iterating the logic update. This will help if the logic is occasionally causing things to run too slow, or if the renderer occasionally pushes the combined frame time over the FPS time. It's not a band-aid for a game that floods a low powered device however, so you still need to code accordingly. But it should help capture issues such as gc spikes or temporarily overloaded CPUs.
  • It now detects 'spiraling' which happens if a lot of frames are pushed out in succession meaning the CPU can never "catch up". It skips frames instead of trying to catch them up in this case. Note: the time value passed to the logic update functions is always constant regardless of these shenanigans.
  • Signals to the game program if there is a problem which might be fixed by lowering the desiredFps
  • Time.desiredFps is the new desired frame rate for your game.
  • Time.suggestedFps is the suggested frame rate for the game based on system load.
  • Time.slowMotion allows you to push the game into a slow motion mode. The default value is 1.0. 2.0 would be half speed, and so on.
  • Time.timeCap is no longer used and now deprecated. All timing is now handled by the fixed time-step code we've introduced.
  • Time.now can no longer be relied upon to contain a timestamp value. If the browser supports requestAnimationFrame then Time.now will contain the high resolution timer value that rAf generates. Otherwise it will contain the value of Date.now. If you require the actual time value (in milliseconds) then please use Time.timeinstead. Note that all Phaser sub-systems that used to rely on Time.now have been updated, so if you have any code that extends these please be sure to check it.
  • Game.forceSingleUpdate will force just a single logic update, regardless of the delta timer values. You can use this in extremely heavy CPU situations where you know you're about to flood the CPU but don't want Phaser to get stuck in a spiral.
  • Tilemap.createFromTiles will convert all tiles matching the given tile index (or an array of indexes) into Sprites. You can optionally then replace these tiles if you wish. This is perfect for games when you want to turn specific tiles into Sprites for extra control. The Sprites have an optional properties object which they can be populated with.
  • Added support for the Wheel Event, which is the DOM3 spec (thanks @pnstickne #1318)
  • Wheel Scroll Event (old non-FF) and DOM Mouse Wheel (old FF) are supported via a non-exported reused wrapper object; WheelEventProxy. The proxy methods are generated one-time dynamically but only when needed.
  • Key.justDown allows you to test if a Key has just been pressed down or not. You can only call justDown once per key press. It will only return true once, until the Key is released and pressed down again. This allows you to use it in situations where you want to check if this key is down without using a Signal, such as in a core game loop (thanks @pjbaron #1321)
  • Key.justUp allows you to test if a Key has just been released or not. You can only call justUp once per key press. It will only return true once, until the Key is pressed down and released again. This allows you to use it in situations where you want to check if this key is up without using a Signal, such as in a core game loop (thanks @pjbaron #1321)
  • Device.whenReady is a new signal that you can use to tell when the device is initialized.
  • Device.onInitialized is dispatched after device initialization occurs but before any of the ready callbacks have been invoked. Local "patching" for a particular device can/should be done in this event.
  • TweenManager.removeFrom method allows you to remove a tween from a game object such as a Sprite (thanks @lewster32 #1279)
  • Tweens have been completely rewritten. They're now much more flexible and efficient than before:
  • When specifying the ease in Tween.to or Tween.from you can now use a string instead of the Function. This makes your code less verbose. For example instead of Phaser.Easing.Sinusoidal.Out and you can now just use the string "Sine".The string names match those used by TweenMax and includes: "Linear", "Quad", "Cubic", "Quart", "Quint", "Sine", "Expo", "Circ", "Elastic", "Back", "Bounce", "Power0", "Power1", "Power2", "Power3" and "Power4". You can append ".easeIn", ".easeOut" and "easeInOut" variants. All are supported for each ease types.
  • Tweens now create a TweenData object. The Tween object itself acts like more of a timeline, managing multiple TweenData objects. You can now call Tween.to and each call will create a new child tween that is added to the timeline, which are played through in sequence.
  • Tweens are now bound to the new Time.desiredFps value and update based on the new Game core loop, rather than being bound to time calculations. This means that tweens are now running with the same update logic as physics and the core loop.
  • Tween.timeScale allows you to scale the duration of a tween (and any child tweens it may have). A value of 1.0 means it should play at the desiredFps rate. A value of 0.5 will run at half the frame rate, 2 at double and so on. You can even tween the timeScale value for interesting effects!
  • Tween.reverse allows you to instantly reverse an active tween. If the Tween has children then it will smoothly reverse through all child tweens as well.
  • Tween.repeatAll allows you to control how many times all child tweens will repeat before firing the Tween.onComplete event. You can set the value to -1 to repeat forever.
  • Tween.loop now controls the looping of all child tweens.
  • Tween.onRepeat is a new signal that is dispatched whenever a Tween repeats. If a Tween has many child tweens its dispatched once the sequence has repeated.
  • Tween.onChildComplete is a new signal that is dispatched whenever any child tweens have completed. If a Tween consists of 4 sections you will get 3 onChildComplete events followed by 1 onComplete event as the final tween finishes.
  • Chained tweens are now more intelligently handled. Because you can easily create child tweens (by simply calling Tween.to multiple times) chained tweens are now used to kick-off longer sequences. You can pass as many Tween objects to Tween.chain as you like as they'll all be played in sequence. As one Tween completes it passes on to the next until the entire chain is finished.
  • Tween.stop has a new complete parameter that if set will still fire the onComplete event and start the next chained tween, if there is one.
  • Tween.delay, Tween.repeat, Tween.yoyo, Tween.easing and Tween.interpolation all have a new index parameter. This allows you to target specific child tweens, or if set to -1 it will update all children at once.
  • Tween.totalDuration reports the total duration of all child tweens in ms.
  • There are new easing aliases:
  • * Phaser.Easing.Power0 = Phaser.Easing.Linear.None
  • * Phaser.Easing.Power1 = Phaser.Easing.Quadratic.Out
  • * Phaser.Easing.Power2 = Phaser.Easing.Cubic.Out
  • * Phaser.Easing.Power3 = Phaser.Easing.Quartic.Out
  • * Phaser.Easing.Power4 = Phaser.Easing.Quintic.Out
  • ScaleManager.windowContraints now allows specifying 'visual' or 'layout' as the constraint. Using the 'layout' constraint should prevent a mobile device from trying to resize the game when zooming.

    Including the the new changes the defaults have been changed to

    windowContraints = { right: 'layout', bottom: '' }

    This changes the current scaling behavior as seen in "Game Scaling" (as it will only scale for the right edge) but also prevents such scaling from going bonkers in some mobile environments like the newer Android browser. (Automatic scroll-to-top, albeit configurable, enabled for non-desktop by default is not a fun situation here.)

    To obtain the current semantics on a desktop the bottom should be changed to 'layout'; although this will result in different behavior depending on mobile device. To make the sizing also follow mobile zooming they should be changed to 'visual'.

    Also added temp Rectangle re-used for various internal calculations.

  • Phaser.DOM now also special-cases desktops to align the layout bounds correctly (this may disagree with CSS breakpoints but it aligns the with actual CSS width), without applying a window height/width expansion as required on mobile browsers.

  • Signals have been heavily restructured to cut down on the number that are generated in-game. New signal proxies manage the setting and creation as required, cutting down on the volume of run-time object creation significantly. No user code needs to change, however if you did override Phaser.Signal or Sprite.Events then please be aware of the changes by inspecting the source (and commit #1389 by @pnstickne).
  • Game.lockRender is a new property. If false Phaser will automatically render the display list every update. If truethe render loop will be skipped. You can toggle this value at run-time to gain exact control over when Phaser renders. This can be useful in certain types of game or application. Please note that if you don't render the display list then none of the game object transforms will be updated, so use this value carefully.
Updates
  • TypeScript definitions fixes and updates (thanks @clark-stevenson @draconisNoctis)
  • The TypeScript definitions have moved to the typescript folder in the root of the repository.
  • Cache._resolveUrl has been renamed to Cache._resolveURL internally and gained a new parameter. This method is a private internal one.
  • Cache.getUrl is deprecated. The same method is now available as Cache.getURL.
  • Loader.useXDomainRequest used to be enabled automatically for IE9 but is now always set to false. Please enable it only if you know your server set-up / CDN requires it, as some most certainly do, but we're finding them to be less and less used these days, so we feel it's safe to now disable this by default (#1248)
  • Game.destroy now destroys either the WebGLRenderer or CanvasRenderer, whichever Pixi was using.
  • Particle.Emitter will now automatically set particle.body.skipQuadTree to true to help with collision speeds within Arcade Physics.
  • Particle.Emitter.explode (or Emitter.start with the explode parameter set to true) will immediately emit the required quantity of particles and not delay until the next frame to do so. This means you can re-use a single emitter across multiple places in your game that require explode-style emissions, just by adjusting the emitter.x andemitter.y properties before calling explode (thanks Insanehero)
  • Phaser.Polygon has been refactored to address some Pixi v2 migration issues (thanks @pnstickne for the original implementation #1267)
  • Polygon.area is now only calculated when the Polygon points list is modified, rather than on every call.
  • Phaser.Polygon can now accept the points list in a variety of formats: Arrays of Points, numbers, objects with public x/y properties or any combination of, or as a parameter list (thanks @pnstickne for the original implementation #1267)
  • All of the Input classes now use the more consistent enabled property instead of disabled. I.e. you can now checkif (input.mouse.enabled) rather than if (!input.mouse.disabled). The disabled property has been moved to a getter for backwards compatibility but is deprecated and will be removed in a future version (thanks @pnstickne #1257)
  • The Input class has been given a minor refactor to tidy things up. Specifically:
    • pointerN are aliases to backed pointers[N-1] array. This simplifies (and increases the efficiency of) looping through all the pointers when applicable; also eliminates pointer-existence checks Removes various hard-coded limits (added MAX_POINTERS); changed maxPointers default
    • Removed some special-casing from cases where it did not matter
    • Removed === false/true, == usage for consistency, changed missing value check to typeof, etc.
    • Updated documentation for specificity; added @public\@protected
    • @deprecated currentPointers due to odd set pattern; totalCurrentPointers is more appropriate. (thanks @pnstickne #1283)
  • Various ScaleManager fixes and updates (thanks @pnstickne):
    • Scale modes can now be set independently
    • Switching between fullscreen and normal correctly restores modes
    • Alignment does not incorrectly offset in fullscreen mode (#1255)
    • Changing scale/alignment promptly refreshes layout
    • isFullScreen returns a boolean, as it should
    • Faster parent checks (if required)
    • NO_SCALE should not not scale (vs previous behavior of having no behavior)
    • Correct usage of scaleMode depending on mode
    • Fullscreen Mode always scaling to fill screen in Firefox (#1256)
  • AudioSprite - removed an unnecessary if-statement (thanks @DaanHaaz #1312)
  • ArcadePhysics.skipQuadTree is now set to true by default. A QuadTree is a wonderful thing if the objects in your game are well spaced out. But in tightly packed games, especially those with tilemaps or single-screen games, they are a considerable performance drain and eat up CPU. We've taken the decision to disable the Arcade Physics QuadTree by default. It's all still in there and can be re-enabled via game.physics.arcade.skipQuadTree = false, but please only do so if you're sure your game benefits from this.
  • Phaser.DOM now houses new DOM functions. Some have been moved over from ScaleManager as appropriate.
  • Key.justPressed has bee renamed to Key.downDuration which is a much clearer name for what the method actually does. See Key.justDown for a nice clean alternative.
  • Key.justReleased has bee renamed to Key.upDuration which is a much clearer name for what the method actually does. See Key.justUp for a nice clean alternative.
  • Keyboard.justPressed has bee renamed to Keyboard.downDuration which is a much clearer name for what the method actually does.
  • Keyboard.justReleased has bee renamed to Keyboard.upDuration which is a much clearer name for what the method actually does.
  • Keyboard.downDuration, Keyboard.upDuration and Keyboard.isDown now all return null if the Key wasn't found in the local keys array.
  • The Phaser.Device class has been made into a singleton and removed it's dependency on Phaser.Game (thanks @pnstickne #1328)
  • ArrayList has been renamed to ArraySet (as it's actually a data set implementation) and moved from the corefolder to the utils folder (thanks @pnstickne)
  • If you are reloading a Phaser Game on a page that never properly refreshes (such as in an AngularJS project) then you will quickly run out of AudioContext nodes. If this is the case create a global var called PhaserGlobal on the window object before creating the game. The active AudioContext will then be saved towindow.PhaserGlobal.audioContext when the Phaser game is destroyed, and re-used when it starts again (#1233)
  • Camera.screenView is now deprecated. All Camera culling checks are made against Camera.view now instead.
  • Various CocoonJS related hacks removed thanks to fixes from Ludei directly in CocoonJS! Woohoo :)
  • Phaser.HEADLESS check removed from the core game loop. If you need to disable rendering you can now override the Phaser.Game.updateRender method instead with your own.
  • Group.forEach fixed against browser de-optimization (thanks @pnstickne #1357)
  • Phaser.Signals have been taken on a diet. They have been updated such that there is significantly less penalty for having many unused signals. The changes include:
  • * Changing it so there is no dispatch closure created. This is a potentially breaking change for third party code.
  • * In the rare case that code needs to obtain a dispatch-closure, the boundDispatch property can be used to trivially obtain a cached closure.
  • * The properties and default values are moved into the prototype; and the _bindings array creation is deferred. This change, coupled with the removal of the automatic closure, results in a very lightweight ~24bytes/object (in Chrome) for unbound signals.
  • With this change in place Signals now consume less than 50KB / 50KB (shallow / retained memory) for 200 sprites, where-as before they used 300KB / 600KB (thanks @pnstickne #1359)
  • Time.elapsedMS holds the number of milliseconds since the last Game loop, regardless of raF or setTimout being used.
  • Incorrectly prepared tilemap images (with dimensions not evenly divisible by the tile dimensions) would render incorrectly when compared to the display seen in Tiled. The Phaser tilemap code has been adjusted to match the way Tiled deals with this, which should help if you're using tileset images that contain extra padding/margin pixels. Additional console warnings have been added. However the fact remains that you should carefully prepare your tilesets before using them. Crop off extra padding, make sure they are the right dimensions (thanks @SoulBeaver for the report and @pnstickne for the fix #1371)
  • Text.setShadow has had the default color value changed from rgba(0,0,0,0) to rgba(0,0,0,1) so it appears as a black shadow by default - before the alpha channel made it invisible.
  • Math.getRandom will now return null if random selection is missing, or array has no entries (thanks @pnstickne #1395)
  • Array.transposeArray has had a small off-by-one error fixed. It didn't effect the results but meant returned arrays were 1 element bigger than needed (thanks @nextht #1394)
  • State.preRender is now sent two parameters: a reference to the Phaser.Game instance and a new parameter:elapsedTime which is the time elapsed since the last update.
Bug Fixes
  • Tilemaps in WebGL wouldn't update after the first frame due to a subtle change in how Pixi uploads new textures to the GPU.
  • XML files weren't being added to the URL map.
  • Cache._resolveURL was causing a Sound double-load in Firefox and causing errors (thanks @domonyiv #1253)
  • Loader.json was using the wrong context in IE9 with XDomainRequest calls (thanks @pnstickne #1258)
  • Text.updateText was incorrectly increasing the size of the texture each time it was called (thanks @spayton #1261)
  • Polygon.contains now correctly calculates the result (thanks @pnstickne @BurnedToast #1267)
  • Setting Key.enabled = false while it is down did not reset the isDown state (thanks @pnstickne #1190 #1271)
  • The Gamepad.addCallbacks context parameter was never actually remembered, causing the callbacks to run in the wrong context (thanks @englercj #1285)
  • Animation.setFrame used the wrong frames array if useLocalFrameIndex was false and a numeric frame ID was given (thanks @Skeptron #1284)
  • Fullscreen mode in IE11 now works (thanks @pnstickne)
  • Cache.addBitmapData now auto-creates a FrameData (thanks @pnstickne #1294 #1300)
  • P2.BodyDebug circles were drawing at half widths (thanks @enriqueto #1288)
  • FrameData.clone fixed when cloning data using frame names rather than indexes (thanks pjbaron)
  • Lots of the Cache getters (such as Cache.getbitmapData) would return undefined if the asset couldn't be found. They now all consistently return null for missing entries (thanks @Matoking #1305)
  • Phaser games should now work again from the CocoonJS Launcher.
  • Only one of the mouse wheel events is listened to, newest standard first. This fixes a bug in FF where it would use the default DOMMouseWheel (thanks @pnstickne #1313)
  • Stage.smoothed needed to modify the value of PIXI.scaleMode.DEFAULT instead of PIXI.scaleMode.LINEAR (thanks @pixelpicosean #1322)
  • Newly created Groups always had zero z index (thanks @spayton #1291)
  • Sprite.autoCull now properly works if the camera moves around the world.
  • Sprite.inCamera uses a much faster check if auto culling or world bounds checks are enabled and properly adjusts for camera position.
  • Camera.totalInView is a new property that contains the total number of Sprites rendered that have autoCull set to true and are within the Cameras view.
  • Emitter.setScale fixed minX minY order precedence (thanks spayton)
  • Group.iterate can now accept undefined/null as the arguments (thanks @pnstickne #1353 @tasos-ch #1352)
  • When you change State the P2 Physics world is no longer fully cleared. All of the bodies, springs, fixtures, materials and constraints are removed - but config settings such as gravity, restitution, the contact solver, etc are all retained. The P2.World object is only created the very first time you call Physics.startSystem. Every subsequent call hits P2.World.reset instead. This fixes "P2.World gravity broken after switching states" (and other related issues) (#1292 #1289 #1176)
  • Text.lineSpacing works correctly again. Before no space was added between the lines (thanks @intimidate #1367 and @brejep #1366)
  • P2.BodyDebug always lagged behind the position of the Body it was tracking by one frame, which became visible at high speeds. It now syncs its position in the Body.postUpdate which prevents this from happening (thanks @valueerror)
  • A State.preRender callback wasn't removed correctly when switching States.

 

Phaser is available here.

 

Of course, GameFromScratch has a comprehensive Phaser with TypeScript series available here.

News


3. December 2014

 

In this video tutorial we look at all aspects of audio programming in LibGDX.  We look at how to play back sound effects, stream music audio we even record audio from the microphone and play it back.  We also touch on a couple important concepts like cross platform file formats and creating transitions between music tracks.

 

You can see the video in full 1080p resolution here.  All of the code used in this video is presented below.  For more LibGDX video tutorials, go here.

 

 

 

Sound Effects

 

Playing a Sound Effect

ackage com.gamefromscratch;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.utils.Timer;

public class SoundFXDemo extends ApplicationAdapter {

   Sound sound;
   @Override
   public void create () {
      sound = Gdx.audio.newSound(Gdx.files.internal("glassBreak.wav"));
      sound.play();
   }

   @Override
   public void render () {
   }

   @Override
   public void dispose() {
      sound.dispose();
   }
}

 

Setting Volume

      sound = Gdx.audio.newSound(Gdx.files.internal("glassBreak.wav"));
      long id = sound.play(1.0f);
      sound.setVolume(id,1.0f);

 

Setting Pitch

      sound = Gdx.audio.newSound(Gdx.files.internal("glassBreak.wav"));
      long id = sound.play();
      sound.setPitch(id,0.2f);

 

Setting Pan

      // Track must be in mono format to support panning
      sound = Gdx.audio.newSound(Gdx.files.internal("glassBreakMono.wav"));
      long id = sound.play();
      sound.setPan(id,1.0f,1f);

 

Setting Volume, Pitch and Pan all at once

      sound = Gdx.audio.newSound(Gdx.files.internal("glassBreakMono.wav"));
      sound.play(1.0f,0.0f,0.8f);

 

Looping and Pausing

      sound = Gdx.audio.newSound(Gdx.files.internal("glassBreak.wav"));
      sound.loop();

      // Wait 10 seconds, then pause playback
      Timer.schedule(new Timer.Task(){
         @Override
         public void run(){
            sound.pause();
         }
      }, 10);

 

Multiple Concurrent Sounds

      sound = Gdx.audio.newSound(Gdx.files.internal("glassBreakMono.wav"));

      // Play 2, one fast, one slow
      final long fastSound = sound.loop(1f,2.5f,0f);
      final long slowSound = sound.loop(1f,0.5f,0f);

      // Wait 5 seconds, then stop ALL
      Timer.schedule(new Timer.Task() {
         @Override
         public void run() {
            //sound.stop();
            // Or stop just one
            sound.stop(fastSound);
         }
      }, 5);

 

 

Music

 

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.utils.Timer;

public class MusicDemo extends ApplicationAdapter {

   Music song1,song2;

   @Override
   public void create () {
      song1 = Gdx.audio.newMusic(Gdx.files.internal("song1.mp3"));
      song2 = Gdx.audio.newMusic(Gdx.files.internal("song2.mp3"));

      song1.play();


      // Then set it so song 2 plays when song1 ends
      song1.setOnCompletionListener(new Music.OnCompletionListener() {
         @Override
         public void onCompletion(Music music) {
            song2.play();
         }
      });

      // You can pan and set volume like sound fx, but cant play multiple instances and cant set pitch
      // You can however get the position of the sound ( but not set it )
      // Let's do a timer that lower the volume over the last 5 seconds of the song
      // Built in options are pretty limited, so there is no way to poll for the length of a song
      // Without using the audio-extension
      Timer.schedule(new Timer.Task() {
         @Override
         public void run() {
            if(song1.isPlaying())
               if(song1.getPosition() >= 10)
                  song1.setVolume(song1.getVolume() - 0.2f);
         }
      },10,1,4);

   }

   @Override
   public void render () {
   }

   @Override
   public void dispose() {
      song1.dispose();
      song2.dispose();
   }
}

 

Audio Recording and Playback

 

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.AudioDevice;
import com.badlogic.gdx.audio.AudioRecorder;

public class AudioRecorderDemo extends ApplicationAdapter {

   @Override
   public void create () {

      // This code may not actually work during video recording due to Microphone being in use.
      AudioRecorder recorder = Gdx.audio.newAudioRecorder(44100,true);
      short[] audioBuffer = new short[44100 * 5];

      // read() will fail if device doesnt support given resolution or a mic is not attached
      recorder.read(audioBuffer,0,audioBuffer.length);

      AudioDevice audioDevice = Gdx.audio.newAudioDevice(44100,true);
      audioDevice.writeSamples(audioBuffer,0,audioBuffer.length);
      audioDevice.dispose();
      recorder.dispose();
      Gdx.app.exit();
   }

   @Override
   public void render () {
   }
}

Programming


2. December 2014

 

Starting life as niche technology, costing millions of dollars and used only on high end films, 3D graphics have now become nearly ubiquitous these days.  Still used in movies ( nearly all movies these days ), 3D graphics are used heavily in games, TV, marketing, conceptualization, engineering and much much more.

 

In this particular guide we are going to look at the more popular options out there, with an obvious bias towards gaming.  If you are just starting out and trying to get an idea of what’s available, this should be the perfect page for you!

 

This entire discussion ( and much more I think ) is available as a 56 minute talk in 1080p on YouTube as well as embedded at the bottom of this post.

 

A Quick History Lesson

 

Actually, the big two would probably be more accurate, as Autodesk recently put a bullet in one of these apps.  These are probably the three most used commercial 3D graphics applications, and to really understand them probably requires a bit of a history lesson.  Don’t worry, I’ll keep it brief.

 

Way back in the stone age of computer graphics, there were a handful of really successful 3D applications, but at the forefront were a pair of applications.  One was a product called Power Animator created by a company called Alias ( who became Alias/Wavefront ) which eventually morphed into a project called Maya.  Power Animator was used in such early and high profile 3D movies like Terminator 2 and The Abyss and on early 3D video games like Super Mario 64.  The other major player of the day was a product called Softimage ( the one that just took a bullet actually… ) which was used to make Jurassic Park and the Virtua Fighter series of games.  By no means were they the only players, many others existed such as Nichimen nWorlds, Lightwave and more, but these two were the big players used in big budget movies.

 

A few things started to happen however…  In these early days, the computers capable of running these 3D applications were dedicated workstations like those from Silicon Graphics Inc (SGI) and Digital (DEC).  These machines ran from $10K to $50K and much much more.  3D graphics applications certainly weren’t and neither were the machines that ran them. 

 

There was a movement towards running 3D applications on “mere mortal” machines.  The earlier mentioned Lightwave ran on Amiga’s for example, as did a popular-at-the-time application called Imagine.  But three major things happened to bring 3D graphics to the unwashed masses

 

  1. home computers became less crappy.  OpenGL arrived, 3D cards arrived, processors got faster and memory increased
  2. Autodesk created a program called 3D Studio that ran on DOS.  It was a very small player in the industry (outside of CAD that is), but opened 3D up to a world of people that never had access.
  3. Microsoft released Windows NT and wanted to move into the 3D market, so they did what they did and bought it.  That is, they bought Softimage and ported it to Windows.  Coupled with companies like Intergraph releasing workstation class PCs and the rise of the video card, they succeed.

 

Fast forward a few years and many amazing things happened.  Kinetics became Autodesk ( of AutoCAD fame ) and 3D Studio became 3D Studio Max, moving from DOS to Windows.  In fact, Windows is now the new home of 3D.  SGI is fast becoming a fading memory and all the major applications have been ported to Windows.  Then a wonderful thing happens… prices all start to fall!  A version of Maya and Softimage are available for under $1,000 and free game focused versions are available of Softimage ( Mod Tool ) and 3D Studio ( GMax ).  3D had truly started coming to the masses.

 

Enter Autodesk.  There was a LOT of consolidation in the industry…  Alias and Wavefront merged to form Alias/Wavefront, Microsoft purchased Softimage and eventually sold it to Avid.  In the end, Autodesk purchased both Avid and Alias resulting in all three major 3D applications being owned by a single company.  Almost over night the price wars predictably enough ended, and the prices went up.  That said, it hasn’t all been bad.  These days, for students and educators anyways, the entire Autodesk suite is available for free.  There is also a game focused version of Maya available, which we will discuss shortly.  So in some ways, 3D has become a great deal more expensive and a great deal cheaper all at once.

 

So, that brings us to today.

 

The Big Three… Er… Two

 

In commercial studios, be it for games, TV or movies, the same three products are the ones most encountered:

3D Studio MAX

Maya

Softimage Xsi

 

As I mentioned earlier, after a very long and successful run, Softimage is being put out to pasture.  Softimage 2015 was the most recent, and final, release ever.  Obviously if you are just starting out today and need to pick a package, Softimage is no longer a good choice.

 

That leaves Maya and Max to choose from.  Traditionally 3D Studio Max was strongest in games, with many game developer friendly features ( excellent plugin system, ability to build your renderer into Max, great low polygon tools, good texturing tools, etc ), while Maya was known more for Film and TV, with animation certainly being it’s strong suit.  That all said, it’s become more and more common to see Max used for film work and Maya used for game work, so the old stereotypes don’t really hold.

 

Actually, being under the same rough has leads to a convergence of sorts.  Over time the feature list of the two products is quickly becoming virtually identical.  As have the keyboard shortcuts, even the file formats are standardizing ( FBX, read more about it here if interested ).  With each new release, each product is starting to feel more alike than different.

 

Of the two, I personally prefer Maya, although the mouse heavy UI drives me nuts.  3D Studio MAX is just getting so long in the tooth that it’s massively in need for a major overhaul.  I first used Max when it was initially released and frankly the Max of today is very very very similar.  Same UI, even a lot of the same tools, tools that have long since been obsoleted.  This is just cluttering things up and making the learning curve higher.  Maya was the result of a complete re-write on the other hand, so the code base is much newer with less years of cruft.  The menus though, those mouse heavy radial menus…  ugh.  Of course, this is all personal opinion.

 

At the end of the day, Max and Maya are your two safe choices if you want a job in the industry.  Max probably has an edge for getting you into a game studio, while Maya probably has an edge getting you into a film studio.  At the end of the day though, they are owned by the same company, speak the same language and are often both used.

 

Neither however is cheap.  3D Studio MAX is $3,675.  Interesting fact, it’s always been that price… even during the 3D application price wars, Autodesk never dropped their price.  The big difference is you can now lease monthly or annually, for $185 / month or $1470 / year.  Maya is now the exact same price as Max ( see how they are becoming more and more similar… ) down from a pricetag of about $5K last year.  Softimage was around the same price.  I’m not sure if you can even buy it anymore, if you can, Autodesk sure don’t make it easy.

 

Oh yeah, Max is Windows only, while Maya is available on Windows and Mac.  If you are a Mac user, that’s a pretty important tidbit of info, no?

 

Maya for Indie Developers

 

Last year, Autodesk took a step towards courting the indie game developer market with the release of Maya LT.  This is a stripped down version of Maya that targets indie game developers specifically.  It is priced at $30 a month, or $795 to purchase a perpetual license (with, I believe, one year of support).

 

So, the obvious question is, what’s stripped out?  Initially the limitations really sucked…  polygon caps, no scripting support, it was pretty much crippled.  Over time though they revisited things and made up for some of the glaring mistakes.  The big areas that are cut are Visual FX stuff and most of the renderers.  This means, if for example, you wanted to create a pre-rendered cutscene, you couldn’t.  The animation features have also been stripped back, leaving mostly the stuff you would use for real-time games.

 

At the end of the day Maya is really only suitable for modeling and rigging game assets or possibly level creation/design.  That said, for a great many game developers, that’s all you actually use it for.  For a full breakdown of Maya vs Maya LT features, you can check here.

 

 

The 900lb Open Source Gorilla in the Room

 

If you are sitting here thinking 4 grand?!?!?!??! OUCH!

 

Well, meet Blender.

 

Blender started life as an in-house 3D tool for a company called NeoGeo… yeah, not the NeoGeo game console, but instead it was the Netherlands largest 3D house… or is that haus?  Eventually a company named NaN was formed to “productize” Blender.  NaN died in 2002 and a project was launched to open source the Blender code… in many ways this was one of the first highly successful KickStarter campaigns!  Blender was eventually open sources, the community took it and ran with it and Blender is thriving today.

 

Blender is entirely free and open source.  It’s nowhere near as commonly used commercially as Max or Maya, but it is certainly used, such as for pre-viz work on Spiderman 2.  I would lie though…  the vast majority of commercial games *aren’t* created using Blender.  If you are looking for something to stick on your resume, Max and Maya certainly carry more weight.

 

HOWEVER, and this is where we drop heavily into opinion land for a bit…

 

If you are working on a 3D game and need to create textured, animated, 3D models, Blender is just as capable as 3D Studio Max or Maya, even without factoring in the price tag.  For many years, Blender had a reputation for being chosen solely because it’s free.  Those days are starting to pass however.   Put into the simplest terms, for the last several years I would say with each new release, Blender is the application that is improving by far the most of the three.  Now, you will find all three apps are quite capable, with Maya/Max and Blender all being strong and weak in different categories.

 

Blender used to ( ok… still does ) have a reputation for being hard to use with an unwieldy interface and in many ways, this was quite fair.  Blender followed it’s own idioms and was a VERY keyboard heavy workflow which takes some time to get.  Also, rather bluntly, Blender 2.4’s interface was pretty much terrible.  Blender 2.5 however was a massive rewrite and rework and it really bore fruit.  Then the 2.6 releases improved the rough edges, while 2.7 has a heavy focus on usability, and it’s make a huge difference.  If you haven’t checked out Blender since the 2.4 days you really owe it to yourself to try it again.

 

The single biggest flaw with Blender IMHO, at least as far as game development is concerned, is the file support.  Autodesk owns the FBX format and the COLLADA file format is a bloody mess of complication to the point that nobody really does it all that well.  This means getting assets into and out of Blender can certainly be more of a challenge than using Autodesk products.  This is an area Blender have recently focused their efforts on and the Unreal Engine folks have kicked in some cash toward the effort so hopefully this improves.

 

So, my summary on Blender… it’s honestly an equal to the two Autodesk products, with as I said, it’s strengths and weaknesses.  I also personally think it’s improving at a much greater rate than either of those products.  Of course, it’s also a hell of a lot cheaper.  That said, once you start paying actual salaries, the cost of software licenses quickly become peanuts.  Can you use Blender for your own game project?  Certainly.  Should you?  That depends on you really, but you should certainly try it out.  The functionality is certainly there, with the biggest flaw easily being the content pipeline.  However, if you are a student looking for a job, Max and Maya will certainly look better on a resume.  A great artist will be able to make great art in any three of those tools, and a good studio will hire an artist will a great reel, regardless to the tool it was created in.  That said, at the end of the day, human resources will be looking for Max or Maya on your CV… they will not be looking for Blender.  Which is actually kind of sad.

 

My much shorter summary…  Blender is free and very good, you should certainly give it a look.  Oh, being Open Source… it’s available on basic everything… possibly even your Toaster… although oddly enough, not been ported to iPad.  Seriously, someone really should port Blender to the iPad!

 

 

Sculpting… the new hotness

 

Another major development in the world of 3D is 3D sculpting.  Sculpting is like working in digital clay for quickly creating hyper detailed, very organic meshes.  In in the world of real-time games, this is still quite useful, as high resolution versions of game models are often used to create something called a normal map.  This allows you to use texture maps to fake super high levels of detail.  Another common operation is to sculpt hyper high resolution models, then basically “trace” a lower detailed version over top.  This is a process called retopology.  Now the good news… all three of the above solutions have sculpting built and retopology tools built in.  That said, compared to the tools we are about to discuss, they simply sucks at it.  So it’s becoming increasingly common to see these kinds of applications pop up in studios.  That said, these are more like initial tools in your toolbox, and certainly not where you should start!

 

zBrush

Pixologic’s zBrush is where the whole sculpting movement started and it’s by far the biggest player in the space.  It’s also $800 by the way.  It’s not an end to end solution, it’s designed to do what it does, then passes the results off to a different program ( Max… Maya… Blender… ) for animation, rendering, etc.  For sculpting though, it’s hard to beat zBrush.  You should at least be aware of it’s existence.

 

Sculptris

$800 a little rich for you?  How does free sound?  Sculptris is an interesting project… it actually started life as a fan’s attempt to replicate zBrush.  Said Fan did a pretty damned good job of it, to the point the Pixologic bought the rights and make it available for free.  So go, download it now… even if you don’t ever do anything with it, it’s an amazing amount of fun.

 

Mudbox

Of course you couldn’t be a 3D application without having an Autodesk product, could you?  Mudbox is Autodesk’s offering in the 3D sculpting space.  It started life as a tool used by Weta on King Kong and Lord of the Rings.  Eventually Autodesk bought them and now it’s available for $500 or $10 a month.  In all honesty, that’s the end of my knowledge, I’ve never used Mudbox, nor have I ever talked to an artist that chose it over zBrush.  There is however a trial available, so I really should check it out one of these days…

 

3D Coat

3D Coat is another interesting product out there that focuses on 3D tasks…  Sculpting, 3D Painting and Retopology.  3D Coat is about $400 at full retail.  It is however available on Steam so keep an eye out for amazing discounts.  Be warned, you need the commercial version if you are using 3D Coat for a commercial product.  3D Coat does have a trial available.

 

 

Hey What About _________!

 

 

In all honesty, we just ticked off the major boxes… but of course that was by no means comprehensive.  There are a few other packages you should certainly be aware of, so let’s discuss them now.  These aren’t rated lower for functionality, but simply for popularity.

 

Modo

Modo started life somewhat recently ( by 3D application standards ) as a dedicated 3D modeler.  The company itself was formed by a number of former Lightwave developers and this application has a huge following.  Over time it’s evolved to become much more than a modeler, although it’s still not quite a full 3D suite like Max, Maya or Blender, it’s getting very close.  As the functionality has grown, so to has the price tag.  Currently its 1000Euro.  Animation is a somewhat recent addition to Modo, so the functionality is a bit limited compared to more mature packages.  That all said, Modo has grown in functionality at an amazing rate and is getting quite popular.  There is also a trial available.

 

Lightwave

As I just stated, Modo was formed by a bunch of ex-Lightwave developers.  Lightwave is a once great package that has seemingly lost it’s way and as a result, a great deal of it’s user base.  Lightwave still exists today, there was a new release in 2014, but it seems to be developing at a snails pace and the community around it seems to have mostly disappeared.  Lightwave has been used in a staggering number of TV and Film projects, as you can see here, but the number of recent projects seems to have dried up.  Lightwave costs $1000USD and there is a free trial available.

 

Sketchup

This application is actually used a surprising amount.  It was purchased by Google and used to create 3D models for Google Earth.  Eventually however Google sold it off and it’s a stand alone product again.  Sketchup is available in both a free and commercial version.  At the end of the day, for commercial game dev you will probably need the pro version, which has a $600 price tag.  Sketchup is a 3D modeller only, but damn is it an easy to use one!  For an introduction to 3D modeling, the free version may be the perfect place to start.  You can read more about Sketchup’s use in game design here.

 

Silo

This program is most similar to Modo in functionality and I was a huge fan when it came out.  It’s mostly a modeler that’s gained more features over time, it has a nice low price tag of $109, is cross platform and great to use.  So why the negativity?  Well, the developers basically abandoned it for many years, only recently started working on it again.  I have no idea how much support there is behind this application.  It’s such a shame too, as this product could have been truly great.  There is a trial available and it’s worth checking out, but I wouldn’t rely on too many new features or bugs being fixed going forward.

 

Wings

Wings is an awesome 3D modeler, based heavily on Nichimen Nendo, an awesome 3D modeler that came before it.  It is free and open source and uses a completely different technology called Winged Edge meshes.  Unfortunately it’s also written in a programming language about 4 people on earth use, so when the primary developer stopped supporting it, it effectively died.  It’s still available, and still very cool, but in the last few years it’s stayed still while the world around it got a whole lot better.

 

Cinema4D

This package has quietly existed for years, gaining more and more features and a rabidly loyal user base.  To be honest, I’ve never really got it, especially with a $3,700 price tag.  That said, there must be advantages, as it wouldn’t still exist otherwise.  There is a trial available if you want to check it out for yourself.

 

Houdini

This one has also been around for a very long time and was traditionally used quite heavily for 3D effects in movies.  In all honesty, this is the single most confusing piece of software I have ever tried to use! ;)  It takes a procedural approach to 3D and frankly, I don’t really understand how it works, so I’m not even going to try to explain it.  There is a trial available and Houdini is available for $2000.  They do however have an Indie friendly version available for $200, that is tied to company revenue.  Remember that somewhat famous Dead Island trailer that took the web by storm?  That was created in Houdini.  Again though, this program is very very very weird. 

 

Animation Master

Here is another application that’s quietly been around forever, 1987.  It’s a low cost modeling and animation tool that is based around splines and patches ( most modern modelers are polygonal / subD surfaces ).  Organic models are easy to create, the animation tools are surprisingly comprehensive and the price tag starts at $80.  It’s an interesting package and there is a trial available, but it’s a bit of a pain to get it running. 

 

Shade3D

To be honest, I know almost nothing about this application, even though it’s been around forever.  It’s a full suite 3D package like Max or Maya and they recently released a Unity version.  I tried Shade shortly after that release ( a trial is available ), and the documentation was extremely lacking at the time, so I got pretty much nowhere.

 

Hexagon

A free 3D modeling package made available by Daz.  Reviewed it quite a while back, not a fan.  An advance warning, Daz will spam the ever loving hell out of you if you give them an email address!

 

 

TL;DR

 

So, you got to this point and now you are probably asking… now what?  That’s a lot of options, what should I do???

 

The answer really depends on you and what your goals are.  If you are a student (secondary or post-secondary) and looking to get into AAA 3D for games or film, the answer is pretty much a no-brainer.  Get in the free student program from Autodesk and start learning either Max or Maya.  If it’s games you are most interested in, Max is probably the route to go between the two.

 

If you aren’t a student, or aren’t trying to work on your CV, the answer gets much trickier.  The choice between Maya, Max and Blender on strictly technical merits comes down mostly to the persons opinion and work-style.  If you are working with a large team, or on a free product, Blender quickly becomes a no brainer solution as well.  If you’ve got no budget at all, Blender simply wins by default.  Otherwise I would recommend taking advantage of the free trial, trying out all three and seeing which one has a workflow that fits you.  Just be warned, it’s going to take some time to come to terms with each package.

 

For all others, I saw this.  At least download Blender and Sculptris.  Both are completely free, both are very good and both are excellent places to start.  The skills you learn transfer very well to other packages.

 

The Video Version

 

Art


AppGameKit Studio

See More Tutorials on DevGa.me!

Month List