Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon
11. December 2013

 

So far we’ve look at what Scene2D provides in terms of Actors, Actions as well as handling input, now we will look at some of the scene management functionality it provides.  One of the very powerful capabilities of Scene2D is grouping.  Let’s jump right in with an example.  In this example I used these two graphics:

jet(look, there is a space here!)flame

 

By the way, those are two different images.  One is a jet and the second is the engine exhaust.  Let,s take a look at grouping them together in a scene as a single transformable entity, a pretty common game development task.

 

 

package com.me.mygdxgame;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Group;
import com.badlogic.gdx.scenes.scene2d.Stage;
import static com.badlogic.gdx.scenes.scene2d.actions.Actions.*;


public class SceneManagementDemo implements ApplicationListener {
    
    private Stage stage;
    private Group group;
    
    @Override
    public void create() {        
        stage = new Stage(Gdx.graphics.getWidth(),Gdx.graphics.getHeight(),true);
        final TextureRegion jetTexture = new TextureRegion(new Texture("data/jet.png"));
        final TextureRegion flameTexture = new TextureRegion(new Texture("data/flame.png"));
        
        final Actor jet = new Actor(){
            public void draw(Batch batch, float alpha){
                batch.draw(jetTexture, getX(), getY(), getOriginX(), getOriginY(), getWidth(), getHeight(), 
                        getScaleX(), getScaleY(), getRotation());
            }
        };
        jet.setBounds(jet.getX(), jet.getY(), jetTexture.getRegionWidth(), jetTexture.getRegionHeight());
    
        final Actor flame = new Actor(){
            public void draw(Batch batch, float alpha){
                batch.draw(flameTexture, getX(), getY(), getOriginX(), getOriginY(), getWidth(), getHeight(),
                        getScaleX(), getScaleY(), getRotation());
            }
        };
        flame.setBounds(0, 0, flameTexture.getRegionWidth(), flameTexture.getRegionHeight());
        flame.setPosition(jet.getWidth()-25, 25);
        
        group = new Group();
        group.addActor(jet);
        group.addActor(flame);
        
        group.addAction(parallel(moveTo(200,0,5),rotateBy(90,5)));
        
        stage.addActor(group);
        
        
    }

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

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

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

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }
}

 

When you run it, you will see:

SceneManagementCompressed

 

As you can see, once grouped, the attached actors inherit any transformations applied to the group.  The code in this example should be pretty straight forward at this point, not much new here.  First we load our two images as TextureRegions.  We then create an actor for each, in both cases setting it’s boundaries with setBounds() or it wont render correctly.  For each Actor we implement the full batch.draw() function to make sure rotation and scaling are properly rendered.  Finally for the flame texture, we set it’s position relative to the jet Actor.

 

Then we create a new Group object, this is the secret sauce behind Scene2D grouping.  Then instead of adding our two Actors to the Scene, we instead add them to the Group, which is then added to the Scene.  So that we can actually see something happening in this example, we apply a moveTo and rotateBy Action to our group.  We covered Actions in the last tutorial post if you want more details.  One important thing I didn’t show here is, it is possible to translate the individual Actors within the Group.

 

One other aspect of Scene2D is determining if a hit occurred.  Back in Scene2D Tutorial Part 1 we saw the touchDown function, which is called when an Actor within a Scene is touched.  Now we will briefly look at the logic driving this process.  The Stage’s hit() method is called, which in turn calls the hit() method of every Actor within the stage.  hit() passes in un-translated coordinates, making the process easier on the developer.  The default hit() method simply checks the bounding box of the Actor…  the following example instead checks the bounding circle… in case you had a say… circular object! Smile

 

package com.me.mygdxgame;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.Touchable;

import java.util.Random;

public class SceneManagementDemo implements ApplicationListener {
    
    // Create an Actor "Jet" that displays the TextureRegion passed in
    class Jet extends Actor {
        private TextureRegion _texture;
        
        public Jet(TextureRegion texture){
            _texture = texture;
            setBounds(getX(),getY(),_texture.getRegionWidth(), _texture.getRegionHeight());
            
            this.addListener(new InputListener(){
                public boolean touchDown(InputEvent event, float x, float y, int pointer, int buttons){
                    System.out.println("Touched" + getName());
                    setVisible(false);
                    return true;
                }
            });
        }

        // Implement the full form of draw() so we can handle rotation and scaling.
        public void draw(Batch batch, float alpha){
            batch.draw(_texture, getX(), getY(), getOriginX(), getOriginY(), getWidth(), getHeight(),
                    getScaleX(), getScaleY(), getRotation());
        }
        
        // This hit() instead of checking against a bounding box, checks a bounding circle.
        public Actor hit(float x, float y, boolean touchable){
            // If this Actor is hidden or untouchable, it cant be hit
            if(!this.isVisible() || this.getTouchable() == Touchable.disabled)
                return null;
            
            // Get centerpoint of bounding circle, also known as the center of the rect
            float centerX = getWidth()/2;
            float centerY = getHeight()/2;
            
            // Square roots are bad m'kay. In "real" code, simply square both sides for much speedy fastness
            // This however is the proper, unoptimized and easiest to grok equation for a hit within a circle
            // You could of course use LibGDX's Circle class instead.
            
            // Calculate radius of circle
            float radius = (float) Math.sqrt(centerX * centerX +
                    centerY * centerY);

            // And distance of point from the center of the circle
            float distance = (float) Math.sqrt(((centerX - x) * (centerX - x)) 
                    + ((centerY - y) * (centerY - y)));
            
            // If the distance is less than the circle radius, it's a hit
            if(distance <= radius) return this;
            
            // Otherwise, it isnt
            return null;
        }
    }
    
    private Jet[] jets;
    private Stage stage;
    
    @Override
    public void create() {        
        stage = new Stage(Gdx.graphics.getWidth(),Gdx.graphics.getHeight(),true);
        final TextureRegion jetTexture = new TextureRegion(new Texture("data/jet.png"));
        
        jets = new Jet[10];
        
        // Create/seed our random number for positioning jets randomly
        Random random = new Random();
        
        // Create 10 Jet objects at random on screen locations
        for(int i = 0; i < 10; i++){
            jets[i] = new Jet(jetTexture);
            
            //Assign the position of the jet to a random value within the screen boundaries
            jets[i].setPosition(random.nextInt(Gdx.graphics.getWidth() - (int)jets[i].getWidth())
                    , random.nextInt(Gdx.graphics.getHeight() - (int)jets[i].getHeight()));
            
            // Set the name of the Jet to it's index within the loop
            jets[i].setName(Integer.toString(i));
            
            // Add them to the stage
            stage.addActor(jets[i]);
        }
        
        Gdx.input.setInputProcessor(stage);
    }

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

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

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

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }
}

 

 

When you run this app, 10 randomly located jet images will be drawn on screen. 

 

SceneManagement2Compressed

 

As you click each, it will disappear.  One important thing to realize is hit() is evaluated in reverse order objects are created.  So, the last object you add to the stage will be the first one hit() if more than one object occupy the same space.  The determining function whether a Jet has been touched or not is hit().  hit() works by creating a bounding circle to fit the image within, then checking to see if the mouse pointer is within the bounds of that circle.  When a hit occurs, we return the object hit, otherwise we return null.

 

The nice thing about this system is the user doesn’t have to worry about any translations applied to it’s parent or other translations that have occurred.  It’s also important to realize this is a pretty derived example.  If you removed the overriden hit() method, the default implementation would actually work better.  You do NOT need to provide a hit() method in your derived Actor classes unless the default doesn’t fit your needs.  This example is merely to show how the Scene2D hit detection works, and how to implement a custom detector.  Should you wish to say, implement pixel-perfect detection, you could do it this way.  I commented this example a bit more than I regularly do, to explain the bits I’ve glossed over.

Programming


9. December 2013

 

Back in Tutorial 3 I created a simple animation using a Timer and setting the frame manually from a TextureAtlas.  This is not the ideal way to perform animation using LibGDX and was done to illustrate how to use a TextureAtlas, not how to perform animation.  Instead the better way to perform animations is using the Animation class.  Here is an example using the same spritesheet from tutorial 3, remember you need to add it to the Android project assets folder.

 

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;

public class AnimationDemo implements ApplicationListener {
    private SpriteBatch batch;
    private TextureAtlas textureAtlas;
    private Animation animation;
    private float elapsedTime = 0;
    
    @Override
    public void create() {        
        batch = new SpriteBatch();
        textureAtlas = new TextureAtlas(Gdx.files.internal("data/spritesheet.atlas"));
        animation = new Animation(1/15f, textureAtlas.getRegions());
    }

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

    @Override
    public void render() {        
        Gdx.gl.glClearColor(0, 0, 0, 1);
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
        
        batch.begin();
        //sprite.draw(batch);
        elapsedTime += Gdx.graphics.getDeltaTime();
        batch.draw(animation.getKeyFrame(elapsedTime, true), 0, 0);
        batch.end();
    }

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

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }
}

When you run it, you should see:

Animation

 

The key here is the Animation, created here:

animation = new Animation(1/15f, textureAtlas.getRegions());

When creating an animation you pass in the amount of time per frame ( 15 frames per second in this case ) and an array of TextureRegions.  These represent the individual frames of animation within the TextureAtlas.  In this particular example we are simply using all of the frames available within the Atlas as a single animation.  The next key change is:

elapsedTime += Gdx.graphics.getDeltaTime();

batch.draw(animation.getKeyFrame(elapsedTime, true), 0, 0);

Here we are simply drawing the current frame from the animation to the screen.  We pass in the amount of time that has elapsed so the animation knows where it is in the sequence.  The true parameter is telling it to loop the animation.

 

Of course, you can have multiple animations from a single texture Atlas.  Consider the spritesheet we are currently working with.  If you take a look at the .atlas file, you will see each individual frame is named:

image

 

Looking at the spritesheet, you will see the animations are organized like such:

spritesheet

 

What we want to do is treat this as two sepeate animations.  Frames 1 through 10 represent an upward roll, while 11-20 are a downward roll.  Let’s take a look at how we do that:

public void create() {        
    batch = new SpriteBatch();
    textureAtlas = new TextureAtlas(Gdx.files.internal("data/spritesheet.atlas"));
    
    TextureRegion[] rotateUpFrames = new TextureRegion[10];
    
    // Rotate Up Animation
    // Create an array of TextureRegions
    rotateUpFrames[0] = (textureAtlas.findRegion("0001"));
    rotateUpFrames[1] = (textureAtlas.findRegion("0002"));
    rotateUpFrames[2] = (textureAtlas.findRegion("0003"));
    rotateUpFrames[3] = (textureAtlas.findRegion("0004"));
    rotateUpFrames[4] = (textureAtlas.findRegion("0005"));
    rotateUpFrames[5] = (textureAtlas.findRegion("0006"));
    rotateUpFrames[6] = (textureAtlas.findRegion("0007"));
    rotateUpFrames[7] = (textureAtlas.findRegion("0008"));
    rotateUpFrames[8] = (textureAtlas.findRegion("0009"));
    rotateUpFrames[9] = (textureAtlas.findRegion("0010"));

    rotateUpAnimation = new Animation(0.1f,rotateUpFrames);
    
    // Rotate Down Animation
    // Or you can just pass in all of the regions to the Animation constructor
    rotateDownAnimation = new Animation(0.1f,
            (textureAtlas.findRegion("0011")),
            (textureAtlas.findRegion("0012")),
            (textureAtlas.findRegion("0013")),
            (textureAtlas.findRegion("0014")),
            (textureAtlas.findRegion("0015")),
            (textureAtlas.findRegion("0016")),
            (textureAtlas.findRegion("0017")),
            (textureAtlas.findRegion("0018")),
            (textureAtlas.findRegion("0019")),
            (textureAtlas.findRegion("0020")));

    Gdx.input.setInputProcessor(this);
}

 

As you can see, it’s easy to create multiple animations from a single TextureAtlas.  Keep in mind, when creating a TextureAtlas using TexturePacker, it was the filename that you passed in that created the region names.  So what you would generally do is name your separate animations accordingly, such as WalkLeft, WalkRight, etc. 

Programming


9. December 2013

 

In the second part of the LibGDX Scene2D tutorial we will now look at Actions.  Actions are a convenient ( and completely optional! ) way to get your game’s Actors to “do stuff”.

 

Let’s jump straight in to an example:

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.actions.MoveToAction;


public class SceneDemo3 implements ApplicationListener {
    
    public class MyActor extends Actor {
        Texture texture = new Texture(Gdx.files.internal("data/jet.png"));
        public boolean started = false;

        public MyActor(){
            setBounds(getX(),getY(),texture.getWidth(),texture.getHeight());
        }
        
        @Override
        public void draw(Batch batch, float alpha){
            batch.draw(texture,this.getX(),getY());
        }
    }
    private Stage stage;
    
    @Override
    public void create() {        
        stage = new Stage();
        Gdx.input.setInputProcessor(stage);
        
        MyActor myActor = new MyActor();
        
        MoveToAction moveAction = new MoveToAction();
        moveAction.setPosition(300f, 0f);
        moveAction.setDuration(10f);
        myActor.addAction(moveAction);
        
        stage.addActor(myActor);
    }

    @Override
    public void dispose() {
    }

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

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

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }
}

 

When you run this example, it will slowly move the jet until it is at the position 300,0.

MoveToActionReduced

 

Much of this code is recycled from the previous Scene2D example, so we will only discuss the new portions, which reside in the create() function:

@Override
public void create() {        
    stage = new Stage();
    Gdx.input.setInputProcessor(stage);
    
    MyActor myActor = new MyActor();
    
    MoveToAction moveAction = new MoveToAction();
    moveAction.setPosition(300f, 0f);
    moveAction.setDuration(10f);
    myActor.addAction(moveAction);
    
    stage.addActor(myActor);
}

Here we create a MoveToAction, which moves the attached Actor to a given position over time.  You set the position to move to with the method setPosition() and the total duration of the action using setDuration().  You assign the action to the Actor using addAction().  All actions are derived from the Action class, while MoveToAction is derived from TemporalAction, which is an action that has a duration.  In addition to MoveToAction, there are a number of others such as MoveByAction, ScaleToAction, ColorAction, DelayAction, RepeatAction, RotateByAction, etc.

 

There is one important thing to be aware of here.  Back in part one of the Scene2D tutorial our custom Actor MyActor overrode the act method.  It is the act() method of Actor that updates all of the Actions connected to an Actor.  Therefore if you create your own Actor class, be sure to call it’s parent’s act() or call each attached Action manually like so:

@Override
public void act(float delta){
    for(Iterator<Action> iter = this.getActions().iterator(); iter.hasNext();){
        iter.next().act(delta);
    }
}

If you run more than one actions like so:

MoveToAction moveAction = new MoveToAction();
RotateToAction rotateAction = new RotateToAction();
ScaleToAction scaleAction = new ScaleToAction(); 

moveAction.setPosition(300f, 0f);
moveAction.setDuration(5f);
rotateAction.setRotation(90f);
rotateAction.setDuration(5f);
scaleAction.setScale(0.5f);
scaleAction.setDuration(5f);

myActor.addAction(moveAction);
myActor.addAction(rotateAction);
myActor.addAction(scaleAction);

stage.addActor(myActor);

 

You will see:

RotateScaleMoveActionReduced

As you can see, all of your actions run concurrently by default.

 

There is one important thing to be aware of.  The draw() method in MyActor we’ve been using until this point is not capable of displaying rotation or scaling by default.  If you wish to enabled scaled/rotated drawing like above, you need to make a minor adjustment to draw(), like so:

 

@Override
public void draw(Batch batch, float alpha){
    batch.draw(texture,this.getX(),getY(),this.getOriginX(),this.getOriginY(),this.getWidth(),
            this.getHeight(),this.getScaleX(), this.getScaleY(),this.getRotation(),0,0,
            texture.getWidth(),texture.getHeight(),false,false);
}

 

Obviously there are going to be many times when you want to delay an action, or run them in sequence.  Fortunately LibGdx supports exactly this.  Say you wanted to scale then rotate then move your Actor, you can accomplish this using a SequenceAction like so:

 

final MyActor myActor = new MyActor();

SequenceAction sequenceAction = new SequenceAction();

MoveToAction moveAction = new MoveToAction();
RotateToAction rotateAction = new RotateToAction();
ScaleToAction scaleAction = new ScaleToAction(); 

moveAction.setPosition(300f, 0f);
moveAction.setDuration(5f);
rotateAction.setRotation(90f);
rotateAction.setDuration(5f);
scaleAction.setScale(0.5f);
scaleAction.setDuration(5f);

sequenceAction.addAction(scaleAction);
sequenceAction.addAction(rotateAction);
sequenceAction.addAction(moveAction);


myActor.addAction(sequenceAction);

stage.addActor(myActor);

 

Simply add all the actors to the SequenceAction, then one will run, followed by the next then the next.

By statically importing Actions, like so:

import static com.badlogic.gdx.scenes.scene2d.actions.Actions.*;

You can also chain actions, so it’s possible to express the above action like:

myActor.addAction(sequence(scaleTo(0.5f,0.5f,5f),rotateTo(90.0f,5f),moveTo(300.0f,0f,5f)));
stage.addActor(myActor);

 

Or run in parallel, like so:

myActor.addAction(parallel(scaleTo(0.5f,0.5f,5f),rotateTo(90.0f,5f),moveTo(300.0f,0f,5f)));
stage.addActor(myActor);

There are several other actions, such as Int or FloatAction, that allow you to modify a value over time, DelayAction for running an action, um, after a delay, RepeatAction, for performing an Action over and over.  However, they all follow the same basic layout, so you should be able to figure them out easily at this point.  One last thing to point out, all Actions are poolable.  This means you can keep a pool of actions instead of allocating them over and over.  Here is one such example of a Pool of MoveToActions:

final MyActor myActor = new MyActor();
Pool<MoveToAction> actionPool = new Pool<MoveToAction>(){
    protected MoveToAction newObject(){
        return new MoveToAction();
    }
};

MoveToAction moveAction = actionPool.obtain();
moveAction.setDuration(5f);
moveAction.setPosition(300f, 0);

myActor.addAction(moveAction);

stage.addActor(myActor);

 

You can also have an action simply be code.  Consider the above example, let’s say you wanted to log when the MoveToAction was complete.  You could accomplish this by doing the following:

myActor.addAction(sequence(moveAction,
        run(new Runnable(){
            @Override
            public void run() {
                Gdx.app.log("STATUS", "Action complete");
            }
            
})));

 

A Runnable action simply runs the code specified in the run method when, um, run. That’s about it for Actions.  On their own they are pretty simply, but can easily be chained to create very complex interactions.

Programming


1. December 2013

 

I make no effort to disguise my dislike for Eclipse yet when working with LibGDX Eclipse is the path of least resistance. Fortunately there are other IDEs out there that can work with LibGDX, IntelliJ is my favorite of the options available. 

 

First off, you should be aware that there are currently some key limitations with the Gradle install.  Right now there is no support for GWT ( HTML5 ) or iOS projects.  So if those targets are important you should stick with Eclipse for now.

 

OK, let’s jump right in.  There are a couple things you need to have installed already.  First is a Java JDK, the second is the Android SDK ( not the ADT version! ).  I am not going into detail about how to install these, I simply assume you already have.  If you haven’t already, be sure to download and install IntelliJ IDEA, the free version has everything you need.

 

Installing the template

 

Now you need the LibGDX Gradle Template.  If you have git installed and configured ( you should! ) you can clone the template.  Open a command prompt or teminal, change to the directory you want your project to reside and run the command:

git clone https://github.com/libgdx/libgdx-gradle-template.git

It should run like so:

image

 

Alternately, you can download from github as a zip archive, download and then extract the contents to where you want the project to reside.

 

Now we will install gradle, this process is entirely automated.  In an elevated permission command prompt, change into the directory you just cloned/copied.  Now run the command:

gradlew clean

This will take a bit of time as it downloads and configured gradle and all the dependencies on your project.  Ideally you will see:

image

Now let’s make sure things worked right.  Run the command

gradlew desktop:run

 

Assuming everything went right, you should see:

image

 

Now generate a Intellij project.  Run the command

gradlew idea

This will create an IntelliJ project file with a .ipr extension.

image

 

Configuring IntelliJ IDEA

 

Load IntelliJ IDEA.

At the Quick Start Window, select Open Project

image

 

Navigate to the IPR file then select OK.

image

 

Configure the desktop project

 

Your Project should ( after you click the Project Tab ), look like this:

image

 

Let’s configure the desktop project first.  Select desktop in the project view and hit F4 or right click and select Open Module Settings:

image

 

Since you’ve never configured a JDK, you need to do that first.  In the future you shouldn’t have to do this step.  In the resulting dialog, select Project, then New… on the right side:

image

 

Select JDK in the drop down.  In the next dialog, navigate to and select the directory where you installed the Java JDK, then click OK.

image

 

Now that the Desktop project is configured, we need to create a Run or Debug configuration.  In the Run menu, select either Run… or Debug…

image

 

A menu will pop up, select Edit Configurations…

image

 

In the next dialog, click the + icon:

image

 

In the drop down, select Application:

image

 

Now we need to fill out the form.  You can optionally name your configuration, I went with Debug Desktop.  Next select “Use Classpath of module” and select Desktop.  In working directory, choose the assets folder in the Android project.  Click the … button to the right of Main Class and choose DesktopLauncher.  Finally click Apply then Debug.

image

 

If everything went right you should see:

image

 

Configure the Android Project

 

Now lets take a look at configuring the Android project, it’s a very similar process.

Right Click the Android project and select Open Module Settings.

Select Project, New->Android SDK

image

 

Browse to where you installed the Android SDK then click OK:

image

Pick whatever Java version and Android target you want.  Keep in mind, you need to install the SDKs as part of the Android SDK installation process:

image

 

Click OK, then Apply.

 

Now you can create a Debug or Run configuration for Android.  Select Run->Debug… or Run->Run…

Select Edit Configuration…

Click the + Icon, then select Android Application:

image

 

Now configure your run/debug configuration.  Name it, select Android from the Module pull down, pick what you want to target ( run the emulator vs run on a device ).  Finally click apply then Debug.

image

 

Once you’ve created a Run configuration, you can run your various different projects using this pull down:

image

 

 

Renaming your project

 

It is possible there is a much easier way to do this as part of the Gradle process ( I am certainly no Gradle expert! ) but once you have your project running, you probably want to rename it to something you want.  This means altering directories to match your new naming convention.  IntelliJ makes this fairly simple.

 

In the Project setting, select the Gear icon, then disable Compact Empty Middle Packages.

image

 

 

In this case, instead of com.badlogic.gradletest, I want it to be com.gamefromscratch.gradletest.

In the core project, select badlogic, right click and select Refactor->Rename…

image

 

Select Rename All

image

 

Select your new name, then click Refactor:

image

 

Now repeat the process in the Android folder, select and refactor badlogicgames.

image

 

This time select Rename Directory

image

Once again select the new value you want then click Refactor.

 

Finally locate AndroidManifest.xml and update the package name there as well

image

 

 

A world of note, refactoring wont update your project Run Configuration.  If you rename the project after creating a Run Configuration, you will see:

image

 

This is easily fixed, simply select Run->Edit Configurations:

image

 

Select your Desktop configuration and updated the Main Class to your newly renamed value:

image

 

… now you are up and running in IntelliJ.  It’s a bit more work, but certainly worth it in the end, especially if you dont need GWT or iOS support.  Hopefully those get added soon!

Programming


27. November 2013

 

In this section we are going to take a look at the Scene2D library.  The first thing you need to be aware of is scene2d is entirely optional!  If you don’t want to use it, don’t.  All the other parts, except the bits built over Scene2D, will continue to work just fine.  Additionally if you want to use Scene2D for parts of your game ( such as a HUD overlain over your game ) you can.

 

So, what is scene2D?  In a nutshell, it’s a 2D scene graph.  So you might be asking “what’s a scene graph?”.  Good Question!  Essentially a scene graph is a data structure for storing the stuff in your world.  So if your game world is composed of dozens or hundreds of sprites, those sprites are stored in the scene graph.  In addition to holding the contents of your world, Scene2D provides a number of functions that it performs on that data.  Things such as hit detection, creating hierarchies between game objects, routing input, creating actions for manipulating a node over time, etc.

 

You can think of Scene2D as a higher level framework for creating a game built over top of the LibGDX library.  Oh it also is used to provide a very good UI widget library… something we will discuss later.

 

The object design of Scene2D is built around the metaphor of a play ( or at least I assume it is ).  At the top of the hierarchy you have the stage.  This is where your play (game) will take place.  The Stage in turn contains a Viewport… think of this like, um… camera recording the play ( or the view point of someone in the audience ).  The next major abstraction is the Actor, which is what fills the stage with… stuff.  This name is a bit misleading, as Actor doesn’t necessarily mean a visible actor on stage.  Actors could also include the guy running the lighting, a piece of scenery on stage, etc.  Basically actors are the stuff that make up your game.  So basically, you split your game up into logical Scenes ( be it screens, stages, levels, whatever makes sense ) composed of Actors.  Again, if the metaphor doesn’t fit your game, you don’t need to use Scene2D.

 

So, that’s the idea behind the design, let’s look at a more practical example.  We are simply going to create a scene with a single stage and add a single actor to it.

It’s important to be using the most current version of LibGDX, as recent changes to Batch/SpriteBatch will result in the following code not working!

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;

public class SceneDemo implements ApplicationListener {
    
    public class MyActor extends Actor {
        Texture texture = new Texture(Gdx.files.internal("data/jet.png"));
        @Override
        public void draw(Batch batch, float alpha){
            batch.draw(texture,0,0);
        }
    }
    
    private Stage stage;
    
    @Override
    public void create() {        
        stage = new Stage(Gdx.graphics.getWidth(),Gdx.graphics.getHeight(),true);
        
        MyActor myActor = new MyActor();
        stage.addActor(myActor);
    }

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

    @Override
    public void render() {    
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
        stage.draw();
    }

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

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }
}

 

 

Also note, I also added the jet image I used in earlier examples to the assets folder as a file named jet.png.  See the earlier tutorials if you are unsure of how to do this.  When you run the application you should see:

image

 

As you can see it’s fairly simple process working with Stage2D.  We create an embedded Actor derived class named MyActor.  MyActor simply loads it’s own texture from file.  The key part is the draw() method.  This will be called every frame by the stage containing the actor.  It is here you draw the actor to the stage using the provided Batch.  Batch is the interface that SpriteBatch we saw earlier implements and is responsible for batching up drawing calls to OpenGL.  In this example we simply draw our Texture to the batch at the location 0,0.  Your actor could just as easily be programmatically generated, from a spritesheet, etc.  One thing I should point out here, this example is for brevity, in a real world scenario you would want to manage things differently, as every MyActor would leak it’s Texture when it is destroyed!

 

In our applications create() method we create our stage passing in the app resolution.  The true value indicates that we want to preserve our devices aspect ratio.  Once our stage is created, we create an instance of MyActor and add it to the stage with a call to stage.addActor().  Next up in the render() function, we clear the screen then draw the stage by calling the draw() method.  This in turn calls the draw() method of every actor the stage contains.  Finally you may notice that we dispose of stage in our app’s dispose() call to prevent a leak.

 

So, that is the basic anatomy of a Scene2D based application.  One thing I didn’t touch upon is actually having actors do something or how you would control one.  The basic process is remarkably simple with a couple potential gotchas.  Let’s look at an updated version of this code, the changes are highlighted:

 

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.Touchable;

public class SceneDemo2 implements ApplicationListener {
    
    public class MyActor extends Actor {
        Texture texture = new Texture(Gdx.files.internal("data/jet.png"));
        float actorX = 0, actorY = 0;
        public boolean started = false;

        public MyActor(){
            setBounds(actorX,actorY,texture.getWidth(),texture.getHeight());
            addListener(new InputListener(){
                public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
                    ((MyActor)event.getTarget()).started = true;
                    return true;
                }
            });
        }
        
        
        @Override
        public void draw(Batch batch, float alpha){
            batch.draw(texture,actorX,actorY);
        }
        
        @Override
        public void act(float delta){
            if(started){
                actorX+=5;
            }
        }
    }
    
    private Stage stage;
    
    @Override
    public void create() {        
        stage = new Stage();
        Gdx.input.setInputProcessor(stage);
        
        MyActor myActor = new MyActor();
        myActor.setTouchable(Touchable.enabled);
        stage.addActor(myActor);
    }

    @Override
    public void dispose() {
    }

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

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

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }
}

 

When you run it you will see:

scene2

Click the jet sprite and it’s action will start.  Let’s take a closer look at the code now.

 

Let’s start with the changes we made to the MyActor class.  The most obvious change you will see is the addition of a constructor.  I did this so I could add an event listener to our actor, that works a lot like event listeners we worked with earlier when dealing with input.  This one however passes an InputEvent class as a parameter which contains the method getTarget(), which is the Actor that was touched.  We simply cast it to a MyActor object and set the started boolean to true.  One other critical thing you may notice is the setBounds() call.  This call is very important!  If you inherit from Actor, you need to set the bounds or it will not be click/touch-able!  This particular gotcha cost me about an hour of my life.  Simply set the bounds to match the texture your Actor contains.  Another thing you may notice is a lot of the examples and documentation on Actor event handling is currently out of date and there were some breaking changes in the past!

 

Other than the constructor, the other major change we made to MyActor was the addition of the act() method.  Just like with draw(), there is an act() method that is called on every actor on the stage.  This is where you will update your actor over time.  In many other frameworks, act would instead be called update().  In this case we simply add 5 pixels to the X location of our MyActor each frame.  Of course, we only do this once the started flag has been set.

 

In our create() method, we made a couple small changes.  First we need to register an InputProcessor.  Stage implements one, so you simply pass the stage object to setInputProcessor().  As you saw earlier, the stage handles calling the InputListeners of all the child actors.  We also set the actor as Touchable, although I do believe this is the default behavior.  If you want to make it so an actor cannot be touched/clicked, pass in Touchable.disabled instead.  The only other change is in the render() method, we now call stage.act() passing in the elapsed time since the previous frame.  This is is what causes the various actors to have their act() function called.

 

Scene2D is a pretty big subject, so I will be dealing with it over several parts.

Programming


AppGameKit Studio

See More Tutorials on DevGa.me!

Month List