Difficult game logi with Entity systems?

I have used Entity systems a bit now and quite like them. I have found them fast and useful and fairly easy to integrate with GUIs and multiplier.

However i have only done fairly simple game logic type games. For example platforms or something similar. I keep one simple rule, a system or behavior can only change the entity it is operating on. To change parameters of other entities i use a message which gets actioned next tick. This really has made for very easy to write up games and over all been a big improvement over POJOs.

But now I am working on something more complicated. I will use a concrete example from an existing game since that is often an easier way to understand things.

Say i have a space ship, and i want to activate my gun. Note the gun is a “module” and can be replaced by different guns so it is an entity. The gun uses power, and if the ship (another entity) does not have the power it won’t activate. If it activates it could apply damage to the target. But if the target is say not shootable, then the gun should also fail to activate. Thus i have 3 entities and all their states matter on what can be done.

I can’t see a nice way to implement this with my current implementation with messages between entities easily. Any ideas? Has anyone implemented quite complicated game logic into an entity system?

Components and systems. Components contain data; systems contain logic. Here’s an example: http://gamadu.com/artemis/tutorial.html

I know that and it doesn’t answer the question. Details matter when implementing the details.

I’m not quite groking your problem (didn’t sleep last night), but this is the best ECS tutorial I’ve ever seen: http://gamedev.stackexchange.com/questions/31473/role-of-systems-in-entity-systems-architecture

Sounds like you need a system that accepts the relevant state data from those entities, and then fires the gun or not.

I’m double buffering the entities, so any modifications to an entity do not apply to the current “logic frame”. Instead they are deferred to the next frame. It keeps my model consistent so far - all logic is based off the same instant of time.

But if i have 2 things happen, then both end up using more power than is available. The problem is that 3 entities need to be updated atomically as a transaction. ie if it fails roll back. But its not clear the easiest way to implement that. A system does not take 3 entities in at once. So you can’t just have a system.

You have:

  • A space ship.
  • A gun.
  • A target.

I agree with BurntPizza. There’s no problem since a space ship has a GunModule component and a CanTarget component. The FireGun system processes all entities which have both these modules, reads the gun module and the target entity, checks if all the requirements for firing are fulfilled (energy > gun.energyRequired() && target.isShootable()) and fires if it can.

EDIT: It would probably make much more sense to separate the targeting code from the firing code. In strategy games units generally acquire a single target, then tries to fire at it until it dies. If the target dies or goes out of range, it tries to acquire a new target. With such a scheme, it’s relatively simple to filter out targets that you cannot fire at in the first place, which would simplify the firing code immensely.

So you have two or more things competing for a limited power budget. Isn’t this just a job for the AI, to pick the right system to power up based on current threats etc? In the real world, without command and control, both systems would try to power up, and immediately go down again, maybe taking out the life support system with them :slight_smile: So you do have a missing component, it’s called the ship’s captain.

Or you could just redesign the ship with enough power for all its systems.

With the entity system I described above, you’d simply have multiple guns (and possibly multiple targets) and simply prioritize them.

I should say its not single threaded. Any one system does not deal with more than one entity at a time so i don’t need synchronization between them because they could all be updated on different threads.

So a System only can, by contract change the values of the entity it is on right now. Never others. Others are changed in previous versions var a message that never fails to apply. Now however i have messages that can fail.

So let me try better to explain. My gun entity, is activated. This has the effect of applying power drain from the ship and damage to the target. Both the ship and target are different entities that are not on the same thread and therefore can’t just be “updated”. Or some consistent type of synchronization is needed.

Ah, I understand. In my opinion I think the problem is that the gun is an entity in the first place. Since the gun is under the control of the AI of the ship, it’s actually a component of the ship, not a separate entity. That’ll eliminate the gun entity from the equation at least.

Concerning the target problem, there are a few solutions depending on what you need. The simplest solution is simply to synchronize on the target and modify what you want. You may not want multiple entities to shoot at the same target if the target dies from the first shot. Although entities would be able to detect if the entity they’re firing at has died, the actual firing order will depend on the threads (= semi-random, non-deterministic).

If you require determinism here, it might be possible to completely switch the problem around. I’ll take inspiration from GPUs. GPUs can multithread each pixel since you’re only ever allowed to write to the current pixel. You’re however free to read from whatever texture at whatever point you wish. This is called “gathering”, e.g. gather data from multiple places but only write to one place. You’re doing the opposite since each entity reads from itself but writes to multiple places, AKA “scattering”. It’s possible to switch the problem around so we get gathering instead. Instead of checking which entities the current entity is targeting, you check which entities that are targeting the current entity. That way you can easily apply the damage in a predictable order, but this instead introduces problems with checking if the entities that are firing actually can fire. You can split this up into four passes to get rid of that problem though. For a completely deterministic threaded algorithm, you can do the following:

System 1, targetting: Each entity searches for targets for its guns, validates those targets (checks if they’re shootable) and stores them if targets are found.

System 2, firing AI: Each entity figures out which guns to fire based on the available power but does not actually modify the power yet. When the entity decides to shoot another entity, you schedule a shot by storing an object representing the shot in a unsynchronized list in the firing entity, and also write the shot object to a synchronized list in the target being shot.

System 3, damage updating: Each entity checks its list of shots firing at it, sorts them to guarantee determinism (sort by entity ID or something) and then applies their damage. If the damage is applied successfully, you mark the shot as successful. If the object dies and cannot be damaged anymore, you mark the shot as unsuccessful.

System 4, power draw: Each entity checks each shot that it fired, checks if it was successful and updates the current amount of power based on the cost of the successful shots.

Psuedo code:

System 1


for(Entity e : entities){
    for(Gun gun : e.getGuns()){
        gun.acquireTarget();
    }
}

System 2


for(Entity e : entities){
    e.figureOutWhichGunsToFire();
    for(Gun gun : e.getWhichGunsToFire()){

        Entity target = gun.getTarget();

        Shot shot = new Shot(e, target, powerDraw, damage);
        e.firedShots.add(shot);
        synchronized(target){
            target.hits.add(shot);
        }

    }
}

System 3


for(Entity e : entities){
    List<Shot> hits = e.getHits();
    sort(hits);
    for(Shot hit : hits){
        if(e.isAlive()){
            e.health -= hit.getDamage();
            hit.setSuccessful(true);
        }else{
            hit.setSuccessful(false);
        }
    }
}

System 4


for(Entity e : entities){
    List<Shot> firedShots = e.getFiredShots();
    for(Shot firedShot : firedShots){
        if(firedShot.wasSuccessful()){
            e.power -= shot.getPowerDraw();
        }
    }
}

EDIT: System 1 and 2 could be combined into a single system:


for(Entity e : entities){
    for(Gun gun : e.getGuns()){
        gun.acquireTarget();
    }

    e.figureOutWhichGunsToFire();
    for(Gun gun : e.getWhichGunsToFire()){

        Entity target = gun.getTarget();

        Shot shot = new Shot(e, target, powerDraw, damage);
        e.firedShots.add(shot);
        synchronized(target){
            target.hits.add(shot);
        }

    }
}

Multithreading AIs is IMO a very bad idea outside of massively multiplayer. All the extra overhead is going to lower (probably drastically) throughput. (However tossing time-consuming bits out to other threads can make sense).

Well its not a game, its an agent experiment. I also have some clusters to use on this. Like about 5-10 thousand cores if need be. I used a game example because its more accessible and well NDAs.

My reply was more for general consumption. With many cores: why not actors?

If we forget about the model for a moment. You have entity A which want to attack entity B (which might be on different machines) with a weapon element composed with A we’ll call ‘W’. The checking avail power to use W should be doable locally in exactly the same way an uber basic game would do. Assuming {A,W} don’t know enough about ‘B’ to perform the triggering then can’t you just message ‘B’ with “can W be triggered on you, respond to ‘A’”?

We are trying something different from actors. Yes the “can i do this” message is probably the way we are leaning. But it ends up being a distributed transaction protocol more or less.