标签:
UE4这篇官方文档大体上讲解了UE4本身的诸多特性以及如何编写基本的C++代码,对理解UE4的编程基础内容非常有帮助,还是因为没有对于的汉语翻译。所以,这里我把这篇文档翻译出来,之后还会简洁的对其进行必要的总结。由于内容比较多,会分两个部分进行编写,持续更新~
This guide is about learning how to write C++ code in Unreal Engine. Do not worry, C++ programming in Unreal Engine is fun, and actually not hard to get started with! We like to think of Unreal C++ as "assisted C++", because we have so many features to help make C++ easier for everyone.
Before we go on, it is really important that you are already familiar with C++ or another programming language. This page is written with the assumption that you have some C++ experience, but if you know C#, Java, or JavaScript, you should find many aspects familiar.
If you are coming in with no programming experience at all, we have got you covered also! Check out our Blueprint Visual Scripting guide and you will be on your way. You can create entire games using Blueprint scripting!
It is possible to write "plain old C++ code" in Unreal Engine, but you will be most successful after reading through this guide and learning the basics about the Unreal programming model. We will talk more about that as we go along.
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
这篇编程指导是讲解如何在虚幻引擎书写C++代码的。并不用担心,虚幻引擎中的C++编程会比你想象的要有趣的多,而且上手难度也并不大。我们认为虚幻的C++是“自动协助的C++”,因为我们有许多让C++用起来更简单的特性来给大家去使用。
在开始前,你应该至少熟悉C++或者其他任何一门语言,这是非常重要的。这篇文档就假设你有一些C++的编程经验 ,不过如果你了解C#,java或者JavaScript,你也会发现他们都是想相通的。
如果你之前一点编程经验都没有,那我们也替你考虑到了!去查看一下 蓝图可视化指导(Blueprint Visual Scripting guide),你也可以开始你的UE4旅程了。你完全可以使用UE4提供的蓝图功能做一款游戏,不需要写一行代码!
在UE引擎里面你可以写原生的C++代码,但是一旦你浏览过这篇文档并学会了UE引擎里的编程模型,你就会收获的更多。接下来我们会讲解更多的内容。
Unreal Engine provides two methods, C++ and Blueprints Visual Scripting, to create new gameplay elements. Using C++, programmers add the base gameplay systems that designers can then build upon or with to create the custom gameplay for a level or the game. In these cases, the C++ programmer works in their favorite IDE (usually Microsoft Visual Studio, or Apple‘s Xcode) and the designer works in the Unreal Editor‘s Blueprint Editor.
The gameplay API and framework classes are available to both of these systems, which can be used separately, but show their true power when used in conjunction to compliment each other. What does that really mean, though? It means that the engine works best when programmers are creating gameplay building blocks in C++ and designers take those blocks and make interesting gameplay.
With that said, let us take a look at a typical workflow for the C++ programmer that is creating building blocks for the designer. In this case, we are going to create a class that is later extended via Blueprints by a designer or programmer. In this class, we are going to create some properties that the designer can set and we are going to derive new values from those properties. The whole process is very easy to do using the tools and C++ macros we provide for you.
虚幻引擎提供两种方式两种方式来创建游戏元素,分别是C++和可视化蓝图。使用C++,程序员可以创建一个游戏系统来让设计者在上面给游戏关卡构建或创建游戏内容。这样,C++程序员就可以在他们最喜欢的IDE(一般是Visual Studio和Xcode)里工作,而设计师只在虚幻编辑器里面操作就好了。
游戏里面的API以及框架相关的类在这两个系统都可以获取到,你可以分开来使用,但是当你将这两个系统结合的时候你才会真正的体会虚幻的强大所在。那么这意味着什么?当程序员创建了游戏的C++代码块并提供给设计师来制作丰富有趣的游戏时,才能发挥虚幻引擎的真正优势。
既然已经整体的介绍了C++模块,不妨让我们看看C++程序员给设计师创建游戏模块的一个工作流程。这样,我们会创建一个类并让设计师或程序员通过蓝图对其进行扩展。接下来,我们会创建一些属性,设计师可以设置这些属性并产生一些新的值。使用引擎提供的工具和C++的宏,整个流程其实非常简单。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
First thing we are going to do is use the class wizard within the Unreal Editor to generate the basic C++ class that will be extended by Blueprints later. The image below shows the wizard’s first step where we are creating a new Actor.
第一件要做的事就是使用虚幻编辑器来说来生成C++的类并在之后用蓝图去拓展。这个图片展示了创建一个新的Actor(后面会介绍Actor,也可以参考链接:UE4碰撞规则详解 的第一条)的第一步。
The second step in the process tells the wizard the name of the class you want generated. Here‘s the second step with the default name used.
第二步是在流程中告诉向导你想要生成的类名。下面是步骤二,使用了默认的名称。
Once you choose to create the class, the wizard will generate the files and open your development environment so that you can start editing it. Here is the class definition that is generated for you. For more information on the Class Wizard, follow this link.
一旦你选择了创建类,向导就会生成文件并且打开你的开发环境来让你进行编辑。下面是引擎生成类的定义如果想知道更多关于类向导的内容,点击这个链接link.
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"
UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor‘s properties
AMyActor();
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// Called every frame
virtual void Tick( float DeltaSeconds ) override;
};
The class wizard generates your class with BeginPlay() and Tick() specified as overloads. BeginPlay() is an event that lets you know the Actor has entered the game in a playable state. This is a good place to initiate gameplay logic for your class. Tick() is called once per frame with the amount of elapsed time since the last call passed in. There you can do any recurring logic. However if you do not need that functionality, it is best to remove it to save yourself a small amount of performance. If you remove it, make sure to remove the line in the constructor that indicated ticking should occur. The constructor below contains the line in question.
类的向导给你的类生成了BeginPlay()与Tick()两个重载函数(分别在游戏开始和每帧循环时调用)。BeginPlay()是触发一个事件,让你知道角色刚刚进入了游戏模式。这个位置很适合你的类来初始化一些数据与逻辑。Tick()就是在上次调用过后每过一小段时间来触发的帧循环事件。在这里你可以做任何的循环逻辑处理。不过如果你不需要这两个功能,你可以移除这这个函数并保存为自己的风格。如果你移除了他,确保你移除了构造函数内设置循环是否开启的语句。下面的构造函数包含了问题中描述的这个开关。
AMyActor::AMyActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you do not need it.
PrimaryActorTick.bCanEverTick = true;
}
We have our class created, so now let us create some properties that can be set by designers in the Unreal Editor. Exposing a property to the editor is quite easy using our special macro, UPROPERTY(). All you have to do is use the UPROPERTY(EditAnywhere) macro before your property declaration as seen in the class below.
我们现在已经创建了自己的类,接下来要创建一些可以在虚幻编辑器设置的一些属性。通过使用引擎提供的宏( UPROPERTY())来将一个属性显示在编辑器其实很简单。你所需要做的就是将宏 UPROPERTY(EditAnywhere) 声明在属性的前面。
UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()
UPROPERTY(EditAnywhere)
int32 TotalDamage;
...
};
That is all you need to do to be able to edit that value in the editor. There are more ways to control how and where it is edited. This is done by passing more information into the UPROPERTY() macro. For instance, if you want the TotalDamage property to appear in a section with related properties, you can use the categorization feature. The property declaration below shows this.
想在编辑器里面编辑这个属性,这么做就足够了。不过有更多的方式去控制如何以及在哪里去编辑他。想实现这些效果就在宏 UPROPERTY()里面添加更多的参数就可以了。举例来说,如果你想 TotalDamage 属性 出现在一个里面含有很多相关联属性的模块,你可以使用分类特性。属性的声明如下所示。(所有Category=“Damage”的属性都会被归为一个名为Damage的分支里面)
UPROPERTY(EditAnywhere, Category="Damage")
int32 TotalDamage;
When the user looks to edit this property, it now appears under the Damage heading along with any other properties that you have marked with this category name. This is a great way to place commonly used settings together for editing by designers.
Now let us expose that same property to Blueprints.
当用户去查看并编辑该属性时,他就会出现在Damage标题的分类下面,同时你也会看到其他你标记过的分类标题。对于设计师来说,这是一个非常好的将常用属性分类的方式。
下面我们换个写法同样将这个属性添加进去。
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Damage")
int32 TotalDamage;
As you can see, there is a Blueprint specific parameter to make a property available for reading and writing. There‘s a separate option, BlueprintReadOnly, you can use if you want the property to be treated as const in Blueprints. There are quite a few options available for controlling how a property is exposed to the engine. To see more options, follow this link.
Before continuing to the section below, let us add a couple of properties to this sample class. There is already a property to control the total amount of damage this actor will deal out, but let us take that further and make that damage happen over time. The code below adds one designer settable property and one that is visible to the designer but not changeable by them.
正如你所看到的,宏里面有一个特定的蓝图参数来让属性可以阅读和编辑。这个位置也可以换成另外一个参数,BlueprintReadOnly,如果想要你的属性在蓝图里面是const常量你就可以使用这个参数。当然,虚幻还提供了许多的参数来决定你的属性以什么样的方式显示在引擎中。想看到更多的参数选项,点击这个链接link.
UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Damage")
int32 TotalDamage;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Damage")
float DamageTimeInSeconds;
UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Transient, Category="Damage")
float DamagePerSecond;
...
};
DamageTimeInSeconds is a property the designer can modify. The DamagePerSecond property is a calculated value using the designer‘s settings (see the next section). The VisibleAnywhere flag marks that property as viewable, but not editable in the Unreal Editor. The Transient flag means that it won‘t be saved or loaded from disk; it is meant to be a derived, non-persistent value. The image below shows the properties as part of the class defaults.
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.
给属性设置默认值和平时的C++类操作一样。下面是两种在构造函数里面设置默认值例子并且在功能上是相同的。(不过对C++有一定理解的朋友知道,第二种列表初始化省去了拷贝的操作,对于类类型的初始化效率要高)
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.
下面是在构造器里面添加默认值后的效果图。
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.
为了使设计师可以随时修改每一个实例的值,这些值也会在一个类的实例构建完成后被及时加载。这些数据在构造函数执行后被提交。你可以通过关联到函数PostInitProperties(),创建一个新的基于之前已经有的属性而计算出来的值。下面是一个例子,该例子里面的 TotalDamage 和DamageTimeInSeconds是由关卡设计师来设计编辑的。即使这些值是由设计师来决定的,你仍可以像我们在上述例子中那样给他设置默认值。
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.
在添加了上述的代码PostInitProperties后,就会像下面一样显示属性视图。
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:
下面要展示虚幻引擎一个非常酷的特性,如果你在其他的工程中使用过C++编程的话,你肯定会大吃一惊。那就是你可以在不关闭编辑器的同事重新编译C++工程!有两种方式去做:
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! your changes instantly! your changes instantly! your changes instantly! your changes instantly! 在编辑器运行的时候,就像你在VS或Xcode平时那样编译的操作一样。编辑器会察觉到新的需要编译的DLL并且立刻重新加载修改的代码!随时编译修改的代码!
(Note that if you are attached with the debugger, you‘ll need to detach first so that Visual Studio will allow you to Build.提示一下,如果你是正在调试,你得先将调断开这样VS才能允许你重新编译)
Or, simply click the Compile button on the editor‘s main toolbar.
You can use this feature in the sections below as we advance through the tutorial. What a time saver!
你可以在接下来的教学中使用这个特性。这无疑会大大节省你的时间!
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.
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.
目前为止,我们
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.
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.
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()
{
DamagePerSecond = TotalDamage / DamageTimeInSeconds;
}
#if WITH_EDITOR
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. 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.
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.
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.
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.
This uses the same function that we added earlier to calculate our dependent property. Much of the engine is exposed to Blueprints via theUFUNCTION() 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 _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 future versions of the build tools the auto generated _Implementation() declaration will go away and you‘ll be expected to explicitly add that to the header. As of version 4.7, the auto generation of that declaration still occurs.
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.
I see you are still with me on this adventure. Excellent. The next topics of discussion revolve around what our gameplay class hierarchy looks like. In this section, we‘ll start with the base building blocks and talk through how they relate to each other. This is where we‘ll look at how the Unreal Engine uses both inheritance and composition to build custom gameplay features.
There are 4 main class types that you derive from for the majority of gameplay classes. They are UObject, AActor, UActorComponent, and UStruct. Each of these building blocks are described in the following sections. Of course, you can create types that do not derive from any of these classes, but they will not participate in the features that are built into the engine. Typical use of classes that are created outside of the UObject hierarchy are: integrating 3rd party libraries; wrapping of OS specific features; etc.
在所有的和游戏进行相关的类里面(gameplay),有四个主要的基类来让你去继承,分别是UObject,AActor,UActorComponent,UStruct,他们几乎涵盖了所有的游戏内容。接下来会逐个对每个部分进行描述。当然,你完全可以创建一个不继承于上述任何的类的类。但是这样的话,你的类就不能实现引擎独有的一些特性。一些典型的没有使用UObject的类如下所示:第三方的库,一些系统特性封装的包等。
---------------------------------------------------------------------------------------------------------------------------------------------------------------
The base building block in the Unreal Engine is called UObject. This class, coupled with UClass, provides a number of the most important base services in the engine:
Reflection of properties and methods
Serialization of properties
Garbage collection
Finding UObjects by name
Configurable values for properties
Networking support for properties and methods
Each class that derives from UObject has a singleton UClass created for it that contains all of the meta data about the class instance. UObject and UClass together are at the root of everything that a gameplay object does during its lifetime. The best way to think of the difference between a UClass and a UObject is that the UClass describes what an instance of a UObject will look like, what properties are available for serialization, networking, etc. Most gameplay development does not involve directly deriving from UObjects, but instead from AActor and UActorComponent. You do not need to know the details of how UClass/UObject works in order to write gameplay code, but it is good to know that these systems exist.
虚幻引擎构建的基础模块是UObject。这个类与UClass一同合作可以给引擎提供许多重要有基础的服务。
●属性和方法的反射
●属性的序列化
●垃圾回收
●通过名字找到UObject
●可以配置的属性值
●属性与方法的网络支持
每一个继承自UObject的类都有一个匹配的UClass宏,他包含了所有该类实例相关的数据与代码。UObject和UClass二者在游戏对象生命周期中是一切的基础。区别二者最好的方式就是UClass描述了一个UObject实例的样子,有什么特性可以用于序列化及网络传输。大部分的游戏开发不会直接涉及到UObject,反而AActor和UActorComponent会被大量使用。你并不需要很清楚UObject和UClass在代码里面是如何工作的但是有必要知道他们的存在。
---------------------------------------------------------------------------------------------------------------------------------------------------------------
An AActor is an object that is meant to be part of the gameplay experience. AActors are either placed in a level by a designer or created at runtime via gameplay systems. All objects that can be placed into a level extend from this class. Examples include AStaticMeshActor,ACameraActor, and APointLight actors. AActor derives from UObject, so enjoys all of the standard features listed in the previous section. AActors can be explicitly destroyed via gameplay code (C++ or Blueprints) or via the standard garbage collection mechanism when the owning level is unloaded from memory. AActors are responsible for the high-level behaviors of your game‘s objects. AActors are also the base type that can be replicated during networking. During network replication, AActors can also distribute information for any UActorComponents owned by that AActor that require network support.
AActors have their own behaviors (specialization through inheritance), but they also act as containers for a hierarchy of UActorComponents (specialization through composition). This is done through the AActor‘s RootComponent member, which contains a single UActorComponent that, in turn, can contain many others. Before an AActor can be placed in a level, that AActor must contain at least a USceneComponentwhich contains the translation, rotation, and scale for that AActor.
AActors have a series of events that are called during the lifecycle of the AActor. The list below is a simplified set of the events that illustrate the lifecycle.
BeginPlay - called when the object first comes into gameplay existence
Tick - called once per frame to do work over time
EndPlay - called when the object is leaving the gameplay space
See Actor for a more detailed discussion on AActor.
Just above we discussed a subset of an AActor‘s lifecycle. For actors that are placed in a level, understanding the lifecycle is pretty easy to imagine: actors are loaded and come into existence and eventually the level is unloaded and the actors are destroyed. What is the process for runtime creation and destruction? Unreal Engine calls the creation of an AActor at runtime spawning. Spawning an actor is a bit more complicated than creating a normal object in the game. The reason is that an AActor needs to be registered with a variety of runtime systems in order to serve all of its needs. The initial location and rotation for the actor need to be set. Physics may need to know about it. The manager responsible for telling an actor to tick needs to know. And so on. Because of this, we have a method devoted to the spawning of an actor, UWorld::SpawnActor(). Once that actor is spawned successfully, its BeginPlay() method is called, followed by Tick() the next frame.
Once an actor has lived out its lifetime, you can get rid of it by calling Destroy(). During that process EndPlay() will be called where you can do any custom logic for destruction. Another option for controlling how long an actor exists is to use the Lifespan member. You can set a timespan in the constructor of the object or with other code at runtime. Once that amount of time has expired, the actor will automatically have Destroy() called on it.
To learn more about spawning actors see the 生成 Actors page.
UActorComponents have their own behaviors and are usually responsible for functionality that is shared across many types of AActors, e.g. providing visual meshes, particle effects, camera perspectives, and physics interactions. While AActors are often given high-level goals related to their overall roles your game, UActorComponents usually perform the individual tasks that support those higher-level objectives. Components can also be attached to other Components, or can be the root Component of an Actor. A Component can only attach to one parent Component or Actor, but it may have many child Components attached to itself. Picture a tree of Components. Child Components have location, rotation, and scaling relative to their parent Component or Actor.
While there are many ways to use Actors and Components, one way to think of the Actors-Component relationship is that Actors might answer the question "what is this thing?" while Components might answer "what is this thing made of?"
RootComponent - this is the member of AActor that holds the top level Component in the AActor‘s tree of Components
Ticking - Components are ticked as part of the owning AActor‘s Tick()
Over the last few sections we have done a lot of talking and not a lot of showing. In order to illustrate the relationship of an AActor and its UActorComponents, let us dig into the Blueprint that is created when you generate a new project based off of the First Person Template. The image below is the Component tree for the FirstPersonCharacter Actor. The RootComponent is the CapsuleComponent. Attached to the CapsuleComponent is the ArrowComponent, the Mesh component, and the FirstPersonCameraComponent. The leaf most component is the Mesh1P component which is parented to the FirstPersonCameraComponent, meaning that the first person mesh is relative to the first person camera.
Visually, this tree of Components looks like the image below, where you see all of the components in 3D space except for the Meshcomponent.
This tree of components is attached to the one actor class. As you can see from this example, you can build complex gameplay objects using both inheritance and composition. Use inheritance when you want to customize an existing AActor or UActorComponent. Use composition when you want many different AActor types to share the functionality.
To use a UStruct, you do not have to extend from any particular class, you just have mark the struct with USTRUCT() and our build tools will do the base work for you. Unlike a UObject, UStructs are not garbage collected. If you create dynamic instances of them, you must manage their lifecycle yourself. UStructs are meant to be plain old data types that have the UObject reflection support for editing within the Unreal Editor, Blueprint manipulation, serialization, networking, etc.
Now that we have talked about the basic hierarchy used in our gameplay class construction, it is time to choose your path again. You can read about our gameplay classes here, head out to our samples in the launcher armed with more information, or continue digging deeper into our C++ features for building games.
Introduction to C++ Programming in UE4——UE4官方文档翻译与理解(一)
标签:
原文地址:http://blog.csdn.net/u012999985/article/details/51295225