2
\$\begingroup\$

I've been using Unity's component based design, and the biggest problem I've faced is how to control an object's( not component's ) state.

The problem is, some components must act differently based on the object's state. However, checking an object's state within a non-specific component( imagine we have a "DamageEnemyComponent" ) won't work at all because it makes assumptions about the object it's attached to.

The alternative is to make each component perform one, and only one function. The components are then disabled/enabled or added/removed depending on the object's state( probably managed by a more specific component. )

Imagine we have a "Boomerang" object. Assume it has only two states; idle( being held by the player ) and firing. In the idle state, the boomerang would have very few components, because we don't want it to interact with the world while it's simply being held. It may have components to listen for a "throw", though. Once it is thrown, some state manager on the object needs to update the object to have components for moving, damaging enemies, maybe picking up items etc. When the boomerang is back in the hands of the player, it's component list would need to be restored. This would work, but it would basically be building a new game object for each state, which is incredibly tedious and messy without Unity's editor. Not to mention the inheritance and interfaces can't be used well here( since the state specific components will be built into the state manager script. )

I can't see a way to control state using Unity's components. How is this usually done? It feels as if Unity should provide a way to build different states in the editor, much like building an object.

\$\endgroup\$
  • \$\begingroup\$ I find that, basically, having components with zero assumptions about the object is wishful thinking. It just never happens in reality. So I don't fret when a component needs to assume existence of some other component(s) on the same object and access them. \$\endgroup\$ – Nevermind Nov 19 '14 at 5:37
  • \$\begingroup\$ @Nevermind Is it right that the "DamageEnemy" component should know about a boomerang? At that point, the DamageEnemy component is better off being built into the state manager. \$\endgroup\$ – Ben Nov 19 '14 at 5:47
  • \$\begingroup\$ No, but you can do it the other way round: Boomerang component holds state, and knows to enable/disable DamageEnemy depending on it. That's how I'd do that anyway. \$\endgroup\$ – Nevermind Nov 19 '14 at 5:48
  • \$\begingroup\$ @Nevermind Add properties to the script to force adding a component to the object.( A dependency ) \$\endgroup\$ – darkgaze Oct 25 '18 at 10:46
2
\$\begingroup\$

For states, I usually use Enums, and then do a switch statement within the Update method. Enabling/disabling components is probably easier than adding/removing them.

If you need to listen for or be ready to execute a method that should only happen within a specific state, check the state at the start of the method.

If the file gets too long and confusing, you can certainly break certain parts down into their own components and link/reference the various classes between each component as required, then enable/disable each component as required when changing states.

Here's a rough version of how I might code the start of a Boomerang:

// Various states the boomerang can be in
enum BoomerangState {
    Idle = 1,
    Firing = 2
}

BoomerangState state;

// Decide if we should launch the boomerang
bool WeShouldLaunch() {
    return state == BoomerangState.Idle ? true : false;
}

// Launch the boomerang and enable its collider
void Launch() {
    GetComponent<BoxCollider>().enabled = true;
    state = BoomerangState.Firing;
}

// Return boomerang to idle state and disable its collider
void MakeIdle () {
    state = BoomerangState.Idle;
    GetComponent<BoxCollider>().enabled = false;
}

void Update () {
    // Do things based on current state
    switch (state) {
        case BoomerangState.Idle:
            // Hold boomerang and be ready to launch it
            if (WeShouldLaunch()) {
                Launch();
            }
            break;
        case BoomerangState.Firing:
            // Make boomerang fly where needed
            // ...
            // Then when done:
            MakeIdle();
            break;
    }
}

// Only deal damage if boomerang is out and about (eg not idle)
void OnCollisionEnter(Collider col) {
    if (state == BoomerangState.Firing) {
        // Damage the collider we hit
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.