Plugin Development in Unreal Engine: Part 1

Creating a New Plugin, Getting Comfortable and Duplicating Objects

Hi there,

This is the first of a series of posts dealing with the development of Unreal Engine 4 plugins.

To give a bit of background, Unreal Engine provides a set of tools for game developers and other creatives to work on their project, whatever that may be. The tools provided by UE4 are great, in the sense that they are reliable, well thought out and super useful.

One thing that I found, though, is that while these tools are good, some people may be under the wrong impression that you can make your own UE4 project using these tools and everything will work out of the box for you exactly in the way you imagine. This is frequently not the case, and in my experience, most dealings with UE4 will require some custom code, and frequently, many projects will require to re-use the same code. This is where UE4 plugins fill that gap, allowing developers to work on plugins that they sell on the marketplace or develop in-house.

Personally, I find UE4 plugin development very fun, because with very little code you can achieve big changes and big improvements in other people’s day to day tasks and workflows.

After thinking for a while, I decided to work on this tutorial series. While we will not be creating a particularly useful plugin during this tutorial, we will be focusing on tasks that I think are generic enough to be useful to you no matter what kind of plugin you are going to develop. These tasks are as follows:

  • Implementing a toolbar button that will create a new asset in the level. In this case we will be doing that by copying a Map from the plugin directory to the project directory.

  • Implement a custom action for other Map assets to add them as a sublevel for the asset we created. This is useful to know because it will require a deep dive into how to modify assets in editor. This is implemented in part 2.

  • Modifying project settings. We will be modifying project settings in order to make the map we create the default.

  • Teleport an object between locations of a level, as well as between sublevels.

  • Implement unit testing and automation testing. Unit testing is something which I believe has a positive impact on code quality. I will not be aiming to have 100% test coverage on this test project, but I want to aim to have one unit test and one automation test.

As homework prior to getting started with this tutorial, I would recommend checking out the “Best Practices for Creating and Using Plugins” tutorial on UE4’s learn site. I will be releasing the code for the tutorial here under an MIT license.

Getting Started

In order to get started, I will create a plugin using one of the templates provided by Unreal. This is done by navigating to “Edit > Plugins” and clicking “New Plugin”. Then, from the choices available to you, pick “Editor Toolbar Button” as shown below:

Creating a new plugin

Creating a new plugin

I chose to do this with a toolbar button because I think it is what makes more sense in this case. If you were going to create a new asset type and not a level then you would probably want to create an asset factory as documented here. Creating a factory as described there requires additional steps as of version 4.25.1 because of a change in behaviour.

This creates a button after you restart UE4:

New button appearing

New button appearing

To get started, we will make sure changes we make to the module are reflected in the UI. This is important to have a quick iteration loop when we are developing. To do this, I opened “TutorialExample.cpp”, and modified the “PluginButtonClicked()” function, so that the text was “Hello, world.”. We can compile the code by hitting Ctrl + Shift + B or clicking “Build > Build Solution”.

Even though there is a “Hot Reload Successful” message popping by, clicking the button again yields the same message. Now, we could restart the editor every time we want to test a change, but I found online that you can reload the module by going to “Windows > Developer Tools > Modules”. You can filter until you find your module and click compile and reload. Now the new message pops up:

Hello, World!

Hello, World!

You can also choose to launch Visual Studio first, and then launch UE4 pressing the F5 key. This will have the advantage of enabling functionality such as debugging and breakpoints.

Copying Assets from the Plugin Content Folder to the Project.

The generic goal of this project is the ability to copy an asset from the plugin’s Content folder into the project’s Content folder. I chose to implement things this way because as a plugin developer, you can easily use the asset editor in order to create the asset, instead of having to create it programmatically. For example, if we needed to create a Map that could then be used by users of our plugin, we can easily create a sample map that contains the assets that we need.

Users of our plugin could be instructed to simply copy an asset over from the plugin folder, but I think that copying the files programmatically results in a better user experience. For example, if your plugin allows users to create a new kind of object that is customizable in some way, you can use a base asset, use this code, and then perform modifications to the asset when the button is clicked. The possibilities are many, and they apply to any kind of asset that is supported by UE4.

In order to get started, first we need to configure our plugin so that it is allowed to have content. In order to do that, I modified the “TutorialExample.uplugin” file. The modification I made was to change “"CanContainContent": false,” to “"CanContainContent": true,”. After that, I restarted Unreal Engine and created a new map with some sample content:

Manually creating a new map from one of the templates to use as a “Base Level”

Manually creating a new map from one of the templates to use as a “Base Level”

Once that is done and you restarted the editor, you should see the plugin’s content folder. If you cannot see it, ensure “Show plugin content” is ticked in the view options, a little eye on the lower right corner.

Copying assets is not one of those well-documented Unreal Engine features. I’ve had a look around and  there are a couple of examples online, most notably this one, titled “Create a new Level from C++ code”. Searching for similar examples online also yields other users of the same code, so presumably the code works for some people.

Here is the code below:

 auto WorldTemplateObj = StaticLoadObject(UWorld::StaticClass(), NULL, TEXT("World'/MyPlugin/LevelTemplate.LevelTemplate'"));
 ObjectTools::FPackageGroupName PGN;
 pgn.ObjectName = MyNewLevelName;
 pgn.PackageName = MyNewPackageName;
 TSet<UPackage*> ObjectsUserRefusedToFullyLoad;
 
 auto World = CastChecked<UWorld>(ObjectTools::DuplicateSingleObject(WorldTemplateObj, PGN, ObjectsUserRefusedToFullyLoad));

There are certain aspects that are a little bit mystifying. For example, I was slightly confused at the path ` World'/MyPlugin/LevelTemplate.LevelTemplate'`. This doesn’t look like any paths I have ever seen, and the documentation for that parameter only describes it as “fully qualified path name”, which can have many meanings depending on the context but never looks like anything like that. A little experimentation shows that this syntax results in an exception due to a null pointer being returned by the StaticLoadObject function.

What I ended up with is a slightly tweaked version of the code which does not use weird paths like that, but normal UE4 paths that follow the standard UE4 path naming conventions, and also implements null checking to prevent accidental crashes. Additionally, it also deals with saving the level to disk, and opening the newly created level.

The basic working code is linked here, and also below for your reference:

void FTutorialExampleModule::PluginButtonClicked()
{
    UE_LOG(LogTemp, Warning, TEXT("Entering world duplicate code."));

    UObject* worldTemplateObj = StaticLoadObject(UWorld::StaticClass(), NULL, TEXT("/TutorialExample/BaseLevel"));
    if (IsValid(worldTemplateObj)) 
    {
        ObjectTools::FPackageGroupName pgn;
        pgn.ObjectName = TEXT("BaseLevel");
        pgn.PackageName = TEXT("/Game/Maps/BaseLevel");
        TSet<UPackage*> objectsUserRefusedToFullyLoad;

        UObject* worldObj = ObjectTools::DuplicateSingleObject(worldTemplateObj, pgn, objectsUserRefusedToFullyLoad);
        if (IsValid(worldObj)) {
            UWorld* world = CastChecked<UWorld>(worldObj);
            FEditorFileUtils::SaveLevel(world->PersistentLevel);
            FEditorFileUtils::LoadMap(TEXT("/Game/Maps/BaseLevel"));
        } else {
            UE_LOG(LogTemp, Warning, TEXT("Could not duplicate object. Rejected by user?"));
        }
    } else {
        UE_LOG(LogTemp, Warning, TEXT("World template invalid! Exiting."));
        return;
    }
}

At this point, any modifications we make to the BaseLevel within the plugin will be automatically copied over when we click that button. This means that we can modify the level in whatever way we need, without the need to modify any C++ code. Additionally, while the code above is specifically for dealing with maps, we can easily modify it to deal with other asset types by changing the code that is present after the `if (IsValid(worldObj))` line. You can also perform modifications to the object you duplicate. For example, UWorld has a series of functions that can be called, noted here.

The code above surprises me in how simple it ends up being, as frequently UE4 code does. Firstly, we call the “StaticLoadObject” function with two parameters: the first one being the class reference for UWorld, and the last one the path to the file. This gives us an instance of UObject, which all UE4 classes inherit from. We then call the “DuplicateSingleObject” method with three parameters, of which only “pgn” is new and important. “pgn” corresponds to a struct on which we set the properties of the new package. Keep in mind that in this context, package refers to how files are stored on disk.

And this completes this part of our plugin’s feature set. To see the next post in the Unreal Engine plugin development series, stay tuned! We will cover how to modify a map in order to add sublevels, as well as discovering how to implement this by reading Unreal Engine’s source code.

Edit: Part 2 is now available here.

Previous
Previous

Plugin Development in Unreal: Part 2