Advertisement

basic state machine

Started by July 15, 2020 02:03 PM
12 comments, last by Calin 4 years, 4 months ago

I`m trying to figure out how cycling through unit states is supposed to take place. Should the algorithm be contained in a for loop? Bellow I`m trying to get a unit head towards a new destination once it has reached its first destination. When the unit reaches its destination it stops and goes idle, this is the bit I have working. When I have several units in the scene I can`t get the same unit do a (complex) sequence of things however. i.e. they all stop at reaching the end of path and go idle but when I want one of them to automatically take off again upon reaching path end, another unit does it, not the one I intended.

//status:
// 1 walk
 // 2 idle
   for (int i = 0; i < UnitMaxIndex[0] + 1; i++)

   {
       if (Characters[i].Status == 1)
       {
           Arrived = FolloWPath(nms * 20, &amp;Characters[0], i, false, 10,TheScreenLetterGroup, Characters[i].ID,&amp;Accept[0]);
           if (Arrived)
           {
           Characters[i].Status = 2;
   ConsoleMoveUp(&amp;amp;amp;amp;amp;amp;amp;amp;TheScreenLetterGroup[0],&amp;Accept[0]);
   StringCchPrintfA(debugmess, 27, "arrived ix %d ",i);
    AddConsoleText2(debugmess,&amp;TheScreenLetterGroup[0],&amp;Accept[0]);
               
           }
       }
       
       if ((Characters[i].Status == 2)&amp;&amp;(Characters[i].ID == 1))
       {
       
    	int uniqueid = 1;
     	int index = i;
    ResetPath(index, &amp;Characters[0]);
                   FindPath( Characters[index].StartPosX , Characters[index].StartPosZ, 14, 10, &amp;amp;amp;amp;amp;amp;amp;amp;Characters[0], index, Chart, ChartSide[0],&amp;amp;amp;amp;amp;amp;amp;amp;TheScreenLetterGroup[0],uniqueid,&amp;Accept[0]);
                   Characters[index].Status = 1;
     
               
               if (animationstage == 9)
               {
   
                   
                   timecounter++;
                   
               }
               if (timecounter == 10)
               {
    
     
   				}
  }
               
           
       
   }

pseudo code to explain that mess code

FindPath(Destination1, Charcaters[0]);
for(int i=0 i < UnitCount; i++)
{
if(Characters[i].Status==1)
{
bool Arrived = FolloWPath(Characters[i]);
if(Arrived)
{
 Characters[i].Status = 2;
}
}
if(Characters[i].Status==2)&&(Characters[i].ID == 0)
{
 ResetPath(Characters[?]);
 FindPath(Destination2,Characters[?]);
 Characters[?].Status = 1;

}
}

My project`s facebook page is “DreamLand Page”

Out of curiosity, why does the 3rd line have a space before the // and why is there a blank line between the ‘if’ and code block (line 5)? It doesn't seem to be a forum formatting problem. Those are just the first 2 spacing issues, there are plenty more and plenty worse. If you're using Visual Studio, there is an option (configurable even) to correctly format your code all at once. It's very easy in fact. Barely an inconvenience.

It's a lot easier to reason through your code when it's formatted correctly. If not for you, for the people that read it.

🙂🙂🙂🙂🙂<←The tone posse, ready for action.

Advertisement

So hard coding your states that way is generally not a good thing. It becomes highly inflexible and complicated very quickly.

Typically you will have a set of states and predicates to determine when a state should transition to another state. A predicate could be something complicated (such as your example where you could only apply it to a specific character) or could be driven by events or other similar systems. An example would be instead of checking if you have arrived each time you update the state machine, you could instead have it such that your path finding system can notify you when the user has completed their move. If you then encode your states as bits of data, then your actions which each state executes become effectively co-routines you execute to completion before performing a transition.

That transition could transition you from some state X to another state Y or even back to state X again (X → X is a valid transition in many cases). You often see these kinds of things in animation state machines for instance.

That being said, if you are going to write your state machine in code you should most certainly name your states. Using magical integers is a great way to completely fubar yourself. Additionally, you should try and separate out each state into its own “functional unit” (functions for instance), and ensure your predicates are also fairly clear and concise, if for no other reason than clarity.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

Washu said:
So hard coding your states that way is generally not a good thing. It becomes highly inflexible and complicated very quickly.

Thanks Washu, so doing it by the use of events is better

Out of curiosity, why does the 3rd line have a space before the //

you`re seeing things. on a serious note it`s the result of multiple edits. I don`t know exactly what happened. I edit my posts like hell, so it becomes messy

My project`s facebook page is “DreamLand Page”

Calin said:
so doing it by the use of events is better

Just different. State machines are a concept of edges and nodes, and transitions between them. Events are one of many ways to trigger transitions.

There are many, many, many ways to implement the concept. I wrote up an article on it a few years back here. There are assorted other articles on additional ways to implement state machines and use them in your AI system. They can be used to feed into decision trees, behavior/action systems, and much more.

Hard-coding a bunch of manual states is one way to make them. It does not expand well and requires programmer effort on everything, but for a small game developed by one person, it can work just fine. It is not wrong, but it only works while the program is relatively small.

Building a state machine interface where each game object or game system implements the interface, that can work better. It is still manually created, but again, if you're doing it alone or programmers are the only ones doing the work, it can work just fine. Each update takes building the code, but unless you're building heavier infrastructure to work with data, that's just fine.

Data driven state machines are more complex to create but scale better. Data can be created dynamically by anyone, so it isn't just programmers that can create and update values. Designers and producers can modify values. Artists, animators, and sound folks can fine-tune data to match whatever they've got in their systems. As projects grow to work with large teams they will have people dedicated to work exclusively on building and maintaining these systems. They get features like live previews and live updates, in-game editing systems, hot reloads, and much more. At that point it becomes heavily data driven, and cannot exist exclusively in code any more.

Thanks frob, it`s encouraging to receive feedback from someone who has actually wrote an article about the subject.

My project`s facebook page is “DreamLand Page”

Advertisement

Calin said:
ResetPath(index, &Characters[0]);

Calin said:
when I want one of them to automatically take off again upon reaching path end, another unit does it, not the one I intended.

Shouldn't it be Characters[i] ? Also, what does testing for ID==1 mean?

Omae Wa Mou Shindeiru

LorenzoGatti said:

Calin said:
ResetPath(index, &Characters[0]);

Calin said:
when I want one of them to automatically take off again upon reaching path end, another unit does it, not the one I intended.

Shouldn't it be Characters[i] ? Also, what does testing for ID==1 mean?

Under normal circumstances the character index within the array should be enough to individualize a character but I`m using z sorting, (I have isometric graphics) so the characters indexes don`t stay the same, I`m using Characters ID to figure out which unit is which. 1 is the unit I want to redirect to a new destination.

Characters[0] because I`m passing the right index as index to ResetPath(), I can access Characters[i] from within the function

Just as a side note I`m not going to trade JoeJ or Nikito for someone else. I got my `general guidance` dose from seniors and I`m ready to move on.

I just realized I`m doing it wrong. It should be


 for(int i=0 i < UnitCount; i++)
  { 
  if(Characters[i].Status==1)
   {
    bool Arrived = FolloWPath(Characters[i]);
     if(Arrived)
      {
       Characters[i].Status = 2;
      }
    } 
    if(Characters[i].Status==2)
      { 
        if(Characters[i].HasDestinationSector)
        {
        Characters[i].HasDestinationSector = false;
        ResetPath(Characters[i]); 
        FindPath(Characters[i].DestinationsList,Characters[i]);
        Characters[i].Status = 1;
        } 
      } 
       } 

My project`s facebook page is “DreamLand Page”

I made a model for harvesting resources (Starcraft/AOE type resource gathering). I didn`t cover it all yet

//status:
// 1 - walk
//2 - idle
//3 - mine
// 4 - deposit
//mission:
// 1 - collect
//2 - return
for(int i=0 i < UnitCount; i++)
 {
 if(Characters[i].Status==1)
 { bool Arrived = FolloWPath(Characters[i]);
 if(Arrived) 
{
 Characters[i].Status = 2;
 } 
}
 if(Characters[i].Status==2) 
{
 if(Characters[i].Mission == 1)
Characters[1].Status = 3;
if(Characters[i].Mission == 2)
Characters[1].Status = 4;

 }
if(Characters[i].Status ==3)
{
bool Finished = Work(time,Characters[i]);

if(Finished)
{
 Characters[i].Status = 2;
Characters[i].Mission = 2;
}
}
if(Characters[i].Status ==4)
{
bool Finished = Deposit(time,Characters[i]);

if(Finished)
{
 Characters[i].Status = 2;
Characters[i].Mission = 1;
}
}
 }

My project`s facebook page is “DreamLand Page”

Calin said:
for(int i=0 i < UnitCount; i++)

Wow, a nice for loop starting from zero! Just like everyone else does : ) I like it!

Probably you want to replace your number constants with an enum or #defines?

Characters[i].Status = STATUS_IDLE;

would be much easier to read than this:

Characters[i].Status = 2;

This topic is closed to new replies.

Advertisement