C++ Mutator Tutorial

From Epic Wiki
Jump to: navigation, search

C++ Mutator Tutorial

This tutorial will teach you how to make a simple mutator for UT using C++. We'll be making a UE4 plugin that defines a child class of AUTMutator.

Requirements

  • Engine version: 4.14
  • Skill level: intermediate C++ knowledge

Directory Structure

  • If UnrealTournament\Plugins does not already exist, create it
  • Create a directory inside of UnrealTournament\Plugins to hold our mutator, in this case it would be "SampleMutator"
  • Create a "Source" directory inside of the SampleMutator directory
  • Finally create a "Public" directory and a "Private" directory inside of the "Source" directory that we made

UPlugin File

  • We need to create SampleMutator.uplugin to let UE4 know we have a plugin it should load.
  • On Windows, one way to do this is to create a new text file and rename the file extension to .uplugin. Copying an existing .uplugin file and editing it may not work!
  • It should live at the same level that the Source directory does

SampleMutator.uplugin - Plugin defintion

<syntaxhighlight lang="cpp"> { "FileVersion" : 3,

"FriendlyName" : "Sample Mutator", "Version" : 1, "VersionName" : "1.0", "CreatedBy" : "Epic Games, Inc.", "CreatedByURL" : "http://epicgames.com", "EngineVersion" : "4.14.0", "Description" : "Sample Mutator", "Category" : "UnrealTournament.Mutator", "EnabledByDefault" : true, "CanContainContent" : false, "Modules" : [ { "Name" : "SampleMutator", "Type" : "Runtime", "WhitelistPlatforms" : [ "Win32", "Win64", "Linux" ] } ] } </syntaxhighlight>

Build.cs File

  • We need to create SampleMutator.Build.cs so that Unreal Build tool knows how to generate our dll file
  • It should live at the same level that the Public and Private directories do (inside the "Source" directory)
  • We'll use the PrivateIncludesPaths array to make #include cleaner throughout our source files

SampleMutator.Build.cs - Unreal Build Tool definitions

<syntaxhighlight lang="cpp"> namespace UnrealBuildTool.Rules { public class SampleMutator : ModuleRules { public SampleMutator(TargetInfo Target)

       	{
           		PrivateIncludePaths.Add("SampleMutator/Private");

PublicDependencyModuleNames.AddRange( new string[] { "Core", "CoreUObject",

                   			"Engine",
                   			"UnrealTournament",

} ); } } } </syntaxhighlight>

Header File

  • Unreal Build Tool requires that we have a shared header file as the first include in every cpp source file in the plugin.
  • We'll put the SampleMutator.h header file inside of the Public directory
  • We need to #include Core.h, Engine.h, UTMutator.h and UTWeapon.h to have enough definitions to make a child class from AUTMutator
    1. include "SampleMutator.generated.h" is required by Unreal Header Tool
  • We're only overriding CheckRelevance_Implemenation in this example, but more powerful mutators will overload more of the functions exposed by AUTMutator

SampleMutator.h - Sample Mutator class definition

<syntaxhighlight lang="cpp">

  1. pragma once
  1. include "Core.h"
  2. include "Engine.h"
  3. include "UTMutator.h"
  4. include "UTWeapon.h"
  1. include "SampleMutator.generated.h"

UCLASS(Blueprintable, Meta = (ChildCanTick)) class ASampleMutator : public AUTMutator { GENERATED_UCLASS_BODY()

bool CheckRelevance_Implementation(AActor* Other) OVERRIDE;


UPROPERTY() TSubclassOf<AUTWeapon> RocketLauncherClass; }; </syntaxhighlight>

Plugin Module Interface

  • Create SampleMutatorPlugin.cpp inside of the Private directory
  • There's not much interesting in this file, it just provides UE4 a way to load this mutator as a plugin so I'm going to gloss over this file
  • Note that SampleMutator.h must be the first #include and the IMPLEMENT_MODULE macro does all the heavy lifting

SampleMutatorPlugin.cpp - Plugin Module Interface

<syntaxhighlight lang="cpp">

  1. include "SampleMutator.h"
  1. include "Core.h"
  2. include "Engine.h"
  3. include "ModuleManager.h"
  4. include "ModuleInterface.h"

class FSampleMutatorPlugin : public IModuleInterface { /** IModuleInterface implementation */ virtual void StartupModule() override; virtual void ShutdownModule() override; };

IMPLEMENT_MODULE( FSampleMutatorPlugin, SampleMutator )

void FSampleMutatorPlugin::StartupModule() {

}

void FSampleMutatorPlugin::ShutdownModule() {

} </syntaxhighlight>

The Mutator

  • This sample is going to replace all weapon pickups with Rocket Launcher pickups
    • We'll accomplish that by overriding the CheckRelevance_Implementation function and watching for AUTPickupWeapon
    • When we find a AUTPickupWeapon, we'll replace its WeaponType reference with a reference to BP_RocketLauncher_C
  • Our mutator is going to live in SampleMutator.cpp inside the Private directory
  • Note that once again SampleMutator.h has to be the first #include
  • Our constructor is blank, but feel free to put initialization code in there in a future plugin
  • StaticLoadClass is used to get the reference to Blueprint'/Game/RestrictedAssets/Weapons/RocketLauncher/BP_RocketLauncher.BP_RocketLauncher_C'
    • The use of a hardcoded reference here is very brittle and not considered good design, a mutator that requires direct blueprint references might be more easily implemented in blueprints
  • Calling Super::CheckRelevance_Implementation(Other) is highly recommended as failing to do so won't call other mutators in the mutator list

SampleMutator.cpp - Replace all weapon pickups with rocket launcher pickups

<syntaxhighlight lang="cpp">

  1. include "SampleMutator.h"
  1. include "UTPickupWeapon.h"

ASampleMutator::ASampleMutator(const FObjectInitializer& ObjectInitializer) : Super(PCIP) { }

bool ASampleMutator::CheckRelevance_Implementation(AActor* Other) { // Find the rocket launcher blueprint if (RocketLauncherClass == nullptr) { RocketLauncherClass = StaticLoadClass(AUTWeapon::StaticClass(), nullptr, TEXT("Blueprint'/Game/RestrictedAssets/Weapons/RocketLauncher/BP_RocketLauncher.BP_RocketLauncher_C'")); }

// If a weapon pickup has a current weapon type, replace it with the rocket launcher AUTPickupWeapon *PickupWeapon = Cast<AUTPickupWeapon>(Other); if (PickupWeapon) { if (PickupWeapon->WeaponType != nullptr) { PickupWeapon->WeaponType = RocketLauncherClass; } }

return Super::CheckRelevance_Implementation(Other); } </syntaxhighlight>

Building your Mutator

  • Right click your UnrealTournament.uproject in Window Explorer and select "Generate Visual Studio project files" or run "GenerateProjectFiles.bat"
  • Open up the Unreal Tournament solution file in Visual Studio
  • Build the Unreal Tournament project in Development Editor configuration
  • You will notice that in addition to UE4Editor-UnrealTournament.dll, UnrealTournament\Plugins\SampleMutator\Binaries\Win64\UE4Editor-SampleMutator.dll was generated

Testing your Mutator

  • Run from a commandline "UE4Editor.exe UnrealTournament -game Example_Map?mutator=SampleMutator.SampleMutator?botfill=0"
  • You should notice that the weapon pickups in the map have been replaced by Rocket Launchers