The demo application – time for code

In this section we will take a closer look at the actual code of the demo project. Thereafter, we will do some simple modifications to the code and also use the debugger.

Inspecting an example code of the demo application

Let us take a first look at the generated code of MyDemo.java from the demo project.

The following code listing shows the class definition:

public class MyDemo implements ApplicationListener {
  // ...
}

As you can see the MyDemo class implements the ApplicationListener interface. Before we move on to the implementation details of the interface, we will spend some time on the remaining part of this class.

You will find a definition of four member variables, each with a class provided by Libgdx.

private OrthographicCamera camera;
private SpriteBatch batch;
private Texture texture;
private Sprite sprite;

Here is a brief explanation of the classes from the preceding code listing to give you a basic background knowledge for the code inspection that will follow shortly.

The camera variable is of the class type OrthographicCamera. We will use the orthographic camera for displaying our 2D scenes. The camera is the player's view of the actual scene in the game which is defined by a certain width and height (also called viewport).

For more information about projections, check out the great article Orthographic vs. Perspective by Jeff Lamarche at http://iphonedevelopment.blogspot.de/2009/04/opengl-es-from-ground-up-part-3.html.

The batch variable is of the class type SpriteBatch. This is where you send all your drawing commands to Libgdx. Beyond the ability of this class to draw images, it is also capable of optimizing the drawing performance under certain circumstances.

The texture variable is of the class type Texture. It holds a reference to the actual image; the texture data that is stored in memory at runtime.

The sprite variable is of the class type Sprite. It is a complex data type that contains lots of attributes to represent a graphical object that has a position in 2D space, width, and height. It can also be rotated and scaled. Internally, it holds a reference to a TextureRegion class that in turn is a means to cut out a certain portion of a texture.

Now that we have a basic knowledge of the involved data types, we can advance to the implementation details of the ApplicationListener interface.

In the MyDemo class, the only methods containing code are create(), render(), and dispose(). The remaining three methods are left empty, which is just fine.

The create() method

This method contains the initialization code to prepare the application on startup:

@Override
public void create() {
  float w = Gdx.graphics.getWidth();
  float h = Gdx.graphics.getHeight();
  
  camera = new OrthographicCamera(1, h / w);
  batch = new SpriteBatch();
  
  texture = new Texture(Gdx.files.internal("data/libgdx.png"));
  texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
  
  TextureRegion region =
    new TextureRegion(texture, 0, 0, 512, 275);
  
  sprite = new Sprite(region);
  sprite.setSize(0.9f,
    0.9f * sprite.getHeight() / sprite.getWidth());
  sprite.setOrigin(sprite.getWidth() / 2,sprite.getHeight() / 2);
  sprite.setPosition(-sprite.getWidth() / 2,-sprite.getHeight() / 2);
}

At first, the graphics module is queried to return the width and height of the display (for example, a desktop window or the screen of an Android device) and calculate an appropriate width and height for the field of view of the camera. Then a new instance of SpriteBatch is created so that images can be drawn and made visible with the camera. The next step is to load a texture by using the files module to get a file handle to data/libgdx.png.

The loaded texture looks like the following screenshot:

As you can see, there is a lot of empty space in this image. In order to be able to use the filled part of this image only, a new instance of TextureRegion is created. It references the previously loaded texture that contains the full image and has the additional information to cut out all the pixels starting from (0, 0) to (512, 275). These two points describe a rectangle starting at the top-left corner of the image with a width and height of 512 by 275 pixels. Finally, a sprite is created using the information of the previously created texture region. The sprite's size is set to 90 percent of its original size. The sprite's origin is set to half of its width and height to move the origin to its center. Eventually, the position is set to the negative half of the sprite's width and height so that the sprite moves to the center of the scene.

Note

Libgdx uses a coordinate system which has its origin (0, 0) at the bottom-left corner. This means that the positive x axis points right while the positive y axis points up.

The render() method

This method contains the commands to render a scene on screen:

@Override
public void render() {
  Gdx.gl.glClearColor(1, 1, 1, 1);
  Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
  batch.setProjectionMatrix(camera.combined);
  batch.begin();
  sprite.draw(batch);
  batch.end();
}

The first two lines call low-level OpenGL methods to set the clear color to a solid white and then execute the clear screen command.

Next, the projection matrix of the sprite batch is set to the camera's combined projection and view matrix. You do not have to understand what this means in detail at the moment. It basically just means that every following drawing command will behave to the rules of an orthographic projection, or simply spoken drawing will be done in 2D space using the position and bounds of the given camera.

The methods begin() and end() will always have to appear in pairs and must not be nested or there will be errors. The actual drawing of the sprite is accomplished by calling the draw() method of the sprite to draw and pass the instance of the sprite batch.

The dispose() method

This is the place where you should clean up and free all resources that are still in use by an application:

@Override
public void dispose() {
  batch.dispose();
  texture.dispose();
}

There is an interface called Disposable that is implemented by every Libgdx class that allocates resources (that is, memory) and can be easily de-allocated by calling the corresponding dispose() method. In the preceding code, this is done for the sprite batch and the loaded texture.

The following is a complete listing of the MyDemo.java source file from the demo project:

package com.packtpub.libgdx.demo;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;

public class MyDemo implements ApplicationListener {
  private OrthographicCamera camera;
  private SpriteBatch batch;
  private Texture texture;
  private Sprite sprite;
  
  @Override
  public void create() {
    float w = Gdx.graphics.getWidth();
    float h = Gdx.graphics.getHeight();
    
    camera = new OrthographicCamera(1, h / w);
    batch = new SpriteBatch();
    
    texture = new Texture(Gdx.files.internal("data/libgdx.png"));
    texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
    
    TextureRegion region =
      new TextureRegion(texture, 0, 0, 512, 275);
    
    sprite = new Sprite(region);
    sprite.setSize(0.9f,
      0.9f * sprite.getHeight() / sprite.getWidth());
    sprite.setOrigin(sprite.getWidth() / 2,sprite.getHeight() / 2);
    sprite.setPosition(-sprite.getWidth() / 2,-sprite.getHeight() / 2);
  }
  
  @Override
  public void dispose() {
    batch.dispose();
    texture.dispose();
  }
  
  @Override
  public void render() {
    Gdx.gl.glClearColor(1, 1, 1, 1);
    Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
    batch.setProjectionMatrix(camera.combined);
    batch.begin();
    sprite.draw(batch);
    batch.end();
  }
  
  @Override
  public void resize(int width, int height) {
  }
  
  @Override
  public void pause() {
  }
  
  @Override
  public void resume() {
  }
}

Having fun with the debugger and Code Hot Swapping

In this section we are going to use the debugger to take a look inside the demo application at runtime. To do this, we first set a breakpoint where the execution of the application should be halted so that we can easily inspect the current state. Open the MyDemo.java source file in Eclipse and set a breakpoint at the line where a new instance of SpriteBatch is created.

Note

Double-click on the shaded, empty space at the very left side of the editor window in Eclipse to set or toggle already existing breakpoints, which will put blue dot to signify the breakpoint as shown in the preceding screenshot.

Next, right-click on the demo-desktop project in Project Explorer in Eclipse and then navigate to Debug As | Java Application. The application should be halted almost directly after the application window becomes visible. Eclipse should have automatically changed to the debugging perspective, which shows lots of extra information about an application running in the debug mode.

In the Variables tab, you can now inspect every variable that is within the current scope of execution. For example, the two floating-point variables w and h have already been set. You can check this by looking for them in the Variables tab. The correct values of the variables are displayed as 480.0 for w and 320.0 for h. To step through, resume, or stop the execution of the application, you can go to the Run menu and choose the appropriate menu item. Choose to resume the application for now.

Let us try to do Code Hot Swapping now. Make sure that the demo application is currently running and is being executed right now.

The following listing is a modified version of the render() method. The modification is highlighted:

@Override
public void render() {
  Gdx.gl.glClearColor(1, 1, 1, 1);
  Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
  batch.setProjectionMatrix(camera.combined);
  batch.begin();
 sprite.setRotation(45);
  sprite.draw(batch);
  batch.end();
}

Add the following line to your code inside the MyDemo.java source file right before sprite.draw() is called:

  sprite.setRotation(45);

It will make the sprite rotate by 45 degrees in a counter-clockwise direction. The next step is to save your changes to the source file. What you should see now is that the change you have just made to the code is immediately reflected in the still running application!

Note

For Code Hot Swapping to work, it is required that the automatic (re)build feature is enabled. You can quickly check this by going to the Project menu and making sure that the menu item Build Automatically is checked.

You might already sense the possibilities that this great feature enables a developer to do. Just think of a somewhat more complex scene where you are trying to find the best-looking positions for your objects, or you just want to see how it would look with a couple of different settings. It is a piece of cake with a tool like Code Hot Swapping at your disposal.

Let us take the preceding example a bit further and make the image rotate continuously.

We will need a variable to store the current rotation value. This value is going to be increased over time by adding lots of small deltas of time to it. To avoid a possible overflow in rot, we calculate the remainder of the new rotation value divided by 360 degrees. This can be done in an easy way by using the modulo operator (%) to wrap around a certain value.

The rotation is calculated in degrees per second. Afterwards, we set the new rotation value of the sprite and draw it while the rotation value is advanced step-by-step.

The following listing is the modified code for the rotating image:

private float rot;

@Override
public void render() {
  Gdx.gl.glClearColor(1, 1, 1, 1);
  Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
  
  batch.setProjectionMatrix(camera.combined);
  batch.begin();
 final float degressPerSecond = 10.0f; 
 rot = (rot + Gdx.graphics.getDeltaTime() * degressPerSecond) % 360;
 sprite.setRotation(rot);
  sprite.draw(batch);
  batch.end();
}

Now that we have a changing value for the rotation, let us have some more fun with it and turn the continuous rotation effect into a shake effect.

Since the Sine (or Cosine) function has an oscillating behavior, we can make perfect use of it to make the image shake by a certain amount to the left and right. The amount (amplitude) can be increased and decreased by multiplying it with the answer of the Sine function.

The following listing is the modified code for the shaking image:

@Override
public void render() {
  Gdx.gl.glClearColor(1, 1, 1, 1);
  Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
  batch.setProjectionMatrix(camera.combined);
  batch.begin();
  float degressPerSecond = 10.0f;
  rot = (rot + Gdx.graphics.getDeltaTime() * degressPerSecond) % 360;
 final float shakeAmplitudeInDegrees = 5.0f;
 float shake = MathUtils.sin(rot) * shakeAmplitudeInDegrees;
 sprite.setRotation(shake);
  sprite.draw(batch);
  batch.end();
}

The following diagram visualizes the effect of both the rotation and the shake: