C++ Unreal 2

Setting defaults in my constructor

Setting default values for properties in a constructor works the same as your typical C++ class. Below are two examples of setting default values in a constructor and are equivalent in functionality.

AMyActor::AMyActor()
{
    TotalDamage = 200;
    DamageTimeInSeconds = 1.f;
}

AMyActor::AMyActor() :
    TotalDamage(200),
    DamageTimeInSeconds(1.f)
{
}

Here is the same view of the properties after adding default values in the constructor.
// why two constructors???    

image alt text

In order to support per instance designer set properties, values are also loaded from the instance data for a given object. This data is applied after the constructor. You can create default values based off of designer set values by hooking into the PostInitProperties() call chain. Here is an example of that process where TotalDamage and DamageTimeInSeconds are designer specified values. Even though these are designer specified, you can still provide sensible default values for them, as we did in the example above.

If you do not provide a default value for a property, the engine will automatically set that property to zero or nullptr in the case of pointer types.

void AMyActor::PostInitProperties()
{
    Super::PostInitProperties();
    DamagePerSecond = TotalDamage / DamageTimeInSeconds;
}

Here again is the same view of the properties after we have added the PostInitProperties() code that you see above.

image alt text

啥意思啊 你想干嘛 卧槽 就多用了个member function而已 你想干嘛呀

Hot Reloading

Here is a cool feature of Unreal that you might be surprised about if you are used to programming C++ in other projects. You can compile your C++ changes without shutting down the editor! There are two ways to do this:

  1. With the editor still running, go ahead and build from Visual Studio or Xcode like you normally would. The editor will detect the newly compiled DLLs and reload your changes instantly!

    image alt text

    If you are attached with the debugger, you'll need to detach first so that Visual Studio will allow you to Build.

  2. Or, simply click the **Compile **button on the editor's main toolbar.

    image alt text

You can use this feature in the sections below as we advance through the tutorial.  // wow , that's really impressive 


Extending a C++ Class via Blueprints

So far, we have created a simple gameplay class with the C++ Class Wizard and added some properties for the designer to set. Let us now take a look at how a designer can start creating unique classes from our humble beginnings here.
// I 'm not humble at all .- _ -

First thing we are going to do is create a new Blueprint class from our AMyActor class. Notice in the image below that the name of the base class selected shows up as MyActor instead of AMyActor. This is intentional and hides the naming conventions used by our tools from the designer, making the name friendlier to them. 
// well, that's great!


image alt text

Once you choose Select, a new, default named Blueprint class is created for you. In this case, I set the name to CustomActor1 as you can see in the snapshot of the Content Browser below.

image alt text

This is the first class that we are going to customize with our designer hats on. First thing we are going to do is change the default values for our damage properties. In this case, the designer changed the TotalDamage to 300 and the time it takes to deliver that damage to 2 seconds. This is how the properties now appear.

image alt text

Our calculated value does not match what we would expect. It should be 150 but it is still at the default value of 200. The reason for this is that we are only calculating our damage per second value after the properties have been initialized from the loading process. Runtime changes in the Unreal Editor are not accounted for. There is a simple solution to this problem because the engine notifies the target object when it has been changed in the editor. The code below shows the added hooks needed to calculate the derived value as it changes in the editor.

void AMyActor::PostInitProperties()
{
    Super::PostInitProperties();

    CalculateValues();
}

void AMyActor::CalculateValues() // that ' s another member function  I really appreciate that
{
    DamagePerSecond = TotalDamage / DamageTimeInSeconds;
}

#if WITH_EDITOR // wtf is that mean 
void AMyActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
    CalculateValues();

    Super::PostEditChangeProperty(PropertyChangedEvent);
}
#endif

One thing to note is that the PostEditChangeProperty() method is inside an editor specific #ifdef.
// But why this shit comes up without any attention?
this is so that building your game only the code that you need for the game, removing any extra code that might increase the size of your executable unnecessarily. Now that we have that code compiled in, the DamagePerSecond value matches what we would expect it to be as seen in the image below.

image alt text

Calling Functions across the C++ and Blueprint Boundary

So far, we have shown how to expose properties to Blueprints, but there is one last introductory topic that we should cover before you dive deeper into the engine. During the creation of the gameplay systems, designers will need to be able to call functions created by a C++ programmer as well as the gameplay programmer calling functions implemented in Blueprints from C++ code. Let us start by first making the CalculateValues() function callable from Blueprints.

Exposing a function to Blueprints is just as simple as exposing a property. It takes only one macro placed before the function declaration! The code snippet below show what is needed for this.// well  that' fancy ,you re reall proud of it huh?
UFUNCTION(BlueprintCallable, Category="Damage")
void CalculateValues();

The UFUNCTION() macro handles exposing the C++ function to the reflection system. The BlueprintCallable option exposes it to the Blueprints Virtual Machine. Every Blueprint exposed function requires a category associated with it, so that the right click context menu works properly. The image below shows how the category affects the context menu.

image alt text

As you can see, the function is selectable from the Damage category. The Blueprint code below shows a change in the TotalDamage value followed by a call to recalculate the dependent data.

image alt text

This uses the same function that we added earlier to calculate our dependent property. Much of the engine is exposed to Blueprints via the UFUNCTION() macro, so that people can build games without writing C++ code. However, the best approach is to use C++ for building base gameplay systems and performance critical code with Blueprints used to customize behavior or create composite behaviors from C++ building blocks.

Now that our designers can call our C++ code, let us explore one more powerful way to cross the C++/Blueprint boundary. This approach allows C++ code to call functions that are defined in Blueprints. We often use the approach to notify the designer of an event that they can respond to as they see fit. Often that includes the spawning of effects or other visual impact, such as hiding or unhiding an actor. The code snippet below shows a function that is implemented by Blueprints.

UFUNCTION(BlueprintImplementableEvent, Category="Damage")
void CalledFromCpp();

This function is called like any other C++ function. Under the covers, the Unreal Engine generates a base C++ function implementation that understands how to call into the Blueprint VM. This is commonly referred to as a Thunk. If the Blueprint in question does not provide a function body for this method, then the function behaves just like a C++ function with no body behaves: it does nothing. What if you want to provide a C++ default implementation while still allowing a Blueprint to override the method? The UFUNCTION() macro has an option for that too. The code snippet below shows the changes needed in the header to achieve this.

UFUNCTION(BlueprintNativeEvent, Category="Damage")
void CalledFromCpp();

This version still generates the thunking method to call into the Blueprint VM. So how do you provide the default implementation? The tools also generate a new function declaration that looks like <function name>_Implementation(). You must provide this version of the function or your project will fail to link. Here is the implementation code for the declaration above.

void AMyActor::CalledFromCpp_Implementation()
{
    // Do something cool here
}

Now this version of the function is called when the Blueprint in question does not override the method. One thing to note, is that in previous versions of the build tools the _Implementation() declaration was auto generated. In any version 4.8 or higher, you'll be expected to explicitly add that to the header.

Now that we have walked through the common gameplay programmer workflow and methods to work with designers to build out gameplay features, it is time for you to choose your own adventure. You can either continue with this document to read more about how we use C++ in the engine or you can jump right into one of our samples that we include in the launcher to get a more hands on experience.



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值