Search code examples
c++abstract-classunreal-engine4unreal-blueprint

How can I create an abstract class in code, let a blueprint extend it, and give that blueprint back to code to instantiate?


Suppose I want to create an AI class in code that will be used to control a pawn, but I want my designers to be able to modify the behavior using blueprints for rapid iterations.

Suppose I have this in MyProjectBaseAI.h:

#pragma once

#include "EngineMinimal.h"
#include "GameFramework/Actor.h"
#include "MyProjectBaseAI.generated.h"

UCLASS()
class MYPROJECT_API AMyProjectBaseAI : AActor
{
    GENERATED_BODY()
    
public:
    AMyProjectBaseAI(const FObjectInitializer& ObjectInitializer);

    virtual void BeginPlay() override;

    virtual void Tick(float DeltaSeconds) override;

    void SetGoalPosition(FVector pos);
  
    virtual void DetermineGoalPosition(int32 stateData1, int32 stateData2) = 0;

private:
    FVector goalPosition;
};

I would like to make this class abstract and have a blueprint able to implement DetermineGoalPosition(...); to call SetGoalPosition(FVector pos) depending on their design of the AI.

Then, how can the designers set the implementing class in the inspector so that my C++ code can instantiate it? Suppose I have a class that wants to instantiate the implementing class like this (currently using base class):

AMyProjectBaseAI* ai = World->SpawnActor<AMyProjectBaseAI>(...);

Solution

  • Abstract class setup:

    1. Use the Abstract and Blueprintable specifiers in UCLASS to mark the class as abstract and blueprintable (although, when you are inheriting from AActor or another blueprintable class, the Blueprintable specifier is redundant).

    2. Use BlueprintImplementableEvent in a UFUNCTION specifier for DetermineGoalPosition. This allows blueprints to implement the function.

    Also, do not declare the function as virtual, per Unreal's standard since ver 4.8:

    New: Removed "virtual" keyword from several engine-level BlueprintImplementableEvents to conform to the new "BlueprintImplementableEvents should not be virtual" standard.

    1. Use the BlueprintCallable property in a UFUNCTION specifier for SetGoalPosition. This allows the function to be called from a blueprint.

       #pragma once
      
       #include "EngineMinimal.h"
       #include "GameFramework/Actor.h"
       #include "MyProjectBaseAI.generated.h"
      
       UCLASS(Abstract, Blueprintable)
       class MYPROJECT_API AMyProjectBaseAI : AActor 
       {
           GENERATED_BODY()
      
       public:
           AMyProjectBaseAI(const FObjectInitializer& ObjectInitializer);
      
           virtual void BeginPlay() override;
      
           virtual void Tick(float DeltaSeconds) override;
      
           UFUNCTION(BlueprintCallable)
           void SetGoalPosition(FVector pos);
      
           UFUNCTION(BlueprintImplementableEvent)
           void DetermineGoalPosition(int32 stateData1, int32 stateData2);
      
       private:
           FVector goalPosition;
       };
      

    Instantiator setup

    1. Then in the header of the class you would like to set the implementing class in the inspector, have a public TSubclassOf<AMyProjectBaseAI> variable with the EditAnywhere specifier (or EditDefaultsOnly or EditInstanceOnly if the subclass should be set only at the blueprint or instance level in the editor):

       public:
           UPROPERTY(EditAnywhere)
           TSubclassOf<AMyProjectBaseAI> AIImplementationClass;
      
    2. And then where you instantiate it, simply use AIImplementationClass as the class:

       AMyProjectBaseAI* ai = World->SpawnActor<AMyProjectBaseAI>(AIImplementationClass);