Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon
18. November 2014

 

In this video tutorial, we create our first LibGDX project.  This tutorial covers that basic layout of a LibGDX application, adding new assets to a project and looks at how the initial project is composed and how multiple platforms are supported.  We then go through the initial code, then look in depth at coordinate systems, then move on to creating a sprite.  Finally we look at positioning, scaling and rotating a sprite with a SpriteBatch.

 

This tutorial assumes you already have your development environment setup and know how to run a LibGDX application in your chosen IDE.  If you do no, please watch these videos first.

 

The following is the video tutorial.  It was recorded in 1080p and the complete version can be viewed here.

 

 

The following is the final code generated:

 

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class HelloWorld extends ApplicationAdapter {
   SpriteBatch batch;
   Texture img;
   Sprite sprite;
   
   @Override
   public void create () {
      batch = new SpriteBatch();
      img = new Texture("HelloWorld.png");
      sprite = new Sprite(img);
      sprite.setPosition(
            Gdx.graphics.getWidth()/2 - sprite.getWidth()/2,
            Gdx.graphics.getHeight()/2 - sprite.getHeight()/2);
      sprite.setScale(1.0f,2.0f);
   }

   @Override
   public void render () {
      Gdx.gl.glClearColor(0, 0, 0, 1);
      Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
      batch.begin();
      batch.draw(sprite, sprite.getX(),sprite.getY(),sprite.getWidth()/2,
            sprite.getHeight()/2,sprite.getWidth(),
            sprite.getHeight(),sprite.getScaleX(),
            sprite.getScaleY(),sprite.getRotation());
      batch.end();
   }
}

 

Here is the image that was used in this tutorial:

HelloWorld

Programming


12. November 2014

 

The title pretty much says it all.  The installation process to get a fully working Java, Android and GWT development environment up and running can be a bit tricky at times.  These three videos walk through the entire process, from downloading and installing a Java JDK, then the Android SDK and GWT then finally creating your first LibGDX project.  The second video then looks at working with IntelliJ IDEA with LIbGDX, the third video shows the same thing for Eclipse.  Each of these videos walks through loading a project, then running a Desktop, Android then HTML5 project.  Each of the videos is hosted on YouTube and is available in 1080p.  Click the link above the video below to open it directly in YouTube.

 

Part 1: Configuring a Java Development Environment for LibGDX and Android Development

 

 

Part 2: Using IntelliJ IDEA with LibGDX

 

 

Part 3: Using Eclipse with LibGDX

 

Programming


3. November 2014

 

 

Now we are going to look at using particles in LibGDX.  This is a somewhat complex subject, so I am going to break it over a couple posts.  In this first example, we are going to create a 2D particle system in the editor.  Then we are going to write the code to run the particle system.  This process can be somewhat confusing, especially running the Particle Editor, so I have also done a video version of this tutorial.  Don’t worry, I will always continue to do text versions of all LibGDX tutorials, I am just going to mix in video when I think it will help.

 

So if you prefer, this topic is available in the video below.  Click directly on Youtube to see it in full resolution.  Once again, this video covers exactly the same content the text below does.

 

In this tutorial we are going to use one of the tutorials included with LibGDX, the particle editor.  In order to follow along you need to install the gdx-tools in your project.  Fortunately this is a pretty easy process.  When you run the setup utility to create your project, make sure you set tools to true.

 

image

 

This will result in the particle editor being included in your project dependencies.  Now it’s a matter of running the ParticleEditor, which isn’t as obvious as you would think.

 

You can run it from the command line, but this presents a certain special challenge, since LibGDX moved to a Gradle based setup…  so, where exactly is gdx.jar these days?  That’s a good question, and the answer is very very well hidden.

 

First make sure you’ve built your project at least once, so then all of the dependencies have been resolved ( AKA, Gradle downloaded LibGDX ).  Now LibGDX will be located somewhere in your Maven repository, which is a hidden folder.  The actual directory it will be in is random thanks to a GUID key.  On my computer on Windows its:

C:\Users\Mike\.gradle\caches\modules-2\files-2.1\com.badlogicgames.gdx\gdx-tools\1.4.1\SOMEREALLYLONGGUID

 

Obviously on your computer this will be somewhere else, and it will change with each version.  Now that you located it, you can run it.  Fortunately there is a much easier option for running the particle editor if you use either Eclipse or IntelliJ IDEA.  I will cover each in turn below ( and also show both in the video above ):

 

IntelliJ

 

Once you’ve loaded your project into IntelliJ IDEA.  Now in your Projects window, locate gdx-tools.jar ( there will be a version number too, currently 1.4.1 ).

 

image

 

Expand it until you find particleeditor, then locate the class ParticleEditor.

 

Now right click ParticleEditor and select Run ParticleEditor… main():

 

image

 

The first time you need to set some configuration values:

image

 

Really, all you need to do is set the Use classpath value to your desktop module.  Now click Run:

image

 

One working Particle Editor.

 

Eclipse

 

Eclipse is actually much simpler… if it works, which is a big if.

Simply import your project.  Then right click your desktop project, select Run As or Debug As->Java Application.

 

image

 

It should now prompt you to select which Java Application to run:

image

Select ParticleEditor – com.badlogic.gdx.tools.particleeditor and click OK.

The Particle Editor will now load.

 

Your First Particle Effect

 

Now that you’ve got the particle editor open, let’s save an effect to work with. 

In this particular tutorial we are just going to go with the default as it is already configured, which is a flame looking effect.  Locate and click the save button:

image

We want to save the resulting file to our assets folder.  If you have an Android project it will be located at [project]/android-proj/assets otherwise it will be in [project]core-proj/assets.  Save the file there, the extension doesn’t matter.  For this code example I am using particles.party.

 

Code Time

 

Now let’s take a look at the code required to display this effect:

 

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.ParticleEffect;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class Particles extends ApplicationAdapter {
   SpriteBatch batch;
   ParticleEffect pe;

   @Override
   public void create () {
      batch = new SpriteBatch();

      pe = new ParticleEffect();
      pe.load(Gdx.files.internal("Particles.party"),Gdx.files.internal(""));
      pe.getEmitters().first().setPosition(Gdx.graphics.getWidth()/2,Gdx.graphics.getHeight()/2);
      pe.start();
   }

   @Override
   public void render () {
      Gdx.gl.glClearColor(0, 0, 0, 1);
      Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

      pe.update(Gdx.graphics.getDeltaTime());
      batch.begin();
      pe.draw(batch);
      batch.end();
      if (pe.isComplete())
         pe.reset();
   }
}

 

When you run this app you should see:

 

Results

 

It’s pretty consistent with the way most other systems work in Android.  You create the ParticleEffect then load it from file at the root of your assets folder.  We then position the emitter at the center of the screen.  Particle emitters are the source of particles and are often used to control over all behavior.  We then start the particle system running.

 

In render, just like with physics systems, we need to advance the particle effect using update().  This causes the simulation to calculation the position, colour, etc… of all the particles it controls.  Next we draw the ParticleEffect using draw() and drawing to our sprite batch.   Finally we check to see if the effect is complete, and if it is, we restart it.

 

Obviously we glossed over the Particle Editor tool, as well as 3D particle effects, so expect more tutorials soon.

 

Programming


28. October 2014

 

A question came up in a comment in the Scene2D part of the LibGDX tutorial series about re-using actions.  You very much can re-use actions, so I decided to do it in post form here.  This entire post is mostly just one large code sample.  It’s just easier to do it here than in comments.  For a greater context of what this code is doing, see the earlier linked tutorial section.

 

Here is a sample showing action re-use.

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationListener;
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.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 Scenetest implements ApplicationListener, InputProcessor {


   public class MyActor extends Actor {
      Texture texture = new Texture(Gdx.files.internal("badlogic.jpg"));

      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;
   private MyActor myActor;
   MoveToAction moveToOrigin,moveToCenter;

   @Override
   public void create() {
      stage = new Stage();
      myActor = new MyActor();

      myActor.setPosition(Gdx.graphics.getWidth()/2 - myActor.getWidth()/2,
            Gdx.graphics.getHeight()/2 - myActor.getHeight()/2);
      
      moveToOrigin = new MoveToAction();
      moveToOrigin.setPosition(0f, 0f);
      moveToOrigin.setDuration(2f);
      

      moveToCenter = new MoveToAction();
      moveToCenter.setPosition(Gdx.graphics.getWidth()/2 - myActor.getWidth()/2,
            Gdx.graphics.getHeight()/2 - myActor.getHeight()/2);
      moveToCenter.setDuration(2f);

      myActor.addAction(moveToOrigin);
      stage.addActor(myActor);
      Gdx.input.setInputProcessor(this);
   }

   @Override
   public void dispose() {
   }

   @Override
   public void render() {
      Gdx.gl.glClear(GL20.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() {
   }


   @Override
   public boolean keyDown(int keycode) {
      if(keycode == Input.Keys.NUM_1)
         if(myActor.getActions().contains(moveToOrigin,true)) {
            // this is "A" currently running action, do nothing
            // If you wanted you could restart the action which 
            // would cause the duration to start over, like so:
            moveToOrigin.restart();
            // This action will now have a 2 second tween between 
            // its current location and target
         }
         else {
            moveToOrigin.reset();
            myActor.addAction(moveToOrigin);
         }
      if(keycode == Input.Keys.NUM_2)
         if(myActor.getActions().contains(moveToCenter,true)) {
            // this is "A" currently running action so do nothing
         }
         else {
            moveToCenter.reset();
            myActor.addAction(moveToCenter);
         }
      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) {
      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;
   }  
}

When you run this code, the graphic will start at the center of the screen and move towards the origin, with a total duration of 2 seconds.  If you press the 2 key, it will start an action to move back to the center of the screen.  Pressing 1 will move back to the origin.  Pressing 1 while a moveToOrigin is active will cause that action to restart, basically resetting the total duration back to 2 seconds again, just from your current position.

 

The only things to really be aware of here are that an Actor getActions() will only return actions that are currently run.  When the action is finished, it is removed from the Actor.  The other thing of note is, although you can reuse an Action, you will have to reset() it before you can add it again, or it will already be over.  If it is currently run, calling restart() has the same effect as resetting.

Programming


25. September 2014

 

Remember in our earlier example where we used a ContactListener to check for a collision between two types of FixtureDef and how ungainly the check was?  Remember when I said there was a much better way of doing this?  Well, welcome the the better way, Filters.

 

Filters are actually rather simple, except they use bitmasking, a concept that might be somewhat new to you.  I actually went into some detail on the use of bitmasks in this tutorial if you need some details.  Basically its about using individual bits in a number as “on/off” flags.  Basically it allows you to track multiple values in a single variable.  In Box2D, there are a pair of bitmask, the categoryBits and maskBits.  Those names can be a bit confusing but don’t worry about that.

 

Basically you can think of categoryBits as saying “This is what I am” and maskBits as “This is what I collide with”.  Keep in mind, you can set multiple bits for either, so you can both be multiple things and collide with multiple things.  That may still sound somewhat confusing, so let’s look at an example.  This is a modification of the code from the previous tutorial.  In this case, we want our two sprites to NOT collide with each other, but to collide with the edge that represents the ground in the world.

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.math.MathUtils;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.*;

public class Physics4 extends ApplicationAdapter {
    SpriteBatch batch;
    Sprite sprite,sprite2;
    Texture img;
    World world;
    Body body,body2;
    Body bodyEdgeScreen;

    Matrix4 debugMatrix;
    OrthographicCamera camera;

    final float PIXELS_TO_METERS = 100f;


    final short PHYSICS_ENTITY = 0x1;    // 0001
    final short WORLD_ENTITY = 0x1 << 1; // 0010 or 0x2 in hex

    @Override
    public void create() {
        batch = new SpriteBatch();
        img = new Texture("badlogic.jpg");

        // Create two identical sprites slightly offset from each other vertically
        sprite = new Sprite(img);
        sprite.setPosition(-sprite.getWidth()/2,-sprite.getHeight()/2 +200);
        sprite2 = new Sprite(img);
        sprite2.setPosition(-sprite.getWidth()/2 + 20,-sprite.getHeight()/2 + 400);

        world = new World(new Vector2(0, -1f),true);

        // Sprite1's Physics body
        BodyDef bodyDef = new BodyDef();
        bodyDef.type = BodyDef.BodyType.DynamicBody;
        bodyDef.position.set((sprite.getX() + sprite.getWidth()/2) /
                        PIXELS_TO_METERS,
                (sprite.getY() + sprite.getHeight()/2) / PIXELS_TO_METERS);

        body = world.createBody(bodyDef);


        // Sprite2's physics body
        BodyDef bodyDef2 = new BodyDef();
        bodyDef2.type = BodyDef.BodyType.DynamicBody;
        bodyDef2.position.set((sprite2.getX() + sprite2.getWidth()/2) /
                        PIXELS_TO_METERS,
                (sprite2.getY() + sprite2.getHeight()/2) / PIXELS_TO_METERS);

        body2 = world.createBody(bodyDef2);

        // Both bodies have identical shape
        PolygonShape shape = new PolygonShape();
        shape.setAsBox(sprite.getWidth()/2 / PIXELS_TO_METERS, sprite.getHeight()
                /2 / PIXELS_TO_METERS);

        // Sprite1
        FixtureDef fixtureDef = new FixtureDef();
        fixtureDef.shape = shape;
        fixtureDef.density = 0.1f;
        fixtureDef.restitution = 0.5f;
        fixtureDef.filter.categoryBits = PHYSICS_ENTITY;
        fixtureDef.filter.maskBits = WORLD_ENTITY;


        // Sprite2
        FixtureDef fixtureDef2 = new FixtureDef();
        fixtureDef2.shape = shape;
        fixtureDef2.density = 0.1f;
        fixtureDef2.restitution = 0.5f;
        fixtureDef2.filter.categoryBits = PHYSICS_ENTITY;
        fixtureDef2.filter.maskBits = WORLD_ENTITY;

        body.createFixture(fixtureDef);
        body2.createFixture(fixtureDef2);

        shape.dispose();

        // Now the physics body of the bottom edge of the screen
        BodyDef bodyDef3 = new BodyDef();
        bodyDef3.type = BodyDef.BodyType.StaticBody;

        float w = Gdx.graphics.getWidth()/PIXELS_TO_METERS;
        float h = Gdx.graphics.getHeight()/PIXELS_TO_METERS;

        bodyDef3.position.set(0,0);
        FixtureDef fixtureDef3 = new FixtureDef();
        fixtureDef3.filter.categoryBits = WORLD_ENTITY;
        fixtureDef3.filter.maskBits = PHYSICS_ENTITY;

        EdgeShape edgeShape = new EdgeShape();
        edgeShape.set(-w/2,-h/2,w/2,-h/2);
        fixtureDef3.shape = edgeShape;


        bodyEdgeScreen = world.createBody(bodyDef3);
        bodyEdgeScreen.createFixture(fixtureDef3);
        edgeShape.dispose();

        camera = new OrthographicCamera(Gdx.graphics.getWidth(),Gdx.graphics.
                getHeight());
    }

    @Override
    public void render() {
        camera.update();
        // Step the physics simulation forward at a rate of 60hz
        world.step(1f/60f, 6, 2);

        sprite.setPosition((body.getPosition().x * PIXELS_TO_METERS) - sprite.
                        getWidth()/2 ,
                (body.getPosition().y * PIXELS_TO_METERS) -sprite.getHeight()/2 );


        sprite.setRotation((float)Math.toDegrees(body2.getAngle()));
        sprite2.setPosition((body2.getPosition().x * PIXELS_TO_METERS) - sprite2.
                        getWidth()/2 ,
                (body2.getPosition().y * PIXELS_TO_METERS) -sprite2.getHeight()/2 );
        sprite2.setRotation((float)Math.toDegrees(body.getAngle()));

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

        batch.setProjectionMatrix(camera.combined);
        batch.begin();

        batch.draw(sprite, sprite.getX(), sprite.getY(),sprite.getOriginX(),
                sprite.getOriginY(),
                sprite.getWidth(),sprite.getHeight(),sprite.getScaleX(),sprite.
                        getScaleY(),sprite.getRotation());
        batch.draw(sprite2, sprite2.getX(), sprite2.getY(),sprite2.getOriginX(),
                sprite2.getOriginY(),
                sprite2.getWidth(),sprite2.getHeight(),sprite2.getScaleX(),sprite2.
                        getScaleY(),sprite2.getRotation());
        batch.end();
    }

    @Override
    public void dispose() {
        img.dispose();
        world.dispose();
    }
}

 

When a collision occurs, the collider will check it’s categoryBits against the collided’s maskBits.  If both are true, a collision occurs.  So in this example, both sprites will fall, completely ignoring each other.  However when they run in to the WORLD_ENTITY, a collision will occur.

 

Now, taking the same scenario, if you wanted to have the sprites collide with each other AND WORLD_ENTITY objects, you would simply change to

fixtureDef.filter.maskBits = WORLD_ENTITY|PHYSICS_ENTITY;

 

Now this object will have collisions with either kind of entity.

There is one other kind of filter in Box2D, and it's confusing as hell in my opinion. It's basically an override for grouping entities together called groupIndex.  I will let someone else explain how it works:

 

When checking two fixtures to see if they should collide:

  • if either fixture has a groupIndex of zero, use the category/mask rules as above
  • if both groupIndex values are non-zero but different, use the category/mask rules as above
  • if both groupIndex values are the same and positive, collide
  • if both groupIndex values are the same and negative, don't collide

 

The default value for the groupIndex is zero

 

So, if you want to have fine tune control over which entities interact with each other, Filter is how you do it.  Ok, let’s keep this part short and sweet.

Programming


GFS On YouTube

See More Tutorials on DevGa.me!

Month List