{"id":5223,"date":"2023-10-19T23:38:42","date_gmt":"2023-10-19T15:38:42","guid":{"rendered":"http:\/\/blog.coolcoding.cn\/?p=5223"},"modified":"2023-10-21T22:28:35","modified_gmt":"2023-10-21T14:28:35","slug":"speedrun","status":"publish","type":"post","link":"https:\/\/blog.coolcoding.cn\/?p=5223","title":{"rendered":"Speedrun"},"content":{"rendered":"\n<p>This article assumes significant experience with C++, but not necessarily within the Unreal Engine ecosystem. Some C# is also required.<\/p>\n\n\n\n<p>Although most of your knowledge will carry right into Unreal, there\u2019s a lot that I had to find out the hard way. Some things are either not documented at all, or only \u201cdocumented\u201d in the form of unstructured multi-hour Twitch livestreams archived on YouTube.<\/p>\n\n\n\n<p>That said,&nbsp;<a href=\"https:\/\/dev.epicgames.com\/community\/learning\/courses\/KJ\/converting-blueprint-to-c\/\">this<\/a>&nbsp;series of videos from the official learning site is relatively good, even if the title of this course is somewhat misleading.<\/p>\n\n\n\n<p>As of writing, the latest Unreal Engine version is 5.3.0 preview 1.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"language-choice\">Language choice<\/h1>\n\n\n\n<p>Blueprints or C++? The only correct answer is&nbsp;<strong>both<\/strong>. BP is not limited to the spaghetti node graph but also provides essential features such as providing references to assets, configuration, or runtime types that don\u2019t exist in C++.<\/p>\n\n\n\n<p>Don\u2019t get frustrated looking for C++ resources and finding that most of the documentation or online resources target BP. Most of those nodes are wrappers of some C++ engine function that you were really looking for, and are often found by searching for&nbsp;<code>ExampleName<\/code>&nbsp;or&nbsp;<code>\"Example Name\"<\/code>&nbsp;in the engine source code.<\/p>\n\n\n\n<p>It\u2019s very common to build a C++ base class and a BP subclass together that form a single logical unit, especially with actor classes. C++ is great at core architecture, performance, and being source controlled; BP is great at asset references, visuals, ease of programming, fast iteration, and async code. Good luck replicating a Timeline node in C++ without special extensions such as C++20&nbsp;coroutines. It\u2019s far simpler to define a BlueprintImplementableEvent (more on this later) and call it from C++.<\/p>\n\n\n\n<p>Since BP is essentially unmergeable, you\u2019ll need to consider this for your version control setup. The most common approach is to use exclusive checkouts and file locks.<\/p>\n\n\n\n<p>There is a thing as too much C++. Do not ever hardcode an asset path into C++, these often lead to broken references and packaging errors. This includes a total ban on&nbsp;<code>ConstructorHelpers<\/code>&nbsp;and similar functionality. Configuration is also better done using the engine\u2019s functionality instead of piles of #defines.<\/p>\n\n\n\n<p>There\u2019s an official&nbsp;<a href=\"https:\/\/docs.unrealengine.com\/5.1\/en-US\/epic-cplusplus-coding-standard-for-unreal-engine\/\">coding standard<\/a>. It\u2019s relatively unusual in that it overuses PascalCase to the extreme. Some studios follow it, some don\u2019t; both approaches have merit. Not even the engine follows it consistently.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"development-environment\">Development environment<\/h1>\n\n\n\n<p>Windows is the primary (arguably only) platform fully supported for development. Linux and macOS do not have Epic\u2019s full attention for the Editor, but support is better for them as target platforms for shipped games.<\/p>\n\n\n\n<p>There are two practical choices for an IDE, Visual Studio and JetBrains Rider. As of writing, Rider on Windows requires you to have a license for Visual Studio according to JetBrains (which may be Community if you\u2019re eligible). For the other two platforms, you\u2019re left with only Rider.<\/p>\n\n\n\n<p>Visual Studio is only viable starting from 2022 version 17.7; avoid everything earlier. Rider gained Unreal support in version 2022.1. Visual Studio&nbsp;<strong>Code<\/strong>&nbsp;is an entirely different product and is considered broken for Unreal development. CLion, despite being the C++ IDE in the IntelliJ world, does not support Unreal.<\/p>\n\n\n\n<p>The decision between Visual Studio and Rider mostly boils down to price and GUI preference. If your computer has 16 GB of RAM or less, you might also want to consider that VS generally uses less RAM than Rider.<\/p>\n\n\n\n<p>VS Community can\u2019t be beaten on its zero price, and essentially all of Rider\u2019s features are available as a VS add-in in the form of ReSharper for slightly cheaper. If you\u2019re not eligible for Community, having a VS license needs to be factored into Rider\u2019s price on Windows.<br>There\u2019s also a discounted ReSharper+Rider bundle. Some people like using both at the same time, generally preferring Rider for editing code and VS for debugging in this case.<\/p>\n\n\n\n<p>Although JetBrains is heavily pushing for subscriptions, perpetual licenses are still available in a&nbsp;<a href=\"https:\/\/sales.jetbrains.com\/hc\/articles\/207240845\">hybrid scheme<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"vs2022\">VS2022<\/h2>\n\n\n\n<p>Out of the box, VS does not understand Unreal macros or the build system, and IntelliSense will often break, displaying nonsensical errors. The Error List is essentially useless for Unreal; you\u2019ll always want to look at the Output panel for the full, unabridged errors.&nbsp;<a href=\"https:\/\/docs.unrealengine.com\/5.2\/en-US\/setting-up-visual-studio-development-environment-for-cplusplus-projects-in-unreal-engine\/#turnofftheerrorlistwindow\">Here<\/a>\u2019s how to turn it off and prevent it from coming back.<\/p>\n\n\n\n<p>While VS2022 17.7 is a massive leap in usability compared to earlier versions, it\u2019s still not perfectly stable and IntelliSense will sometimes \u201cgive up\u201d on some files. This usually manifests in the files losing highlighting, or getting red squigglies on everything. To fix this, there are two popular commercially-available extensions that add full Unreal support and various other convenience features:<\/p>\n\n\n\n<ul><li><a href=\"https:\/\/www.jetbrains.com\/resharper-cpp\/\">ReSharper<\/a>&nbsp;(JetBrains)<\/li><li><a href=\"https:\/\/www.wholetomato.com\/\">Visual Assist<\/a>&nbsp;(Whole Tomato)<\/li><\/ul>\n\n\n\n<p>Visual Assist\u2019s quality has been declining for a while, and its development lags behind ReSharper. Its users also report the purchasing\/renewal process as being annoying. As such, I cannot recommend it, but if you\u2019re already using it, you know what to expect.<\/p>\n\n\n\n<p>UE ships with a VS extension called UnrealVS (found in Engine\/Extras\/UnrealVS), which is highly recommended. Even for non-Unreal projects, it provides a convenient way to set command-line arguments for debugging. The third-party&nbsp;<a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=Fiquegnima-productions.UnrealWiz\">Unreal Wizard<\/a>&nbsp;extension is also strongly recommended for additional\/replacement functionality where UnrealVS is broken.<\/p>\n\n\n\n<p>Since the .sln that Unreal generates doesn\u2019t have real folders in it (only \u201cfilters\u201d), attempting to create new files using built-in methods will cause them to default to somewhere in Intermediate where they won\u2019t build. Use one of the extensions above to create new files, or create them \u201craw\u201d in the filesystem and regenerate your solution to pick them up.<\/p>\n\n\n\n<p>If you\u2019re using VS without Visual Assist or ReSharper, I highly recommend having Unreal Wizard\u2019s toolbar on your main toolbar for the \u201cGenerate Project Files\u201d button. When IntelliSense breaks, pressing that and reloading often resolves the issue. If that doesn\u2019t work, the next step is deleting the&nbsp;<code>.vs<\/code>&nbsp;folder in your project.<\/p>\n\n\n\n<p>Check if you have accidentally installed IncrediBuild from the VS installer. If you don\u2019t have a license and a server farm set up for it, it will only slow your builds down, so get rid of it. It\u2019s also been reported to break shader compilation in this case.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"rider\">Rider<\/h2>\n\n\n\n<p>Since Rider contains ReSharper out of the box, it mostly Just Works\u2122. Although it can work with Unreal-generated .slns, it also supports opening the .uproject file directly as a project, meaning that you\u2019ll never need to bother with (re-)generating a solution.<\/p>\n\n\n\n<p>The Error List advice from VS partially applies to Rider, as it also likes to discard useful parts of error messages in its pretty display. Where and how you get to the raw textual output varies between versions and the classic\/new GUI: the button could be \u201cToggle Console View\u201d next to \u201cBuild Output\u201d or \u201cToggle Tree View Mode\u201d to the left of the errors. 2023.2\/new GUI seems to default to showing both, which is a nice compromise.<\/p>\n\n\n\n<p>For Rider,&nbsp;<a href=\"https:\/\/plugins.jetbrains.com\/plugin\/16411-ezargs\">EzArgs<\/a>&nbsp;can replicate some of the functionality of UnrealVS. Contrary to Rider\u2019s claims in the pop-up that you will almost certainly see, the RiderLink plugin is&nbsp;<strong>NOT<\/strong>&nbsp;required and only provides some minor features. It may be used, of course, but if you ever have build errors referring to&nbsp;<code>module rules named 'RD'<\/code>, it should be the prime suspect. The fix is to delete every Rider plugin from your project and\/or engine.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"project-structure\">Project structure<\/h1>\n\n\n\n<p>When you create a new Unreal project, you\u2019re given a set of options, notably including a toggle between a BP or C++ project. This has misled many people. There\u2019s only one kind of .uproject: picking either is not a commitment but simply a set of default settings and starter code that can be changed entirely.<\/p>\n\n\n\n<p>BP-only projects do not contain a Source folder by default, but one can be added by creating any C++ class from the editor. Projects with a Source folder can return to being BP only by deleting Source and editing the .uproject file (which is JSON) to no longer refer to the modules that are now gone.<\/p>\n\n\n\n<p>The generated Visual Studio solution and its accompanying vcxprojs exist only for IDE compatibility and are not used in the build process aside from invoking Unreal\u2019s custom build system. As such, it\u2019s pointless to try and change compiler\/linker\/nmake settings in them. Unreal\u2019s own build tool is the creatively-named UnrealBuildTool (UBT), which makes heavy use of the UnrealHeaderTool (UHT) for code generation.<\/p>\n\n\n\n<p>Your actual build files are the Target.cs and Build.cs files you get when you create any starter C++ project template or add a C++ class to a BP-only project.&nbsp;<a href=\"https:\/\/docs.unrealengine.com\/5.1\/en-US\/unreal-engine-modules\/\">Here<\/a>\u2019s the official documentation on them.<\/p>\n\n\n\n<p>Target.cs files govern building your entire project. Targets are things such as editor, standalone, dedicated server. These get mixed with the familiar \u201cdebug\u201d and \u201crelease\u201d configurations (but more nuanced in Unreal) to form your overall list of project configurations. See the documentation&nbsp;<a href=\"https:\/\/docs.unrealengine.com\/5.1\/en-US\/build-configurations-reference-for-unreal-engine\/\">here<\/a>.<\/p>\n\n\n\n<p>Build.cs files govern an individual module (.dll or static lib, C++20 modules are not supported or used), not unlike .asmdefs in Unity. An&nbsp;<code>Example<\/code>&nbsp;module would live in the Example folder under Source with an&nbsp;<code>Example.Build.cs<\/code>&nbsp;file in it, its exported classes would be marked with&nbsp;<code>EXAMPLE_API<\/code>&nbsp;(the dllimport\/dllexport macro is automatically defined), and other modules would reference it as&nbsp;<code>\"Example\"<\/code>&nbsp;in, e.g., their PublicDependencyModuleNames. These all correspond to each other.<\/p>\n\n\n\n<p>All of these .cs files in a project get compiled together, therefore you can, e.g., define an intermediate base class or extension methods to apply common build settings. Approaches to manage this include adding extra functionality to Target.cs files or defining an empty C++ module for nothing else but its C# functionality.<\/p>\n\n\n\n<p>Modules may be part of your project directly or belong to plugins. In addition to modules, plugins may also contain content (assets, blueprints) and have extra control for being toggled and their loading order in the corresponding .uplugin file. Content-only plugins are also possible and all plugins may be installed directly into the engine and shared between projects.<\/p>\n\n\n\n<p>BP classes are considered content, but C++ classes are not. As a result, the Unreal editor\u2019s \u201cC++ Classes\u201d folder in the Content Browser is mostly useless and should be ignored.<\/p>\n\n\n\n<p>The Public\/Private folder structure commonly seen is a convention directly supported by the build system and the editor, but it\u2019s not strictly required. Include paths default to a module\u2019s Public folder when it\u2019s added as a dependency. Otherwise it\u2019s a longer path relative to the Build.cs file.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"compiling\">Compiling<\/h1>\n\n\n\n<p>Launching your project via the .uproject file runs the&nbsp;<code>Development Editor<\/code>&nbsp;configuration. By default, Unreal will check if a build for this&nbsp;<em>exists<\/em>, but not if it\u2019s&nbsp;<em>up to date<\/em>. This is the source of many \u201cwhy is my code not doing what it should?\u201d issues, serialization errors, possible data corruption, etc.&nbsp;<a href=\"https:\/\/landelare.github.io\/2022\/09\/27\/tips-and-tricks.html#automatically-update-c-binaries\">This setting<\/a>&nbsp;helps to avoid running with outdated binaries.<\/p>\n\n\n\n<p>To update C++ code while the editor is running, Unreal Engine comes with not one, but two methods for live patching, and both are broken in different ways. ?<\/p>\n\n\n\n<p><strong>It\u2019s recommended to have Live Coding ON and reinstancing OFF as safe settings, which will prevent accidental Hot Reloads even if you do not use Live Coding.<\/strong><\/p>\n\n\n\n<p>Note that restarting the editor is not enough to get back to a clean state if you used either of these. You\u2019ll need to build (not rebuild, it\u2019s a waste of CPU time)&nbsp;<strong>while Unreal is closed<\/strong>.<\/p>\n\n\n\n<p>The only 100% reliable and stable method of building C++ is treating Unreal like you would any other C++ program and compiling while it\u2019s&nbsp;<strong>NOT<\/strong>&nbsp;running. Launching it for debugging from your IDE of choice is the best workflow for this, as it avoids both outdated binaries (assuming your IDE is set to build before running), and one method of triggering Hot Reload.<\/p>\n\n\n\n<p>Don\u2019t use Unreal\u2019s built-in C++ class wizard, it tries to compile and load the new class \u201clive\u201d. There\u2019s a setting to disable this, but the editor needs to be closed in order to compile the new class safely anyway. Use an \u201cadd Unreal class\u201d feature from your IDE instead.&nbsp;<a href=\"https:\/\/landelare.github.io\/2023\/01\/07\/cpp-speedrun.html#development-environment\">This section<\/a>&nbsp;has a few suggestions for both paid and free addons with such functionality, in case you scrolled past it. Ultimately, you\u2019re just creating two text files and compiling; anything will do.<\/p>\n\n\n\n<p>Launching in a debugger has the added benefit of immediately catching rare issues that sadly do happen during development. I have regretted&nbsp;<strong>NOT<\/strong>&nbsp;running in a debugger on multiple occasions. The Epic crash reporter will grab your call stack, but you\u2019ll lose runtime state. I\u2019m personally using&nbsp;<code>DebugGame Editor<\/code>&nbsp;as my daily driver configuration, but there\u2019s nothing wrong with&nbsp;<code>Development Editor<\/code>&nbsp;either. It runs somewhat faster, helps keep your binaries up-to-date for .uproject launches while you unlearn to do that, but it\u2019s less reliable for debugging.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"hot-reload\">Hot Reload<\/h2>\n\n\n\n<p>This is Unreal\u2019s own implementation that predates the Visual Studio hot reload feature.<\/p>\n\n\n\n<p>It\u2019s enabled by default on UE4 and suppressed by Live Coding on UE5. Live Coding may be enabled on UE4 to suppress it there as well.<\/p>\n\n\n\n<p>It can corrupt data in memory that may get persisted if you save any .uassets after it happens. This can corrupt these files permanently, requiring a revert or having to recreate them from scratch to recover. Copy-pasting nodes or data from one to another can carry the corruption with it.<\/p>\n\n\n\n<p>Hot Reload can also cause random crashes and misleading results while debugging due to the aforementioned corruption.<\/p>\n\n\n\n<p>It kicks in automatically if you compile from your IDE while Unreal is running unless disabled or suppressed. If you accidentally did this, quit the editor and do not save anything when it asks. Technically, using Hot Reload while ensuring that no .uassets are ever saved once it has happened in a running process is \u201csafe\u201d but also highly error-prone and thus not recommended.<\/p>\n\n\n\n<p><em>Tools \u2192 Debug \u2192 Modules<\/em>&nbsp;in the Unreal editor is an interface to Hot Reload with the same issues.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"live-coding-live\">Live Coding (Live++)<\/h2>\n\n\n\n<p>Enabled by default in UE5, disabled but available in UE4. Windows only. Also known as&nbsp;<a href=\"https:\/\/liveplusplus.tech\/\">Live++<\/a>, named after the underlying tech that Epic licenses.<\/p>\n\n\n\n<p>With reinstancing on (default enabled in UE5, unavailable in UE4) it exhibits corruption similar to Hot Reload, and the same recommendations apply.<\/p>\n\n\n\n<p>With reinstancing turned off, it \u201cmerely\u201d causes crashes and runtime corruption, but your BPs\/uassets remain safe. This is often considered a worthwhile tradeoff for the speedhack-like iteration benefits it provides. At the first sign of things going wrong, quit the editor, build, and start it anew.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"unity\">Unity<\/h2>\n\n\n\n<p>UBT supports automatically-managed&nbsp;<a href=\"https:\/\/en.wikipedia.org\/wiki\/Unity_build\">Unity builds<\/a>, which is enabled by default. This dramatically speeds up clean rebuilds, and UBT\u2019s adaptive unity system tries to strike a balance between this and not slowing down incremental builds.<\/p>\n\n\n\n<p>However, unity can hide #include problems and even break otherwise-correct code that, e.g., uses unnamed namespaces. This can surface as code that\u2019s assumed to be correct \u201crandomly\u201d breaking due to UBT rearranging what .cpp files belong to what unity translation unit.<\/p>\n\n\n\n<p>Debugging these issues should start by turning unity off: add&nbsp;<code>bUseUnity = false;<\/code>&nbsp;to your Build.cs, and your compiler\/linker errors will often make more sense and directly correspond to the real issue at hand.<\/p>\n\n\n\n<p>There\u2019s a similar&nbsp;<code>bUseUnityBuild<\/code>&nbsp;setting available for Target.cs files that affects&nbsp;<strong>EVERYTHING<\/strong>, including forcing a rebuild of the entire engine if you\u2019re on a source build. Only use this if you really mean it.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"unreal-type-system\">Unreal type system<\/h1>\n\n\n\n<p>Unreal has its parallel, garbage-collected world that\u2019s heavily inspired by the .NET type system.&nbsp;<code>UCLASS<\/code>&nbsp;and&nbsp;<code>USTRUCT<\/code>&nbsp;correspond to the C# concept of a&nbsp;<code>class<\/code>&nbsp;and a&nbsp;<code>struct<\/code>&nbsp;instead of C++\u2019s. UCLASSes are only passed around by pointer (at least in UFUNCTIONs) and are heap only. USTRUCTs are semantically passed by value even if they\u2019re references, Blueprints will happily object slice them.<\/p>\n\n\n\n<p>These types are required for anything the engine needs to reflect, such as usage in Blueprints, automatic serialization, DYNAMIC delegates (see later), etc.<\/p>\n\n\n\n<p>Their macros (also&nbsp;<code>UENUM<\/code>,&nbsp;<code>UFUNCTION<\/code>,&nbsp;<code>DECLARE_DYNAMIC_..._DELEGATE...<\/code>, etc.) are #defined to nothing in C++ but parsed by UHT as part of the build process and get an additional .h\/.cpp pair generated per affected file.<\/p>\n\n\n\n<p>It is automatically enforced to #include the&nbsp;<code>.generated.h<\/code>&nbsp;as the last item and use&nbsp;<code>GENERATED_BODY()<\/code>&nbsp;within these types to inject extra members that the engine needs. If you ever see a similar three-word macro, such as&nbsp;<code>GENERATED_UCLASS_BODY<\/code>, those are all obsolete and have been replaced by&nbsp;<code>GENERATED_BODY<\/code>&nbsp;in all cases.<\/p>\n\n\n\n<p>This type system is limited in what it can do, e.g., it\u2019s enforced that UFUNCTIONs can only pass UObjects by pointer or reference-to-pointer, and struct pointers are disallowed. Templates are essentially whitelisted to some built-in containers such as TArray, TMap, etc.<\/p>\n\n\n\n<p>UFUNCTIONs must have a unique parameter list, \u201coverloads\u201d need to have a different name.<\/p>\n\n\n\n<p>The Epic naming convention (USomething, ASomething, FSomething, \u2026) is enforced for classes participating in this type system.<\/p>\n\n\n\n<p>Sadly, these types are incompatible with namespaces. The common workaround is to use namespaces for your \u201craw\u201d types that can be in one and a short (3-6 characters) prefix based on your project\u2019s or company\u2019s name otherwise, to avoid name collisions with the engine and third-party plugins.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"uobject\">UObject<\/h2>\n\n\n\n<p>UObjects are UE\u2019s .NET&nbsp;<code>System.Object<\/code>&nbsp;equivalent. They exclusively live on the heap with a garbage collector taking care of them and are usually passed around by pointers or references to pointers (think&nbsp;<code>ref object<\/code>). You mostly create them with&nbsp;<code>NewObject&lt;T&gt;<\/code>&nbsp;or&nbsp;<code>CreateDefaultSubobject&lt;T&gt;<\/code>.<\/p>\n\n\n\n<p>The most important subclass of UObject is AActor, it even has its own class prefix. AActors have a&nbsp;<a href=\"https:\/\/docs.unrealengine.com\/5.1\/en-US\/unreal-engine-actor-lifecycle\/\">complex lifecycle<\/a>&nbsp;that goes beyond mere garbage collection and reachability analysis, and even come with their own creation function,&nbsp;<code>UWorld::SpawnActor<\/code>.<\/p>\n\n\n\n<p>The garbage collector only \u201csees\u201d members marked with&nbsp;<code>UPROPERTY<\/code>; anything not reachable through them is considered unreachable and may be deleted. USTRUCTs can be held by value as a UPROPERTY and in turn hold UObject* UPROPERTYs which would then be considered reachable, etc. This is not reference counting; circular references are perfectly fine.<\/p>\n\n\n\n<p><sub>Currently, objects can be explicitly marked as \u201cpending kill\u201d, automatically setting UPROPERTY pointers pointing to them to nullptr when they\u2019re garbage collected. This also applies to destroyed actors, but there\u2019s a newer GC operation mode where this is no longer the case. The new mode fixes some issues such as formerly-different TMap keys now all being nullptr. Currently, this is opt-in with the inversely-named gc.PendingKillEnabled 0 setting, with Lyra notably opting in. Epic employees on social media hinted that this would eventually become the default GC behavior. It\u2019s worth considering this if you\u2019re starting a new project, as it has fewer surprises in store and will save you porting work if\/when the engine switches.<\/sub><\/p>\n\n\n\n<p>See&nbsp;<a href=\"https:\/\/docs.unrealengine.com\/5.1\/en-US\/lyra-sample-game-in-unreal-engine\/\">Lyra<\/a>&nbsp;itself.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"object-pointers\">Object pointers<\/h3>\n\n\n\n<p>There are smart pointers that let you reference UObjects from a \u201cregular\u201d C++ object that cannot have&nbsp;<code>UPROPERTY<\/code>s. These all have&nbsp;<code>Object<\/code>&nbsp;in their names, such as&nbsp;<code>TStrongObjectPtr&lt;T&gt;<\/code>,&nbsp;<code>TWeakObjectPtr&lt;T&gt;<\/code>, but&nbsp;<strong>NOT<\/strong>&nbsp;<code>TObjectPtr&lt;T&gt;<\/code>. TObjectPtr has no runtime effect in shipped projects, where it is equivalent to a&nbsp;<code>T*<\/code>&nbsp;raw pointer. It is optional (and cumbersome) to use and its existence can be ignored. It supposedly has some nebulous benefits in the editor only.<\/p>\n\n\n\n<p>Classes with only&nbsp;<code>Ptr<\/code>&nbsp;such as&nbsp;<code>TSharedPtr<\/code>&nbsp;or&nbsp;<code>TUniquePtr<\/code>&nbsp;are reinventions of their STL counterparts (with varying levels of quality\u2026) and work with common C++ objects.&nbsp;<code>Ref<\/code>s are the same but disallow being nullptr.<\/p>\n\n\n\n<p>Object and non-object pointers may not be mixed in either direction.&nbsp;<code>TSharedPtr&lt;UObject&gt;<\/code>&nbsp;and&nbsp;<code>TWeakObjectPtr&lt;FVector&gt;<\/code>&nbsp;are both invalid.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"casts\">Casts<\/h3>\n\n\n\n<p>Unreal is compiled without exceptions or RTTI so dynamic_cast is not available. It\u2019s #defined as a horrendous macro hack that\u2019s better avoided entirely.&nbsp;<code>Cast&lt;T&gt;<\/code>&nbsp;is its replacement for UObjects, with&nbsp;<code>CastChecked&lt;T&gt;<\/code>&nbsp;behaving like static_cast but with additional debug checks that are not compiled in shipping. With these, you only write&nbsp;<code>T<\/code>&nbsp;instead of&nbsp;<code>T*<\/code>&nbsp;as the template parameter.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"class-default-objects-cdo\">Class Default Objects (CDO)<\/h3>\n\n\n\n<p>Unreal will default construct (for the sake of discussion, this includes constructors taking&nbsp;<code>const FObjectInitializer&amp;<\/code>) every reflected type to query its defaults and keep it in memory, accessible via&nbsp;<code>UClass::GetDefaultObject<\/code>. This will explain most of the \u201cunexpected\u201d constructor calls you might see.<\/p>\n\n\n\n<p>For this reason, USTRUCTs and UCLASSes must come with a default constructor. You can use other constructors with USTRUCTs, but this is not the case with UCLASSes where you have virtually no control over what constructor gets called.<\/p>\n\n\n\n<p>The replacement for copy constructors is the&nbsp;<code>Template<\/code>&nbsp;parameter to NewObject, and there\u2019s no direct replacement for parameterized constructors. Common workarounds include an Init method, a static Create method, or SpawnActorDeferred for actors.<\/p>\n\n\n\n<p>Subclasses, UPROPERTYs, etc., are serialized as deltas from CDOs. This is how the \u201crevert arrow\u201d works in the editor\u2019s Details panel. This serialization process is brittle and requires you to get some things right, detailed below.<\/p>\n\n\n\n<p>Blueprint classes also have CDOs but these are only created when the BP is loaded.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"default-subobjects-components\">Default subobjects, components<\/h4>\n\n\n\n<p>Classes might have default subobjects, that is, additional objects that get created together with them when instantiated. The most common practical usage of this is an actor constructor creating its components.<\/p>\n\n\n\n<p>For these, use&nbsp;<code>CreateDefaultSubobject&lt;UComponentClass&gt;(\"PropertyName\")<\/code>&nbsp;instead of NewObject, and store the result in a&nbsp;<strong>Visible<\/strong>&nbsp;UPROPERTY. Do not use spaces in these FName parameters, doing so triggers bugs.<\/p>\n\n\n\n<p>Never use&nbsp;<strong>Edit<\/strong>&nbsp;specifiers (EditAnywhere, etc.) for these properties. That leads to incorrect serialization and BP corruption. The component\u2019s properties will still be editable; it\u2019s the pointer itself that\u2019s affected here.<\/p>\n\n\n\n<p>You\u2019ll often see the TEXT() macro used for CreateDefaultSubobject in questionable learning resources and tutorials. This is a pure waste of CPU if your FName is ASCII (as it should be) and should not be used. Not even Epic employees are fully aware of how this works. TEXT() is for FString literals.<\/p>\n\n\n\n<p>Constructors use&nbsp;<code>-&gt;SetupAttachment()<\/code>&nbsp;to build component hierarchies.<\/p>\n\n\n\n<p>At runtime, components are made by NewObject+RegisterComponent+AddInstanceComponent. SetupAttachment may be used only before calling RegisterComponent; after that, components are attached with the \u201cusual\u201d functions that are also available in BP. Engine code randomly forgets to call AddInstanceComponent, which leads to, e.g., your component existing but not being visible in the inspector.<\/p>\n\n\n\n<p>A BP Construction Script\u2019s C++ equivalent is the OnConstruction method, not the constructor. Components are made with NewObject in this case, and to match BP behavior, you\u2019ll need to set this flag manually:<br><code>YourNewComponent-&gt;CreationMethod = EComponentCreationMethod::UserConstructionScript;<\/code><\/p>\n\n\n\n<p>There\u2019s another \u201cconstruction script\u201d concept in the engine, called a simple construction script. This is what contains BP-added components in actor blueprints.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"uclass\">UClass<\/h2>\n\n\n\n<p>UClass (not to be confused with&nbsp;<code>UCLASS<\/code>) is the closest equivalent of .NET\u2019s&nbsp;<code>System.Type<\/code>. UClass itself is used the most often, but there are multiple related classes that represent types, such as UStruct, UBlueprintGeneratedClass, etc.<\/p>\n\n\n\n<p>The two most common ways of obtaining these objects are&nbsp;<code>T::StaticClass()<\/code>&nbsp;and&nbsp;<code>Obj-&gt;GetClass()<\/code>&nbsp;(cf. typeof and GetType() in .NET). Many engine functions that take a UClass* parameter come with template overloads that let you retain some amount of type safety, e.g.,&nbsp;<code>Foo&lt;UExample&gt;()<\/code>&nbsp;replaces&nbsp;<code>Cast&lt;UExample&gt;(Foo(UExample::StaticClass()))<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"tsubclassof\">TSubclassOf<\/h3>\n\n\n\n<p>This wrapper is often used instead of UClass* to limit the potential values that may be set or used. It implicitly converts from\/to UClass*.<\/p>\n\n\n\n<p>In the editor, UPROPERTYs of this type will limit what options are offered. At runtime, if a TSubclassOf contains a \u201cwrong\u201d class, it will be returned as nullptr:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>TSubclassOf&lt;UObject> Example1; \/\/ Defaults to no class\nTSubclassOf&lt;UObject> Example2 = UObject::StaticClass(); \/\/ Straightforward\nTSubclassOf&lt;AActor>  Example3 = UObject::StaticClass(); \/\/ Still OK to do\ncheck(Example1 == nullptr);\ncheck(Example2 != nullptr);\ncheck(Example3 == nullptr); \/\/ UObject is not a child of AActor\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"delegates\">Delegates<\/h2>\n\n\n\n<p>.NET\u2019s delegates and events (member function pointers + their&nbsp;<code>this<\/code>) also come in Unreal flavor.&nbsp;<a href=\"https:\/\/benui.ca\/unreal\/delegates-intro\/\">This<\/a>&nbsp;is a good overview.<\/p>\n\n\n\n<p>Dynamic delegates work based on the reflection system and can only call UFUNCTIONs. This limitation is required for them to work in BP. These are declared with the various&nbsp;<code>DECLARE_DYNAMIC_..._DELEGATE_...<\/code>&nbsp;macros and can be stored in UPROPERTYs, passed into UFUNCTIONs, etc.<\/p>\n\n\n\n<p>Dynamic multicast delegates can be UPROPERTY(BlueprintAssignable) and unicast ones can be taken and returned from UFUNCTIONs, among other things.<\/p>\n\n\n\n<p>Regular delegates come with additional features over something like std::function+std::bind, such as being able to weakly reference a UObject and auto-unbind if it gets garbage collected. There are&nbsp;<code>DECLARE_DELEGATE_...<\/code>&nbsp;macros (without DYNAMIC), but these are practically obsolete as all of them resolve to something like&nbsp;<code>using FYourDelegate = TDelegate&lt;void(int)&gt;;<\/code>&nbsp;(or TMulticastDelegate, etc.) that you can write directly in a cleaner syntax or not define an alias at all. The macros remain required for dynamic delegates as they involve generated code.<\/p>\n\n\n\n<p>These macros have issues with types having commas in them, such as TMaps. The fix is trivial in the non-dynamic case (don\u2019t use the macros), but dynamics require wrapper USTRUCTs to avoid the comma.<\/p>\n\n\n\n<p>Events are worth mentioning (<code>DECLARE_EVENT<\/code>). They used to provide extra encapsulation compared to a delegate field but are obsolete and have been replaced by delegates according to a comment in the engine\u2019s source code. The implementation of these macros has changed from how it used to work and the additional encapsulation is no longer provided.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"delegates-and-cdos\">Delegates and CDOs<\/h3>\n\n\n\n<p>Do not bind to delegates in UCLASS\/USTRUCT constructors. That will include the CDO that probably shouldn\u2019t respond to broadcasts.<\/p>\n\n\n\n<p>UPROPERTY delegates get serialized, which is an even bigger problem as it leads to corruption and weird behavior. Use an appropriate overload, such as BeginPlay or PostInitProperties, to bind early at runtime instead.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"blueprint-interop\">Blueprint interop<\/h1>\n\n\n\n<p>BP can access the reflection information built by UHT and \u201csees\u201d types and members marked with the various UMACROs. There\u2019s often an extra specifier required, such as UPROPERTY(BlueprintReadWrite) or UFUNCTION(BlueprintCallable), to expose something to BP.<\/p>\n\n\n\n<p>Conversely, C++ is compiled before BP is even loaded and cannot access anything without brittle, slow, and string-based runtime reflection hacks of desperation. You might have guessed that this approach is best avoided.<\/p>\n\n\n\n<p>The main way for the two languages to talk to each other is for C++ to offer values or hooks that BP uses or implements.<\/p>\n\n\n\n<p>There are multiple competing naming conventions indicating that something is intended for BP. The most commonly used is&nbsp;<code>K2_Something()<\/code>, K2 standing for Kismet 2.0. Some other, less common ones include&nbsp;<code>BP_Something()<\/code>,&nbsp;<code>ReceiveSomething<\/code>, and&nbsp;<code>NotifySomething<\/code>. These are often paired with a DisplayName specifier in their UFUNCTIONs to get sanitized for BP.<\/p>\n\n\n\n<p><sub>Kismet was the original visual scripting language of Unreal Engine 3. It only worked for levels, and its direct UE4 equivalent is a level blueprint (ALevelScriptActor). It still exists in UE5 but has issues when World Partition is involved.<\/sub><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"properties\">Properties<\/h2>\n\n\n\n<p>UPROPERTY is the main method of sending runtime data to and from blueprints. A helpful trick is having UPROPERTYs as&nbsp;<code>private<\/code>&nbsp;but adding the&nbsp;<code>AllowPrivateAccess<\/code>&nbsp;meta-specifier, which will expose them anyway. This will prevent direct access to the field from C++, enforcing the use of your accessors, and can be treated as something like \u201cprotected for BP only\u201d.<\/p>\n\n\n\n<p>Accessor UFUNCTIONs can also be provided (along with full encapsulation), but use BlueprintGetter and BlueprintSetter for this to keep your designers happy and not leak \u201cC++-isms\u201d into BP.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"types\">Types<\/h2>\n\n\n\n<p>BP has its own take on classes, enums, structs, etc.<\/p>\n\n\n\n<p>Classes are great, use them as much as you want, but BP enums and BP structs have bugs and are considered essentially unportable to C++. Define a C++ UENUM or USTRUCT instead from the get-go.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"asset-references\">Asset references<\/h3>\n\n\n\n<p>One of the most important usages of BP is to provide assets and configuration values to UPROPERTYs. This is most commonly done through literal blueprints (e.g., actor subclasses), but there are other similar systems to achieve this, such as UDeveloperSettings.<\/p>\n\n\n\n<p>Raw pointers (including TObjectPtr, which is mostly useless) are force loaded before you have a chance to read them, while soft pointers (TSoftObjectPtr) are loaded on demand by your code. Soft pointers store a path to the asset and act as weak pointers after they\u2019ve been loaded.<\/p>\n\n\n\n<p>They\u2019re great for reducing editor startup time and ingame loading screen times: you can issue a load well in advance and only do a last-resort synchronous block when your player was unexpectedly quick. For instance, you could have an invisible trigger leading up to a boss room, that started loading the textures of the boss while the player is traversing the last corridor.<\/p>\n\n\n\n<p>Note that UPROPERTYs are set&nbsp;<strong>AFTER<\/strong>&nbsp;your C++ constructor has returned. Therefore, you cannot read their values just yet. Various callbacks are invoked after properties have been written, such as OnConstruction, PostInitProperties or PostLoad. You can use these to react to the new values. The editor has additional callbacks for \u201clive\u201d editing of properties in the Details panel once an object has been constructed and loaded, such as PostEditChangeProperty or PostEditChangeChainProperty.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"ublueprint\">UBlueprint<\/h4>\n\n\n\n<p>It can be a little confusing what UBlueprint even is, since blueprints can have a parent class that you decide, and that class doesn\u2019t inherit from UBlueprint at all.<\/p>\n\n\n\n<p>In the case of&nbsp;<code>BP_ExampleActor<\/code>, that would be the UBlueprint asset that contains the class&nbsp;<code>BP_ExampleActor_C<\/code>&nbsp;actually inheriting from AActor.<\/p>\n\n\n\n<p>Data assets (UDataAsset, UDataTable, etc.) are saved directly.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"functions\">Functions<\/h2>\n\n\n\n<p>UFUNCTION(BlueprintImplementableEvent) functions are only declared in .h files and not implemented in C++ but may be called from there. They will become an overrideable function (or \u201cevent\u201d if not returning anything, i.e., a&nbsp;coroutine) in BP.<\/p>\n\n\n\n<p>UFUNCTION(BlueprintNativeEvent) functions may have a C++ implementation, which is nearly always virtual. For&nbsp;<code>UFUNCTION(BlueprintNativeEvent) void Example();<\/code>, implement&nbsp;<code>void ClassName::Example_Implementation(){}<\/code>. This naming convention is enforced.<\/p>\n\n\n\n<p>Always call&nbsp;<code>Example()<\/code>&nbsp;instead of its _Implementation, or you\u2019ll bypass BP. An exception to this is calling&nbsp;<code>Super::Example_Implementation()<\/code>&nbsp;in overrides.<\/p>\n\n\n\n<p>Using a BIE instead of a BNE can be beneficial if you want to ensure that BP cannot possibly forget to call the parent implementation. Have a BlueprintCallable function (Example) do its mandatory C++ thing, then call a BlueprintImplementableEvent version of itself (K2_Example) to provide the \u201coverride\u201d opportunity for BP. This is how AActor::BeginPlay() itself is written, for instance.<\/p>\n\n\n\n<p>UPROPERTY(BlueprintAssignable) delegates are also implementable in BP, despite being properties. The BP event will be bound to the delegate automatically.<\/p>\n\n\n\n<p>Going in the opposite direction, UFUNCTION(BlueprintCallable) and UFUNCTION(BlueprintPure) let C++ functions be called from BP. Callable functions have an \u201cexec\u201d (white) pin in BP and run only if that line transfers control, while Pure functions don\u2019t have one. This invokes the idea of purity from languages such as Haskell, but in practice, it&nbsp;<strong>only<\/strong>&nbsp;means that pure functions are called every time data is pulled out of them. BlueprintPure is frequently misused for functions that are technically impure.<\/p>\n\n\n\n<p><code>const<\/code>&nbsp;functions default to being pure, but can be marked impure with UFUNCTION(BlueprintPure=false). Otherwise purity is opt-in.<\/p>\n\n\n\n<p>The C++-&gt;BP and BP-&gt;C++ specifiers may be combined, e.g.,&nbsp;<code>UFUNCTION(BlueprintImplementableEvent, BlueprintCallable)<\/code>&nbsp;is valid.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"interfaces\">Interfaces<\/h2>\n\n\n\n<p>Unreal has a bizarre implementation of interfaces. In concept, they\u2019re similar to C# interfaces (UObjects may only have one UObject parent class but may inherit any number of IInterfaces,&nbsp;<code>#if CPP<\/code>&nbsp;hacks aside), but the way you use or call them is inconsistent depending on whether they\u2019re meant to be used in BP or not.<\/p>\n\n\n\n<p>Purely C++ interfaces (<code>Meta=(CannotImplementInterfaceInBlueprint)<\/code>) work with&nbsp;<code>Cast&lt;T&gt;<\/code>&nbsp;and regular&nbsp;<code>-&gt;Function()<\/code>&nbsp;calls as you would expect.<\/p>\n\n\n\n<p>If BP is involved though, it becomes entirely different:&nbsp;<code>-&gt;Function()<\/code>s will assert, Cast will not work, and you\u2019ll need generated static helpers. Note the usage of both the U and I halves of the interface:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Instead of Thing->Example(1, 2, 3)\nif (Thing->Implements&lt;UMyInterface>())\n    IMyInterface::Execute_Example(Thing, 1, 2, 3);\n<\/code><\/pre>\n\n\n\n<p>This will correctly call C++ or BP regardless of where the interface was implemented. Use TScriptInterface to hold objects that might implement an interface this way since they are not IMyInterface* in this case.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"threading\">Threading<\/h1>\n\n\n\n<p>Epic decided to be friendly to designers by forcing all game code to one thread, called the game thread. This is the thread that the garbage collector pauses when collecting, the one that runs most engine Tick()s, etc. and is key to many threading scenarios.<\/p>\n\n\n\n<p>There are other important threads named by the ENamedThreads \u201cenum\u201d, which is a namespace, a common workaround for UENUMs before&nbsp;<code>enum class<\/code>&nbsp;was added to C++.<\/p>\n\n\n\n<p>The most common way to interact with this system is with the Task Graph, which can be used via the AsyncTask() wrapper, or TGraphTask for more explicit control. This is great for small tasks (usually within a frame) but avoid overloading these threads, or you\u2019ll compete with the engine.<\/p>\n\n\n\n<p>There\u2019s a newer frontend to the low-level tasks system called&nbsp;<a href=\"https:\/\/docs.unrealengine.com\/5.1\/en-US\/tasks-systems-in-unreal-engine\/\">UE::Tasks<\/a>, which is relatively unused by engine systems, mostly due to its relative youth. It has a better API for chaining, dependencies, etc.<\/p>\n\n\n\n<p>Full threads are usually made with the FRunnable interface and launched with, e.g.,&nbsp;<code>FRunnableThread::Create<\/code>. This is usually recommended for long-running tasks (a second or more).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"game-thread-and-uobjects\">Game thread and UObjects<\/h2>\n\n\n\n<p>Barring a few exceptions, UObjects should be considered to live on and be owned by the game thread. The garbage collector expects to be able to scan their UPROPERTYs and delete them without any notice or synchronization. This will only ever happen cooperatively on the game thread, meaning that if your code is running, you own the entire thread and need not worry about local \u201cun-propertied\u201d raw pointers to UObjects. Returning or suspending (return, co_await, co_yield, co_return) a function gives it a chance to run, though.<\/p>\n\n\n\n<p>This setup naturally leads to threading setups where expensive operations are offloaded to other threads, but the results are applied on the game thread in, e.g., an AsyncTask.<\/p>\n\n\n\n<p>It is your responsibility to ensure this will run correctly. Common techniques include arranging that the UObject will be referenced and not GC\u2019d while the off-thread computation is running, or checking if it\u2019s gone before attempting to pass results back (giving a TStrongObjectPtr or a Weak one to the other thread can achieve this), canceling the task and blocking until its completion in the UCLASS\u2019s destructor, etc.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"miscellaneous-extras\">Miscellaneous extras<\/h1>\n\n\n\n<p>Useful things that don\u2019t really fit any other category.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"assertions\">Assertions<\/h2>\n\n\n\n<p>Unreal comes with a rich set of assertion macros, none having&nbsp;<code>assert<\/code>&nbsp;in their names.&nbsp;<a href=\"https:\/\/docs.unrealengine.com\/5.1\/en-US\/asserts-in-unreal-engine\/\">Here<\/a>&nbsp;is the official documentation.<\/p>\n\n\n\n<p>It often surprises people that the \u201cSlow\u201d assertion macros do&nbsp;<strong>NOT<\/strong>&nbsp;run in DebugGame configurations, only full Debug.<\/p>\n\n\n\n<p>You can \u201cfix\u201d this if desired by adding this to your Target.cs:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if (Configuration &lt;= UnrealTargetConfiguration.DebugGame)\n    ProjectDefinitions.Add(\"DO_CHECK_SLOW=1\");\n<\/code><\/pre>\n\n\n\n<p>Or per module into a Build.cs:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if (Target.Configuration &lt;= UnrealTargetConfiguration.DebugGame)\n    PublicDefinitions.Add(\"DO_GUARD_SLOW=1\");\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"subsystems\">Subsystems<\/h2>\n\n\n\n<p>Unreal\u2019s take on singletons. These inherit from&nbsp;<code>USubsystem<\/code>, and you, in turn, inherit from one of the specialized subclasses, such as&nbsp;<code>UWorldSubsystem<\/code>&nbsp;or&nbsp;<code>UGameInstanceSubsystem<\/code>. These get created and destroyed on demand together with the thing they\u2019re a subsystem of, with convenient getters (<code>GetSubsystem&lt;T&gt;<\/code>&nbsp;that you can wrap in a static getter if you wish).<\/p>\n\n\n\n<p>In BP, these receive pure getter nodes, but you might want to provide a BP function library with statics that get the subsystem individually to reduce the spaghetti.<\/p>\n\n\n\n<p>BP subsystems require hacks to even function and are considered infeasible. Assets may be referenced through a UDeveloperSettings subclass.<\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/aquanox\/SubsystemBrowserPlugin\">This plugin<\/a>&nbsp;extends editor functionality for them.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"gameplay-ability-system-gas\">Gameplay Ability System (GAS)<\/h2>\n\n\n\n<p>Unreal\u2019s built-in support for \u201cgame\u201d things such as skills, stats, status effects, etc., in a generic system. Used by Epic in games such as the Lyra starter game and Fortnite itself.<\/p>\n\n\n\n<p>It\u2019s officially not released as a feature but still shipped with the engine on a \u201chere, it\u2019s useful, but you get no support\u201d basis. It\u2019s great for single- and multiplayer both, but especially powerful in multiplayer, simply because it has more things to deal with on your behalf.<\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/tranek\/GASDocumentation\">This<\/a>&nbsp;is one of the best pieces of community documentation for it.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"gameplay-tags\">Gameplay Tags<\/h3>\n\n\n\n<p>FNames on steroids that are hierarchical and can be defined in advance so that you don\u2019t have to keep typing strings and hoping you don\u2019t get one of them wrong. Although it\u2019s commonly used in conjunction with GAS, it\u2019s an independent system.<\/p>\n\n\n\n<p>Here\u2019s the official&nbsp;<a href=\"https:\/\/docs.unrealengine.com\/4.27\/en-US\/ProgrammingAndScripting\/Tags\/\">documentation<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This article assumes significant experience with C++, b [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[27],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/5223"}],"collection":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=5223"}],"version-history":[{"count":1,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/5223\/revisions"}],"predecessor-version":[{"id":5224,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/5223\/revisions\/5224"}],"wp:attachment":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5223"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5223"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5223"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}