Skip to content

Entities

Vrekt edited this page Apr 12, 2024 · 32 revisions

Lunar uses Ashley as an ECS. All entities within Lunar are prefixed with Lunar

Entity components provided

  • EntityPropertiesComponent
    • Stores data about an entities size, entity ID, name, health, moving speed, etc.
  • EntityTextureComponent
    • Basic convenience functions for storing Texture(s) and TextureRegion(s)
    • Only present if an entity implements LunarTexturedEntity or extends AbstractLunarTexturedEntity
  • EntityPositionComponent
    • Contains the entities current position, previous position, and, interpolated position.
  • EntityVelocityComponent
    • Contains the entities current velocity and force being applied.

You can access these components from the base implementing entity or by using the GlobalEntityMapper

GlobalEntityMapper.texture.get(myEntity);
GlobalEntityMapper.position.get(myEntity);

As such, you can access the underlying Ashley entity within all classes that implement LunarEntity or extend AbstractLunarEntity

myLunarEntity.getEntity().add(myComponent);

Entity Types

All entities implement or extend LunarEntity. LunarEntity provides a basic outline for properties and methods all entities should have. Each entity also includes its own default implemention usually prefixed with Abstract, for example AbstractLunarEntityPlayer.

Type Implementation Description
LunarEntity AbstractLunarEntity The base entity holding common properties
LunarTexturedEntity AbstractLunarTexturedEntity Allows you to store textures, regions, sprites and current drawing states
LunarEntityPlayer AbstractLunarEntityPlayer The base of any player. Provides implementation for spawning, networking, etc.
LunarEntityNetworkPlayer AbstractLunarEntityNetworkPlayer The base of a network player. Provides methods for updating and interpolating position/velocity + more

Along with these are two adapters; basically, just default implementations that contain convenient methods. These are Player and NetworkPlayer

So with creating your own custom entities you may extend any of those default implementations which do a large amount of the work for you. Below is an example of a custom player that does a few extra things.

public final class DemoPlayer extends Player {

    public DemoPlayer(boolean initializeComponents, TextureRegion playerTexture) {
        super(initializeComponents);

        setMoveSpeed(6.0f);
        // we don't want to collide with other players
        disablePlayerCollision(true);
        setMoving(true);
        // describes how often position updates will be sent
        setNetworkSendRateInMs(10, 10);
        // default player texture
        addRegion("player", playerTexture);
        // default player configuration
        setSize(16, 16, (1 / 16.0f));
    }

    @Override
    public void pollInput() {
        setVelocity(0.0f, 0.0f);

        if (Gdx.input.isKeyPressed(Input.Keys.W)) {
            setAngle(0f);
            setVelocity(0.0f, getMoveSpeed());
        } else if (Gdx.input.isKeyPressed(Input.Keys.S)) {
            setAngle(1f);
            setVelocity(0.0f, -getMoveSpeed());
        } else if (Gdx.input.isKeyPressed(Input.Keys.A)) {
            setAngle(2f);
            setVelocity(-getMoveSpeed(), 0.0f);
        } else if (Gdx.input.isKeyPressed(Input.Keys.D)) {
            setAngle(3f);
            setVelocity(getMoveSpeed(), 0.0f);
        }
    }

    @Override
    public void render(SpriteBatch batch, float delta) {
        batch.draw(getRegion("player"), getInterpolatedPosition().x, getInterpolatedPosition().y, getScaledWidth(), getScaledHeight());
    }

    @Override
    public void spawnInWorld(LunarWorld world, Vector2 position) {
        super.spawnInWorld(world, position);
        // TODO: Implement any custom logic here.
    }
}

Box2d Entity Definition

By default a simple square body is created for the entity when spawning them in a world. You can override and provide your own custom implementation by using EntityBodyHandler. When implementing this class you can provide your own Body context with however you wish to configure it.

As an example, here is the default implementation to give you an idea.

public class EntityBodyCreator implements EntityBodyHandler {

    private BodyDef definition;
    private FixtureDef fixture;
    private boolean hasSetFixedRotation, hasSetDensity;
    private boolean fixedRotation;
    private float density;

    @Override
    public void setBodyDefinition(BodyDef definition) {
        this.definition = definition;
    }

    @Override
    public void setFixtureDefinition(FixtureDef definition) {
        this.fixture = definition;
    }

    @Override
    public void setHasFixedRotation(boolean fixedRotation) {
        this.hasSetFixedRotation = true;
        this.fixedRotation = fixedRotation;
    }

    @Override
    public void setDensity(float density) {
        this.hasSetDensity = true;
        this.density = density;
    }

    @Override
    public void resetDefinition() {
        definition = null;
        fixture = null;
    }

    @Override
    public Body createBodyInWorld(World world, float x, float y, EntityPropertiesComponent configuration) {
        if (definition == null) {
            definition = new BodyDef();
            definition.type = BodyDef.BodyType.DynamicBody;
        }

        if (this.fixture == null)
            this.fixture = new FixtureDef();

        if (hasSetFixedRotation) {
            definition.fixedRotation = fixedRotation;
        }

        definition.position.set(x, y);

        final Body body = world.createBody(definition);
        PolygonShape shape = null;

        if (fixture.shape == null) {
            shape = new PolygonShape();
            shape.setAsBox(configuration.getScaledWidth() / 2f, configuration.getScaledHeight() / 2f);
            fixture.shape = shape;
            if (!hasSetDensity) {
                fixture.density = 1.0f;
            } else {
                fixture.density = density;
            }
        }

        body.createFixture(fixture);
        if (shape != null) shape.dispose();
        return body;
    }
}

Once you have created your own implementation you can tell the entity to use it by this method:

myEntity.setBodyHandler(handler);

You should do this once an entity is created, maybe in the constructor, before spawning them or anything.

Positioning

You can access any position, previous, or interpolated like such:

entity.getPosition();
//
entity.getPreviousPosition();
//
entity.getInterpolatedPosition();

To update positions it is desirable to use the method setPosition. If you have a box2d entity you can also use transform on this entity. Generally, transform is only reserved for initial spawning or teleporting, otherwise all other position related changes are by velocity.

// for example, I spawned in my player and I need them to start in a fixed location
player.setPosition(position, angle, true);

If no box2d entity is present (their body) then transform will have no effect.

Interpolating

You can interpolate a entities position by using various methods:

public void interpolatePosition();
  
public void interpolatePosition(float alpha);
  
public void interpolatePosition(Interpolation interpolation, float alpha);

You can use these methods to customize which interpolation method to use and the smoothing/alpha. You may wish to override these as-well for your own custom implementation.