{"id":3073,"date":"2021-01-02T13:51:55","date_gmt":"2021-01-02T05:51:55","guid":{"rendered":"http:\/\/blog.coolcoding.cn\/?p=3073"},"modified":"2021-01-02T19:44:10","modified_gmt":"2021-01-02T11:44:10","slug":"implementing-a-native-code-construction-script","status":"publish","type":"post","link":"https:\/\/blog.coolcoding.cn\/?p=3073","title":{"rendered":"Implementing a native code Construction Script"},"content":{"rendered":"\n<p>Within Blueprint, a\u00a0Construction Script\u00a0is an\u00a0Event Graph\u00a0that runs any time a property is changed on the object it is attached to\u00a0\u2013\u00a0whether it&#8217;s being dragged in the editor viewport\u00a0or changed via a direct entry in a\u00a0Details\u00a0panel. Construction Scripts allow the object in question to\u00a0<em>rebuild<\/em>\u00a0itself based on its new location, for instance, or to change the components it contains based on user-selected options. When coding in C++ with Unreal Engine, the equivalent concept is the\u00a0OnConstruction\u00a0function. <\/p>\n\n\n\n<h1 class=\"wp-block-heading\">How to do it&#8230;<\/h1>\n\n\n\n<ol><li>Create a new&nbsp;Actor&nbsp;called&nbsp;OnConstructionActor&nbsp;based on&nbsp;StaticMeshActor:<\/li><\/ol>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2021\/01\/e5a597b2-f38a-4fbf-9095-db45235e8c34.png\" alt=\"\"\/><\/figure>\n\n\n\n<ol><li>Update the header file to the following:&nbsp;<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-preformatted\">#pragma once<br><br>#include \"CoreMinimal.h\"<br>#include \"Engine\/StaticMeshActor.h\"<br>#include \"OnConstructionActor.generated.h\"<br><br>UCLASS()<br>class CHAPTER_09_API AOnConstructionActor : public AStaticMeshActor<br>{<br>  GENERATED_BODY()<br><br><strong>public:<\/strong><br><strong>    AOnConstructionActor();<\/strong><br><br><strong>    virtual void OnConstruction(const FTransform&amp; Transform) override;<\/strong><br><br><strong>    UPROPERTY(EditAnywhere)<\/strong><br><strong>        bool ShowStaticMesh;<\/strong><br>  <br>};<\/pre>\n\n\n\n<ol><li>Go to the implementation file (OnConstructionActor.cpp) and implement the class constructor:<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-preformatted\">#include \"OnConstructionActor.h\"<br><strong>#include \"ConstructorHelpers.h\"<\/strong><br><br><strong>AOnConstructionActor::AOnConstructionActor()<\/strong><br><strong>{ <\/strong><br><strong>    \/\/ Set this actor to call Tick() every frame. You can turn<br>    \/\/ this off <\/strong><strong>to improve performance if you don't need it.<\/strong><br><strong>    PrimaryActorTick.bCanEverTick = true;<\/strong><br><br><strong>    auto MeshAsset = ConstructorHelpers::FObjectFinder&lt;UStaticMesh&gt;( <\/strong><br><strong>                  TEXT(\"StaticMesh'\/Engine\/BasicShapes\/Cone.Cone'\"));<\/strong><br><br><strong>    UStaticMeshComponent * SM = GetStaticMeshComponent();<\/strong><br><br><strong>    if (SM != nullptr)<\/strong><br><strong>    {<\/strong><br><strong>        if (MeshAsset.Object != nullptr)<\/strong><br><strong>        {<\/strong><br><strong>            SM-&gt;SetStaticMesh(MeshAsset.Object);<\/strong><br><strong>            SM-&gt;SetGenerateOverlapEvents(true);<\/strong><br><strong>        }<\/strong><br><strong>        SM-&gt;SetMobility(EComponentMobility::Movable);<\/strong><br><strong>    }<\/strong><br><br><strong>    \/\/ Default value of property<\/strong><br><strong>    ShowStaticMesh = true;<\/strong><br><strong>}<\/strong><\/pre>\n\n\n\n<ol><li>Implement&nbsp;OnConstruction:<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-preformatted\">void AOnConstructionActor::OnConstruction(const FTransform&amp; Transform) \n{ \n  GetStaticMeshComponent()-&gt;SetVisibility(ShowStaticMesh); \n} <\/pre>\n\n\n\n<ol><li>Compile your code and launch the editor.<\/li><li>Drag an instance of your class into the game world, and verify that toggling the Boolean value for&nbsp;ShowStaticMesh&nbsp;toggles the visibility of the mesh in the editor viewport:<\/li><\/ol>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/learning.oreilly.com\/library\/view\/unreal-engine-4x\/9781789809503\/assets\/0e5c8d54-687b-45bc-adb7-19163d4c24b0.png\" alt=\"\"\/><\/figure>\n\n\n\n<ol><li>OnConstruction&nbsp;does not currently run for C++ actors that are placed in a level if they are moved.<\/li><li>To test this, place a breakpoint in your&nbsp;OnConstruction&nbsp;function, and then move your actor around the level.<\/li><\/ol>\n\n\n\n<p>To place a breakpoint, place your cursor on the desired line and hit&nbsp;<em>F9<\/em>&nbsp;in Visual Studio.<\/p>\n\n\n\n<ol><li>You&#8217;ll notice that the function doesn&#8217;t get called, but if you toggle the&nbsp;ShowStaticMesh&nbsp;Boolean, it does, causing your breakpoint to trigger.<\/li><\/ol>\n\n\n\n<p>To see why, take a look at the beginning of the&nbsp;AActor::PostEditMove&nbsp;function:&nbsp;<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">void AActor::PostEditMove(bool bFinished)<br>{<br>    if ( ReregisterComponentsWhenModified() &amp;&amp; !FLevelUtils::IsMovingLevel())<br>    {<br>        UBlueprint* Blueprint = Cast&lt;UBlueprint&gt;(GetClass()-&gt;ClassGeneratedBy);<br>        if (bFinished || bRunConstructionScriptOnDrag || (Blueprint &amp;&amp; Blueprint-&gt;bRunConstructionScriptOnDrag))<br>        {<br>            FNavigationLockContext NavLock(GetWorld(), ENavigationLockReason::AllowUnregister);<br>            RerunConstructionScripts();<br>        }<br>    }<br><br>    \/\/ .... <\/pre>\n\n\n\n<p>The top line here casts\u00a0UClass\u00a0for the current object to\u00a0UBlueprint, and will only run the construction scripts and\u00a0OnConstruction\u00a0again if the class is a Blueprint. <\/p>\n\n\n\n<h1 class=\"wp-block-heading\">How it works&#8230;<\/h1>\n\n\n\n<p>We create a new Actor based on&nbsp;StaticMeshActor&nbsp;for easy access to a visual representation via the Static Mesh.<\/p>\n\n\n\n<p>UPROPERTY&nbsp;is added to give us a property to change, which causes&nbsp;PostEditChangeProperty&nbsp;events to be triggered.<\/p>\n\n\n\n<p>OnConstruction&nbsp;is a virtual function that&#8217;s defined in Actor.<\/p>\n\n\n\n<p>As a result, we override the function in our class.<\/p>\n\n\n\n<p>Within our class constructor, we initialize our mesh as usual, and set the default state of our&nbsp;bool&nbsp;property to match the visibility of the component that it controls.<\/p>\n\n\n\n<p>Inside&nbsp;OnConstruction, the actor rebuilds itself using any properties that are required to do so.<\/p>\n\n\n\n<p>For this simple example, we set the visibility of the mesh to match the value of our&nbsp;ShowStaticMesh&nbsp;property.<\/p>\n\n\n\n<p>This could also be extended to changing other values based on the value of the&nbsp;ShowStaticMesh&nbsp;variable.<\/p>\n\n\n\n<p>You&#8217;ll note that we don&#8217;t explicitly filter on a particular property being changed, like the previous recipe does with&nbsp;PostEditChangeProperty.<\/p>\n\n\n\n<p>The&nbsp;OnConstruction&nbsp;script runs in its entirety for every property that gets changed on the object.<\/p>\n\n\n\n<p>It has no way of testing which property was just edited, so you need to be judicious about placing computationally intensive code within it.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Within Blueprint, a\u00a0Construction Script\u00a0is an\u00a0Event Gra [&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,1],"tags":[26],"_links":{"self":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/3073"}],"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=3073"}],"version-history":[{"count":2,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/3073\/revisions"}],"predecessor-version":[{"id":3348,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/3073\/revisions\/3348"}],"wp:attachment":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3073"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3073"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3073"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}