六一的部落格


行百里者半九十




创建player controller的派生类并绑定键位


创建派生自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}

设置关卡基础类型: player controller


包含头文件

1#include "GeometryPlayerController.h"

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

private

在构造函数中调用

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

比较pawn和player controller键位绑定的位置


pawn

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


player controller

继承自AController的成员函数SetupInputComponent

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


说明

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

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



切换pawn日志


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



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

  1. 初始状态

    • player controller接管GeometryPawn
    • ai controller接管BP_GeometryPawn_Cube
  2. 第一次切换player controller预备接管BP_GeometryPawn_Cube

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

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

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

    player controller释放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}

player controller与pawn



创建player controller的派生类并绑定键位


创建派生自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}

设置关卡基础类型: player controller


包含头文件

1#include "GeometryPlayerController.h"

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

private

在构造函数中调用

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

比较pawn和player controller键位绑定的位置


pawn

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


player controller

继承自AController的成员函数SetupInputComponent

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


说明

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

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



切换pawn日志


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



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

  1. 初始状态

    • player controller接管GeometryPawn
    • ai controller接管BP_GeometryPawn_Cube
  2. 第一次切换player controller预备接管BP_GeometryPawn_Cube

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

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

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

    player controller释放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}