Unreal Gameplay Framework

UObject

引擎里几乎所有类的基类

可以通过额外的设置来实现网络复制

常用代码片段:

1
2
3
4
5
// 创建UObject
{
TSubclassOf<UObject> ClassToCreate;
UObject* NewDesc = NewObject<UObject>(this, ClassToCreate);
}

下面的类名都会省略前缀

Actor

继承自UObject, 场景中所有物体 (可见或不可见) 都属于ActorActor本身没有变换组件,需要依赖于RootComponent(属于SceneComponent)

Actor有网络复制功能, 自带伤害判定逻辑

常用代码片段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 构造函数里调用,开启网络复制
{
SetReplicates(true);
}
// 接收伤害, 参考https://www.unrealengine.com/en-US/blog/damage-in-ue4
{
TakeDamage(...);
UGameplayStatics::ApplyDamage(...);
}
// 实例化
{
FTransform SpawnTM;
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
SpawnParams.Owner = GetOwner();
SpawnParams.Instigator = Cast<APawn>(GetOwner()); // 设置调用者
ASEquippableActor* NewItem = GetWorld()->SpawnActor<ASEquippableActor>(NewItemClass, SpawnTM, SpawnParams);
}
// 遍历和查找(开销大)
{
UGameplayStatics::GetAllActorsOfClass(…)
}

常用成员:

1
2
3
4
5
6
7
BeginPlay // Actor被构造并完全初始化后调用
Tick // 每帧调用, 通常可以用定时事件代替
EndPlay // Actor被移除的时候调用, 参数有`EEndPlayReason`
GetComponentByClass // 获取一个组件, 还有个..Components..版的是获取所有
GetActorLocation // 还有*Rotation, *Scale, 以及Set*
NotifyActorBeginOverlap // 任意组件发生接触时
GetOverlappingActors // 还有GetOverlappingComponents

Actor 生命周期

ActorComponent

依附于Actor的组件,其子类SceneComponent有变换组件,可以设置相对位置和层级关系(通过SetupAttachment)

常用代码片段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 在构造函数里添加组件
{
MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComp"));
RootComponent = MeshComp;
WidgetComp = CreateDefaultSubobject<UWidgetComponent>(TEXT("InteractWidgetComp"));
WidgetComp->SetupAttachment(MeshComp);
}
// 在运行时添加组件
{
UActorComponent* SpawnedComponent = NewObject<UActorComponent>(this, UStaticMeshComponent::StaticClass(), TEXT("DynamicSpawnedMeshCompoent"));
if (SpawnedComponent)
{
SpawnedComponent->RegisterComponent();
}
}

常用成员:

1
2
3
TickComponent // 每帧调用
bIsActive, Activate, Deactivate // 激活和禁用(不会移除)
SetIsReplicated(true) // 开启网络复制

Pawn

继承自Actor,其子类Character自带SkeletonMeshCharacterMovementComponent等组件。可以在GameModeBase里指定具体的Pawn类型,也可以通过SpawnDefaultPawnAtTransform来生成

在多人游戏里,如果有n个玩家,那么客户端和服务器都有nPawn实例,玩家死亡和重生对应着销毁和重生相应的Pawn,所以玩家生命周期里的数据都应该放在Pawn里。

常用代码片段:

1
2
3
4
5
// 获取Pawn
{
PlayerController->GetPawn() // 只有当确实控制了一个Pawn时才有效
GetWorld()->GetPawnIterator() // 返回所有Pawn(包括AI)
}

PlayerController

代表一个玩家,通过接收玩家输入来控制多个或零个Pawn。例如弹出菜单这样的功能应该由PlayerController而不是Pawn来控制。PlayerController有一个PlayerCameraManager,控制着当前朝向、相机变换组件和相机抖动效果。PlayerController还控制着一个HUD(通常可用UMG代替)

当一个玩家加入GameMode时,GameModeBase会调用Login()为他创建一个PlayerController

在多人游戏里,如果有n个玩家,那么PlayerController的数量是: 每个客户端都有一个,而服务器有n个。所以当所有玩家需要某个玩家的数据时,这个数据应该放在Pawn或者PlayerState里,而不是PlayerController(后面介绍Pawn会解释)。

常用代码片段:

1
2
3
4
5
6
// 获取PlayerController
{
GetWorld()->GetPlayerControllerIterator()
PlayerState->GetOwner()
Pawn->GetController() // 只有当`Pawn`被控制时才能获取到
}

AIController

控制游戏里的AI,AIController上会运行一个BehaviourTree, 控制AI所有的视听等感知数据。

Pawn可以设置AutoPossesAI来自动生成AIControllerClass里指定的控制器。可以通过Pawn获取AIController

GameModeBase

指定PlayerController, Pawn, HUD, GameState, PlayerState的类型, 设置游戏规则以及生成玩家。子类GameMode还具有虚幻争霸里用到的诸如MatchState等特性,但是一般还是实现自己的GameModeBase

在多人游戏里,只有服务器上存在GameMode实例,所以要保存的数据应该放到GameState(存在于所有客户端上)。

常用代码片段:

1
2
3
4
5
6
7
8
9
10
11
12
// 获取GameMode
{
GetWorld()->GetAuthGameMode() // UGameplayeStatics里也有一个函数
}
// 获取GameState
{
GetGameState
}
// 初始化游戏规则
{
InitGame(...)
}

HUD

PlayerController通过SpawnDefaultHUD生成一个AHUD,然后由GameModeBase通过InitializeHUDForPlayer初始化指定的HUDClass

一般还是UMG用的比较多。这些UI只存在于客户端上,注意生成任何控件前都要先检查IsLocalController

常用代码片段:

1
2
3
4
// 获取HUD
{
PlayerController->GetHUD() // 本地控制器才可以
}

World

代表一个关卡,里面包含所有的Actor和组件。射线检测都要通过World来执行(比如LineTraceSingleByChannel)。

常用代码片段:

1
2
3
4
5
6
// 获取World
{
GetWorld() // 所有的Actor都可以直接调用
// 某些函数常常要传递一个UObject* WorldContextObject
// 其实也是为了获取World
}

GameInstance

在整个游戏过程中只有一个。可用于处理网络故障, 加载游戏设置, 关卡共享数据等与关卡无关的东西。在游戏开发初期一般用不到。

常用代码片段:

1
2
3
4
5
// 获取GameInstance
{
GetWorld()->GetGameInstance<T>();
Actor->GetGameInstance();
}

PlayerState

保存一个玩家的数据(可在多个客户端之间复制, 不会跟Pawn一样被销毁),本身不包含什么逻辑。由Controller通过InitPlayerState()生成指定的PlayerStateClass(可以在GameMode的构造函数里指定)

只对于多人游戏比较有用

常用代码片段:

1
2
3
4
5
6
// 获取PlayerState
{
Pawn->PlayerState // 只有Pawn被控制时才有效
Controller->PlayerState
GameState->PlayerArray // 所有玩家的
}

GameStateBase

类似于PlayerState,不过保存的是GameMode里的信息。子类GameStateGameMode一样还包含了一些特定的东西,如果游戏模式继承自GameModeBase,那游戏状态就也要继承自GameStateBase,反之同理。

GameStateBase存在于服务器和所有客户端上,因为GameModeBase实例只存在于服务器上, 所以客户端要获取当前游戏信息都要通过GameStateBase(比如游戏时间和分数等)

常用代码片段:

1
2
3
4
5
// 获取
{
World->GetGameState<T>()
MyGameMode->GetGameState() // 因为游戏模式实例只存在于服务器,所以实际上只有服务器可用
}

GamePlayStatics

静态类, 有许多通用的游戏函数,比如PlaySound, SpawnEmitter, SpawnObject, ApplyDamage, GetPlayerPawn, GetPlayerController等。

所有函数参考: UGameplayStatics | Unreal Engine Documentation

常用代码片段:

1
2
3
4
5
// 用法
{
#include "Kismet/GameplayStatics.h"
UGameplayStatics::WhateverFunction(); // 直接调用相关函数
}

参考

[1] Gameplay框架 | 虚幻引擎文档 (unrealengine.com)

[2] Unreal Gameplay Framework Guide for C++ – Tom Looman