๐ง Planner
Reminder of what is a planner
The planner is the most important part of a NACT NPC. While creating your custom NPCs, you might not need to create a custom behavior or a custom perception but 90% of the time, you will create your own planner. The planner is the Brain of the NPC, it's role is to analyze the data gathered by the perceptions in order to decide wich behaviors to do. We will analyze the presets Zombies Planner in order to understand a little bit more how everything is done.
Declaration a constructor and refresh rate
As usual, our first task is to create a ClassLib class representing our Planner. We need to Inherit from the BasePlanner class.
ZombiePlanner = BasePlanner.Inherit("ZombiePlanner")A lot is happening inside a constructor of a Planner, namely:
Registering the perceptions of our NPC
Registering the behaviors of our NPC
Declaring wich Blackboard keys might require our NPC to change our plans
Code wise, this is how it's done:
function ZombiePlanner:Constructor(oNactNpc)
self:Super().Constructor(self, oNactNpc)
self.lastEnemyCheckAt = 0
self.npc = oNactNpc
self.previous_enemies_melee_count = 0
self.previous_lookedaround_enemies_count = 0
self.has_alerted = false
self.npc:AddPerception(Perception_Surroundings, {
identifier = "melee",
radius = 100,
refresh_rate = 50
})
self.npc:AddPerception(Perception_Surroundings, {
identifier = "visible",
radius = 5000,
refresh_rate = 2000
})
self.npc:AddPerception(Perception_Surroundings, {
identifier = "nearby",
radius = 1000,
refresh_rate = 5000
})
self.npc:AddPerception(Perception_LookAround, {
vision_angle = 180
})
self.npc:AddBehavior(ZombieAttack)
self.npc:AddBehavior(ZombieApproach)
self.npc:AddBehavior(ZombieAlert)
self:AddObservedBlackboardKey("surroundings_melee")
self:AddObservedBlackboardKey("lookaround")
endAs a parameter, we always have the NACT Entity in parameter, in this case, this is a NACT NPC wich is a zombie.
Then, we store our NPC and register a few volatile data that will be useful for us later in the Plan function:
Then, we add our perceptions, first the Surroundings perceptions, wich will give informations about the Allies and Ennemies our NPC has in various ranges
We also have anorther perception, the LookAround. LookAround will scan thye surroundings but check if the enemy is actually visible (taking into account walls, props etc). Unlike an human however, a Zombie can see from a wide angle of 180ยฐ
Ok nice, so we have all the perceptions set up now :) We now pass to the behaviors our Zombie can take:
The zombie has not much actions it can take, it can either Attack, Approach or Alert nearby. Finally, we declare wich keys from the Blackboard we want to actively watch for any changes
Pheeeew, that was a lot of work ! We will now see how to everything gets used inside the Plan function
Plan function
The centerpiece of any planner is the Plan function, this is function that will be called:
Atleast every refresh_rate (defined by the GetRefreshRate function of your planner)
Everytime an observer blackboard key changes and requires a planning
When a behavior interrupts (declares it can no longer continue)
The first part of our Plan function is aiming at doing nothing if there is no enemies found in the Surroundings perceptions with the identifier "visible"
As you can see, either when there is no result or if there is no enemies, we will call the NoPlan function. The NoPlan function just makes sure we clear any previous behavior our NPC might had
The next part of the plan is to acquire an enemy, a Zombie is pretty dumb, it will focus the closest enemy within it's range:
The AquireClosestEnemy function uses anorther perception we declared before, the Lookaround perception. Lookaround will also give us the distance towards the enemy, so we try to find the closest and tell our NPC to focus this enemy
Again, if we have not find any enemy after this, we call NoPlan. Because the enemy might be behind a wall or something, so we don't do anything for now
Now, if we have found an enemy, we need to "scream" and alert orther zombies !
As you can see, we finally set a behavior for our NPC, we set the Alert behavior, internaly, this behavior will play a Scream sound and then send a message to orther zombies that we have spotted an enemy. Finally, we check if the enemy is within our Melee range, defined by anorther Surroundings perception with the melee identifier. If the enemy is within our range, then we set the behavior to attack, ortherwise, to approach
Reacting to perceptions changes
One of the great new features of NACT2 is the ability to react to changes in the blackboard. In the constructor, we added thoose lines:
This tells the Planner that when thoose key changes, we might have to reconsider our plans, this is done with the function OnObservedBlackboardKeyChanged
So, when our melee surroundings changes and there is not the same count of enemies within this range, we will call Plan on our planner, because this means either enemies are now within our range, or that the enemies are no longer in range and that we must chase them !
The same applies to the lookaround values, if there is not the same amount of enemies we will try to find the closest one and replan !
Messaging system
Finally, the NPCs are able to communicate with each orthers, the zombies will send an alert message within the Alert behavior, but the planner receives this message. Any message being sent to the NPC will be received with the OnMessage function
Whenever our Zombie receives an alert from anorther zombie, if we are not currently targetting an enemy we will then set our current target to the enemy of the Zombie that gave us this alert!
I hope that this tutorial made it a bit clearer what is the purpose of a planner!
Next, we will learn what is a perception and the behavior that are used in our Planners and how they help us acheive what we want to do!
Last updated