I made some refactorings in my classes. The original code is in this question.
In that question I receive the suggestion to create a Factory for the class BallEntity. I myself forgot that BallEntity is just a kind o 'factory' or 'builder' for a "Ball Entity". Just because Entities in an Entity/Component/System framework is distinguishable only by its components and the creation of these entities is painful and verbose e decide to create this kind of builder class (that is represented by UserEntity and their subclasses).
When I went to use this BallEntity I created I found some problems. Here is a example of use of BallEntity in traditional way, first creating it and then using it in a 'System'.
public class GamePlayScreen{
public void createBall() {
Entity ballEntity = new Entity();
ballEntity.add(BallContextComponent.newInstance());
/* ...
ADD others components
...
*/
/* add it to the engine */
engine.addEntity(ballEntity);
}
}
public class TeamResetSystem extends EntitySystem {
private Entity ball;
@Override
public void addedToEngine(Engine engine) {
ball = engine.getEntitiesFor(Family.all(BallContextComponent.class).get()).first();
}
As you see, whenever I want to retrieve the entity I have to remember components that entity have. In this example it's not difficult, but sometimes I have to pass some components to Family.all. And sometimes I add more components. I want I way to centralize the info about which components an entity have. At the end of the code looks like this:
As suggested I created a Factory:
public class BallEntityFactory {
private final TextureAtlas atlas;
private final RubeSceneHelper rubeSceneHelper;
public BallEntityFactory(TextureAtlas atlas, RubeSceneHelper rubeSceneHelper) {
this.atlas = atlas;
this.rubeSceneHelper = rubeSceneHelper;
}
public BallEntity createBallEntity(String ballImageName, float scaleFactor, Camera camera) {
Sprite ballSprite = new Sprite(atlas.findRegion(ballImageName));
Body ballBody = rubeSceneHelper.getBody("ball");
ScaledSprite scaledSprite = ScaledSprite.createUsingHeight(ballSprite, scaleFactor);
Fixture fixture = rubeSceneHelper.getFixture(ballBody, "ball");
return new BallEntity(scaledSprite, ballBody, fixture, camera);
}
}
I do some refactoring in base class, the 'buildable' argument is because I used BallEntity first to construct the entity and after to retrieve it in the entity system object. When buildable is false, I don't create another entity, since I have interest only in method getDistinctFamily ().
public abstract class UserEntity {
private final boolean buildable;
private Entity entity;
private boolean wasBuilt = false;
protected UserEntity(boolean buildable) {
this.buildable = buildable;
if (buildable) {
this.entity = new Entity();
}
}
public final Entity getEntity() {
if (!buildable) {
throw new IllegalArgumentException("it's not a buildable Entity");
}
if (!wasBuilt) {
final Component[] components = getComponents();
for (Component c : components) {
entity.add(c);
}
afterConstruct(entity);
}
return entity;
}
protected abstract Component[] getComponents();
/* Override when need to init some components */
public void afterConstruct(Entity entity) {
}
public ImmutableArray<Entity> getAllInEngine(Engine engine) {
return engine.getEntitiesFor(getDistinctFamily());
}
protected abstract Family getDistinctFamily();
}
public class BallEntity extends UserEntity {
private Body ballBody;
private Camera camera;
private ScaledSprite ballSprite;
private Fixture ballFixture;
/* this constructor is used only to retrieve others BallEntity objects */
public BallEntity() {
super(false);
}
/* this constructor is used for build a BallEntity*/
public BallEntity(ScaledSprite ballSprite, Body ballBody, Fixture ballFixture, Camera camera) {
super(true);
this.ballSprite = ballSprite;
this.ballBody = ballBody;
this.ballFixture = ballFixture;
this.camera = camera;
}
@Override
public Component[] getComponents() {
return new Component[]{
PositionComponent.newInstance(),
CameraFollowerComponent.newInstance(camera),
SpriteComponent.newInstance(ballSprite.getSprite()),
BodyComponent.newInstance(ballBody),
BallContextComponent.newInstance()};
}
@Override
public void afterConstruct(Entity entity) {
BodyComponent bodyComponent = entity.getComponent(BodyComponent.class);
bodyComponent.setPosition(Vector2.Zero);
ballFixture.setUserData(new FixtureUserData(FixtureType.BALL, entity));
}
/**
* Return the family using the only one component that distinguish this entity from others
*
* @return
*/
@Override
protected Family getDistinctFamily() {
return Family.one(BallContextComponent.class).get();
}
}
GamePlayScreen class:
public void createBall() {
final BallEntity ballEntity = new BallEntityFactory(atlas, rubeSceneHelper)
.createBallEntity("ball", 0.78f, camera);
engine.addEntity(ballEntity.getEntity());
}
TeamResetSystem class:
@Override
public void addedToEngine(Engine engine) {
players = engine.getEntitiesFor(Family.all(PlayerInfoComponent.class, PlayerMatchContextComponent.class).get());
ball = new BallEntity().getAllInEngine(engine).first();
}
How to improve this code?