30
\$\begingroup\$

I'm just starting really getting my head around component based design. I don't know what the "right" way to do this is.

Here's the scenario. The player can equip a shield. The the shield is drawn as bubble around the player, it has a separate collision shape, and reduces the damage the player receives from area effects.

How is such a shield architected in a component based game?

Where I get confused is that the shield obviously has three components associated with it.

  • Damage reduction / filtering
  • A sprite
  • A collider.

To make it worse different shield variations could have even more behaviors, all of which could be components:

  • boost player maximum health
  • health regen
  • projectile deflection
  • etc

  1. Am I overthinking this? Should the shield just be a super component?
    I really think this is wrong answer. So if you think this is the way to go please explain.

  2. Should the shield be its own entity that tracks the location of the player?
    That might make it hard to implement the damage filtering. It also kinda blurs the lines between attached components and entities.

  3. Should the shield be a component that houses other components?
    I've never seen or heard of anything like this, but maybe it's common and I'm just not deep enough yet.

  4. Should the shield just be a set of components that get added to the player?
    Possibly with an extra component to manage the others, e.g. so they can all be removed as a group. (accidentally leave behind the damage reduction component, now that would be fun).

  5. Something else that's obvious to someone with more component experience?

\$\endgroup\$
1
  • \$\begingroup\$ I took the liberty of making your title more specific. \$\endgroup\$
    – Tetrad
    Jun 20, 2011 at 5:08

5 Answers 5

11
\$\begingroup\$

Should the shield be its own entity that tracks the location of the player? That might make it hard to implement the damage filtering. It also kinda blurs the lines between attached components and entities.

Edit: I think there's not enough "autonomous behaviour" for a separated entity. In this specific case, a shield follows the target, works for the target and does not outlive the target. While I tend to agree that there's nothing wrong with the concept of a "shield object", in this case we're dealing with behaviour, which fits just fine in a component. But I'm also an advocate of purely logical entities (as opposed to full-blown entity systems in which you can find Transform and Rendering components).

Should the shield be a component that houses other components? I've never seen or heard of anything like this, but maybe it's common and I'm just not deep enough yet.

See it in a different perspective; adding a component adds other components as well, and upon removal, the additional components are gone too.

Should the shield just be a set of components that get added to the player? Possibly with an extra component to manage the others, e.g. so they can all be removed as a group. (accidentally leave behind the damage reduction component, now that would be fun).

This could be a solution, it would promote reuse, however it is also more error prone (for the issue you mentioned, for instance). It's not necessarily bad. You might find out new spell combinations with trial and error :)

Something else that's obvious to someone with more component experience?

I'm going to elaborate a bit.

I believe you noticed how some components should take priority no matter when they've been added to an entity (this would answer your other question as well).

I'm also going to assume we're using message-based communication (for the sake of discussion, it's just an abstraction over a method call for the moment).

Whenever a shield component is "installed", shield component message handlers are chained with a specific (higher) order.

Handler Stage    Handler Level     Handler Priority
In               Pre               System High
Out              Invariant         High
                 Post              AboveNormal
                                   Normal
                                   BelowNormal
                                   Low
                                   System Low

In - incoming messages
Out - outgoing messages
Index = ((int)Level | (int)Priority)

The "stats" component installs a "damage" message handler at the In/Invariant/Normal index. Every time a "damage" message is received, decrease the HP by its "value" amount.

Fairly standard behaviour (put in some natural damage resistance and / or racial traits, whatever).

The shield component installs a "damage" message handler at the In/Pre/High index.

Every time a "damage" message is received, deplete the shield energy and substract
the shield energy from the damage value, so that the damage down the message
handler pipeline is reduced.

damage -> stats
    stats
        stats.hp -= damage.value

damage -> shield -> stats
    shield
        if(shield.energy) {
            remove_me();
            return;
        }
        damage.value -= shield.energyquantum
        shield.energy -= shield.energyquantum;

     stats
        stats.hp -= damage.value

You can see this is pretty flexible, albeit it would require careful planning when designing component interaction, as you're going to have to determine in which part of the message handling pipeline component message event handlers are installed.

Makes sense? Let me know if I can add more detail.

Edit: regarding multiple component instances (two armour components). You can either keep track of the total instance count in just one entity instance (this kills per-component state, however) and just keep adding message event handlers, or make sure your component containers allow for duplicate component types in advance.

\$\endgroup\$
6
  • \$\begingroup\$ You've replied "No" to the first question without giving any reason. Teaching others is about helping them to understand the reasoning behind any decision. IMO, the fact that in RL a force field would be a separate "physical entity" from your own body is enough to let it be a separate entity in code. Can you suggest good reasons to suggest why going this route is bad? \$\endgroup\$
    – Engineer
    Jun 20, 2011 at 14:29
  • \$\begingroup\$ @Nick, I am by no means trying to teach anything to anyone, rather sharing what I know on the subject. I am however going to add a rationale behind that "no" that hopefully will remove that unpleasant downvote :( \$\endgroup\$
    – raine
    Jun 20, 2011 at 15:22
  • \$\begingroup\$ Your autonomy point makes good sense. But you note: "in this case we're dealing with behaviour". True -- behaviour involving an entirely separate physical object (the shield collision shape). For me, one entity ties to one physics body (or compound set of bodies connected eg. by joints). How do you reconcile this? For my part, I would feel uncomfortable adding a "dummy" physical fixture which would activate only if the player happens to use a shield. IMO inflexible, hard to maintain across all entities. Consider too, a game where shield belts keep shields on even after death (Dune). \$\endgroup\$
    – Engineer
    Jun 20, 2011 at 16:04
  • \$\begingroup\$ @Nick, the majority of entity systems have both logical and graphics-related components alike, so, in this case, having an entity for a shield is absolutely reasonable. In purely logical entity systems, "autonomy" is the product of how complex an object is, its dependencies and lifetime. In the end, requirement is king - and considering that there's no real consensus about what an entity system is, there's plenty of room for project-tailored solutions :) \$\endgroup\$
    – raine
    Jun 20, 2011 at 17:32
  • \$\begingroup\$ @deft_code, please let me know if I can improve my answer. \$\endgroup\$
    – raine
    Jun 28, 2011 at 14:28
4
\$\begingroup\$

1) Am I overthinking this? Should the shield just be a super component?

Maybe, depends on how reusable you want your code to be and if it makes sense.

2) Should the shield be its own entity that tracks the location of the player?

Not unless this shield is some sort of creature that can walk around independently at some stage.

3) Should the shield be a component that houses other components?

This sounds a lot like entity, so the answer is no.

4) Should the shield just be a set of components that get added to the player?

It is likely.

"Damage reduction / filtering"

  • core shield component functionality.

"A sprite"

  • is there a reason you cannot add another SpriteComponent to your character entity (in other words more than one component of certain type per entity)?

"A collider"

  • are you sure you need another one? This depends on your physics engine. Can you send a message to character entity's ColliderComponent and ask it to change shape?

"Boost player maximum health, health regen, projectile deflection etc."

  • other artifacts might be able to do this (swords, boots, rings, spells/potions/visiting shrines etc.) so these should be components.
\$\endgroup\$
0
3
\$\begingroup\$

A shield, as a physical entity, is no different from any other physical entity, e.g. a drone which circles around you (and which in fact, could itself be a type of shield!). So make the shield a separate logical entity (thus allowing it to hold its own components).

Give your shield a couple of components: a Physical/Spatial component to represent it's collision form, and a DamageAffector component which holds a reference to some entity to whom it will apply increased or reduced damage (eg. your player character) every time the entity holding the DamageAffector takes damage. Thus your player is taking damage "by proxy".

Set the shield entity's position to the player's position every tick. (Write a reusable component class that does this: write once, use many times.)

You will need to create the shield entity, eg. on collecting a powerup. I use a generic concept called an Emitter, which is a type of entity component that spawns new entities (usually through the use of an EntityFactory which it references). Where you decide to locate the emitter is up to you -- eg. put it on a powerup and have it trigger as the powerup is collected.


Should the shield be its own entity that tracks the location of the player? That might make it hard to implement the damage filtering. It also kinda blurs the lines between attached components and entities.

There is a fine line between logical subcomponents (Spatial, AI, Weapon slots, Input-processing etc. etc.) and physical subcomponents. You need to decide which side of it you stand on, as this strongly defines what sort of entity system you have. For me, the Physics subcomponent of my Entity handles the physics-hierarchical relationships (such as limbs in a body -- think scenegraph nodes), while the logic controllers noted above are typically what are represented by your entity components -- rather than these representing individual physical "fixtures".

\$\endgroup\$
3
\$\begingroup\$

Should the shield be a component that houses other components?

Maybe not houses other components, but controls the lifetime of sub components. So in some rough pseudo code, your client code would add this "shield" component.

class Shield : Component
{
    void Start() // happens when the component is added
    {
        sprite = entity.add_component<Sprite>( "shield" );
        collider = entity.add_component<Collider>( whatever );
        //etc
    }

    void OnDestroy() // when the component is removed
    {
        entity.remove_component( sprite );
        entity.remove_component( collider );
    }

    void Update() // every tick
    {
        if( ShouldRemoveSelf() ) // probably time based or something
            entity.remove_component( this );
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ It not clear what this means in your answer. Is this referring to the Shield component or did you mean the entity that's using the shield, its parent? The confusion might be my fault. "Component based" is kinda vague. In my version of component based entities, an entity is simply a component container with some minimal functionality of its own (object name, tags, messaging, etc). \$\endgroup\$
    – deft_code
    Jun 20, 2011 at 5:54
  • \$\begingroup\$ It would be less confusing if I used gameObject or something. It's a reference to the current game object/entity/whatever owns the components. \$\endgroup\$
    – Tetrad
    Jun 20, 2011 at 6:18
0
\$\begingroup\$

If your component system allows scripting, the shield component could be almost a super component that just calls a script for its "effect" parameter. That way you maintain simplicity of a single component for shields, and offload all the logic of what it actually does to custom script files that get fed to shields by your entity definitions.

I do something similar for my Moveable component, it countains a field that is of keyreaction script (a subclass of script in my engine) this script defines a method that follows my input message. as such i can simply do something like this in my tempalte definition file

camera template
    moveable
    {
        keyreaction = "scriptforcameramoves"

    }  

player template
    moveable
    {
        keyreaction = "scriptfroplayerkeypress"

    }  

then in my moveable component during message registering i register the scripts Do method (code in C#)

Owner.RegisterHandler<InputStateInformation>(MessageType.InputUpdate, kScript.Do);

of course this relies on my Do method following the pattern of functions that my RegisterHandler takes. In this case its (IComponent sender, ref type argument)

so my "script" (in my case also C# just runime compiled) defines

 public class CameraMoveScript : KeyReactionScript
{
 public override void Do(IComponent pSender, ref InputStateInformation inputState)
 {
    //code here
 }
}

and my base class KeyReactionScript

public class KeyReactionScript : Script
{
      public virtual void Do(IComponent pSender, ref InputStateInformation inputState);
}

then later when an input component sends a message of the type MessageTypes.InputUpdate with the type as such

 InputStateInformation myInputState = InputSystem.GetInputState();
 SendMessage<InputStateInformation>(MessageTypes.InputUpdate, ref myInputState);

The method in the script that was bound to that message and datatype will be fired and handle all the logic.

The code is fairly specific to my engine, but the logic should be functional in any case. I do this for many types in order to keep the component structure simple and flexible.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .