# 生成对象
# 静态加载
静态加载的方法有:
- LoadClass:用来加载蓝图类的资源,比如角色蓝图。它加载的资源必须有_C后缀。
- LoadObject:用来加载非蓝图资源,比如动画,贴图,声音效果,材质等等。它加载的资源不用有_C后缀。
- StaticLoadObject:用来加载资源文件,比如贴图静态网格等文件。
这里提到的静态加载是需要在构造器中完成的,在运行时动态加载时是值得的,在UE4中FObjectFinder是LoadObject的包装,但是FClassFinder并不是LoadClass的包装,FClassFinder内部调用的是LoadObject。
# 生成Actor
下面都是由蓝图生成Actor。
# 方法一
//===================================头文件================================
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "TestSpawn.generated.h"
UCLASS()
class TESTPROJECTLEARN1_API ATestSpawn : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ATestSpawn();
TSubclassOf<AActor> generatedActor;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
//===================================CPP源文件=============================
#include "TestSpawn.h"
// Sets default values
ATestSpawn::ATestSpawn()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
static ConstructorHelpers::FClassFinder<AActor> truckAsset(TEXT("Blueprint'/Game/BP/MyActor.MyActor_C'"));
//static ConstructorHelpers::FClassFinder<AActor> truckAsset(TEXT("/Game/BP/MyActor"));
generatedActor = truckAsset.Class;
}
// Called when the game starts or when spawned
void ATestSpawn::BeginPlay()
{
Super::BeginPlay();
FVector targetLocation(0, 0, 0);
FRotator targetRot(0, 0, 0);
FActorSpawnParameters SpawnInfo;
AActor* temp = GetWorld()->SpawnActor<AActor>(generatedActor, targetLocation, targetRot, SpawnInfo);
}
// Called every frame
void ATestSpawn::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}s
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 方法二
//===================================头文件================================
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "TestSpawn.generated.h"
UCLASS()
class TESTPROJECTLEARN1_API ATestSpawn : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ATestSpawn();
TSubclassOf<AActor> generatedActor;
//UClass* generatedActor;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
//===================================源文件================================
#include "TestSpawn.h"
// Sets default values
ATestSpawn::ATestSpawn()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
static ConstructorHelpers::FObjectFinder<UBlueprint> truckAsset(TEXT("/Game/BP/MyActor"));
generatedActor = truckAsset.Object->GeneratedClass;
}
// Called when the game starts or when spawned
void ATestSpawn::BeginPlay()
{
Super::BeginPlay();
FVector targetLocation(0, 0, 0);
FRotator targetRot(0, 0, 0);
FActorSpawnParameters SpawnInfo;
AActor* temp = GetWorld()->SpawnActor<AActor>(generatedActor, targetLocation, targetRot, SpawnInfo);
}
// Called every frame
void ATestSpawn::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 注意
需要注意以下几点:
- ConstructorHelpers::FClassFinder中路径有两种写法都可以。
- FClassFinder<T>的模板名不能直接写UBlueprint,比如FClassFinder<UBlueprint>是不对的,该蓝图的父类是啥,模板名应该写该父类,如果它的父类是Actor,就应该写为FClassFinder<AActor>。
- 可以把TSubclassOf<T>替换为UClass*。
# 创建UObject对象
如果有UObject的派生类(非Actor、非ActorComponent),那你可以使用NewObject()模板函数来创建其实例对象。
genActor = worldPointer->SpawnActor<AActor>(loc, rot, SpawnInfo);
UStaticMeshComponent* MyMeshComponent = NewObject<UStaticMeshComponent>(genActor, UStaticMeshComponent::StaticClass(), TEXT("MeshComp"));
genActor->SetRootComponent(MyMeshComponent);
USphereComponent* MySphereComponent = NewObject<USphereComponent>(MyMeshComponent, USphereComponent::StaticClass(), TEXT("SphereComp"));
MyMeshComponent->SetStaticMesh(tMesh);
MyMeshComponent->SetVisibility(true);
MyMeshComponent->SetSimulatePhysics(true);
MyMeshComponent->SetEnableGravity(true);
MyMeshComponent->SetCollisionObjectType(ECC_Pawn);
MyMeshComponent->SetCollisionResponseToAllChannels(ECR_Ignore);
MyMeshComponent->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block);
MyMeshComponent->SetCollisionResponseToChannel(ECC_WorldDynamic, ECR_Block);
MyMeshComponent->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block);
MySphereComponent->SetCollisionObjectType(ECC_Pawn);
MySphereComponent->SetCollisionResponseToAllChannels(ECR_Ignore);
MySphereComponent->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block);
MySphereComponent->SetCollisionResponseToChannel(ECC_WorldDynamic, ECR_Block);
MyMeshComponent->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block);
//MyMeshComponent->SetWorldScale3D(FVector(0.1, 0.1, 0.1));
MyMeshComponent->RegisterComponent();
genActor->SetActorLocation(loc);
genActor->SetActorRotation(rot);
genActor->SetActorLabel(actorLabel);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
注意:
- NewObject非构造函数中创建的Box无法及时更新NavMesh,也就是说该box在NavMesh不会被当作障碍物。
# 生成资源(非_C后缀)
# 生成MediaPlayer
//====================头文件=====================
UMediaPlayer* mediaPlayer;
//====================源文件=====================
mediaPlayer = LoadObject<UMediaPlayer>(NULL,TEXT("MediaPlayer'/Game/UI/DisplayVedio.DisplayVedio'"));
2
3
4
# 生成datasmith资源
# 创建组件
UE4中为Actor创建组件,可使用CreateDefaultSubobject:
MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComponent"));
MeshComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
RootComponent = MeshComp;
kernelSphereComp = CreateDefaultSubobject<USphereComponent>(TEXT("kernelSphereComp"));
kernelSphereComp->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
kernelSphereComp->SetCollisionResponseToAllChannels(ECR_Ignore);
kernelSphereComp->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap);
kernelSphereComp->SetupAttachment(MeshComp);
2
3
4
5
6
7
8
9
注意:
- CreateDefaultSubobject必须写在Actor的无参构造函数中,否则crash。
- CreateDefaultSubobject中的TEXT或者FName参数在同一个Actor中不能重复,否则crash。
# 加载资源
在UE4中,项目中的所有资源文件,不要看做是文件,而要理解为"静态对象",也就是对象序列化的产物。加载项目资源可以使用"UObject::StaticLoadObject()"函数,其中重要的参数为对象的Name,而不是文件路径。UE底层提供文件读取功能,无论资源文件是存储到独立的.uasset文件,还是存储到.PAK文件中,对于上层都不需要关心。
UStaticMesh* SM_Vase = Cast<UStaticMesh>(StaticLoadObject(UStaticMesh::StaticClass(),
NULL,
TEXT("/Game/Assets/StaticMeshes/SM_Vase"))
);
StaticMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMeshComponent"));
StaticMeshComponent->SetStaticMesh(SM_Vase);
2
3
4
5
6
7
# 总结
UE4中的对象模型是基于对象原型的,很像JavaScript,UClass被认为是和UObject相关类的默认实例,被称为Class Default Object(CDO),他会在引擎初始化时通过类构造器被分配空间,然后构造一次。CDO可被看作一个模板,其他类的实例可从该模板借鉴,但是构造器不会再被调用了。
这意味着类构造器不能包含任何运行时逻辑,只应该用来初始化CDO和它的属性,如果类包含任何子对象,像actor组件,这些组件也是同样的,它们自己默认的对象也必须得先被构建。对象的真实实例肯定会被延期,得在引擎初始化后。所以每次一个类的新实例被Gameplay代码创建,它的父类以及子类都得从它们对应的类实例化。
下面这些方法是用来处理对象在不同的场景下被创建时应用的:
- UObject::CreateDefaultSubobject只在类的构造器中可调用,被用来实例化CDO及其子类的对象,设置它的外部类为其他对象调用它时的调用对象,当它的类被实例化时被创建的对象会成为属性的默认对象。
- NewObject<T>是被用来在Gameplay逻辑中引擎初始化后正常实例化对象的方法,它提供几种便于使用的重载来处理大部分场景。
- UWorld::SpawnActor<T>便于生成关卡中的Actors,可以指定location和rotation,spawn碰撞设置,它确保生成的是actor类外,同NewObject<AActor>一样。
- ConstructObject因为NewObject被移除了。
关于对象实例化可以在引擎CoreUObject模块中UObject/UObjectGlobal.h来查看,这些函数内部最终会调用StaticConstructObject_Internal,该函数会处理真实对象的创建。
参考资料:
# SpawnActor和NewObject
SpawnActor被用来在当前的UWorld中基于UObject创建AActor,它内部会调用NewObject来创建指定的AActor,但是还会安装Transform,添加到当前关卡的Actors数组中,也会帮助创建Actor的组件,初始化蓝图相关的构造脚本等。