Normally, one of the only ways to create custom console commands in Unreal Engine is in C++ with the EXEC UFUNCTION specifier.

UFUNCTION(Exec)
void MyConsoleCommand();

But this generally sucks for other important use cases:

  • Unable to create commands for systems entirely in blueprints.
  • If it’s a networked game, have to manually route the function call with an RPC (unless you use ServerExec)
  • Has to be defined in a class which extends FExec, and if it doesn’t you need to extend it and make sure the commands are correctly routed.

Here’s how to make commands entirely in blueprints

(and also have them automatically be RPCs)

There are two parts to getting a function to run through the console:

  1. Making the blueprint events be reachable through a ProcessConsoleExec chain.
  2. Having the console be aware of the blueprint events so it can autocomplete. (this is optional but helps with discoverability). The section below describes how this works.

1. Understanding how the console works (a part of it)

UConsole is the class which represents Unreal’s console system.

UConsole has this function

/** Build the list of auto complete console commands */
virtual void BuildRuntimeAutoCompleteList(bool bForce = false);

and inside there is this snippet of code

for (TObjectIterator<UFunction> It; It; ++It)
{
    UFunction* Func = *It;

    /* ... */

    if ((Func->HasAnyFunctionFlags(FUNC_Exec) /* ... */ )
    {
        /* ... */

        const int32 NewIdx = (Idx < AutoCompleteList.Num()) ? Idx : AutoCompleteList.AddDefaulted();
        AutoCompleteList[NewIdx].Command = FuncName;

        /* ... */
    }

    /* ... */
}

This snippet iterates through all loaded functions and checks to see if they have the flag FUNC_Exec i.e UFUNCTION(Exec). If it does, add it to the auto complete list. Therefore, if we can make sure our functions have the FUNC_Exec flag when BuildRuntimeAutoCompleteList is called, they’ll be discovered!

By looking for where BuildRuntimeAutoCompleteList is called, we can see one occurrence is inside UWorld::InitializeActorsForPlay. So, as long as our functions are ready before hitting play, we’re good.

2. Define a UConsoleCommands class

This is going to be the class which we’ll extend in blueprints to hold our commands. I made it an ActorComponent for the sole reason they can automatically replicate when attached to Actors. We could have it extended UObject, but that would require more boilerplate code.

Create a ConsoleCommands.h file and define the class:

UCLASS(Abstract, Blueprintable, BlueprintType)
class UConsoleCommands : public UActorComponent
{
    GENERATED_BODY()

public:
    UConsoleCommands();
};

Now create a ConsoleCommands.cpp file to define the constructor. This is where we want to modify our functions to have the FUNC_Exec flag.

As for why - Unreal has the concept of “Class Default Objects” or CDOs. These objects exist as defaulted instances of a class which are used throughout the engine. They are created as soon as a class is loaded, so for a blueprint class every time they are compiled they are recreated, which in turn causes the constructor will run. We can therefore use the constructor to change our functions before UWorld::InitalizeActorsForPlay calls UConsole::BuildRuntimeAutoCompleteList

In the constructor, we’ll iterate through all of the functions our class has (which includes the blueprint only events) and give them the FUNC_Exec flag.

UConsoleCommands::UConsoleCommands()
{
    SetIsReplicatedByDefault(true);

    for (TFieldIterator<UFunction> It(GetClass()); It; ++It)
    {
        // Make sure the function belongs to us instead of UActorComponent.
        // Its functions will show up here since we extended it.
        if (GetClass()->IsChildOf(Cast<UClass>(It.GetStruct())))
        {
            It->FunctionFlags |= FUNC_Exec;
        }
    }
}

UConsoleCommands should also be replicated so we can send Server RPCs as well

3. Create a UConsoleCommandsManager

UConsoleCommandsManager is going to be an ActorComponent which lives on the PlayerController. It is responsible for spawning our UConsoleCommands objects and routing the commands to these objects.

ConsoleCommandsManager.h

class UConsoleCommands;

UCLASS(Within = PlayerController, BlueprintType)
class UConsoleCommandsManager: public UActorComponent
{
    GENERATED_BODY()

public:
    UConsoleCommandsManager();

    virtual void BeginPlay() override;

    virtual bool ProcessConsoleExec(const TCHAR* Cmd, FOutputDevice& Ar, UObject* Executor) override;

    UPROPERTY(EditDefaultsOnly)
    TArray<TSubclassOf<UConsoleCommands>> ConsoleCommandsClasses;

private:
    UPROPERTY(Transient, Replicated)
    TArray<UConsoleCommands*> ConsoleCommandsObjects;

Some quick notes -

  • ConsoleCommandsClasses is going to be the list of classes we create on BeginPlay.
  • We’ll store these objects inside ConsoleCommandsObjects. This list replicates so we can refer to their references on the client.
  • ProccessConsoleExec will route the command to the correct UConsoleCommands object based on the function name.

ConsoleCommandsManager.cpp

UConsoleCommandsManager::UConsoleCommandsManager()
{
    SetIsReplicatedByDefault(true);
}

void UConsoleCommandsManager::BeginPlay()
{
    Super::BeginPlay();

    // We only want to spawn the objects on the server, so they replicate down to the client.
    if (!GetOwner()->HasAuthority())
    {
        return;
    }

    for (const TSubclassOf<UConsoleCommands>& Class : ConsoleCommandsClasses)
    {
        if (Class != nullptr)
        {
            ConsoleCommandsObjects.Add(NewObject<UConsoleCommands>(GetOwner(), Class));
        }
    }
}

bool UConsoleCommandsManager::ProcessConsoleExec(const TCHAR* Cmd, FOutputDevice& Ar, UObject* Executor)
{
    for (UConsoleCommands* Object : ConsoleCommandsObjects)
    {
        if (Object->ProcessConsoleExec(Cmd, Ar, Executor))
        {
            return true;
        }
    }

    return Super::ProcessConsoleExec(Cmd, Ar, Executor);
}

void UConsoleCommandsManager::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);

    DOREPLIFETIME(UConsoleCommandsManager, ConsoleCommandsObjects);
}

And that’s it for the manager. When a console command is entered which matches a blueprint event name inside one of our ConsoleCommands objects, it’ll be triggered. If the event is a Server RPC, it’ll be routed to the server automatically!

One last piece of glue code is we need to attach UConsoleCommandManager to our PlayerController and call ProcessConsoleExec in the correct place.

4. Setup the PlayerController

In your already existing C++ PlayerController class (if you don’t have one you should make one now), override ProcessConsoleExec and attach our new manager.

// MyPlayerController.h
UCLASS()
class AMyPlayerController : AController
{
        AMyPlayerController();

        virtual bool ProcessConsoleExec(const TCHAR* Cmd, FOutputDevice& Ar, UObject* Executor) override;

        UPROPERTY(EditDefaultsOnly)
        UConsoleCommandsManager* ConsoleCommandsManager;

        /* ... */
};

// MyPlayerController.cpp
AMyPlayerController::AMyPlayerController()
{
    ConsoleCommandsManager = CreateDefaultSubobject<UConsoleCommandsManager>(TEXT("ConsoleCommandsManager"));
}

bool AMyPlayerController::ProcessConsoleExec(const TCHAR* Cmd, FOutputDevice& Ar, UObject* Executor)
{
    bool bHandled = Super::ProcessConsoleExec(Cmd, Ar, Executor);

    if (!bHandled && ConsoleCommandsManager != nullptr)
    {
        bHandled |= ConsoleCommandsManager->ProcessConsoleExec(Cmd, Ar, Executor);
    }

    return bHandled;
}

And that’s it! We can now author functioning console commands with blueprints.

In practice

In order to make a command with our new system:

  1. Create a new blueprint class which extends UConsoleCommands

    img

  2. Create custom blueprint events with reasonable names which represent your console commands.

  • The names will be used in the autocomplete in the console, so I would recommend not to use spaces.

  • These events can also take input such as strings or numbers!

    img

  1. Add your new class to the ConsoleCommandsClasses list inside the ConsoleCommandsManager in your blueprint PlayerController.

    img

    img

  2. Hit play and use them!

    img

    img

    img

    img

Note - when first creating these commands, you might see some duplicate commands in the console. These will go away when you restart the editor.