Advertisement

Implementing spell casts in entity component systems ?

Started by July 24, 2020 01:18 PM
4 comments, last by Oberon_Command 4 years, 3 months ago

I want to implement an entity component system, but I've been some trouble as of late. Let's say that I have a fire ball spell that spawns a ball of fire which moves towards a random enemy in 500 units range. In an OO architecture, I would have a Player class and I could simply detect a keypress and cast the spell like so:

function player:update(dt)
  if is_key_pressed('e') then
     self:cast_fire_ball()
  end
end

and the cast fireball spell implementation would be something close to this:

function Player:cast_fire_ball()
    -- query the world for nearby units, filter the allies out and select a 
    -- random enemy
    local units_in_range = self.world:query('circle', 250)
    local enemies = filter(units_in_range, function(unit) 
                            return unit.is_hostile 
                    end)
    local target = random(enemies)
    -- create a new projectile of type fireball and set the position and    
    -- direction vectors
    self.world:add(Projectile:new(FIREBALL, self.pos, target.pos - self.pos)
end

Now the projectile has an on_collide method that is called by the world whenever a collision occurs between the projectile and any unit.

In an entity component system however, the player isn't an instance of a class, it's just a uuid like 1234 or in some implementations, an id with an array of components that maybe has a bitset to work out what components it has. I can imagine the player having an InputComponent that detects keypresses, and the InputSystem takes care of that. But what happens from there ? and where does the code for the `cast_fire_ball` spell live ?

my advice is: dont implement this aspect as ECS. I personally use ECS to handle generic features like „sprite“, „collision“, or “input“. then i have concrete OOP-bases classes that tie them together to have a behaviour of „when this key is pressed, cast a spell after 500ms“.

trust me, when you try to tie those specific gameplay-loops into an ECS, you are in a world of pain.

Advertisement

@undefined Thanks for the heads up. I'll do it this way then.

So if I understood correctly, you mean a class that has a components array with other methods on it that may or may not access the components in that list? or the other way of updating all components of an entity in the update method of the entity itself and having the game's update loop cycle through all the entities ?

I would have the components be updated in their own systems update loop, by iterating over the array of lets say all input-components.

then for controlling the components behaviour via script, you simply have a way for accessing components that belong to a given entity and modify properties or call methods on them to modify state directly.

inJuly said:
I can imagine the player having an InputComponent that detects keypresses, and the InputSystem takes care of that. But what happens from there ? and where does the code for the `cast_fire_ball` spell live ?

First of all, typically you would have a separate component of some kind that represented what “actions” the player object wanted to take. The player and whatever their in-game representation was (call it a “unit” or “character”) would usually be different components or different objects. That way you could attach the player to arbitrary characters or have the AI or test automation drive the player without having to involve the input system in any way.

The fireball function could be done in a number of different ways, but it's probably best as a free function, because something high-level like “cast fireball” is really going to be an integration point, or “glue code," for the underlying gameplay systems. The specifics of how it does what it does will be dictated by your architecture; does networking have to get involved? Do you need to query for entities within a given location or “damageable components”? Maybe if the fireball has visual effects associated with it you'd want to spawn a new entity with some effects components on it, and maybe a “damage everything within a set radius when the fireball animation hits a certain keyframe” component as well. Lots of options here.

Connecting the two would be whatever system you use to model what spells a player has. It would consume the actions the player wanted to take that frame and determine which spell to invoke. Lots of architectural options there; do you send a message to all the components on the player saying “cast current spell?” Do you have a “spell inventory component” that you directly to tell to invoke the currently-selected spell? Is the currently selected spell a pure UI thing, or does it have to involve networked game state in some way? Hard to say without knowing more about your specific situation.

This topic is closed to new replies.

Advertisement