April 18, 2012

And There Was Light


Lighting with Java & OpenGL

I started game programming with Java2D. While it was enough and easy to use, adding realistic lighting for the field of view, is nearly impossible. So, the Java2D view renderer can only do it in a very primitive way: tiles out of view are drawn a bit darker by adjusting the alpha composite for the drawImage() call.

tile based field-of-view

A nice and easy to set up solution (that means no OpenGL programming knowledge is required) for OpenGL and LibGdx is box2dlights.

field-of-view with box2dlights

Here are the main steps outlined for adding one scene light as shown above:
// light camera 
pixelPerMeter = 64f;
boxLightCamera = new OrthographicCamera();
float boxLightViewportWidth = spriteCamera.viewportWidth / pixelPerMeter; 
float boxLightViewportHeight = spriteCamera.viewportHeight / pixelPerMeter; 
boxLightCamera.setToOrtho(true, boxLightViewportWidth, boxLightViewportHeight); 
boxLightCamera.update(true);

Box2dlights uses the physics simulation Box2D (in its native version as integrated with LibGdx), and Box2D prefers meters as unit of measure instead of pixels. With the rest of the game using pixels, a new camera is needed for mapping pixels to meters. A simple way to do this is to define 1 tile as 1 meter, and thus 1 meter equals 64 pixels.

// world and light setup
 world = new World(new Vector2(), true); 

RayHandler.useDiffuseLight(true);
rayHandler = new RayHandler(world);
rayHandler.setCombinedMatrix(boxLightCamera.combined);
rayHandler.setAmbientLight(ambientLight);

spriteLight = new PointLight(rayHandler, 128, lightColor, 10, 0, 0);

A new yet empty Box2D world (no gravity defined, we only care about light rays) is created.
RayHandler is the main light controller. Setting the diffuse flag is important to prevent an over-illuminated player sprite.
With ambient light, tiles out of view still get some low amount of light.
Finally, a light is created that will be later continually moved to the player sprite position and thus allowing a dynamic field-of-view.
128 rays make up a nice smooth light, 10 meters is the light range. Furthermore, a light color is defined.

As box2d is used to find out about light obstacles like walls from which shadows are created and light is blocked, the tile map must be converted into box2d world objects. To keep it Simply, for each tile one world body is created. This could be theoretically optimized by combining rows and columns of neighbour tiles to only one body, but so far I did not see any performance impact.
protected void createWorldScenery(ITileMap tileMap) {
 // build plan for wall bodies
 float halfBody = tileSize / pixelPerMeter / 2;

 PolygonShape tileShape = new PolygonShape();
 tileShape.setAsBox(0.5f, 0.5f);

 BodyDef tileBodyDef = new BodyDef();
 tileBodyDef.type = BodyType.StaticBody;

 FixtureDef fixtureDef = new FixtureDef();
 fixtureDef.shape = tileShape;
 fixtureDef.filter.groupIndex = 0;

 // create box2d bodies for all wall tiles
 for (int row = 0; row < tileMap.getRows(); row++) {
  for (int col = 0; col < tileMap.getColumns(); col++) {
   int tileClass = tileMap.getTileClassMask(col, row);
   if (Tools.anyBitSet(tileClass, obstacleMask)) {
    float bodyX = col + halfBody;
    float bodyY = row + halfBody;
    tileBodyDef.position.set(bodyX, bodyY);
    Body tileBody = world.createBody(tileBodyDef);
    tileBody.createFixture(fixtureDef);
   }
  }
 }

 tileShape.dispose();
}

Disposing box2d objects must not be forgotten. Tile classes are bit fields for identifying light blocking wall tiles.

For rendering each frame, first tiles and sprites are drawn with a separate camera, then the moving light is updated to match the player's center position:
puppetLight.setPosition(clientCenterX, clientCenterY);

Afterwards, the light camera gets updated following the scrolling tile map:
boxLightCamera.position.set(camX, camY, 0);
boxLightCamera.update();

And finally, the RayHandler does all the magic:
rayHandler.setCombinedMatrix(boxLightCamera.combined, 
boxLightCamera.position.x, boxLightCamera.position.y,
boxLightCamera.viewportWidth * boxLightCamera.zoom, 
boxLightCamera.viewportHeight * boxLightCamera.zoom);

rayHandler.updateAndRender();

That's it. Without one line of OpenGL code.

For even more fun, it is possible to add and remove lights any time, so I illuminated fired bullets in different colors. For bullets, computing power can be saved by reducing the number of rays to 16.
Additionally, a nice effect is to continuously dim bullets instead of switching them off immediately when they hit a wall. Just set new light colors while lowering the alpha composite until it reaches a value of 0.

The game uses OpenGL 2.0, it runs with OpenGL 1.1 as well, but the lighting impression is totally different, lights are much brighter, so to switch between both version, some adjustment would be necessary.

What about the performance impact ?

~ 2500 fps for rendering the scrolling tile map without lights.
~ 1000 fps for rendering with enabled lights.

While a bit of a performance drop, 1000 fps are still way beyond of what is required, so some more lights could be added, I guess :)

April 12, 2012

From Java2D to OpenGL

I started game programming with Java using the default 2D API.
It is easy to use and fast enough for drawing images, which is basically all you need to create a tile based game with some animated sprites.
But the support for creating game eye candy like color effects, lighting, shadows or particles is quite limited. While you have access to every single image pixel, the drawback is a more or less heavy performance loss, emphasis on more, most probably.

Luckily you can use OpenGL with Java.
There are several bindings and frameworks available. I decided to use LibGdx with LWJGL as backend. LibGdx has a good reputation performance wise, and is furthermore based on classes and examples presented in the book "Beginning Android Games" which is also a good and gentle introduction into OpenGL. Then, there is an awesome lighting library available: Box2dLight. That leads to the most important advantage:
Abstraction from OpenGL, which is very low-level, static and stateful. Enough said.

Eventually, with little knowledge about OpenGL, I was able to implement a new renderer based on LibGdx in a few days. Some pitfalls were to overcome, though:

Multithreading is problematic if possible at all. Better do anything OpenGL related in one and only one thread. Loading images, drawing, creating lights, disposing resources. While it shall be possible to activate the OpenGL context in multiple threads, it did not work for me.

The default coordinate system of LibGdx has it origin in the left bottom, which did fit not with the existing game relying on the upper left as the origin. But with configuring a camera that's very easy to overcome, as follows:

OrthographicCamera cam = new OrthographicCamera();
cam.setToOrtho(true);

This y-down camera is then used for drawing sprites and tiles. Additionally, all textures must be flipped vertically. Best to use a TextureRegion for that.

Even if you are using an object abstraction layer on top of OpenGL, you have to keep in mind, that after all, every operation invoked on a Java object ends up in OpenGL which is, well, static and stateful. Thus, there is a lot of dependency and interaction going on under the hood. You can not think and design like it was a real object-oriented system even if certain classes seem to encapsulate OpenGL.

Be sure to adjust the viewport dimension with the camera. I spend some time wondering why I got weired mouse click coordinates. In the end it turned out that the camera was 64 pixels lower than the viewport due a copy-paste-action from my Java2D renderer.You get what you deserve, I guess. It was not visible, OpenGL did a good job in stretching.

The basic and most important classes are: 
  • OrthographicCamera
  • Texture
  • TextureRegion
  • SpriteBatch
  • BitmapFont
SpriteBatch is for drawing all kinds of images, animated sprites, tiles, HUD items and text.

Unfortunately, LibGdx forces you into a predefined application skeleton which I don't need and want to use. That means, access to several configuration classes is restricted, so that I had to put some of my own game classes into a gdx package. It's good to have a standard easy-to-use setup, but it should be an option and not block different valid approaches. But that's only a minor issue.
Good progress so far :)

April 11, 2012

Java Games and Performance

Some basic performance considerations for real-time Java:

Choose collection classes wisely.
What are the most frequently called operations ? Collections classes can give very different performance signatures when it comes to
- iterating all items
- index based direct access
- key based access
- insertion, appending or removing items
- keep items ordered
- concurrent access, whether algorithmic or by synchronization
- scaling / extensions of memory

Some hints:
- FastMap supports ordered maps
- Colt's maps can store primitives
- LinkedList is not that good for index access
- ArrayList isn't the best for finding concrete items
- Ever looked what ArrayList does when you remove an item (except for the last one) ?
- Choose the right initial capacity and growth behaviour
- For fast get/set access of hash maps, check out Trove's maps or just use Java's native HashMap
- Take care of collection classes which create additional objects for each added item like LinkedList and ConcurrentLinkedQueue
- Array based Bags are good and fast for adding as well as removing items when no item order is required
- The various cuckoo hash maps of LibGdx are said to be very fast and worth checking out
- After all, only profiling can show realtime behaviour - and can result in different conclusions for different platforms, machines, CPUs, CPU core counts, etc.

Do you know what the following code does ?
List names = ..
for (String name : names) {
...
}

Certainly you do, but do you also know what happens under the hood ?
These enhanced loops work with implicit created iterator objects. So if you use them in your main game loop, this ends up in creation of tons of avoidable object garbage.
Better choices are ArrayList with index access or maps like FastMap THashMap which offer callback loops.

What about this:
String guitarGod = "Jimi" + " " + " Hendrix";
Well, the coding makes no sense, the meaning very well does, but unfortunately Java creates StringBuilder objects to concatenate such strings. Again, for frequently called code fragments...
Better set up your own controllable StringBuilder.

When dealing with images, be sure to to create compatible images, for example by using
GraphicsConfiguration#createCompatibleImage(...) and
GraphicsConfiguration#createCompatibleVolatileImage(...)

That ensures compatible data and color models to prevent from implicit slow conversion while rendering images.

For hardware accelerated images use VolatileImage. Take care if you intend to fiddle with image pixels by yourself. Prior managed images might very well become unmanaged and thus lose any hardware acceleration (see VolatileBufferedToolkitImage Strategies).

Keep synchronization blocks short and be sure you know what you when it comes to multithreading ;)

Add delay or conditions to certain operation to lower the invocation frequency and save CPU cycles.
For example:
- calculating a field-of-view only needs to be updated if an actor was moved or the environment has changed
- sending postion updates from server to client might only be required for actors which have actually been moved
- don't call your genius-pixel-exact collision detection when the cool rough collection detector said: relax boy, too far away anyway
- write algorithms that are interruptable and can continue later on for spreading their execution over multiple game frames
But beware that processing heavy code kicking in only from time to time can cause cpu spikes and an unsteady game experience. Thus, the right balance must be found.

Using prerendered text images instead of drawing true type antialiased fonts each time letter by letter might relax your machine.

Avoid object creation from auto boxing.

Think about object caching for selected classes (not in general).

A profiler can easily reveal performance hot spots you have never thought of and keep you from tinkering the wrong code fragments.

Be careful with micro benchmarks:
  • let the JVM optimize during an appropriate warm-up period before measuring
  • prevent from dead code optimizations
  • compare results of client and server VMs
  • cache hits or misses influence benchmark results

Game Programming with Java

When you tell someone that you're programming a computer game with Java, the first response might very well be: really, is it fast enough ?

Short answer is: sure it is.
The more detailed answer is simply, as always: it depends.
It depends on the type of game, on the architecture and programming, on CPU and naturally, very important, for polished graphics, on the graphics card.
For casual games, for 2D games, it is.
Sadly, established myths are hard to overcome, and one is the „Java-is-slow“ myth.

In the old days, probably the Assembler guys were thinking similiar of C++, before it became the game development standard. And why not use the standard ? Well, first of all, when I started, Java was my job language, and second, C++, well... let's say I find not that attracting :)

Here is an older (nicer shading soon to come) of my current game under development. It is a 2D top-down-shooter inspired by games from ancient ages...



The scrolling view is made of tiles, each 64x64 pixels. Visible are 16x12 tiles, thus 192 tiles need to be rendered for each view frame. The game has an event driven multithreaded client/server architecture, runnable either via TCP or as standalone game sending in-memory events.

With pure Java2D as rendering module, on a machine with an Intel Q6600 at 2.4GHz and a GeForce GTX 460, after loosing the brakes, I get a framerate of ~1400 fps.
Thus, Java2D draws 16*12*1400 = 268.800 images each second.
Considering that the average monitor only has a refresh rate of 60 fps, that is pretty much fast enough.

The game server, the game's logic core responsible for actor movement, collision detection, game AI, path finding and field-of-view calculation reaches ~ 20.000 fps, where the client side (without rendering) shows up to ~140.000 fps.
Again, just to illustrate possibilites.

So far, this was Java 7 with Java2D for rendering (basically drawing everything with VolatileImage).
Some external libraries and tools are used to keep from reinventing too much wheels:
- collection classes from Trove, Javolution and Colt
- SoundSystem for 3D sounds
- JGoodies FormLayout for the Swing based game lobby
- JInput for game controller support
- Tiled map editor

Later additions were LibGDX, Box2DLights and LWJGL for nicer, more polished graphics based on OpenGL.

So, is it all nice, easy, fun and beautiful ? Certainly not, but where is it like that ?

:)