Ashley 1.7.2 released

ashley-small

Small Ashley release, check the changes for 1.7.2 below:

  • Bug fix: Engine doesn’t use EntitySystem iterator().
  • Bug fix: Fixes Engine left unusable, in the updating state, after an uncaught exception in a subsystem.
  • Bug fix: Fixes FamilyManager not cleaning up properly when a triggering a listener that throws.

To use it, change your dependency to com.badlogicgames.gdx:ashley:1.7.2. The new nightly dependency is com.badlogicgames.gdx:ashley:1.7.3-SNAPSHOT.

Read More

Built-in console for Libgdx games

A lot of games come with a built-in console that lets you enter commands, which is especially useful during development. Most times, it gets disabled before shipping although, occasionally, devs let you bring it up and enter cheat codes.

CryEngine comes with a built-in console.

CryEngine comes with a handy in-game console

When you’re testing a new feature you just wrote, it can be incredibly useful to pause, reset the scene, teleport your character, become invincible, switch to a different level or even adjust some parameters in the AI. Some engines use a scripting language that can hot-reload new or modified scripts, but that may not always be feasible.

Having a built-in console to enter these commands surely is pretty darn handy!

It would be reasonably easy to write a custom console with Libgdx, you just need to leverage Scene2D and have some logic to support arbitrary commands and let the game code handle them.

But why do that when you have libgdx-inGameConsole? No reason at all!

It’s incredibly simple to set it up. First, add the following dependency to your build.gradle file and refresh your project’s dependencies.

compile "com.strongjoshua:libgdx-inGameConsole:0.3.2"

You can instantiate the Console inside your create() method. Use the setSizePercent() and setPositionPercent() methods to configure where your console will show up and how much of the screen it’ll take. Finally, you can also override the key to toggle the console.

private Console console;

@Override
public void create() {
    ...

    console = new Console();
    console.setSizePercent(100, 33);
    console.setPositionPercent(0, 67);
    console.setKeyID(Keys.Z);
}

Remember to call draw() inside your render() method.

console.draw();

Wonderful, we now have a working in-game console capable of doing absolutely nothing. We need to add our command processing logic. Let’s specialize the CommandExecutor class and add some methods to load a level and spawn baddies.

public class GameCommandExecutor extends Command {
    private Console console;

    public GameCommandExecutor(Console console) {
        this.console = console;
    }

    public void loadLevel(String levelName) {
        console.log("loading level " + levelName, Console.LogLevel.SUCCESS);
    }

    public void spawnEnemy(String type, float x, float y, float z) {
        console.log("spawning enemy " + type, Console.LogLevel.SUCCESS);
    }
    
    public void clear() {
        console.clear();
    }
}

At some point, we need to register the executor with the console.

console.setCommandExecutor(new GameCommandExecutor(console));

Done, that’s it!

javaw 2016-02-20 16-22-02-84

The console uses the Libgdx reflection module to figure out which commands your CommandExecutor supports, which means it’s fully compatible with the WebGL backend.

It’s also possible to configure the look and feel of the console if you provide a Skin to its constructor. To know more, check its comprehensive documentation.

Read More

2D vision system with Ashley and Box2D

javaw 2016-01-22 22-59-09-54

In Sloppynauts the player had to remain undetected, avoiding CCTV cameras and alien baddies. We constantly had to determine who could see who and whether the player was hidden behind something.

We wrote a nice reusable system using Ashley and Box2D and I think it’d be a shame if it went to waste. So here it is in case you’d like to use it.

This 2D vision system is generic enough to work with both side-scrolling and top-down games. Surely it can be further optimised and tailored to your needs… Hey, it was done for a game jam! Nevertheless, it could be a decent starting point!

Before we get down to business, make sure you understand what Ashley is and what component based entity systems are all about.

Vision System concepts

We want to give some of the entities in our game world the ability to see other entities. Both observers and observables will necessarily have a location in our game world. However, we need some extra information about our observers, specifically the area they can cover at any given point in time, ie. their field of view.

In the diagram below you can see a couple of observers and three observables. One of the observables can be seen, the second one is complete outside of both FOVs whilst the third one is hidden behind a box.

vision system diagram

We simply want to ask our system: “can this entity see this other entity?”

Observable and Observer components

Our Observable component is pretty trivial, it simply has a position.

public class ObservableComponent implements Component {
    public Vector2 position = new Vector2();
}

The Observer component has a little more information.

public class ObserverComponent implements Component {
    public Vector2 position = new Vector2();
    public float angle = 0.0f;
    public float distance = 5.0f;
    public float fovAngle = 45.0f;
}
  • angle: where the entity is looking at.
  • distance: how far it can see.
  • fovAngle: the angle it can cover.

Members are initialised with default values, you can obviously change these.

The Vision Entity System

The VisionSystem is where the magic happens, you can see the full outline below.

public class VisionSystem extends IteratingSystem implements EntityListener {
    public VisionSystem(World world) {}
    
    public boolean canSee(Entity observer, Entity observable) {}

    // EntitySystem
    public void addedToEngine(Engine engine) {}
    public void removedFromEngine(Engine engine) {}
    
    // EntityListener
    public void entityAdded(Entity entity) {}
    public void entityRemoved(Entity entity) {}

    // IteratingSystem
    protected void processEntity(Entity observer, float deltaTime) {}
    
    // Utility
    private void updateVision(Entity observer) {}
    private void updateVision(Entity observer, Entity observable) {}
    private boolean inFov(Entity entity, Entity target) {}
    private void raycast(Entity entity, Entity target) {}
    private void addToVision(Entity observer, Entity observable) {}
    private void removeFromVision(Entity observer, Entity observable) {}
}

The constructor will take a Box2D World as we need it to make line of sight (LoS) queries. We will also tell Ashley that we want the system to process entities with ObserverComponent.

public VisionSystem(World world) {
    super(Family.all(ObserverComponent.class).get());
    
    this.world = world;
}

In order to be able to answer the canSee() question, we will keep a map of observer entities to the collection of observables that it can see at any given time. The map will be updated every frame.

private ObjectMap<Entity, ObjectSet<Entity>> vision = new ObjectMap();

We will also need the collection of entities with an ObservableComponent, they are the candidates to make into the vision map as targets.

private ImmutableArray<Entity> observables;

The addedToEngine() and removedFromEngine() methods are invoked whenever we register the system with the engine. We can hook into them to grab the immutable list of observables as well as to register our vision system as a listener for observers. That way, we can pre-populate and clear up our vision map as observers come and go.

@Override
public void addedToEngine(Engine engine) {
    super.addedToEngine(engine);
    observables = engine.getEntitiesFor(
        Family.all(ObservableComponent.class).get()
    );
    engine.addEntityListener(getFamily(), this);
}

@Override
public void removedFromEngine(Engine engine) {
    super.removedFromEngine(engine);
    engine.removeEntityListener(this);
}

@Override
public void entityAdded(Entity entity) {
    vision.put(entity, new ObjectSet<Entity>());
}

@Override
public void entityRemoved(Entity entity) {
    vision.remove(entity);
}

The canSee() method is quite trivial, we simply check if the observable is in the set of visible entities for the given observer.

public boolean canSee(Entity observer, Entity observable) {
    ObjectSet<Entity> observables = vision.get(observer);
    
    if (observables == null) {
        return false;
    }
    
    return observables.contains(observable);
}

VisionSystem is an IteratingSystem, so we need to implement the processEntity() method, which will be invoked once a frame for every observer registered with the engine. Here is where the vision map entry for the observer gets updated.

@Override
protected void processEntity(Entity observer, float deltaTime) {
    updateVision(observer);
}

In order to do that, we go through the collection of observables and check whether they should be added or removed from the observer’s visible entities.

private void updateVision(Entity observer) {
    for (Entity observable : observables) {
        updateVision(observer, observable);
    }
}

The addToVision() and removeFromVision() utility methods will simply help us update the vision map. We make the assumption that there will always be an entry for every observer in the engine.

private void addToVision(Entity observer, Entity observable) {
    vision.get(observer).add(observable)
}

private void removeFromVision(Entity observer, Entity observable) {
    vision.get(observer).remove(observable)
}

To know whether an observer can see an observable two conditions need to be met: the observable has to be within the observer’s FoV and there must be an unobstructed LoS between the two. Querying the Box2D world can be costly, that is why we short-circuit the FoV check with the raycast.

private void updateVision(Entity observer, Entity observable) {
    if (!inFov(observer, observable)) {
        removeFromVision(observer, observable);
        return;
    }
    
    raycast(observer, observable);
}

To achieve super-fast component retrieval, we use ComponentMapper.

private ComponentMapper<ObservableComponent> observableMapper = ComponentMapper.getFor(ObservableComponent.class);
private ComponentMapper<ObserverComponent> observerMapper = ComponentMapper.getFor(ObserverComponent.class);

First, check whether the observable is within the vision distance of the observer and if it is, we check whether or not the angle between the two falls within the observer’s vision angle. The math is be pretty simple here.

private boolean inFov(Entity entity, Entity target) {
    ObserverComponent observer = observerMapper.get(entity);
    ObservableComponent observable = observableMapper.get(target);
    
    if (observer.position.isZero() ||
        observable.position.isZero() ||
        observer.position.dst2(observable.position) >
        observer.distance * observer.distance) {
        return false;
    }
    
    toObservable.set(observable.position);
    toObservable.sub(observer.position);
    
    float toObservableAngle = toObservable.angle();
    float angleDifference = Math.abs(toObservableAngle - observer.angle);
    
    angleDifference = Math.min(angleDifference, 360.0f - angleDifference);
    
    if (angleDifference > observer.fovAngle) {
        return false;
    }
    
    return true;
}

It’s time to perform our raycast, which will go from the observer to the observable. Box2D raycasts take a reference to the Callback interface to handle geometry hits. The handler is notified on every fixture hit. Box2D will pass the fixture it encountered as well as the fraction along the segment at which the hit happened.

vision system raycast

The VisionSystem has an inner VisionCallback implementation, which gets reused for every raycast, that way we don’t need to constantly allocate memory.

private VisionCallback callback = new VisionCallback();

Its outline is pretty simple.

private class VisionCallback implements RayCastCallback {
    private Entity observer;
    private Entity observable;
    private float minFraction;
    private float observableFraction;
    
    public void prepare(Entity observer, Entity observable) {}
    public boolean canSee() {}
    
    @Override
    public float reportRayFixture(Fixture fixture, Vector2 point, Vector2 normal, float fraction) {}
}

Before the raycast, we need to prepare the callback.

public void prepare(Entity observer, Entity observable) {
    this.observer = observer;
    this.observable = observable;
    this.minFraction = Float.MAX_VALUE;
    this.observableFraction = Float.MAX_VALUE;
}

Whenever the ray hits a fixture, the reportRayFixture() method gets called. Box2D bodies can hold arbitrary data, i.e. a reference to any Object. We conveniently set this to be a referene to the Entity the body belongs to. That way we can check if the fixture we hit is part of the observer itself. Whenever we encounter the observable we record how far along the ray segment it is.

@Override
public float reportRayFixture(Fixture fixture, Vector2 point, Vector2 normal, float fraction) {
    Object data = fixture.getBody().getUserData();
    
    if (data == observer) {
        return -1;
    }
    
    minFraction = fraction;
    
    if (data == observable) {
        observableFraction = fraction;
        return fraction;
    }
    return 0;
}

Thanks to the information recorded during the raycast, we can then ask VisionCallback whether the object is visible. This question is easy to answer, it will be visible if and only if the observable was the closest object the ray bumped into.

public boolean canSee() {
    return observableFraction < 1.0f && observableFraction <= minFraction;
}

The system raycast() method becomes very simple and can easily update the vision map.

private void raycast(Entity entity, Entity target) {
    ObserverComponent observer = observerMapper.get(entity);
    ObservableComponent observable = observableMapper.get(target);
    
    callback.prepare(entity, target);
    
    world.rayCast(
        callback,
        observer.position,
        observable.position
    );
    
    if (callback.canSee()) {
        addToVision(entity, target);
    }
    else {
        removeFromVision(entity, target);
    }
}

That’s it, we have a nice, reusable vision system for any 2D game!

Room for improvement

Like I said, this is game jam code, you have been warned!

Here’s a few things I could think of to make the system more efficient and nicer in general.

  • Collision filtering: Box2D allows us to set bit masks to bodies to filter collisions. We can leverage that to select behind which bodies observables can hide.
  • Space partitioning: we can use a quadtree to avoid processing every observable for each observer.
  • Deferred raycasting: we probably don’t need one frame accuracy, so we can update the vision maps for a subset of observers each frame. The player won’t ever notice if that guard spotted him a couple of frames later.
  • Prioritisation: if you ever find yourself in a situation where there are just too many observables and observers you can add some sort of prioritisation to your deferred raycast queue, so the important ones get processed first. You may also have to keep track of the time spent in the queue to avoid starvation.

Some games may need slightly more complex vision models. For instance, you may add a small detection circle around observers to represent some kind of sixth sense. A guard would notice a presence right behind him after a short while. That would be quite easy to add to our VisionSystem.

vision system alternate model

Use it, improve it, give feedback

Find the full source for the vision system here:

Let me know what you think, especially if you use it. Would love some feedback on it!

Read More

Sloppynauts, our LibGDXJAM entry

sloppynauts

After a crazy weekend before the deadline, we finally managed to put something playable together for LibGDXJAM.

We’d love you to have a go, and leave some feedback.

We are definitely aware of the shortcomings of the game. We spent a lot of the jam working on a custom Overlap2D level loader and not so much on actual content, so you can actually finish the game in under a minute. I guess the difficulty (or lack of readability) may make it slightly longer, which is also a bit bad. The movement system could have done with a bit more fine tuning as well.

However, we do believe the game has potential! Thanks to @DrillerDev, we had great character art an animations. The mechanics offer nice possibilities, which currently remain hidden due to lack of content. Not to mention the humour opportunities and the characters’ comicality.

I will try post some technical articles on the project. We used Ashley and some folks may find interesting how we tackled certain details such as the scene hierarchy or the animation selection system.

Next time we should follow the advice @McFunkypants gives in The Game Jam Survival Guide!

Read More

Ashley 1.7.1 released

ashley-small

There’s a new Ashley release, check the changes for 1.7.1 below:

  • API addition: expose IntervalSystem interval value (read-only).
  • Bug fix: fixed pending entity operations not being processed in the right order.
  • Bug fix: fixed adding component in empty family EntityListener causing wrong EntityListener calls.
  • Bug fix: fixed some entity operations not being performed under special circumstances.
  • Internals: split Engine internals into loosely coupled, single responsibility smaller classes.
  • Update: uses Libgdx 1.8.0.

To use it, change your dependency to com.badlogicgames.gdx:ashley:1.7.1. The new nightly dependency is com.badlogicgames.gdx:ashley:1.7.2-SNAPSHOT.

It’s really motivating to see how many people are using Ashley in their projects, including for the #LibGDXJAM!

Read More