{"id":17,"date":"2024-06-26T08:34:25","date_gmt":"2024-06-26T08:34:25","guid":{"rendered":"https:\/\/krzysztof.bobnis.eu\/?p=17"},"modified":"2025-02-13T11:02:10","modified_gmt":"2025-02-13T11:02:10","slug":"unreal","status":"publish","type":"post","link":"https:\/\/krzysztof.bobnis.eu\/index.php\/2024\/06\/26\/unreal\/","title":{"rendered":"Unreal Development"},"content":{"rendered":"\n<div class=\"wp-block-ideabox-toc ib-block-toc\" data-anchors='h2,h3,h4,h5,h6' data-collapsable='true' ><div class=\"ib-toc-container ib-toc-list-style-numbers ib-toc-hierarchical ib-toc-expanded\"><div class=\"ib-toc-header\"><div class=\"ib-toc-header-title\">Table of Contents<\/div><div class=\"ib-toc-header-right\"><span class=\"ib-toc-icon-collapse\"><span class=\"dashicon dashicons dashicons-minus\"><\/span><\/span><span class=\"ib-toc-icon-expand\"><span class=\"dashicon dashicons dashicons-plus\"><\/span><\/span><\/div><\/div><div class=\"ib-toc-separator\" style=\"height:2px\"><\/div><div class=\"ib-toc-body\"><ol class=\"ib-toc-anchors\"><\/ol><\/div><\/div><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Debugging<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Disabling optimization of c++ files<\/h3>\n\n\n\n<p>To enable automatic disabling of optimizations, edit BuildConfiguration.xml<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;?xml version=\"1.0\" encoding=\"utf-8\" ?>\n&lt;Configuration xmlns=\"https:\/\/www.unrealengine.com\/BuildConfiguration\">\n    &lt;BuildConfiguration>\n        &lt;bAdaptiveUnityDisablesOptimizations>true&lt;\/bAdaptiveUnityDisablesOptimizations>\n    &lt;\/BuildConfiguration>\n&lt;\/Configuration><\/pre>\n\n\n\n<p>This will disable optimization for all files that are checked out in perforce.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>More info here: <a href=\"https:\/\/dev.epicgames.com\/community\/learning\/tutorials\/dXl5\/advanced-debugging-in-unreal-engine\">https:\/\/dev.epicgames.com\/community\/learning\/tutorials\/dXl5\/advanced-debugging-in-unreal-engine<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">UObject debugger object descriptions<\/h3>\n\n\n\n<p>It is easier to use Debugger uses UObject-&gt;GetName(). So if you will Rename() UObject, then debugger will conveniently show this nice name to you.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Non UObject debugger object descriptions<\/h3>\n\n\n\n<p>If you want nice debugger info about an object that is not a UObject and you can&#8217;t Rename this object, then you can use natvis file to override how debugger prints this object. <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">natvis location<\/h4>\n\n\n\n<ol class=\"wp-block-list\">\n<li>For game types: Open &lt;Engine&gt;Extras\\VisualStudioDebugging\\Unreal.natvis file and add entry for your object. This will work in Rider debugger. I am adding entry for my FMyObject<\/li>\n\n\n\n<li>For plugin types: create a new file: &lt;plugin name&gt;\/Source\/&lt;your module name&gt;&lt;module name&gt;.natvis<\/li>\n<\/ol>\n\n\n\n<p><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?>\n&lt;AutoVisualizer xmlns=\"http:\/\/schemas.microsoft.com\/vstudio\/debugger\/natvis\/2010\">\n\n&lt;Type Name=\"FMyObject\">\n   &lt;DisplayString>(Table={DataTableName} Row={RowName})&lt;\/DisplayString>\n &lt;\/Type>\n\n&lt;\/AutoVisualizer><\/pre>\n\n\n\n<p>Where DataTableName and RowName are FName types in a struct.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>More info here:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/customize-object-displays-in-the-visual-studio-debugger-your-way\/\"> https:\/\/devblogs.microsoft.com\/visualstudio\/customize-object-displays-in-the-visual-studio-debugger-your-way\/<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/dev.epicgames.com\/community\/learning\/tutorials\/dXl5\/advanced-debugging-in-unreal-engine\">https:\/\/dev.epicgames.com\/community\/learning\/tutorials\/dXl5\/advanced-debugging-in-unreal-engine<\/a> &#8211; navigate to Visual Studio &amp; Unreal Engine &#8211; Natvis. This also applies to Rider<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Printing value of enum<\/h3>\n\n\n\n<p>To print a human readable value of an enum, it must be marked as UENUM(). Example<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">UENUM()\nenum class EOptions\n{\n\tOption1,\n\tOption2,\n\tOption3\n};<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">EOptions SelectedOption = EOptions::Option1\n\nUE_LOGFMT(LogTemp, Log, \"Current option is {Options}\", UEnum::GetValueAsName(SelectedOption));\n\n\/\/this will print \"Current option is Option1\"<\/pre>\n\n\n\n<p>To print the display name of an UEnum:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">UENUM()\nenum class ECreatureType\n{\n\tIsRobot UMETA(DisplayName = \"Is a robot\"),\n\tIsHuman UMETA(DisplayName = \"Is a human\")\n};<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const ECreatureType CreatureType = ECreatureType::IsRobot;\n\nFString::Format(TEXT(\"This creature type is a {0}\"), StaticEnum&lt;ECreatureType>()->GetDisplayNameTextByValue(static_cast&lt;int64>(CreatureType )).ToString());<\/pre>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Tests<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Disable functional test<\/h3>\n\n\n\n<p>Functional tests are disabled in the &lt;Project root&gt;\/Config\/DefaultEngine.ini file.<br>Under the [AutomationTestExcludelist] section add a new line: <\/p>\n\n\n\n<p>The test value is a path. If the test is added to a map, then the path looks like &#8220;Project.Functional&#8221;&lt;map path above the content directory, so Content\/Tests\/Assets\/map.umap will be Tests.Assets&gt;&lt;name of the map&gt;&lt;outline structure, so if your test is in a directory in the level called test, then &#8220;Test&#8221; is added&gt;&lt;name of the test in the level&gt;<br><br>Example: <br>&#8211; level: Content\/Tests\/Maps1\/TestMap1.umap<br>&#8211; in the map: Test\/Test_1<br>Then this is Test=&#8221;Project.Functional Tests.Maps1.TestMap1.Test.Test_1&#8243;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>+ExcludeTest=(Map=\"\",Test=\"Project.Functional Tests.Maps1.TestMap1.Test.Test_1\",Reason=\"This test will be disabled now\",RHIs=(),Warn=False)\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Validators<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">C++ validators<\/h3>\n\n\n\n<p>Here&#8217;s a minimal c++ validator header file<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">UCLASS()\nclass UAssetValidator_Filenames : public UEditorValidatorBase\n{\n    GENERATED_BODY()\n\n    virtual bool CanValidateAsset_Implementation(const FAssetData&amp; InAssetData, UObject* InAsset, FDataValidationContext&amp; InContext) const override;\n    virtual EDataValidationResult ValidateLoadedAsset_Implementation(const FAssetData&amp; InAssetData, UObject* InAsset, FDataValidationContext&amp; InContext) override;\n};<\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">How to check can validate asset<\/h4>\n\n\n\n<h5 class=\"wp-block-heading\">Check if asset is data table of my specific type:<\/h5>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">bool UAssetValidator_Filenames::CanValidateAsset_Implementation(const FAssetData&amp; InAssetData, UObject* InAsset, FDataValidationContext&amp; InContext) const\n{\n    if (const UDataTable* LocalDataTable = Cast&lt;UDataTable>(InAsset))\n    {\n        return FMyDataTableRowStruct::StaticStruct() == LocalDataTable->RowStruct;\n    }\n\n    return false;\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Write own settings in c++<\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">UCLASS(Config = Game, defaultconfig)\nclass MYPLUGIN_API UMySettings : public UDeveloperSettings\n{\n\tGENERATED_UCLASS_BODY()\n\n\tUPROPERTY(Config, EditAnywhere)\n\tTArray&lt;FDirectoryPath> Paths;\n}<\/pre>\n\n\n\n<p>By declaring this class, you will find an array of paths in the UE -&gt; Project Settings -&gt; MyPlugin<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Logs<\/h2>\n\n\n\n<p>Declare log verbosity. If you want to print Verbose logs, in any .h file write:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">MYPLUGIN_API DECLARE_LOG_CATEGORY_EXTERN(LogMyPlugin, Verbose, All);<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Unreal Asserts<\/h2>\n\n\n\n<h4 class=\"wp-block-heading\">Check<\/h4>\n\n\n\n<p>Using check will crash the game with the message you provided in check. Not called in shipping build<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Verify<\/h4>\n\n\n\n<p>Crashes the game, the same as check, but is called in all builds.  <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Ensure <\/h4>\n\n\n\n<p>Posts an error in the log<\/p>\n\n\n\n<p>example of ensure with text that will post error to console each time it is encountered: <\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ensureAlwaysMsgf(true, TEXT(\"This is error message posted to logs %s\"), *MyString)<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Editor settings<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">auto load last opened level<\/h3>\n\n\n\n<p>Editor Preferences &#8211; General &#8211; Loading &amp; Saving &#8211; Load Level at Startup &#8211; Last Opened<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">don&#8217;t quit PIE with Esc<\/h3>\n\n\n\n<p>Editor Preferences &#8211; General &#8211; Keyboard Shortcuts &#8211; Play World (PIE\/SIE) &#8211; Stop (Stop simulation) &#8211; Change from Escape to Ctrl + <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Customizing editor<\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">virtual void CustomizeHeader(TSharedRef&lt;class IPropertyHandle> InStructPropertyHandle, class FDetailWidgetRow&amp; HeaderRow, IPropertyTypeCustomizationUtils&amp; StructCustomizationUtils) override;\n\nHeaderRow.NameContent()[InStructPropertyHandle->CreatePropertyNameWidget()]<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Dropdown instead of FName in function parameters<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\tUFUNCTION(BlueprintCallable)\n\tstatic void MethodName(UPARAM(meta = (GetOptions = \"GetSlotNameOptions\")) FName SlotName);\n\n\tUFUNCTION()\n\tstatic TArray&lt;FName&gt; GetSlotNameOptions();\n<\/code><\/pre>\n\n\n\n<p>This method will generate a dropdown instead of a text box in the blueprint context when calling this method.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">GAS<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Filtering gameplay tags in function parameters<\/h3>\n\n\n\n<p>If you want to filter a&nbsp;<code>GameplayTag<\/code> (or GameplayTagContainer)&nbsp;parameter in a function, use the&nbsp;<code>UFUNCTION<\/code>&nbsp;specifier&nbsp;<code>Meta = (GameplayTagFilter = \"ItemType\")<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Packaging the game<\/h2>\n\n\n\n<p>To pack the game locally, on the main UE window click Platforms -&gt; Windows -&gt; Package project<\/p>\n\n\n\n<p>Maps to include in the package are:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>listed in the &lt;game_root&gt;\/Config\/DefaultGame.ini, +MapsToCook= lines<\/li>\n\n\n\n<li>Edit -&gt; Project Settings -&gt; Project &#8211; Packaging -&gt; List of maps to include in a packaged build<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Debugging Disabling optimization of c++ files To enable automatic disabling of optimizations, edit BuildConfiguration.xml This will disable optimization for all files that are checked out in perforce. More info here: https:\/\/dev.epicgames.com\/community\/learning\/tutorials\/dXl5\/advanced-debugging-in-unreal-engine UObject debugger object descriptions It is easier to use Debugger uses UObject-&gt;GetName(). So if you will Rename() UObject, then debugger will conveniently show this [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-17","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/krzysztof.bobnis.eu\/index.php\/wp-json\/wp\/v2\/posts\/17","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/krzysztof.bobnis.eu\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/krzysztof.bobnis.eu\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/krzysztof.bobnis.eu\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/krzysztof.bobnis.eu\/index.php\/wp-json\/wp\/v2\/comments?post=17"}],"version-history":[{"count":31,"href":"https:\/\/krzysztof.bobnis.eu\/index.php\/wp-json\/wp\/v2\/posts\/17\/revisions"}],"predecessor-version":[{"id":96,"href":"https:\/\/krzysztof.bobnis.eu\/index.php\/wp-json\/wp\/v2\/posts\/17\/revisions\/96"}],"wp:attachment":[{"href":"https:\/\/krzysztof.bobnis.eu\/index.php\/wp-json\/wp\/v2\/media?parent=17"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/krzysztof.bobnis.eu\/index.php\/wp-json\/wp\/v2\/categories?post=17"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/krzysztof.bobnis.eu\/index.php\/wp-json\/wp\/v2\/tags?post=17"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}