21.04.2014

libGDX Entity-Klassen

Vorweg:

libGDX bezeichnet sich als "Java game development framework", es ist also keine fertige Spiele-Engine und bietet für die einzelnen Aufgaben keine fixe Lösung, sondern lässt dem Entwickler viel Freiheit, bietet aber für jede Entscheidung die richtigen Werkzeuge. Daher sind alle beschriebenen Implementierungsdetails auch nur eine Lösung von Mehreren, und mehr als Vorschlag als DER einzig richtige Weg zu sehen.

Entities

Da wir es mit einer eher überschaubaren Anzahl an Objekten in der Welt zu tun haben, habe ich mich gegen ein fertiges Entity Framework wie zB Artemis enschieden, unter Anderem auch, da wir Box2D einsetzen, und schon eine physikalische Welt mit unseren Objekten existiert.

Das Ziel: ein Aufbau bei dem ich ohne weiteres Eingreifen auf eine render() Funktion in meiner Entity Zugriff habe und auf meine Spielewelt zeichnen kann.

Dazu habe ich eine kleine Klasse geschrieben, die mir alle Daten zusammenhält die ich sowieso in jeder Entity brauche

public abstract class Entity implements Renderable{
    protected World world;
    protected Body body;
    protected Fixture fixture;
    public Entity(World _world){
        world = _world;
    }
    protected void createBody(BodyDef bodyDef){
        body = world.createBody(bodyDef);
        body.setUserData(this);
    }
    protected void createFixture(FixtureDef fixtureDef){
        fixture = body.createFixture(fixtureDef);
        fixture.setUserData(this);
    }
}

Die Referenz zur Box2D Welt, sowie den Body und die Fixture brauche ich in jedem Fall für die spätere Kollisionsabfrage. Damit ich dann über die Fixtures auch wieder Zugriff auf das Entity Objekt habe, wird das komplette Objekt in den dafür vorgesehenen Platzhalter "userData" geschrieben.

Das Interface "Renderable" zwingt dabei nur die erbende Klasse, meine render() Funktion zu implementieren.

Die Welt selbst wird in meinem GameScreen be - und verarbeitet. Dafür hole ich mir alle Bodies aus der Welt, und prüfe ob das Renderable Interface vorhanden ist.

public class GameScreen implements Screen {

    @Override
    public void render(float delta) {
        Gdx.gl.glClearColor(1, 1, 1, 1);
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
    
        //rendering bodies
        Array bodies = new Array();
        world.getBodies(bodies);
        for (Body b : bodies) {
            Object userData = b.getUserData();
            if (userData != null) {
                // call all render methods from renderable bodies
                if (userData instanceof Renderable) {
                    ((Renderable) userData).render( );
                }
            }
        }
        // TODO remove debug renderer 
        debugRenderer.render(world, camera.combined);

        // step phisycal world
        world.step(1 / 45f, 6, 2);
    }
}

Der debugRenderer ist hier ein eigener Renderer von Box2D, der die Entities nur anhand der Fixtures zeichnet. Nicht hübsch, aber man kommt schnell zu einem Ergebnis.

Da in libGDX es sowas wie eine Game-Loop nicht gibt, werden auch alle Berechnungen und Logik-Updates in der render() Funktion gehandelt. Im Einsatz könnte es also so aussehen:

public class Player extends Entity {
    public Player(World _world) {
        super(_world);
        
        BodyDef bodyDef = new BodyDef();
        bodyDef.type = BodyType.DynamicBody;
        bodyDef.position.set(0, 1);

        createBody(bodyDef);
        
        CircleShape circle = new CircleShape();
        circle.setRadius(2);

        FixtureDef fixtureDef = new FixtureDef();
        fixtureDef.shape = circle;
        fixtureDef.density = 0.5f;
        fixtureDef.friction = 0.4f;

        createFixture(fixtureDef);

        circle.dispose();
    }
    
    @Override
    public void render() {
        //wohooo, i can do all my stuff
    }
    
}

Wunderbar. Ich bin zufrieden, glücklich und kann wunderschöne Entity Klassen bauen :-)


error success