Josh K. wrote:
Thanks everybody, now the constructor/init/process idea finally seems clear to me. =)
Now a couple more questions.
How can we ensure the initialization order of Systems? Call initialize() one by one?
Another one regarding renderSystem: how can we ensure the rendering order of spatials? Like, it's nice when explosion is drawn over the object, not behind it. I guess it's something to do with groups/tags, but not sure what/how exactly.
Btw, Appel, is it ok to ask all these questions here, or it's better to do it at Gamadu forum?
SystemManager.initializeAll() invokes intialize() on systems in the order they were added. The initialize order of systems may matter in cases where you have system B that is initializing depending on some data being already initialized in system A.
RenderSystem is totally game-implementation specific, and that's what Artemis doesn't provide. What you talk about is best solved by using buckets. Whenever a new spatial is created, you insert it into a rendering layer bucket. There may be 4-8 of these buckets, depending on how many you really need. For instance, explosion spatials you may want to render into bucket 5, while monsters and such could go into bucket 4, and blood splatter on the terrain into bucket 3. You can define these layerId's as part of the spatials by providing for an abstract method for your spatials to implement, e.g. int getLayerId(). When the entity dies, you remove it from the bucket. Rendering is just a matter of iterating through the buckets and invoking render(g) on all spatials there.
I also use a Node class, which extends Spatial, but allows you to attach children spatials to it, allowing for some sorts of simple scenegraph. That enables me to make composite spatials, like if I have similar looking entities, like in a RTS game, I might have a shadow underneath all entities, so I create a ShadowSpatial, then I have HealthBarSpatial, then NameSpatial, SelectionHighlightSpatial, and I add these all into a UnitSpatial, then I can create a new spatial called the MammothTankSpatial and implement the look of it there, and also add a child called the UnitSpatial. You can of course compose these spatials any way you like.
Here's an idea:
Code:
public abstract class Spatial {
public abstract Layer getLayer();
private int getLayerId() {
Layer layer = getLayer();
return layer.getLayerId();
}
public void addToRenderBuckets(Bag<Bag<Spatial>> buckets) {
Bag<Spatial> bucket = buckets.get(getLayerId());
if (bucket == null) {
bucket = buckets.set(getLayerId(), new Bag<Spatial>());
}
bucket.add(this);
}
@Override
public void initialize() {
}
@Override
public void update(int delta) {
}
public abstract void render(Graphics g);
}
Code:
public abstract class Node extends Spatial {
private Bag<Spatial> children;
public Node() {
children = new Bag<Spatial>();
}
public Bag<Spatial> getChildren() {
return children;
}
protected void attachChildren() {
}
@Override
public void setOwner(Entity owner) {
super.setOwner(owner);
for (int i = 0; children.size() > i; i++) {
children.get(i).setOwner(owner);
}
}
public void addChild(Spatial spatial) {
spatial.setOwner(owner);
children.add(spatial);
}
public void addToRenderBuckets(Bag<Bag<Spatial>> buckets) {
super.addToRenderBuckets(buckets);
for (int i = 0; children.size() > i; i++) {
children.get(i).addToRenderBuckets(buckets);
}
}
@Override
public void initialize() {
attachChildren();
for (int i = 0; children.size() > i; i++) {
children.get(i).initialize();
}
}
@Override
public void update(int delta) {
for (int i = 0; children.size() > i; i++) {
children.get(i).update(delta);
}
}
}
I use "Layer" instead of hardcoded int, this allows me to rename a layer, find all usages of it using the IDE, and also change the order they are rendered by simply moving one line above another.
Code:
public interface Layer {
int getLayerId();
}
Well, I use enums instead of ints

More convenient. Maybe you don't like it to be so statically typed, then just use int's in your spatials and skip these two classes.
Code:
public enum Layers implements Layer {
TERRAIN,
TERRAIN_DECORATION,
CHARACTERS,
EFFECTS,
OVERLAY
@Override
public int getLayerId() {
return ordinal();
}
}
I'm using RenderManager here, but you can do similar things in a RenderSystem:
Code:
public class RenderManager extends Manager {
private Bag<Bag<Spatial>> buckets;
private Graphics g;
public RenderManager(World world, Graphics g) {
super(world);
this.g = g;
buckets = new Bag<Bag<Spatial>>();
}
public SpriteBatch getSpriteBatch() {
return batch;
}
public void render(Graphics g) {
renderBuckets(g);
}
private void resetBuckets() {
for (int i = 0; buckets.size() > i; i++) {
Bag<Spatial> bag = buckets.get(i);
if(bag != null) {
bag.clear();
}
}
}
private void addSpatialsToBuckets() {
EntityManager em = world.getEntityManager();
ImmutableBag<Entity> spatials = em.getEntitiesByComponentType(Spatial.class);
for (int i = 0; spatials.size() > i; i++) {
Entity entity = spatials.get(i);
Spatial spatial = entity.getComponent(Spatial.class);
if (spatial != null) {
spatial.addToRenderBuckets(buckets);
}
}
}
private void renderBuckets(SpriteBatch batch) {
for (int i = 0; buckets.size() > i; i++) {
Bag<Spatial> bag = buckets.get(i);
if(bag != null) {
for (int a = 0; bag.size() > a; a++) {
Spatial spatial = bag.get(a);
spatial.render(g);
}
}
}
}
@Override
public void added(Entity e) {
resetBuckets();
addSpatialsToBuckets();
}
@Override
public void removed(Entity e) {
resetBuckets();
addSpatialsToBuckets();
}
}
It is fine to ask here
