1

As the title suggests, my question is how to abstract the concept of 'attacking' using an ECS (entity component system). The "game" I'm working on is a roguelike in real-time, where all enemies/NPCs/player is an Entity (just an Id) with components.

I read this link and it helped clarify some things about an attack system, but I'm still confused how to implement this with an ECS.

  • I might be underthinking this, but isn't writing and giving each (attackable) entity an health component and an attack component enough? – Qqwy Jan 12 '13 at 21:43
  • 1
    At first it may seem like that, but other things come into effect, such as: How does an entity attack? Which animation does it go to? How to "activate" an attack (either from keyboard input or from AI)? It's these questions that confuse me when trying to design this. – Mohammed Hossain Jan 12 '13 at 23:02
  • 2
    None of those things really have anything to do with component systems, though. That's just.. y'know.. garden variety programming, right? – Trevor Powell Jan 12 '13 at 23:41

2 Answers2

1

Whether your game uses an Entity-Component model, Inheritence Hierarchy, or some mixin of both strategies, it doesn't dictate what a combat system is within the scope of a game.

As Qqwy points out, we're discussing the notion of some damage modifier applied to health. The damage modifier can also affect the durability of the attacked entity's gear with time too, making it less useful with every attack made. You may need to factor in resistences, temporary stat modifiers too which could make an entity more powerful or more susceptable to damage.

My advice is design your combat system very high-level like. Consider what you want your combat system to involve and how it will behave. Once you have this guideline, you'll quickly notice what makes sense to be components.

One obvious idea would be to just create a CombatStatComponent that has all your stats. This could include the base stats an avatar can have, the extra stats they can acquire possibly from gear, resistence levels the avatar presently has. The combat system uses this components data to measure the damage possible between two entities when an attack is made. When the attack occurs, an event gets sent to the damaged entity with the damage done. It simply modifies it's health component's value.

But keep in mind, all the ECS does in this scenario is describe how you organize the code that makes up this combat system. But the internal steps the system must perform to apply some damage remains the same from a generalistic point of view regardless of how you manage entities as a whole.

Naros
  • 2,012
  • 10
  • 13
0

I have an Attack component that has a boolean for the active status and a timestamp when I did swing the last time, like a swing timer.

The AttackInputSystem on the client will check that Attack component and if the corresponding key is pressed, it either sends a StartAttackAction or StopAttackAction depending on the active status of the Attack component.

The server has an AttackActionSystem that listens for those action events, and if the target is valid, updates the Attack component and confirms the action by sending an EntityUpdateEvent with the updated Attack component:

Entity player = operator.getPlayer();
Attack attack = player.getComponent(Attack.TYPE);
boolean wantsAttack = evt instanceof StartAttackAction;
if (attack.active() != wantsAttack) {
  if (!wantsAttack || validTarget(operator).isPresent()) {
    // update attack component
    attack = new Attack(wantsAttack, attack.timestamp());
    player.setComponent(attack);
    EventQueue eventQueue = operator.getEventQueue();
    eventQueue.publish(this, new EntityUpdateEvent(player.getId(), attack));
  }
}

The server also has an AttackSystem which will execute the attack by checking the conditions necessary to attack and sending an AttackEvent:

long now = System.currentTimeMillis();
Entity player = operator.getPlayer();
Attack attack = player.getComponent(Attack.TYPE);
if (attack.active()) {
  validTarget(operator).ifPresentOrElse(entity -> {
    if (now - attack.timestamp() > WEAPON_SPEED && inRange(player, entity) && isFacing(player, entity)) {
      eventQueue.publish(new AttackEvent(player.getId(), entity.getId()));
      player.setComponent(new Attack(true, now));
    }
  }, () -> {
    // toggle off auto attack
    Entity player = operator.getPlayer();
    eventQueue.publish(new EntityUpdateEvent(player.getId(), new Attack(false, now)));
  });
}

The AnimationSystem on the client takes the Attack component state to animate an 'ATTACK_IDLE' animation and also plays the 'ATTACK' animation on AttackEvent.

The DamageSystem is responsible to react on the AttackEvent by updating the Health component, optionally creating the DeathEvent.

I'm not seeing yet how to transfer this into a more generic AbilitySystem. But it might give a good starting point.

benez
  • 101
  • 1