Slick Forums

Discuss the Slick 2D Library
It is currently Thu Jun 20, 2013 2:14 am

All times are UTC




Post new topic This topic is locked, you cannot edit posts or make further replies.  [ 420 posts ]  Go to page Previous  1 ... 12, 13, 14, 15, 16, 17, 18 ... 28  Next
Author Message
 Post subject:
PostPosted: Sat Jul 09, 2011 6:43 pm 
Offline
User avatar

Joined: Fri Jun 10, 2011 11:46 pm
Posts: 6
Location: Russia
Thank you guys. But still it's unclear for me somehow. If I implement all the menus through a separate system or systems (like RenderGUISystem described here, with Nifty: http://slick.javaunlimited.net/viewtopic.php?t=3436), how should the system be processed in non-gameplay states (where there's no World)? Directly call the system's initialize, render & process methods from the state's init, render & update, respectively?


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jul 10, 2011 6:16 am 
Offline
User avatar

Joined: Wed Aug 26, 2009 2:33 pm
Posts: 36
Location: Melbourne, Australia
Josh K. wrote:
Directly call the system's initialize, render & process methods from the state's init, render & update, respectively?

That sounds like the logical approach. If your GUI is not built from Artemis entities and components, then I don't see why you would need to integrate (read: couple) it into the Artemis World.

One of the great things about Artemis is that although it provides a whole paradigm/framework for you to work with, it doesn't dictate anything. You can use as many or as few entities/components as you like, you can have zero to much logic in the components, the things you're rendering aren't at all constrained by the framework (Spatial doesn't extend anything), and so on. There's no need to integrate it with anything unless you want that thing to be processed at the same time as the existing entities. I can't see any harm in going world.update(); gui.update();, and the same for rendering.

This may not have answered your question, but it's my answer anyway :).


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jul 10, 2011 7:39 am 
Offline
User avatar

Joined: Wed Aug 26, 2009 2:33 pm
Posts: 36
Location: Melbourne, Australia
PS @ appel: I've been noticing the pre-compiled jar, source and JavaDoc on http://gamadu.com/artemis/ are out of date by a few versions. If you're more or less done with it for now, it might be a good idea to get the final, licensed version up on that page (and easier for people like me to not have to build from source ;)).


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jul 10, 2011 1:18 pm 
Offline
Game Developer
User avatar

Joined: Tue Nov 21, 2006 4:46 am
Posts: 620
Location: Iceland
Pie21 wrote:
PS @ appel: I've been noticing the pre-compiled jar, source and JavaDoc on http://gamadu.com/artemis/ are out of date by a few versions. If you're more or less done with it for now, it might be a good idea to get the final, licensed version up on that page (and easier for people like me to not have to build from source ;)).


I've deployed a new version.

Should say "Implementation-Version: 37" in the manifest.

Expect it will break existing code.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jul 10, 2011 3:45 pm 
Offline
User avatar

Joined: Wed Aug 26, 2009 2:33 pm
Posts: 36
Location: Melbourne, Australia
Cheers. I found the only code it broke was when passing EntityManagers to ComponentMapper constructors instead of passing the World (Which is obviously much cleaner). After a quick find-and-replace-all it was all good to go.

Also, philosophical question: what is data-driven design (DDD), what is responsibility-driven design (RDD), and which is Artemis more accommodating of? I don't expect a long answer, but I've heard DDD bandied about as meaning "reads in game-specific properties from a file", but also heard it trashed talked by RDD, saying it leads to bad OO design.

Artemis to me seems to fit the bill for both: the logic (systems) are driven by the data the're processing (components) rather than the entities, yet each piece of the framework has a well defined role and responsibility. There's not much interaction between objects though, so I suppose it's not especially RDD.

Anyway, I'm about to put up two big posts on http://piemaster.net/, one about the entity/component paradigm and the other about Artemis specifically. I'll let you know when there up, and if you could flick through the later to ensure I haven't made any defamatory errors, I'd appreciate it ;).

EDIT: Whew! That took FAR longer than expected. It's now 5am. Bedtime for me. Until next time:
Entity/Component Game Design: A Primer
Entity/Component Game Design That Works: The Artemis Framework
Hopefully I haven't messed up too bad :).


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jul 10, 2011 7:35 pm 
Offline
Regular
User avatar

Joined: Tue Apr 07, 2009 12:58 pm
Posts: 232
Location: Uruguay
What I interpret about Data driven design is, data drives logic of the game, that means, different data implies different behavior.

For example, if you are using a components design, adding/removing a component from an entity means that entity will behave differently.

I believe you can have all the interaction you want using a DDD, one way to achieve that is by using components and storing stuff inside components, that lets you interact with other systems. However it is not conventional interaction, as you said, there is no object interaction (thinking in OOP). Artemis seems to fit right as a DDD imo.

Btw, I was thinking about making a small tutorial/introduction in a blog post that explains how you can transform a direct coding model (having each concept as classes) to an data driven design using a components based entity framework like Artemis. I will let you know if I write something.

_________________
Image


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 11, 2011 12:14 am 
Offline
User avatar

Joined: Fri Jun 10, 2011 11:46 pm
Posts: 6
Location: Russia
Pie21, thanks again. Also, bookmarked your blog.)
Now, as usual, a couple of noobish questions, if you don't mind.

Here's a constructor from Artemoids' PlayerShipControlSystem.java:
Code:
public PlayerShipControlSystem(GameContainer container) {
    super(Transform.class, Player.class, Health.class);
    this.container = container;
}

What does it do? As far as I get it, in initialize() we create mappers to make the System know what entities to process (i.e. entities possessing the Components being mapped), right? And then we process them in process(e). Then what does the constructor do? Why the Components mentioned in constructor and in initialize() do not match?

I also don't quite get the difference between EntitySystem and EntityProcessingSystem yet. In which cases should we use EntitySystem? For example, your Artemoids' CollisionSystem extends EntitySystem in order to override processEntities, to process several entities simultaneously, right? But why is processEntities declared as final in EntityProcessingSystem, in the first place? Is processing several entities simultaneously not considered as entity processing?

Same thing with CameraSystem and PlayerTankMovementSystem in Tankz - why do they extend EntitySystem which "should not typically be used"?

Offtop: I really like the geographical span of Artemis' users. This way it'll soon be recognized at each and every continent (Antarctica anyone? =)


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 11, 2011 12:49 am 
Offline
Game Developer
User avatar

Joined: Tue Nov 21, 2006 4:46 am
Posts: 620
Location: Iceland
Constructor is used to specify the aspect of the system (what set of components it will process).

Initialize is, yes, used to create mappers, but also resolve any dependencies on other systems, e.g. CameraSystem in Tankz depends on the BoundarySystem. Initialize is also supposed to initialize any system specific data required to process the entities.


EntitySystem is the most basic entity system, it's an abstract class. In most cases you don't need to extend it directly.

EntityProcessingSystem is best suited for most cases, it allows you to process aspects of entities, one entity at a time, e.g. "process(Entity e)".

Whenever I have "Processing" in the name of the classes it means that the class processes one entity at a time in the "process(Entity e)". If the "Processing" part is missing from the name then you need to implement your own "processEntities(ImmutableBag<Entity> entities)" method. That's really the only difference.

DelayedEntitySystem/DelayedEntityProcessingSystem and IntervalEntitySystem/IntervalEntityProcessingSystem are really just addon classes, helping you to improve the performance of your game. Many games execute update logic on everything every single game loop, but that's not necessary in some cases.

Delayed systems allows you to define when next to run the system, like if you have entities that need to expire after a certain amount of time you should use this. Whenever you need something to run at intermittent, varying frequencies, then this is your best bet. You simply set the delay until to execute next, and when it executes you reset the delay to a shorter or longer period.

Interval systems allows you to run a system at a fixed frequency. Some systems you want to run every 100 ms, and not every game tick. An example of this is perhaps some sorts of targeting system, if you have a unit that needs to look for targets to shoot at, then use this. Or if you need to check if the player has destroyed all enemy units and the mission is over, you can run a system that checks for that condition every 3 seconds or so. There are many usages.


There's explanation in the javadocs:
http://gamadu.com/artemis/javadoc/index ... ystem.html
You can also navigate the class hierarchy there.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 11, 2011 4:59 am 
Offline
User avatar

Joined: Wed Aug 26, 2009 2:33 pm
Posts: 36
Location: Melbourne, Australia
Very helpful explanation, appel.

Just to elaborate, the component types in the system constructors are the set of components they're interested in. Once that is specified, the processEntities() method will take a bag full of all the entities that contains all of those components. Hence the process(Entity e) method will process a single entity at a time, and you can be sure that it contains all the specified components.

Hence if you have, say, a MovementSystem with requires Transform, Velocity and Acceleration components, then in process you can get those components without worrying about entities not having them.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 11, 2011 8:47 am 
Offline
User avatar

Joined: Wed Aug 26, 2009 2:33 pm
Posts: 36
Location: Melbourne, Australia
Also quick update: I've pushed all my recent changes to Jario into Bitbucket, so you can have a look at what I'm up to. WARNING: It may be completely the wrong way to do things :P. It's getting quite a lot more complex now that it was before, and Artemis is scaling very well in terms of keeping things manageable.

Quick overview of the changes in post form.

EDIT: I think I just ran out of bits :o! What do I have to change to BitSet to make it work again? I'm really not too concerned about performance at this point in time.
DOUBLE EDIT: Maybe not, but the question stands - I will soon enough :P. Not sure why, but this new system simply is not processing.
SUPER EDIT!: lol, never mind, I was just missing a component in my factory. Sigh. I swear I checked that like 3 times.

EDIT: One more question: I'm trying out the DelayedEntityProcessingSystem, and I've done (almost) exactly what you did for Tankz. For some reason though it will work correctly the first time (I add my component, refresh, added() is called, countdown expires, component is removed), but then not work the second time. I add the component and refresh the player exactly the same way, but the added() mehtod is not called when the new component is added if all previous interested components of the delayed system have already expired.

Is this how it's supposed to behave? I understand that I need to call startDelayedRun() to make it go again, but that's what the added() method is for. Why would the added() method be called the first time but not the second time?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 12, 2011 5:10 pm 
Offline
Regular
User avatar

Joined: Tue Apr 07, 2009 12:58 pm
Posts: 232
Location: Uruguay
@appel What do you say about having a flag on Entity class named modified or shouldRefresh, so whenever you add or remove a component you touch the flag.

Then, on each loop start, you refresh those 'modified' entities. One problem of this approach is you have to iterate between all entities :(

On the other hand, if you don't modify entities once the entity was added to the world, you can iterate between all entities only once.

Don't know for sure the best solution, but having to call e.refresh() doesn't seem natural to me. I understand that if you add/remove a component from an entity, you have to update the corresponding systems.

Maybe adding the entity to refresh collection if entity . add/remove component methods where called. Think that you only call those methods from time to time, not on every update so there should be no performance issue.

_________________
Image


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 13, 2011 3:30 am 
Offline
User avatar

Joined: Fri Jun 10, 2011 11:46 pm
Posts: 6
Location: Russia
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?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 13, 2011 3:58 am 
Offline
User avatar

Joined: Wed Aug 26, 2009 2:33 pm
Posts: 36
Location: Melbourne, Australia
Josh K. wrote:
How can we ensure the initialization order of Systems? Call initialize() one by one?

I can't really see why it should matter what order they're initialised. What's your use case? The important part is being able to get references to other systems in the initialize() method, which you can in any case by having used systemManager.setSystem() in your state init method prior to initialisation.

Josh K. wrote:
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.

This is something that appel acknowledges is difficult, and seems to be a problem he's solved in the Apollo framework. Currently I don't think there's a way to ensure painting order with Artemis, though naturally you could create your own workaround.

Currently in Jario it's not a problem for me, because entities don't really overlap, so I don't really have any solutions. In my experience though, things tend to be rendered in the order they're created, but given everything uses Bags behind the scenes I don't think it's guaranteed.

EDIT: Now that I'm streaming my level creation (kind of), I do have the drawing issue. I'll have a go at a fix later on, but let me know if you get anywhere in the meantime :). I suspect ordering the painting by group would be enough for most cases. Maybe some kind of GroupedEntityProcessingSystem?

Josh K. wrote:
Btw, Appel, is it ok to ask all these questions here, or it's better to do it at Gamadu forum?

The Gamadu forums would be the logical place to discuss this stuff, but they seem pretty quiet (last Artemis post was almost a month ago), so you're probably better off here. That's obviously a question for appel though, and I'm happy to jump over there if necessary :).

P.S. Off topic, Jario now has sounds and better handling of passability between colliding entities, which I wrote a bit about. I've just recently added more level content and a streaming level loader (kind of), so it's getting close to being a complete (albeit short) game!


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 13, 2011 9:28 am 
Offline
Game Developer
User avatar

Joined: Tue Nov 21, 2006 4:46 am
Posts: 620
Location: Iceland
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 :)


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 13, 2011 1:34 pm 
Offline
Regular
User avatar

Joined: Tue Apr 07, 2009 12:58 pm
Posts: 232
Location: Uruguay
@appel, did you read my previous question?

On other news, I am using an ScriptSystem, the idea is to have a component with a piece of custom code, for example, imagine you want some entity to behave in an special way, instead creating a SpecialBehaviorSystem and a SpecialBehaviorComponent for that (I don't think that is scalable) you add to the entity an ScriptComponent with a custom implementation of Script.

Script looks like this:

Code:
public interface Script {
   
   void init(World world, Entity e);
   
   void update(World world, Entity e);
   
   void dispose(World world, Entity e);
   
}


Then we have the ScriptComponent:

Code:
public class ScriptComponent extends Component {

   private Script script;

   public Script getScript() {
      return script;
   }

   public void setScript(Script script) {
      this.script = script;
   }

   public ScriptComponent(Script script) {
      this.script = script;
   }

}


And then the ScriptSystem:

Code:
public class ScriptSystem extends EntityProcessingSystem {
   
   ComponentMapper<ScriptComponent> scriptComponentMapper;
   
   public ScriptSystem() {
      super(ScriptComponent.class);
   }
   
   @Override
   protected void initialize() {
      super.initialize();
      scriptComponentMapper = new ComponentMapper<ScriptComponent>(ScriptComponent.class, world.getEntityManager());      
   }
   
   @Override
   protected void added(Entity e) {
      super.added(e);
      Script script = scriptComponentMapper.get(e).getScript();
      script.init(world, e);
   }
   
   @Override
   protected void removed(Entity e) {
      Script script = scriptComponentMapper.get(e).getScript();
      script.dispose(world, e);
      super.removed(e);
   }

   @Override
   protected void process(Entity e) {
      Script script = scriptComponentMapper.get(e).getScript();
      script.update(world, e);
   }

}


As you can see, the API is very simple and, at least for me, it let me do a lot of different custom behaviors in an easy way.

The Script custom implementation is stateless by default, so you can reuse it in several entities, but you could also make it stateful by holding variables inside the Script implementation, in that case you cant reuse it between multiple entities.

An example of a Script:

Code:
new ScriptJavaImpl() {

   // this makes the script stateful
   int time;

   public void init(World world, Entity e) {
      time = 500;
   }

   @Override
   public void update(World world, Entity e) {
      HealthComponent healthComponent = ComponentWrapper.getHealth(e);
      Spatial spatial = ComponentWrapper.getSpatial(e);
      SpriteComponent spriteComponent = ComponentWrapper.getSprite(e);
      TouchableComponent touchableComponent = ComponentWrapper.getTouchable(e);

      time -= world.getDelta();

      if (time <= 0) {
         world.deleteEntity(e);
         return;
      }

      if (touchableComponent.isTouched()) {
         Container health = healthComponent.getHealth();
         float damagePerMs = 10f / 1000f; // 10 damage per second
         float damage = damagePerMs * (float) world.getDelta() * (1f - healthComponent.getResistance());
         health.remove(damage);
      }

      if (healthComponent.getHealth().isEmpty()) {
         createDeadFace(spatial, 6, 1500, spriteComponent.getColor());
         world.deleteEntity(e);
      }

   }
};


That is used in our Face Hunt game to detect when you have to remove a touched face.

Hope it could be useful.

_________________
Image


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic This topic is locked, you cannot edit posts or make further replies.  [ 420 posts ]  Go to page Previous  1 ... 12, 13, 14, 15, 16, 17, 18 ... 28  Next

All times are UTC


Who is online

Users browsing this forum: Bing [Bot] and 0 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group