Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I have the next problem.

A item can have a lot of states:

NORMAL  = 0000000
DRY     = 0000001
HOT     = 0000010
BURNING = 0000100
WET     = 0001000
COLD    = 0010000
FROZEN  = 0100000
POISONED= 1000000

A item can have some states at same time but not all of them

  • Is impossible to be dry and wet at same time.
  • If you COLD a WET item, it turns into FROZEN.
  • If you HOT a WET item, it turns into NORMAL
  • A item can be BURNING and POISON

Etc.

I have tried to set binary flags to states, and use AND to combine different states, checking before if it is possible or not to do it, or change to another status.

Does there exist a concrete approach to solve this problem efficiently without having an interminable switch that checks every state with every new state?

It is relatively easy to check 2 different states, but if there exists a third state it is not trivial to do.

share|improve this question
    
I don't understand why you think a 3rd state makes it not so trivial as it should be as simple as checking 2. Can you post an example of how you are currently doing it and an example using 3 states. –  John Nov 11 '12 at 16:45
    
Maybe you can check the best solution to solve the declaration if it has a lot of states: stackoverflow.com/q/13385744/1077364 –  Piperoman Nov 14 '12 at 19:47

3 Answers 3

up vote 6 down vote accepted

When I need to use flags I usually do something along these lines.

enum obj_state
{
    NORMAL      = 0x00000;
    DRY         = 0x00002;
    HOT         = 0x00004;
    BURNING     = 0x00008;
    WET         = 0x00010;
    COLD        = 0x00020;
    FROZEN      = 0x00040;
    POISONED    = 0x00080;
};

int objFlags;

void DryOn() { objFlags |= DRY; }
void HotOn() { objFlags |= HOT; }
// etc...

void DryOff() { if (FlagOn(DRY)) objFlags ^= DRY; }
void HotOff() { if (FlagOn(HOT)) objFlags ^= HOT; }
// etc...

bool isDryOn() { return FlagOn(DRY); }
bool isHotOn() { return FlagOn(HOT); }
// etc...


// If the given Bit is on this will return true.
bool FlagOn(obj_state s) { return (objFlags & s) == s; }

// returns -1 if failed, 1 if successful
int apply_cold(Object obj)
{
    if (obj.isWetOn())
    {
        obj.ColdOff();
        obj.WetOff();
        obj.FrozenOn();
    }
    else
        return -1;

    return 1;
}


//---------------------------------
// alt way of doing DryOn and WetOn
// since these conditions can not be
// active at the same time.
void DryOn() 
{ 
    if (isWetOn()) 
        return;
    else
        objFlags |= DRY; 
}

void WetOn() 
{ 
    if (isDryOn())
        return;
    else;
        objFlags |= WET; 
}

This makes using them for stuff like apply_cold() very easy and you can obviously build in your state conditions like dry and wet.

share|improve this answer
    
I like this way to solve the problem, thanks –  Piperoman Nov 11 '12 at 21:03
    
Maybe you can check the best solution to solve the declaration if it has a lot of states: stackoverflow.com/q/13385744/1077364 –  Piperoman Nov 14 '12 at 19:47

Representing your states as bitmask like you write, you can just translate your descriptions of the constraints into code:

if ( (state & HOT) && (state & COLD) ) {
    state &= ~HOT;
    state &= ~COLD;   // reset both HOT and COLD flags if both are set
}

if ( (state & COLD) && (state & WET) ) {
    state &= ~WET;    // cold items can't be wet
    state |= FROZEN;  // instead, they're frozen
}

if ( (state & HOT) && (state & WET) ) {
    state &= ~WET;    // hot and wet items dry up...
    state &= ~HOT;    // ...and cool down
}

// add other constraints here...

You could wrap that into an makeStateConsistent() that you can call before testing the state bits to ensure that the state makes sense.

However, one limitation of this approach is that it can't account for the order of state changes. For example, if you want to have a different outcome for hot items that become wet than for wet items that become hot, you can't do it like this: all the makeStateConsistent() method sees is a hot and wet object, with no information about how it got to be that way.

Instead, what you could do is make the item state private (at least conceptually) and manipulate it through a set of methods like coolItem(), heatItem(), wetItem(), dryItem() and so on. That way, the state change methods themselves can take care of any additional changes. For example, the heatItem() method might look something like this:

if ( state & COLD ) {
    state &= ~COLD;    // cold items become normal temp when heated
    if ( state & FROZEN ) {
        state &= ~FROZEN;  // ...and melt if they were frozen
        state |= WET;
    }
} else if ( state & WET ) {
    state &= ~WET;    // wet items dry up when heated, stay normal temp
} else {
    state |= HOT;     // dry normal temp items become hot
}

Of course, you may still want to also have a makeStateConsistent() method as a backup, just in case you have a bug in your state change methods.

Also, in some cases you may be able to simplify your code by eliminating unnecessary states. For example, do you really need a separate FROZEN state, or would it be enough to just treat any cold and wet items as frozen?

share|improve this answer

Two observations:

  1. Your condition system seems to have two orthogonal axes: temperature and poison. Represent them as such.
  2. When thinking about this you should separate transitions from states. COLD and HOT are transitions in the way you mention them, not states.

Combining those observations would result in something like this:

// These is the representation of the two axes.
int temperature; // can be between -2 and +2, 0 is normal, 1 is hot, 2 is burning, -1 is cold, -2 is frozen
bool poisoned;

// These methods represent state transitions.
void applyHeat() {
    if ( temperature <= 2 ) {
        ++temperature;
    }
}

void applyCold() {
    if ( temperature >= -2 ) {
        --temperature;
    }
}

void applyPoison() {
    poisoned = true;
}

void removePoison() {
    poisoned = false;
}
share|improve this answer
    
The point is, i will add more states not orthogonals, is possible to do? HOT is a state too, normal = 30 ºC, hot = 70 ºC cold = 5 ºC. But if add heat and is hot, it suppost to turn into burning. –  Piperoman Nov 11 '12 at 12:34
    
How about you model the temperature as an integer value in degrees Celsius instead of a boolean saying "hot" or "cold"? –  Philipp Nov 11 '12 at 12:45
    
Of course you can add more temperature-states, as you see in my answer I have actually already represented the hot-state. What Philipp says means seeing every degree Celsius as a state, which is fine, although note this might not be what you want from a game design perspective: more simulation doesn't entail deeper game play, per se. –  Eric Nov 11 '12 at 13:06

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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