New AI for Mobs
#1
Hi All,

I am beginning to work on the mob AI and I had a few ideas of how to implement it. I was thinking that the monster class implements the actions that all the mobs can do (like: WalkToDestination, FollowPlayer...), and during the mob tick, the mob executes (or keeps executing) the action that is set in a member variable like m_CurrentAction instead of doing an action depending on the state that it is. Also, during the tick, the mobs "decides" what action to take depending on what's around (check if it sees player, check to see if sees lava). Also, I was thinking that this decision can be done on a lua script. This way it can be easily changed. I am thinking something like an AI folder with all the scripts for each mob.

What do you think? Good idea, bad idea? Smile
Reply
Thanks given by:
#2
When I was considering this, I came to the conslusion that the "state" would be best described at two levels of granularity. On the higher level, the mob would know "I'm chasing this player". On the lower level, the mob would remember "I'm running to XYZ". So that the lower level would be implemented in the cMonster class, it would be the "executive". Then, each time the lower level was finished (or even a few times while it's executing) or something unexpected has happenned, the particular mob class would react in the higher level logic.

But what you'll probably need to start with is a decent pathfinding algorithm. I don't think there's any currently implemented, and it will be needed very much. It would need to be able to accomodate different mob sizes and different mob capabilities (spiders climbing walls, villagers opening doors). I'd suggest implementing this class to use the cBlockArea class, that way it will be easy enough to use and will be even exportable to Lua.

As for implementing the mob AI in Lua, I'd strongly advise against it, at least in the early stage. To implement a reasonable AI, you need access to a lot of stuff, quite a lot of which is not yet exported into the Lua interface. Usually that is because exporting it is non-trivial. You'll also be facing a lot of threading issues that are really difficult to solve for Lua interface.
I'd suggest implementing the base AI in C++, but keeping extensibility in mind, so that later we can export a few hooks at key points so that Lua plugins can tweak the AI, maybe even replace it altogether (if anyone's brave enough Smile )
Reply
Thanks given by:
#3
(03-04-2013, 10:32 PM)xoft Wrote: When I was considering this, I came to the conslusion that the "state" would be best described at two levels of granularity. On the higher level, the mob would know "I'm chasing this player". On the lower level, the mob would remember "I'm running to XYZ". So that the lower level would be implemented in the cMonster class, it would be the "executive". Then, each time the lower level was finished (or even a few times while it's executing) or something unexpected has happenned, the particular mob class would react in the higher level logic.

That's a great idea. The lower level would be the action that the mob is executing while the higher level is the state of the AI. Looking at the code, it seems that the higher level (let's call it the state) is somewhat implemented. I'll look into adding the lower level (the action) added.

(03-04-2013, 10:32 PM)xoft Wrote: But what you'll probably need to start with is a decent pathfinding algorithm. I don't think there's any currently implemented, and it will be needed very much. It would need to be able to accomodate different mob sizes and different mob capabilities (spiders climbing walls, villagers opening doors). I'd suggest implementing this class to use the cBlockArea class, that way it will be easy enough to use and will be even exportable to Lua.

Yeah, we need a pathfinding algorithm. I am looking at implementing the A* algorithm.

(03-04-2013, 10:32 PM)xoft Wrote: As for implementing the mob AI in Lua, I'd strongly advise against it, at least in the early stage. To implement a reasonable AI, you need access to a lot of stuff, quite a lot of which is not yet exported into the Lua interface. Usually that is because exporting it is non-trivial. You'll also be facing a lot of threading issues that are really difficult to solve for Lua interface.
I'd suggest implementing the base AI in C++, but keeping extensibility in mind, so that later we can export a few hooks at key points so that Lua plugins can tweak the AI, maybe even replace it altogether (if anyone's brave enough Smile )

That makes sense. At least for now, we can have everything in C++, and then we can look into migrating the state portion of the AI to lua Smile
Reply
Thanks given by:
#4
Hmm, maybe even better would be to implement the higher level as separate classes, one for each "state". That way, one wouldn't have to implement the same state multiple times in each monster subclass. Then we can simply create an instance of a behavior class and assign it as the mob's current behavior.

So something like:
Code:
class cBehavior
{
  /// Makes the behavior "happen" for the entity. Called every tick for each pawn that has this behavior selected
  virtual void Apply(cPawn & a_Pawn) = 0;
} ;
class cBehaviorFollowPlayer : public cBehavior ... ;
class cBehaviorRunAwayFrom : public cBehavior ... ;

class cMonster
{
  cBehavior & m_CurrentBehavior;
} ;

One more thing that feels like it should be in, the behaviors may need some form of stacking. For example, if a creeper is following a player and at the same time running away from a cat, it would be wise to implement this as two separate behaviors and apply *both* to the creeper. So instead of having cBehavior & m_CurrentBehavior, let's have std::list<cBehavior &> m_CurrentBehaviors;
Reply
Thanks given by:
#5
The server also needs explosions and projectiles for the mobs.
Reply
Thanks given by:
#6
That can be added later, or by someone else in the meantime. The AI doesn't need it all, at least initially. Only when the individual behaviors are implemented, then we'll need all those.

For now, I'll be happy if we have an AI that can walk around and possibly follow the player, hurt him by touchingTongue
Reply
Thanks given by:
#7
well even without those things I can't wait to see the first mob move properly. even though it might take a while
Reply
Thanks given by:
#8
(03-05-2013, 07:43 PM)xoft Wrote: Hmm, maybe even better would be to implement the higher level as separate classes, one for each "state". That way, one wouldn't have to implement the same state multiple times in each monster subclass. Then we can simply create an instance of a behavior class and assign it as the mob's current behavior.

So something like:
Code:
class cBehavior
{
  /// Makes the behavior "happen" for the entity. Called every tick for each pawn that has this behavior selected
  virtual void Apply(cPawn & a_Pawn) = 0;
} ;
class cBehaviorFollowPlayer : public cBehavior ... ;
class cBehaviorRunAwayFrom : public cBehavior ... ;

class cMonster
{
  cBehavior & m_CurrentBehavior;
} ;

That's good idea. I was planning something similar. Having classes for the lower level that represent one single action (with many of them taking multiple ticks to complete), and then organizing them in behaviors (the higher state) to accomplish more complex tasks. For example:


Code:
class cAction
{
  /// Executes a single action for the given pawn
  virtual void DoAction   (cPawn & a_Pawn) = 0;
  virtual void StopAction (cPawn & a_Pawn) = 0;
  virtual void GetType    (cPawn & a_Pawn) = 0;
} ;
class cMoveToCoordinate : public cAction ... ;
class cAttack   : public cAction ... ;
class cExplode  : public cAction ... ;
class cTeleport : public cAction ... ;

class cBehavior
{
    std::deque<cAction &> m_Actionqueue;
    //This function will contain the logic to perform a specific behavior and it will schedule the actions needed
    //in order to accomplish the task
    virtual void PerfromBehavior(cPawn & a_Pawn) = 0;
}
class cBehaviorFollowPlayer : public cBehavior ... ;
class cBehaviorRunAwayFrom : public cBehavior ... ;



(03-05-2013, 07:43 PM)xoft Wrote: One more thing that feels like it should be in, the behaviors may need some form of stacking. For example, if a creeper is following a player and at the same time running away from a cat, it would be wise to implement this as two separate behaviors and apply *both* to the creeper. So instead of having cBehavior & m_CurrentBehavior, let's have std::list<cBehavior &> m_CurrentBehaviors;

What will be the end result? will the creeper follow the player or it will run away from the cat? Or during each tick, the creeper evaluates and decides which behavior needs to do?
Reply
Thanks given by:
#9
Ad multiple behaviors: Either have priorities, or execute both behaviors. So in the creeper example, either make cat-afraidity more important than following the player, or have the two behaviors agree on a compromise.

For start, I think priorities are the way to go, and later on we can add behavior interaction - one behavior asking if the pawn has another specific behavior and doing something accordingly.

Also, this will allow us to make it really simple in the decision making - if a creeper sees a player, make it follow them; if it sees a cat, make it afraid of the cat; just push the behaviors into the creeper's internal list of behaviors and let the creeper's AI decide what to really do.
Reply
Thanks given by:
#10
(03-07-2013, 05:22 PM)xoft Wrote: Ad multiple behaviors: Either have priorities, or execute both behaviors. So in the creeper example, either make cat-afraidity more important than following the player, or have the two behaviors agree on a compromise.

For start, I think priorities are the way to go, and later on we can add behavior interaction - one behavior asking if the pawn has another specific behavior and doing something accordingly.

Also, this will allow us to make it really simple in the decision making - if a creeper sees a player, make it follow them; if it sees a cat, make it afraid of the cat; just push the behaviors into the creeper's internal list of behaviors and let the creeper's AI decide what to really do.

Oh ok, that makes sense. So basically we will have a list of behavior that, on each tick, they get evaluated and if a condition is met, they get executed, right?
Reply
Thanks given by:




Users browsing this thread: 13 Guest(s)