Creating new console commands

During development, console commands can be very helpful by allowing a developer or tester to easily bypass content, or disable the mechanics not relevant to the current test being run. The most common way to implement this is via console commands, which can invoke functions during runtime. The console can be accessed using the tilde key (~) or the equivalent in the upper-left area of the alphanumeric zone of your keyboard.

Creating new console commands

Getting ready

If you haven't already followed the Creating a new editor module recipe, do so, as this recipe will need a place to initialize and register the console command.

How to do it...

  1. Open your editor module's header file, and add the following code:
    IConsoleCommand* DisplayTestCommand;
    IConsoleCommand* DisplayUserSpecifiedWindow;
  2. Add the following within the implementation of StartupModule:
    DisplayTestCommand = IConsoleManager::Get().RegisterConsoleCommand(TEXT("DisplayTestCommandWindow"), TEXT("test"), FConsoleCommandDelegate::CreateRaw(this, &FUE4CookbookEditorModule::DisplayWindow, FString(TEXT("Test Command Window"))), ECVF_Default);
    DisplayUserSpecifiedWindow= IConsoleManager::Get().RegisterConsoleCommand(TEXT("DisplayWindow"), TEXT("test"), FConsoleCommandWithArgsDelegate::CreateLambda(
      [&](const TArray< FString >& Args)
      {
        FString WindowTitle;
        for (FString Arg : Args)
        {
          WindowTitle +=Arg;
          WindowTitle.AppendChar(' ');
        }
        this->DisplayWindow(WindowTitle);
      }
    ), ECVF_Default);
  3. Inside ShutdownModule, add this:
    If (DisplayTestCommand)
    {
      IConsoleManager::Get().UnregisterConsoleObject(DisplayTestCommand);
      DisplayTestCommand = nullptr;
    }
    If (DisplayUserSpecifiedWindow)
    {
      IConsoleManager::Get().UnregisterConsoleObject(DisplayTestCommand);
      DisplayTestCommand = nullptr;
    }
  4. Implement the following function in the editor module:
    void DisplayWindow(FString WindowTitle)
    {
      TSharedRef<SWindow> CookbookWindow = SNew(SWindow)
      .Title(FText::FromString(WindowTitle))
      .ClientSize(FVector2D(800, 400))
      .SupportsMaximize(false)
      .SupportsMinimize(false);
      IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked<IMainFrameModule>(TEXT("MainFrame"));
      if (MainFrameModule.GetParentWindow().IsValid())
      {
        FSlateApplication::Get().AddWindowAsNativeChild(CookbookWindow, MainFrameModule.GetParentWindow().ToSharedRef());
      }
      else
      {
        FSlateApplication::Get().AddWindow(CookbookWindow);
      }
    }
  5. Compile your code, and launch the editor.
  6. Play the level, then hit the tilde key to bring up the console.
  7. Type DisplayTestCommandWindow, and hit Enter.
    How to do it...
  8. You should see our tutorial window open up:
    How to do it...

How it works...

  1. Console commands are usually provided by a module. The best way to get the module to create the command when it is loaded is to place the code in the StartupModule method.
  2. IConsoleManager is the module that contains the console functionality for the engine.
  3. As it is a sub-module of the core module, we don't need to add any additional information to the build scripts to link in additional modules.
  4. In order to call functions within the console manager, we need to get a reference to the current instance of IConsoleManager that is being used by the engine. To do so, we invoke the static Get function, which returns a reference to the module in a similar way to a singleton.
  5. RegisterConsoleCommand is the function that we can use to add a new console command, and make it available in the console:
    virtual IConsoleCommand* RegisterConsoleCommand(const TCHAR* Name, const TCHAR* Help, const FConsoleCommandDelegate& Command, uint32 Flags);
  6. The parameters for the function are the following:
    1. Name: The actual console command that will be typed by users. It should not include spaces.
    2. Help: The tooltip that appears when users are looking at the command in the console. If your console command takes arguments, this is a good place to display usage information to users.
    3. Command: This is the actual function delegate that will be executed when the user types the command.
    4. Flags: These flags control visibility of the command in a shipping build, and are also used for console variables. ECVF_Default specifies the default behavior wherein the command is visible, and has no restrictions on availability in a release build.
  7. To create an instance of the appropriate delegate, we use the CreateRaw static function on the FConsoleCommand delegate type. This lets us bind a raw C++ function to the delegate. The extra argument that is supplied after the function reference, the FString "Test Command Window", is a compile-time defined parameter that is passed to the delegate so that the end user doesn't have to specify the window name.
  8. The second console command, DisplayUserSpecifiedWindow, is one that demonstrates the use of arguments with console commands.
  9. The primary difference with this console command, aside from the different name for users to invoke it, is the use of FConsoleCommandWithArgsDelegate and the CreateLambda function on it in particular.
  10. This function allows us to bind an anonymous function to a delegate. It's particularly handy when you want to wrap or adapt a function so its signature matches that of a particular delegate.
  11. In our particular use case, the type of FConsoleCommandWithArgsDelegate specifies that the function should take a const TArray of FStrings. Our DisplayWindow function takes a single FString to specify the window title, so we need to somehow concatenate all the arguments of the console command into a single FString to use as our window title.
  12. The lambda function allows us to do that before passing the FString onto the actual DisplayWindow function.
  13. The first line of the function, [&](const TArray<FString>& Args), specifies that this lambda or anonymous function wants to capture the context of the declaring function by reference by including the ampersand in the capture options [&].
  14. The second part is the same as a normal function declaration specifying that our lambda takes in const Tarray containing FStrings as a parameter called Args.
  15. Within the lambda body, we create a new FString, and concatenate the strings that make up our arguments together, adding a space between them to separate them so that we don't get a title without spaces.
  16. It uses a range-based for loop for brevity to loop over them all and perform the concatenation.
  17. Once they're all concatenated, we use the this pointer (captured by the & operator mentioned earlier) to invoke DisplayWindow with our new title.
  18. In order for our module to remove the console command when it is unloaded, we need to maintain a reference to the console command object.
  19. To achieve this, we create a member variable in the module of type IConsoleCommand*, called DisplayTestCommand. When we execute the RegisterConsoleCommand function, it returns a pointer to the console command object that we can use as a handle later.
  20. This allows us to enable or disable console commands at runtime based on gameplay or other factors.
  21. Within ShutdownModule, we check to see if DisplayTestCommand refers to a valid console command object. If it does, we get a reference to the IConsoleManager object, and call UnregisterConsoleCommand passing in the pointer that we stored earlier in our call to RegisterConsoleCommand.
  22. The call to UnregisterConsoleCommand deletes the IConsoleCommand instance via the passed-in pointer, so we don't need to deallocate the memory ourselves, just reset DisplayTestCommand to nullptr so we can be sure the old pointer doesn't dangle.
  23. The DisplayWindow function takes in the window title as an FString parameter. This allows us to either use a console command that takes arguments to specify the title, or a console command that uses payload parameters to hard-code the title for other commands.
  24. The function itself uses a function called SNew() to allocate and create an SWindow object.
  25. SWindow is a Slate Window, a top-level window using the Slate UI framework.
  26. Slate uses the Builder design pattern to allow for easy configuration of the new window.
  27. The Title, ClientSize, SupportsMaximize, and SupportsMinimize functions used here, are all member functions of SWindow, and they return a reference to an SWindow (usually, the same object that the method was invoked on, but sometimes, a new object constructed with the new configuration).
  28. The fact that all these member methods return a reference to the configured object allows us to chain these method invocations together to create the desired object in the right configuration.
  29. The functions used in DisplayWindow create a new top-level Window that has a title based on the function parameter. It is 800x400 pixels wide, and cannot be maximized or minimized.
  30. With our new Window created, we retrieve a reference to the main application frame module. If the top-level window for the editor exists and is valid, we add our new window instance as a child of that top-level window.
  31. To do this, we retrieve a reference to the Slate interface, and call AddWindowAsNativeChild to insert our window in the hierarchy.
  32. If there isn't a valid top-level window, we don't need to add our new window as a child of anything, so we can simply call AddWindow, and pass in our new window instance.

See also

  • Refer to Chapter 5, Handling Events and Delegates, to learn more about delegates. It explains payload variables in greater detail.
  • For more information on Slate, refer to Chapter 9, User Interface.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.64.126