六一的部落格


关关难过关关过,前路漫漫亦灿灿。




创建PlayerController的派生类并绑定键位


创建派生自APlayerController的C++类

-
基类 PlayerController
属性 Public
名称 GeometryPlayerController

添加动作映射,按空格时切换当前Pawn

Project Settings > Engine > Input

-
函数描述 ChangePawn
键位 Space Bar



实现切换Pawn逻辑

GeometryPlayerController


包含头文件

1#include "Engine/World.h"
2#include "Kismet/GameplayStatics.h"
3#include "GeometryPawn.h"
4#include "Components/InputComponent.h"

添加静态日志类型

1DEFINE_LOG_CATEGORY_STATIC(LogGeometryPlayerController, All, All)

添加数据成员: 保存Pawn的数组, 当前Pawn索引

private

1TArray<AActor*>Pawns;
2int32 CurrentPawnIndex = 0;

添加函数成员: 初始化Pawn数组

private

在BeginPlay中调用

获取场景中派生自AGeometryPawn的Pawn对象

1void AGeometryPlayerController::InitPawns()
2{
3    UGameplayStatics::GetAllActorsOfClass(GetWorld(), AGeometryPawn::StaticClass(), Pawns);
4}

实现BeginPlay

1void AGeometryPlayerController::BeginPlay() {
2    Super::BeginPlay();
3    InitPawns();
4}

添加函数成员: 切换Pawn的回调函数

private

  1. Pawn个数为1时,切换无效
  2. 挨个轮转Pawn对象
 1void AGeometryPlayerController::ChangePawn()
 2{
 3    if (Pawns.Num() <= 1)
 4    {
 5        UE_LOG(LogGeometryPlayerController, Log, TEXT("Pawn Num %d"), Pawns.Num());
 6        return;
 7    }
 8    AGeometryPawn *CurrentPawn = Cast<AGeometryPawn>(Pawns[CurrentPawnIndex]);
 9    CurrentPawnIndex = (CurrentPawnIndex + 1) % Pawns.Num();
10    AGeometryPawn *NextPawn = Cast<AGeometryPawn>(Pawns[CurrentPawnIndex]);
11
12    if (!CurrentPawn || !NextPawn) return;
13
14    UE_LOG(LogGeometryPlayerController, Log, TEXT("%s switch pawn from %s to %s"), *GetName(), *CurrentPawn->GetName(), *NextPawn->GetName());
15
16    Possess(NextPawn);
17}

添加函数成员: 绑定键位描述和回调函数

private

在SetupInputComponent中调用

为函数描述绑定回调函数

1void AGeometryPlayerController::BindChangePawn()
2{
3    if (InputComponent)
4    {
5        InputComponent->BindAction("ChangePawn", IE_Pressed, this, &AGeometryPlayerController::ChangePawn);
6    }
7}

实现SetupInputComponent

1void AGeometryPlayerController::SetupInputComponent()
2{
3    Super::SetupInputComponent();
4    BindChangePawn();
5}

设置关卡基础类型: PlayerController


包含头文件

1#include "GeometryPlayerController.h"

添加函数成员: 设置PlayerController的基础类型

private

在构造函数中调用

1void AGeometryGameModeBase::InitPlayerController()
2{
3    PlayerControllerClass = AGeometryPlayerController::StaticClass();
4}

比较Pawn和PlayerController键位绑定的位置


Pawn

在成员函数SetupPlayerInputComponent中完成,其参数为UInputComponent指针


PlayerController

继承自AController的成员函数SetupInputComponent

继承自AActor的数据成员InputComponent,其类型为UInputComponent指针


说明

  1. APawn和APlayerController都会处理输入
  2. 二者均继承自AActor:

    APlayerController的输入处理来自AActor,而APawn对输入处理进行了封装



切换Pawn日志


在虚幻编辑器中查看GameMode基础类型



在虚幻编辑器中添加BP_GeometryPawn_Sphere实例,用以切换

  1. 初始状态

    • PlayerController接管GeometryPawn
    • AIController接管BP_GeometryPawn_Cube
  2. 第一次切换 PlayerController预备接管BP_GeometryPawn_Cube

    • PlayerController释放GeometryPawn
    • AIController释放BP_GeometryPawn_Cube
    • PlayerController接管BP_GeometryPawn_Cube
    • GeometryPawn处于等待接管状态
  3. 第二次切换 PlayerController预备接管GeometryPawn

    • PlayerController释放BP_GeometryPawn_Cube
    • PlayerController接管GeometryPawn
    • BP_GeometryPawn_Cube处于等待接管状态
  4. 第三次切换 PlayerController预备接管BP_GeometryPawn_Cube

    • PlayerController释放GeometryPawn
    • PlayerController接管BP_GeometryPawn_Cube
    • GeometryPawn处于等待接管状态
  5. 结束游戏

    PlayerController释放BP_GeometryPawn_Cube



对BP_GeometryPawn_Sphere实例禁用AI自动控制

  1. 默认 Placed in World


  2. 禁用 Disabled


  3. 日志



按住方向键切换Pawn,切换后,前Pawn仍在移动

情况说明:

  • 基类UnPossessd函数中,会调用DestroyPlayerInputComponent函数,删除输入组件
  • 即在删除输入组件之前,不会更新速度方向

解决思路:

  • 在Tick函数的最后,复位速度矢量
  • 只有当Pawn对象被使用时,调用VelocityInForce; 切换后,前Pawn不会移动,但切换回前Pawn,会保持移动

解决切换Pawn后, 前Pawn继续移动问题

GeometryPawn


添加函数成员: 在Tick函数的最后,复位速度矢量

private

在Tick函数的最后调用

1void AGeometryPawn::ClearVelocityVector()
2{
3    VelocityVector = FVector::ZeroVector;
4}

在调用VelocityInForce之前,判断Pawn是否被接管

 1// Called every frame
 2void AGeometryPawn::Tick(float DeltaTime)
 3{
 4    Super::Tick(DeltaTime);
 5
 6    if (IsPawnControlled())
 7        VelocityInForce(DeltaTime);
 8
 9    ClearVelocityVector();
10}

PlayerController与Pawn



创建PlayerController的派生类并绑定键位


创建派生自APlayerController的C++类

-
基类 PlayerController
属性 Public
名称 GeometryPlayerController

添加动作映射,按空格时切换当前Pawn

Project Settings > Engine > Input

-
函数描述 ChangePawn
键位 Space Bar



实现切换Pawn逻辑

GeometryPlayerController


包含头文件

1#include "Engine/World.h"
2#include "Kismet/GameplayStatics.h"
3#include "GeometryPawn.h"
4#include "Components/InputComponent.h"

添加静态日志类型

1DEFINE_LOG_CATEGORY_STATIC(LogGeometryPlayerController, All, All)

添加数据成员: 保存Pawn的数组, 当前Pawn索引

private

1TArray<AActor*>Pawns;
2int32 CurrentPawnIndex = 0;

添加函数成员: 初始化Pawn数组

private

在BeginPlay中调用

获取场景中派生自AGeometryPawn的Pawn对象

1void AGeometryPlayerController::InitPawns()
2{
3    UGameplayStatics::GetAllActorsOfClass(GetWorld(), AGeometryPawn::StaticClass(), Pawns);
4}

实现BeginPlay

1void AGeometryPlayerController::BeginPlay() {
2    Super::BeginPlay();
3    InitPawns();
4}

添加函数成员: 切换Pawn的回调函数

private

  1. Pawn个数为1时,切换无效
  2. 挨个轮转Pawn对象
 1void AGeometryPlayerController::ChangePawn()
 2{
 3    if (Pawns.Num() <= 1)
 4    {
 5        UE_LOG(LogGeometryPlayerController, Log, TEXT("Pawn Num %d"), Pawns.Num());
 6        return;
 7    }
 8    AGeometryPawn *CurrentPawn = Cast<AGeometryPawn>(Pawns[CurrentPawnIndex]);
 9    CurrentPawnIndex = (CurrentPawnIndex + 1) % Pawns.Num();
10    AGeometryPawn *NextPawn = Cast<AGeometryPawn>(Pawns[CurrentPawnIndex]);
11
12    if (!CurrentPawn || !NextPawn) return;
13
14    UE_LOG(LogGeometryPlayerController, Log, TEXT("%s switch pawn from %s to %s"), *GetName(), *CurrentPawn->GetName(), *NextPawn->GetName());
15
16    Possess(NextPawn);
17}

添加函数成员: 绑定键位描述和回调函数

private

在SetupInputComponent中调用

为函数描述绑定回调函数

1void AGeometryPlayerController::BindChangePawn()
2{
3    if (InputComponent)
4    {
5        InputComponent->BindAction("ChangePawn", IE_Pressed, this, &AGeometryPlayerController::ChangePawn);
6    }
7}

实现SetupInputComponent

1void AGeometryPlayerController::SetupInputComponent()
2{
3    Super::SetupInputComponent();
4    BindChangePawn();
5}

设置关卡基础类型: PlayerController


包含头文件

1#include "GeometryPlayerController.h"

添加函数成员: 设置PlayerController的基础类型

private

在构造函数中调用

1void AGeometryGameModeBase::InitPlayerController()
2{
3    PlayerControllerClass = AGeometryPlayerController::StaticClass();
4}

比较Pawn和PlayerController键位绑定的位置


Pawn

在成员函数SetupPlayerInputComponent中完成,其参数为UInputComponent指针


PlayerController

继承自AController的成员函数SetupInputComponent

继承自AActor的数据成员InputComponent,其类型为UInputComponent指针


说明

  1. APawn和APlayerController都会处理输入
  2. 二者均继承自AActor:

    APlayerController的输入处理来自AActor,而APawn对输入处理进行了封装



切换Pawn日志


在虚幻编辑器中查看GameMode基础类型



在虚幻编辑器中添加BP_GeometryPawn_Sphere实例,用以切换

  1. 初始状态

    • PlayerController接管GeometryPawn
    • AIController接管BP_GeometryPawn_Cube
  2. 第一次切换 PlayerController预备接管BP_GeometryPawn_Cube

    • PlayerController释放GeometryPawn
    • AIController释放BP_GeometryPawn_Cube
    • PlayerController接管BP_GeometryPawn_Cube
    • GeometryPawn处于等待接管状态
  3. 第二次切换 PlayerController预备接管GeometryPawn

    • PlayerController释放BP_GeometryPawn_Cube
    • PlayerController接管GeometryPawn
    • BP_GeometryPawn_Cube处于等待接管状态
  4. 第三次切换 PlayerController预备接管BP_GeometryPawn_Cube

    • PlayerController释放GeometryPawn
    • PlayerController接管BP_GeometryPawn_Cube
    • GeometryPawn处于等待接管状态
  5. 结束游戏

    PlayerController释放BP_GeometryPawn_Cube



对BP_GeometryPawn_Sphere实例禁用AI自动控制

  1. 默认 Placed in World


  2. 禁用 Disabled


  3. 日志



按住方向键切换Pawn,切换后,前Pawn仍在移动

情况说明:

  • 基类UnPossessd函数中,会调用DestroyPlayerInputComponent函数,删除输入组件
  • 即在删除输入组件之前,不会更新速度方向

解决思路:

  • 在Tick函数的最后,复位速度矢量
  • 只有当Pawn对象被使用时,调用VelocityInForce; 切换后,前Pawn不会移动,但切换回前Pawn,会保持移动

解决切换Pawn后, 前Pawn继续移动问题

GeometryPawn


添加函数成员: 在Tick函数的最后,复位速度矢量

private

在Tick函数的最后调用

1void AGeometryPawn::ClearVelocityVector()
2{
3    VelocityVector = FVector::ZeroVector;
4}

在调用VelocityInForce之前,判断Pawn是否被接管

 1// Called every frame
 2void AGeometryPawn::Tick(float DeltaTime)
 3{
 4    Super::Tick(DeltaTime);
 5
 6    if (IsPawnControlled())
 7        VelocityInForce(DeltaTime);
 8
 9    ClearVelocityVector();
10}