LibGDX Tutorial 9: Scene2D Part 1

27. Tháng Mười Một 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 , ,







blog comments powered by Disqus
New Moai SDK release. 1.4p0

New Moai SDK release. 1.4p0

3. March 2013

 

After a 4 month pause, there has been a new Moai release.  They also removed sign up requirements to download the SDK ( which was misleading before, so nice move ) as well as added nightly builds for those that like to walk on the wild side.

 

The biggest new features are android keyboard support ( finally! ), better threading and off screen frame buffering.  The full changelog is below

 

tTerrainMask () to MOAIPathFinder; renamed setTerrainScale () to setTerrainWeight ()
* Some work has been done on threading. Check MOAIThread.
* added support for default texture; finished async load; samples for async texture load
  and default texture
* added support in MOAIDataBuffer to detect zip files, inflate on load; added inflate on load
  support to MOAIDataIOTask
* moai coding style guide in .doc format
* first phase of offscreen rendering architecture completed
* added setClearColor () to MOAILayer
* samples to test multiple viewports inside an offscreen buffer
* integrated sfmt random number generator
* Merged playnomi contribs to MOAIImage, MOAIImageTexture, MOAIParticleSystem and MOAIParticlePexPlugin

 


by Adam Villalobos ( https://github.com/adamvillalobos )
* Adding GCM support ( replacing C2DM ) to Android host.
* Adding file not found log if a texture file fails to load
* Fixing build errors for DataTaskIO
* Fixing issue with setting visibility not properly updating dependencies
* Fixing bug with Font reloading on Android
* Added Android keyboard support

 

by John Copic ( https://github.com/JohnCopic )
* Adding sfmt lib to android build.

 


by JoakimBlomberg ( https://github.com/JoakimBlomberg )
* Script node apply of attributes should unpack attribute id or it will not be applied

 

by Bogdan Tatarov ( https://github.com/btatarov )
* adding MOAIUnitzSound::_getFilename()

 


by mxttie ( https://github.com/mxttie )
* updated box2d physics sample adding mouse manipulation using mouse joint

 


by Tommo Zhou ( https://github.com/tommo )
* fix edge case that new-created-paused action cannot receive further update
* fix: MOAIImageTexture not override OnInvalidate

 


by Antonio ( https://github.com/antback )
* Initial Camera Support for iOS

 


by sporksmith ( https://github.com/sporksmith )
* Log file-open failures

 


by Clement Farabet ( https://github.com/clementfarabet )
* Fixed VSYNC issue for OSX host.
* Added missing files to iOS (XCode) project.

 


by Francisco Tufro ( https://github.com/franciscotufro )
* Several fixes in build systems for all platforms
* New release script in bin/distribute

 


by Stephen Belanger ( https://github.com/Qard )
* Recommended to enable this with C-Ares
http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTNOSIGNAL

 


by Ben Borowski ( https://github.com/typeoneerror )
* Adds call to FMOD update to iOS sample project.

 


by Robert Wahler ( https://github.com/robertwahler )
* GlutHost will run "main.lua" if host binary is called without arguments
* add Lua global 'arg' that mimics the lua/luajit built-in global 'arg' to GlutHost

 


by Alexey Zankevich ( https://github.com/Nepherhotep )
* MOAIJsonParser fix - checking if lua number is integer has been added
* Added EVENT_PAUSE, EVENT_RESUME events to MOAISim
* Fixed MOAIEnvironment.screenDpi nil issue for Android
* Fixed MOAIEnvironment.screenDpi nil issue for iOS host

 


by Seebs ( https://github.com/seebs )
* Clarify description of textbox color escapes, fixing one typo and reducing confusion
  between number of bits and number of values represented.
* build.sh: Terser output
* add (untested) support for controlling GL blend equations.

 


by ChairGraveyard ( https://github.com/ChairGraveyard )
* Add support for collideConnected flag to MOAIBox2DWorld::_addRopeJoint
by out-of-pixel ( https://github.com/out-of-pixel )
* Fixed last Key Error from MOAIAnimCurve

 


by superquadratic ( https://github.com/superquadratic )
* Fix Clang 4.2 (Xcode 4.6) Compiler Warnings

 

They also changed the file structure as follows:

bin/ Development binaries for Windows and MacOSX, Linux will be added soon
docs/ Documentation for this release and attribution images
hosts/ Source and IDE projects for the different platform's hosts
include/ Include headers
lib/ Compiled (static and/or dynamic) libraries for all the supported platforms
samples/ Moai SDK feature samples

 

Head on over to the Moai website to download.

News ,







blog comments powered by Disqus