Took me over 10 hours to convert this to C++! What a learning progress. I want to share my Code with you guys. Hopefully it helps somebody

Okay! So this tutorial was done purely by blueprints, but i wanted to do this with C++ to learn. I will go my code step by step.

First I Created a new AI Controller in C++, derived from AIController.

Lets go to the .H file. First thing to do is to include some headers. This is what we need!

#include "Perception/AiPerceptionComponent.h"
#include "BehaviorTree/BehaviorTree.h"
#include "BehaviorTree/BlackboardComponent.h"

First i added these:

ANPC_Ai_Controller();

	virtual void BeginPlay() override;

	// We can set Blackboard in BP
	UPROPERTY(EditDefaultsOnly, Category = "Blackboard")
	UBlackboardData* BlackboardToUse;
	
	// We can set BehaviorTree in BP
	UPROPERTY(EditDefaultsOnly, Category = "Blackboard")
	UBehaviorTree* BehaviorTreeToUse;

So we basically create a pointers that can point to BehaviorTree and blackboard. Then we gonna override the original BeginPlay. Notice that BehaviorTree and Blackboard are EditDefaultsOnly. We can edit them in Blueprint later on, this is super important to notice!

Then we add these:

// This is the Blackboardcomponent that will be as Return value later. 
	UPROPERTY()
	UBlackboardComponent* BB;

	// Perception component. 
	UPROPERTY(VisibleAnywhere, Category = "AI")
	UAIPerceptionComponent* AIPerception;

    // Main function, when sensing stuff.
	UFUNCTION()
	void SenseStuff(AActor* UpdatedActor, FAIStimulus Stimulus);

BlackboardComponent is a component that lets us control the blackboard data. Like in Blueprints, we can set the KeyValues ETC. Notice that this is seperate from the UBlackboardComponent we added earlier. You will later see what i mean! After that we create a pointer that can point to a Perception Component. Notice that the pointer is still NULL at this point. Only a hollow shell we can fill later. Finally there is SenseStuff function that will be called everytime we sense something.

Finally, this is seperate part of the class but i added these. These are the TSubclass type. That means, we can see them as a class in BP-screen and can change them. The class is set to UAISense, so we can set any sense we want to that variable.

UPROPERTY(EditDefaultsOnly, Category = "Blackboard")
	TSubclassOf<UAISense> HearingSense;

	UPROPERTY(EditDefaultsOnly, Category = "Blackboard")
	TSubclassOf<UAISense> SightSense;

Now that we have the Header File ready, lets go to .CPP

We have a constructor, that is empty.

ANPC_Ai_Controller::ANPC_Ai_Controller()
{
	// Constructor is empty.

}



void ANPC_Ai_Controller::BeginPlay()
{
	Super::BeginPlay(); // We run Blueprint-beginplay, if we need it for some reason.

	if (!ensure(BlackboardToUse)) { return; } // We ensure that pointer isn't null
	UseBlackboard(BlackboardToUse, BB);
	if (!ensure(BehaviorTreeToUse)) { return; }// We ensure that BehaviorTree isn't null

	// Run the behavior tree
	RunBehaviorTree(BehaviorTreeToUse);


	// We get the perception component from the components list
	AIPerception = FindComponentByClass<UAIPerceptionComponent>();
	if (!ensure(AIPerception)) { return; }
	AIPerception->OnTargetPerceptionUpdated.AddDynamic(this, &ANPC_Ai_Controller::SenseStuff);	
	TArray<AActor*> ActorsSensed;
	AIPerception->GetPerceivedActors(HearingSense, ActorsSensed);

}

Then comes the big one. The BeginPlay. I Am sure, that there could be other, better places to set these, but i used a BeginPlay. It works, in our scenario. First we Ensure that we have something in Blackboard and Behaviortree variables. If not, we exit BeginPlay function! Otherwise, the whole project could crash very fatally!

After we are sure, we call function UseBlackboard and feed our BlackboardToUse variable there and then we feed the BlackboardComponent there. If you look carefully in code you will see this:

Basically that means, the UseBlackboard function takes two argument. Second argument is reference argument (&). It means that it GIVES us something, So when we put the BB in there, we fill it with all the goodies we need to mess with the Blackboard.

After that we run function RunBehaviorTree and pass our BehaviorTreeToUse variable there. In bottom. We use the template function FindComponentByClass to find the PerceptionComponent and we will set that as our AIPerception variable. We do this, because there are tons of different settings, and i just preferred to create the Perception component within the blueprint and then just get the reference from it to our C++ variable. This is the way to do it.

After that we ensure, we actually got something and if yes, we use the AddDynamic macro. This means that eveytime perception updates, it will run function we want. In this case. SenseStuff function.

Below it is just a test. I learned hot to get all the perceived actors using the sense, we want. GetPerceivedActors is the way to do it! It takes two argument. Sense and the Array, we want to put the actors. Notice that again, the second argument is Reference (&), that is why we dont need ‘=’ anywhere. Reference fills out ActorsSensed array! You can call that easily anywhere now!

// We try to SenseStuff around.
void ANPC_Ai_Controller::SenseStuff(AActor* UpdatedActor, FAIStimulus Stimulus)
{
	// We set Focalpoint if the sense was successfull
	if (Stimulus.WasSuccessfullySensed())
	{
		BB->SetValueAsObject("FocalPoint", UpdatedActor);
		UE_LOG(LogTemp, Warning, TEXT("Sensing Actor"));
	}
	else
	{
		BB->ClearValue("FocalPoint");
		UE_LOG(LogTemp, Warning, TEXT("Ain't sensing anything"));
	}
}

Finally the last one is the SenseStuff. This is easy (well easy after i used four hour to find how to use this damn functon!). I will show you how i found this. When you go AIPerceptionComponent.h you will find that there is two Delegates

With those i figured out the arguments the Dynamic Macro needs. It was Perceived actor and the FAIStimulus struct. So lets use them. Finally use the same logic as in BP. Notice that now we can use the BB variable we got earlier on!

Now you can compile the code. Go to your game and create a Blueprint Class Based on you C++. Notice that this isn’t working yet, because all the pointers are still NULL!

When that is created go into your BP and add the AIPerceptionComponent. Remember, we only have null pointer pointing to Component, we haven’t actually created any!

Then, normally add the hearing and Sight to that component, as you normally would. Final Push is to fill our NULL pointers. So lets go to Class Defaults tab and we can see them! Just set them correctly. As you can see, Hearing Sense and Sight Sense are the TSubclassOf -type we wrote in code! Here we can set them. You only need to set them once and then you can use the GetAllPerceivedActors and use sense you like :)!

Thats’s it. Now just set your Characters to use the new AI Blueprint you created from the C++. I know, this is lot more work than in Blueprints, but hey, atleast we can do stuff in Code now! And of course, when you do this once, it is literally Copy paste to other actors etc. Cheers!

8 Likes

I appreciate the amazing amount of work you put in to not only implementing all the functionality in C++ but also documenting it in a very very clear way with pitfalls and explanations; something I wish the official Unreal Engine documentation had more of for frequently used features such as this.

Thanks a lot !!

Loved the step by step walk-through. Would definitly try again.

If your project is up on GitHub, can you post the link, it would be helpful to see what you did after this and actually seeing the code together.

1 Like

This is brilliant work mate! really inspiring. Can I ask how helpful you found the API in completing this? Did you find it easy to reference or were you scrolling though autocomplete answers in VisualStudio and using trial and error?

Thank you for the kind words! API is super helpful. In fact, reading the API is kind the same as reading the documents. Everything inside unreal API is documented so you can just scroll down the page and check all the functions and read what they do. I really recommend to check out a AActor and other important classes in which you inherit most of the stuff. It really open eyes what you can actually do with the classes :)!

And one you really should learn is the Delegates. So many thing uses delegates so if you don’t know what they are and how you can spot them, it really makes Unreal programming difficult. I wrore something about them in:

http://deliciouscookieproduction.com/delegates/

But, you should check out the Official versions too! They are super helpful. They can be found: https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/Delegates/

Cheers.

1 Like

Hey thanks mate, I’m finding its not as helpful as the Unity API, but people like yourself make it a whole lot easier!! cheers =)

Thanks a lot for this!

Privacy & Terms