UE4中蓝图构造脚本和C++中构造函数

前言

初学UE4和C++,在尝试将蓝图函数转为C++代码时候,发现了一个很坑人的玩意,就是蓝图中的构造脚本(Consttruction Script)和C++中类的构造函数不是一个东西!因此想记录下来。

准备工作

先创建一个继承Actor的类,现在我有个现成的继承Pawn的类(下文就直接说Actor),可以凑合着用。

CameraOnPlane.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "CameraOnPlane.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(TestLog, Log, All);

UCLASS()
class TRAJECTORY_API ACameraOnPlane : public APawn
{
	GENERATED_BODY()

public:
	// Sets default values for this pawn's properties
	ACameraOnPlane();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
	virtual void OnConstruction(const FTransform& Transform) override;

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
};

CameraOnPlane.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "CameraOnPlane.h"
DEFINE_LOG_CATEGORY(TestLog);

// Sets default values
ACameraOnPlane::ACameraOnPlane()
{
	// Set this pawn to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
	UE_LOG(TestLog, Warning, TEXT("构造函数被调用"));
}

void ACameraOnPlane::OnConstruction(const FTransform& Transform)
{
	UE_LOG(TestLog, Warning, TEXT("OnConstruction"));
}

// Called when the game starts or when spawned
void ACameraOnPlane::BeginPlay()
{
	Super::BeginPlay();
}

// Called every frame
void ACameraOnPlane::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
}

// Called to bind functionality to input
void ACameraOnPlane::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
}

其中,这两行代码是用来输出一个名字为TestLog的日志:

// 这个放在.h文件中
DECLARE_LOG_CATEGORY_EXTERN(TestLog, Log, All);
// 这个放在.cpp文件中
DEFINE_LOG_CATEGORY(TestLog);

最后就是打开关卡蓝图,通过SpawnActor让它在开始运行的时候自动在场景中生成一个Actor。

UE4中蓝图构造脚本和C++中构造函数_第1张图片 在关卡蓝图事件开始运行节点添加

好了,准备工作完成,先看一下编译运行的效果。

测试

编译日志输出:

编译结果

开始运行日志输出:

UE4中蓝图构造脚本和C++中构造函数_第2张图片 运行结果

分析

编译结果在编译的时候调用了一次构造函数,运行结果显示,在运行的时候构造函数调用了一次,OnConstruction函数也被调用了一次,先来说一下OnConstruction函数,这个函数是Actor生命周期的一部分,在官方文档中是这么说的(这里是原文):

官方文档-Actor生命周期

文档怎么一直在说蓝图蓝图的,这不是写在C++里的吗,管蓝图什么事情!(气愤)等等!蓝图好像也有个构造什么玩意:

UE4中蓝图构造脚本和C++中构造函数_第3张图片 随便在一个蓝图类截的,无伤大雅

 对!就是这个玩意,Construction Script——构造脚本,我经常在这个函数里边给类弄点默认参数啊什么的,说白了就是为Actor的生成来一些准备工作,我这么说可能不太准确,来看看官方的说法(这里是原文):

创建蓝图类的实例时,构造脚本(Construction Script) 在组件列表之后运行。它包含的节点图表允许蓝图实例执行初始化操作。构造脚本的功能可以非常丰富,它们可以执行场景射线追踪、设置网格体和材质等操作,从而根据场景环境来进行设置。 例如,光源蓝图可判断其所在地面类型,然后从一组网格体中选择合适的网格体, 或者,栅栏蓝图可以向各个方向射出射线, 从而确定栅栏可以有多长。

可能我语文不太好,第一次看完一头雾水,只知道它用来初始化,功能非常丰富。等等,初始化?我自然而然又想到了C++里边的构造函数:

// Sets default values
ACameraOnPlane::ACameraOnPlane()
{
	// Set this pawn to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
	UE_LOG(TestLog, Warning, TEXT("构造函数被调用"));
}

好了,构造脚本,OnConstruction和构造函数三个东西穿起来了,都能初始化,那这就是一个东西嘛(顺带一说,我起初认为的是构造脚本和构造函数是一个东西,只不过一个在蓝图一个在C++),如果这么想的话,那刚刚的测试结果怎么说?

直到看到了这篇文档(这里是原文),才懂了那么一丝。

virtual void OnConstruction(const FTransform& Transform) override;

其实,这个OnConstruction函数才是和蓝图里边的构造脚本是一个玩意,跟构造函数毛关系都没有(其实我也不知道是不是毛关系都没有),这个玩意你可得小心着点用,不然就像那个文章里说的,指不定你的编辑器怎么就crash(崩)掉了,而且一运行就让send and restart(别问我是怎么知道的),所以,我们得大概了解一下这个玩意是怎么玩的。

先是这个C++里边的构造函数,这个不多说了,从官方文档里边的好多例子也看出来可以在里边创建一些组件啊,这个组件加到这个组件上,那个组件加到这个组件上,在给组件整点默认的参数什么的,你看那方法上也写了Sets default values,还有就是这个构造函数会在编译和运行的时候调用(从我那个测试结果看出来)。

然后就是这个构造脚本和OnConstruction函数了,这玩意主要在下面三种情况调用:

  1. Actor生成的时候
  2. Actor移动的时候(可能不止调用一次)
  3. Actor一些参数改变的时候

在那个文章中我注意到一句话:

It is the most interesting point of the Construction Script: it is called in the editor, and not at runtime

 这句话的意思是说ConstructionScript会在编译的时候调用而不是在运行的时候调用吗?如果是这样的话好像跟我测试的结果不太一样,难道我哪里搞错了?欢迎指正讨论!

咱接着说这个构造脚本,文章中谈到这个滥用构造脚本会出现一些问题:

  1. 对项目性能有影响:有时候你啥玩意都写到这个构造脚本里边,东西多了运行的就慢,运行一次假设1秒,欸,还可以,但是关键是,这玩意可能不止运行一次,比如你移动一次,就可能调用好几次,然后编辑器就卡住了,然后就......
  2. 循环调用的情况:属性之间循环调用可能没什么大问题,但是要在父子组件之间出现循环调用情况可能编辑器就会crash掉,痛苦的是根本发现不了问题出在哪里。

总结

总的来说,这次学习算是知道了构造脚本和构造函数不是一个东西,和OnConstruction才是一个东西,用的时候还是要慎重一点,鬼知道会出现什么匪夷所思的问题。

最后碎碎念

第一次写这么长的文章,如有错误和不足之处还请指正。

参考文章:UE4 - Be careful with the Construction Script - Isara Tech.

你可能感兴趣的:(c++,ue4)