0
\$\begingroup\$

I'm working on an 2D overhead topdown game in Unity3D and I want the enemies to have lots of diverse behaviours.

How should I design the architecture with this in mind?

For example I want:

  • A simple walking monster, which can move on walkable tilemaps, attack the player on sight.

  • A spider like creature, which jumps from wall tile to wall tile, and approaches the player in this manner.

  • I want a snail which walks on a route, and makes a trail (ie by flagging the tiles) behind itself, apart from this, it's unresponsive (like doesn't care if the player attacks itself)

  • A werewolf, which can smell the player from X tile away. (Like there is a walkable path to the player)

  • 50 more unique behaviours...

Needless to say, I'm not talking about how to implement this, but how could I design a system which will be robust enough for this many behaviours, not really error-prone, low amount of code duplication, etc.

Thanks in advance!

\$\endgroup\$
  • \$\begingroup\$ Have you tried an object oriented approach? \$\endgroup\$ – TomTsagk Sep 9 at 13:51
  • \$\begingroup\$ my approach would be to create a script for each different type (enemy jumper, enemy slimer, enemy tracker etc.) and have most of my variables serialized in order to have a lot of control for each game object. I personally don't see any reason that a complex system needs to be made for this. (of course I could be wrong) :) \$\endgroup\$ – Millard Sep 11 at 18:04
4
\$\begingroup\$

i think you are looking for a generic way to make an script that works for big range of NPC's.

for making these types of behaviours, there are some techniques. you can research for each of them and think which one works for you.

1)FSM(finite state machine) -> HFSM(Hierarchical FSM), FSM with stack very easy to implement. simply a Switch-case programming or using action delegates can make this happen. the problem is state connections are hard-coded so you cant extend it easily.

2) GOAP(goal oriented action planning) its like FSM but connection are not hard-coded. there is a planner algorithm that calculates best way to achieve goal state from current state.

3) behavioural Tree its a tree that leafs are actions. simple left to right parsing is performed to find right action for current conditions. unity and unreal have great visual designing plugins for this. problem is you need change design of tree for wanted behaviour.

4)Utility Theory its latest approach for designing character AI. it uses game theory and fuzzy logic. i dont think there are so many games that used this. but there are good learning references in the internet. in every condition you only have too feed Utility System condition variables to a fuzzy based system to select action with best utility. in this techniques utility is best based in condition. for example when hungry, eating is best action but when you arent, eating is not the best action. its good because you simply can change behaviour with changing condition variables and fuzzy rules.

if you want to go generic as much as possible, number 4 is your best choice.

\$\endgroup\$
2
\$\begingroup\$

You could implement most of the low level functionalities which are shared by many creatures as service-components which don't do much on their own but provide functionality to be used by the creature-specific behaviours.

For example, if a lot of your entities should be able to follow a route of waypoints, create a WaypointFollow MonoBehaviour with methods like FollowRouteTo(Vector3 destination). If multiple creatures should be able to detect the player, create a PlayerDetector MonoBehaviour which exposes properties like bool seesPlayer or Vector3 lastKnownPlayerLocation. If the monster has a melee attack, give it a component MeleeAttacker with properties like range, damage, speed, animation and so on.

Then you create a behaviour for each enemy which describes the high-level functionality of that creature. But in order to keep the code simple and concise, it should depend on other components for the implementation details.

The behaviour of the monster which attacks on sight could look like this:

 // references to service components
 private PlayerDetector playerDetector;
 private WaypointFollow waypointFollow;   
 private MeleeAttacker meleeAttacker;

 Start() {
     playerDetector = GetComponent<PlayerDetector>();
     waypointFollow = GetComponent<WaypointFollow>();    
     meleeAttacker = GetComponent<MeleeAttacker>(); 
 }

 Update() {
     // the actual behavior 
     if (playerDetector.seesPlayer) {
         if (meleeAttacker.CanAttack(playerDetector.detectedPlayer) {
            meleeAttacker.Attack(playerDetector.detectedPlayer);
         } else {
            waypointFollow.FollowRouteTo(playerDetector.lastKnownPlayerLocation);
         }
     }
 }

Concise and readable.

All the nitty gritty details, like how to find out which player is the closest player, if they are visible, if something is within attack range or how to find the route to the player are hidden in the service components.

\$\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.