Professional Documents
Culture Documents
W09 - Blackboard To The Future V1.01
W09 - Blackboard To The Future V1.01
W09 - Blackboard To The Future V1.01
WEEK 9 Laboratory
BLACKBOARD TO THE FUTURE
Welcome to the Week 9 lab! This will be our second of two weeks of AI where we are
going to smart-ify our enemies even more!
Here we’re going to be creating a new AI Controller which makes use of a behaviour tree
and blackboard to manage how it reacts to the player. We’re going to add in the
functionality we had last week, but also add the ability for our enemies to shoot at our
players!
To clone our repository, first open up GitHub Desktop and skip the login screen, and enter your
name and Monash email address in the next screen.
Then, click the Clone a repository from the Internet button and then switch to the URL tab. Head
to the BeatEmUp project we made on GitLab last week, and click the Clone dropdown button and
copy the Clone with HTTPS link to the clipboard. Paste this into GitHub Desktop in the
Repository URL textbox, then set the Local Path to wherever you would like to clone the repository
to. Hit the Clone button.
If you are on the lab machines: We have to clone the repository to the actual machine we are
working on, instead of the Monash network drives. There a couple of places we can choose from, but
we suggest creating a new folder named Git inside of your Downloads folder. We create a new
folder because the folder we clone to has to be an empty one.
This will open up an Authentication failed window, enter your authcate as the Username and your
Personal Access Token we created earlier as your Password. Then hit Save and retry. If you have
forgotten your Personal Access Token, you can create another one with the instructions on Page 8
of the Week 0 Supplementary.
2
FIT2096 - Games Programming
Week 9: Blackboard To The Future
Go into the C++ Classes folder in your Content Drawer and then make a New C++ Class with AI
Controller as the Parent Class. Name it EnemyBTController and hit create.
Once Rider opens, the first thing we’re going to do is add one more thing to our modules like we did
last week.
In the Solution Explorer to the left of the screen, open BeatEmUp.Build.cs and add the following
module inside of the AddRange statement:
, "GameplayTasks"
Next, add the following includes up the top of EnemyBTController.h:
#include "NavigationSystem.h"
#include "Perception/AISenseConfig_Sight.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "Perception/AIPerceptionComponent.h"
UFUNCTION()
void OnSensesUpdated(AActor* UpdatedActor, FAIStimulus Stimulus);
Most of this is very similar to what we did last week, with a couple of differences, we have removed a
couple of the old functions like OnMoveComplete, and we have added in a function to generate a
random location for us as well as made a UFUNCTION which will be called when our sight updates.
3
FIT2096 - Games Programming
Week 9: Blackboard To The Future
Make sure you Right Click and Generate Definitions for all of our new functions!
Before we head into .cpp for this class, instead head into Enemy.cpp and add the following to the
top of the Ragdoll function:
Cast<AEnemyBTController>(GetController())->BrainComponent->PauseLogic("Ragdolling!");
SightConfiguration->SightRadius = SightRadius;
SightConfiguration->LoseSightRadius = LoseSightRadius;
SightConfiguration->PeripheralVisionAngleDegrees = FieldOfView;
SightConfiguration->SetMaxAge(SightAge);
SightConfiguration->DetectionByAffiliation.bDetectEnemies = true;
SightConfiguration->DetectionByAffiliation.bDetectFriendlies = true;
SightConfiguration->DetectionByAffiliation.bDetectNeutrals = true;
GetPerceptionComponent()->SetDominantSense(*SightConfiguration->GetSenseImplementation());
GetPerceptionComponent()->ConfigureSense(*SightConfiguration);
First here we set this actor to be able to tick as we want to be able to use its tick function, then
create its sight configuration and set its perception component to be a new perception component.
After that, we set each of our sight configuration variables, such as it’s radius, lose sight radius, field
of few, as well as what type of pawns it can see, in this case, all types of pawns. We lastly then set
our dominant sense to be our sight, and configure our sense from our sight configuration. Don’t
worry if you don’t fully understand these, as it will be almost exactly the same for your agents!
UseBlackboard(AIBlackboard, BlackboardComponent);
RunBehaviorTree(BehaviourTree);
GetPerceptionComponent()->OnTargetPerceptionUpdated.AddDynamic(this, &AEnemyBTController::OnSensesUpdated);
Here we first get our navigation system from our world and set our own variable, then tell our AI to
use our blackboard and start running the behaviour tree, and lastly we bind our perception update to
our senses update function we made before so we can check what we actually saw!
5
FIT2096 - Games Programming
Week 9: Blackboard To The Future
After that, add the following to GetControlLocation() above the super call:
if(GetPawn())
{
return FRotator(0, GetPawn()->GetActorRotation().Yaw,0);
}
This is the same as what we did last week, here we just make sure we’re following our enemy’s yaw.
6
FIT2096 - Games Programming
Week 9: Blackboard To The Future
That’s it for this class, but we’re not done just yet with the coding! First head back to Unreal and hit
Compile.
3. Creating a Custom Behaviour Tree Task
Right Click in the C++ Classes again and Create a New C++ Class, this time based on
BTTaskNode as the Parent Class and name this GenerateNewRandomLocationTask.
7
FIT2096 - Games Programming
Week 9: Blackboard To The Future
BTController->GenerateNewRandomLocation();
return EBTNodeResult::Succeeded;
Here we first get a reference to the behaviour tree component, and if that returns null, we say our
node has failed. If it’s not null, then we get our Enemy Behaviour Tree Controller, and again if that is
null, we say our task failed. If it doesn’t return null, then we call our GenerateNewRandomLocation
function from the controller and say our task succeeded.
That’s it! Head back into Unreal and hit Compile again.
After that, open the Content Drawer and then open the Blueprints folder. Right Click and then go
to Artificial Intelligence and create a Behavior Tree named EnemyBT and a Blackboard named
EnemyBlackboard.
Open up EnemyBlackboard and then in the top left, click New Key and then create a Bool and
name it ChasePlayer. Create another 2 keys for PlayerPosition and PatrolPoint but this time of
type Vector.
Our behaviour tree does exactly what it says on the tin, it is a tree that reads top left to bottom right
that controls behaviour!
First, drag off from the Root and select a Selector. Selectors will call each of the nodes under it
from left to right, and will keep trying the next node even if the node before it fails.
After that, drag off the Selector to the left and this time choose a Sequence. A sequence node will
try each of its child nodes one by one, but if any of them return fail, the entire sequence will fail.
Next Right Click on the Sequence and click on Add Decorator->Blackboard. Decorators are ways
we can add conditions to our behaviour tree, and in this case, we only want our enemies to chase
our player if they can see them, so select the Decorator and then change Blackboard Key to be
ChasePlayer, and then change the Observer Aborts to Lower Priority. This will make it so if our
ChasePlayer is ever set, it will abort anything lower priority than this, so we’ll stop patrolling and
actually chase our player.
Next, drag off to the left from our sequence and select a Rotate to Face BB Entry, then change
Blackboard Key to be PlayerPosition. This will make our enemy rotate towards our player. Then
drag off the sequence again, this time to the right of the existing node, and choose a Move To
node. Change the Blackboard Key to PlayerPosition and tick Observe Blackboard Value. This
will make it so our enemy will chase our player, and ticking observe blackboard value makes it so if
the value updates, the move to updates as well.
After this, back up to the top Selector node, drag off to the right of the existing nodes, and create
another sequence, then drag off to the left and select Generate New Random Location Task
which will call our custom task. Then drag another three nodes off from the sequence and select in
order: Rotate to Face BB Entry, Move To, and Wait. Set the Blackboard Key for both the Rotate
to Face BB Entry and the Move To Node to be PatrolPoint and then set the Wait time to be 1s.
That’s it, your first behaviour tree should look like the image to the right and you should be able to
test it out, and get pretty much the same behaviour as last week!
9
FIT2096 - Games Programming
Week 9: Blackboard To The Future
Open up the Content Drawer, go to the C++ Classes folder and Right Click to Create a New C++
Class that inherits from Actor as the parent, named Bullet.
Make sure to Right Click and Generate a Definition for this function and also add an include for
ProjectileMovementComponent!
MovementComponent->bShouldBounce = true;
MovementComponent->BounceVelocityStopSimulatingThreshold = MovementSpeed / 2;
MovementComponent->bSweepCollision = true;
MovementComponent->InitialSpeed = MovementSpeed;
MovementComponent->UpdatedComponent = Mesh;
MovementComponent->ProjectileGravityScale = 0;
Here like normal we create our components, that being our mesh as well as our movement
10
component. We then set our movement component’s variables, such as how it bounces, how it
collides, its initial speed as well as what component it should move.
FIT2096 - Games Programming
Week 9: Blackboard To The Future
This will add our OnHit function to the OnComponentHit event, and also make sure it actually calls
hit events on our mesh.
Next, open Enemy.h and add the following inside a public section:
UPROPERTY(EditAnywhere)
TSubclassOf<ABullet> BulletClass;
void Shoot(FVector Direction);
Here we add a variable to store our bullet class (don’t forget the include!) and then a function to
actually shoot the bullet.
11
FIT2096 - Games Programming
Week 9: Blackboard To The Future
Almost done here, next, head into EnemyBTController.h and add the following to a public section:
UPROPERTY(EditAnywhere)
float Ammo = 5;
void Shoot();
Just a quick variable to store the amount of ammo our enemies have, then a function to actually
shoot which we can call from a new custom task. Make sure to Generate a Definition!
Head back into Unreal and Compile, then Create a New C++ Class based on BTTaskNode again,
this time named ShootTask.
BTController->Shoot();
return EBTNodeResult::Succeeded;
Here we’re doing almost exactly the same thing as in our previous task, but this time we’re calling Shoot instead… that’s it! Make sure to add an include for AEnemyBTController!
Once that finishes, Right Click on our Bullet class and Create a Blueprint based on it named BP_Bullet saved in the Blueprints folder.
Open up BP_Bullet and set the Mesh Static Mesh to be Shape_Sphere and then set the Scale to be 0.1 for all axes.
After that, open up EnemyBT and disconnect the Rotate and MoveTo nodes from the ChasePlayer sequence by holding Alt and clicking on the arrows. Move them out of the way
for now.
Next, drag off 2 Sequence Nodes from the new Selector node. On the leftmost one, drag off a Shoot Task and a Simple Parallel. Right click on the Shoot Task and add 2
Decorators: Blackboard and Cooldown. Set the Blackboard Key to be HasAmmo and set the Cool Down Time to be 1s. This will make it so we will only shoot if we have ammo,
and only once every second.
Then, from the purple (left) side of the Simple Parallel, drag off a Wait node and set that to 1s, then from the grey (right) side, drag off a Rotate to Face BB Entry and set the
Blackboard Key to be PlayerPosition.
Lastly, connect the Rotate and Move To Player Position nodes we disconnected earlier to the empty right sequence. 13
FIT2096 - Games Programming
Week 9: Blackboard To The Future
Give it a test! Your enemies should try and shoot at you now until they run out of ammo
then rush you like before!
6. Additional Tasks
For this week the we want you to have a think about what additional Enemy
Type/Behaviour you’ll be adding for your assignment.
Mark Explanation
0 Student was absent or has not attempted lab tasks
0.5 An attempt has been made at the lab tasks but they are unfinished
14