[LibGDX] Entity Components - Should I read from file?

I am a little stumped on the best approach for this, I want to clean up my code a bit and move my entity components to an external file that is loaded in at runtime.

However I am a little unsure how to go about this, as I have never done any extensive I/O coding, I usually just write the code once and when I am happy with it, dump the entire thing to disk using Protobuf or Json and then read it in later.

I would like it to be somewhat readable though, so that if I want to make small changes (like balancing) down the road, I don’t have to re-write it and redump it.

I am using the Ashley Entity Framework which is fairly lightweight, along with LibGDX and Box2D. I have it setup so that entities bodies and fixtures are not created until they are added to the Ashley engine, so there is no serializing of actual Box2D bodies; which is nice.

Serializing sprites is bad though, correct? So my option there would be to store the file path and instead grab it from the AssetManager after the entity has been loaded from disk.

Any ideas? Suggestions?, for the record, playable characters (of which there are currently 5) have between 15 and 20 different component combinations that make them unique.

The main idea here is to remove a lot of horrible code into a nice file that is readable and can be altered.

You basically don’t want to serialize components like Sprite, instead rely on having an AssetReference component or similar. I tend to divide my components into plain data and transient components. A transient component is basically a component which doesn’t readilylend itself to de/serialization. You have to construct those from plain data components, in the case of saving/loading.

Some more info here, though managers are specific to artemis, I’m sure something similar can be used for Ashley too: https://github.com/junkdog/artemis-odb/issues/128#issuecomment-52243456

As for save format, json is pretty convenient although it can be verbose unless you take steps optimize it.

Edit: example AssetReference component.


public final class AssetReference extends Component {
    public String atlas;
    public String frame;
    public boolean animated;
    public float duration;

    public AssetReference(String atlas, String frame) {
        set(atlas, frame);
    }

    public void set(String atlas, String frame) {
        this.atlas = atlas;
        this.frame = frame;
    }
}

Very nice information here.

Do you recommend serializing components using json, then add them to the entity rather than serializing the entire entity with the components?

The first one seems cleaner, as a serialized character at the moment has literally over 1000 lines in the json file.

However, I don’t want to have to read the data in every time I spawn a new character or object, as this will make it slow.

For reference, this is what a basic character looks like:


	private static Entity createHaldor(Vector2 position) {
		Entity e = new Entity();
		PhysicsComponent physicsComponent = new PhysicsComponent(
				BodyType.DynamicBody, true, 2);
		DrawableComponent drawableComponent = new DrawableComponent(new Sprite(
				VRD.ASSETS.get("sprites/01/jump.png",
						Texture.class)), DepthBuffers.VD);
		SizeComponent sizeComponent = new SizeComponent(1.5f,
				MathHelper.getRatioY(1.5f, drawableComponent.getSprite()
						.getWidth(), drawableComponent.getSprite().getHeight()));
		MotorComponent motorComponent = new MotorComponent();
		AnimationComponent a = new AnimationComponent();
		LabelComponent labelComponent = new LabelComponent();
		ContactComponent contactComponent = new ContactComponent();
		GroundColliderComponent groundColliderComponent = new GroundColliderComponent();
		DirectionComponent directionComponent = new DirectionComponent();
		AirborneStateComponent airborneComponent = new AirborneStateComponent();

		groundColliderComponent.transform.setPosition(new Vector2(0, -.6f));
		groundColliderComponent.transform.setParent(physicsComponent.transform);
		groundColliderComponent.width = 0.35f;
		groundColliderComponent.height = 0.25f;

		labelComponent.name = "Haldor";
		labelComponent.internalName = "haldor";
		a.current = AnimationCache.getAnimation(labelComponent.internalName,
				"jump");
		motorComponent.maxSpeed = 4f;
		motorComponent.accelerationRateX = 25f;
		PolygonShape poly = new PolygonShape();
		poly.setAsBox(0.75f / 2, 1.5f / 2);
		CircleShape circle = new CircleShape();
		circle.setRadius(0.375f);
		circle.setPosition(new Vector2(0, -.375f));
		physicsComponent.addNewFixtureDef(FixtureData.BUILDER.name("foot")
				.density(0.25f).restitution(0f).friction(0f).shape(circle)
				.dataToSet(contactComponent).build());
		circle = new CircleShape();
		circle.setRadius(0.365f);
		circle.setPosition(new Vector2(0, .375f));
		physicsComponent.addNewFixtureDef(FixtureData.BUILDER.name("head")
				.density(0.25f).restitution(0f).friction(0f).shape(circle)
				.dataToSet(contactComponent).build());
		physicsComponent.transform.setPosition(position);
		drawableComponent.transform.setParent(physicsComponent.transform);
		drawableComponent.transform.setPosition(new Vector2(-sizeComponent
				.getWidth() / 2, -sizeComponent.getHeight() / 2));
		drawableComponent.transform.setAngleAxis(0, 0, 1, 0);
		
		e.add(sizeComponent).add(physicsComponent)
		.add(drawableComponent).add(motorComponent)
		.add(contactComponent).add(a).add(labelComponent)
		.add(directionComponent).add(groundColliderComponent).add(airborneComponent);
		
		return e;
	}

You can probably see why it might be beneficial to read the parameters from a file lol.

EDIT: I was considering using Lua, as this will allow me to have components configured in a script outside of the code base. I thought about this approach given the fact I won’t be maintaining this project (or won’t be the only one) in the long run.

Sorry for the late reply, I saw your reply late in the night, decided I’d attend it tomorrow - and “tomorrow” was several days ago.

You probably don’t want to serialize the entire entity as this would include a lot of data tied to the engine. Json might be a little verbose, but it’s very easy to work with. I have some old artemis code lying around which reads entity definitions from json; it should be rather easy to adopt, as ashley and artemis have very similar API:s. Not the most efficient code, but it works.