六一的部落格


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



Reset All Players


说明

回合每次开始时, 重新创建所有游戏角色, 让他们呆在初始位置

回合结束时, 游戏角色会被销毁

通过Controller完成: Controller自创建后一直存在于场景中


添加接口: 通过Controller重建游戏角色

private

ShootThemUp: STUGameModeBase.h

1void ResetOnePlayer(AController *Controller);       

如果游戏角色未死亡: 销毁控制器当前控制的游戏角色

-
APawn::Reset 调用Destroy销毁Pawn: 当前Pawn上级Controller一定存在, 需满足控制器拥有PlayerState条件
1if (Controller && Controller->GetPawn())
2{
3    Controller->GetPawn()->Reset();
4}

通过RestartPlayer创建游戏角色

动态创建游戏角色

-
AGameModeBase::RestartPlayer > AGameModeBase::RestartPlayerAtPlayerStart 只有当控制器名下没有Pawn时才会创建Pawn

完整实现

ShootThemUp: STUGameModeBase.cpp

1void ASTUGameModeBase::ResetOnePlayer(AController *Controller)
2{
3    if (Controller && Controller->GetPawn())
4    {
5        Controller->GetPawn()->Reset();
6    }
7
8    RestartPlayer(Controller);
9}

添加接口: 遍历所有Controller, 重建其名下游戏角色

private

ShootThemUp: STUGameModeBase.h

1void ResetPlayers();
-
UWorld::GetControllerIterator 获取控制器列表的迭代器

通过迭代器的Get方法得到指向Controller的指针

ShootThemUp: STUGameModeBase.cpp

1void ASTUGameModeBase::ResetPlayers()
2{
3    if (!GetWorld()) return;
4    for (auto It = GetWorld()->GetControllerIterator(); It; ++It)
5    {
6        ResetOnePlayer(It->Get());
7    }
8}       

回合结束时, 调用ResetPlayers重新创建游戏角色

ShootThemUp: STUGameModeBase.cpp

1// GameTimerUpdate
2
3if (CurrentRound + 1 <= GameData.RoundsNum)
4 {
5     ++CurrentRound;
6     ResetPlayers();
7     StartRound();
8 }

查看

BP_STUGameModeBase

回合结束后, 创建了超多NPC



NPC的AutoPossessAI参数

BP_STUAICharacter

当前AutoPossessAI的值为Placed in World or Spawned: 当在关卡中添加游戏角色, 或者在代码中动态创建, 会为游戏角色创建上级控制器


这也是为什么在动态创建NPC那节会出现6个控制器的原因, 除了我们创建的3个, 当控制器创建NPC时, 虚幻引擎根据设置为这3个NPC又创建了控制器

当前有2个AIController


而当第一个回合结束, 下一回合开始, 使用迭代器遍历控制器时, 不停创建新NPC, 不停创建新控制器, 真是遍历不完

BP_STUAICharacter > AutoPossessAI 设为Disabled



再次查看

AIController个数正常


所有回合结束, 玩家操控的游戏角色重新生成三次, 而NPC仍是同一个


只有一个PlayerState



AIController的WantsPlayerState参数

之前在Reset函数中讲到, 如果控制器没有PlayerState, 不会对名下Pawn调用Destroy

因此, NPC没有被销毁, RestartPlayer中也没有重新创建Pawn

PlayerController会自动创建PlayerState, AIController需要指示创建: 通过将WantsPlayerState设为true



查看

有两个PlayerState


一局结束时, 所有游戏角色均重建三次, 标号从0开始



在代码中查看AutoPossessAI

-
Disabled
PlacedInWorld
Spawned
PlacedInWorldOrSpawned
  1. APawn构造函数中, AutoPossesAI的默认值为PlacedInWorld
  2. 在APawn::PostInitializeComponents中左右控制器的生成

    当值为不为Disabled时, Pawn会调用SpawnDefaultController创建Controller
     1void APawn::PostInitializeComponents()
     2{
     3    QUICK_SCOPE_CYCLE_COUNTER(STAT_Pawn_PostInitComponents);
     4
     5    Super::PostInitializeComponents();
     6
     7    if (IsValid(this))
     8    {
     9        UWorld* World = GetWorld();
    10
    11        // Automatically add Controller to AI Pawns if we are allowed to.
    12        if (AutoPossessPlayer == EAutoReceiveInput::Disabled
    13            && AutoPossessAI != EAutoPossessAI::Disabled && Controller == nullptr && GetNetMode() != NM_Client
    14#if WITH_EDITOR
    15            && (GIsEditor == false || World->IsGameWorld())
    16#endif // WITH_EDITOR
    17            )
    18        {
    19            const bool bPlacedInWorld = (World->bStartup);
    20            if ((AutoPossessAI == EAutoPossessAI::PlacedInWorldOrSpawned) ||
    21                (AutoPossessAI == EAutoPossessAI::PlacedInWorld && bPlacedInWorld) ||
    22                (AutoPossessAI == EAutoPossessAI::Spawned && !bPlacedInWorld))
    23            {
    24                SpawnDefaultController();
    25            }
    26        }
    27
    28        // update movement component's nav agent values
    29        UpdateNavAgent();
    30    }
    31}

在代码中设置AutoPossessAI和WantsPlayerState的方法

  1. AutoPossessAI

    ShootThemUp: AI/STUAICharacter.cpp

    1// 构造函数
    2
    3AutoPossessAI = EAutoPossessAI::Disabled;
  2. WantsPlayerState

    ShootThemUp: AI/STUAIController.cpp

    1// 构造函数
    2
    3bWantsPlayerState = true;

回合中重建游戏角色


Reset All Players


说明

回合每次开始时, 重新创建所有游戏角色, 让他们呆在初始位置

回合结束时, 游戏角色会被销毁

通过Controller完成: Controller自创建后一直存在于场景中


添加接口: 通过Controller重建游戏角色

private

ShootThemUp: STUGameModeBase.h

1void ResetOnePlayer(AController *Controller);       

如果游戏角色未死亡: 销毁控制器当前控制的游戏角色

-
APawn::Reset 调用Destroy销毁Pawn: 当前Pawn上级Controller一定存在, 需满足控制器拥有PlayerState条件
1if (Controller && Controller->GetPawn())
2{
3    Controller->GetPawn()->Reset();
4}

通过RestartPlayer创建游戏角色

动态创建游戏角色

-
AGameModeBase::RestartPlayer > AGameModeBase::RestartPlayerAtPlayerStart 只有当控制器名下没有Pawn时才会创建Pawn

完整实现

ShootThemUp: STUGameModeBase.cpp

1void ASTUGameModeBase::ResetOnePlayer(AController *Controller)
2{
3    if (Controller && Controller->GetPawn())
4    {
5        Controller->GetPawn()->Reset();
6    }
7
8    RestartPlayer(Controller);
9}

添加接口: 遍历所有Controller, 重建其名下游戏角色

private

ShootThemUp: STUGameModeBase.h

1void ResetPlayers();
-
UWorld::GetControllerIterator 获取控制器列表的迭代器

通过迭代器的Get方法得到指向Controller的指针

ShootThemUp: STUGameModeBase.cpp

1void ASTUGameModeBase::ResetPlayers()
2{
3    if (!GetWorld()) return;
4    for (auto It = GetWorld()->GetControllerIterator(); It; ++It)
5    {
6        ResetOnePlayer(It->Get());
7    }
8}       

回合结束时, 调用ResetPlayers重新创建游戏角色

ShootThemUp: STUGameModeBase.cpp

1// GameTimerUpdate
2
3if (CurrentRound + 1 <= GameData.RoundsNum)
4 {
5     ++CurrentRound;
6     ResetPlayers();
7     StartRound();
8 }

查看

BP_STUGameModeBase

回合结束后, 创建了超多NPC



NPC的AutoPossessAI参数

BP_STUAICharacter

当前AutoPossessAI的值为Placed in World or Spawned: 当在关卡中添加游戏角色, 或者在代码中动态创建, 会为游戏角色创建上级控制器


这也是为什么在动态创建NPC那节会出现6个控制器的原因, 除了我们创建的3个, 当控制器创建NPC时, 虚幻引擎根据设置为这3个NPC又创建了控制器

当前有2个AIController


而当第一个回合结束, 下一回合开始, 使用迭代器遍历控制器时, 不停创建新NPC, 不停创建新控制器, 真是遍历不完

BP_STUAICharacter > AutoPossessAI 设为Disabled



再次查看

AIController个数正常


所有回合结束, 玩家操控的游戏角色重新生成三次, 而NPC仍是同一个


只有一个PlayerState



AIController的WantsPlayerState参数

之前在Reset函数中讲到, 如果控制器没有PlayerState, 不会对名下Pawn调用Destroy

因此, NPC没有被销毁, RestartPlayer中也没有重新创建Pawn

PlayerController会自动创建PlayerState, AIController需要指示创建: 通过将WantsPlayerState设为true



查看

有两个PlayerState


一局结束时, 所有游戏角色均重建三次, 标号从0开始



在代码中查看AutoPossessAI

-
Disabled
PlacedInWorld
Spawned
PlacedInWorldOrSpawned
  1. APawn构造函数中, AutoPossesAI的默认值为PlacedInWorld
  2. 在APawn::PostInitializeComponents中左右控制器的生成

    当值为不为Disabled时, Pawn会调用SpawnDefaultController创建Controller
     1void APawn::PostInitializeComponents()
     2{
     3    QUICK_SCOPE_CYCLE_COUNTER(STAT_Pawn_PostInitComponents);
     4
     5    Super::PostInitializeComponents();
     6
     7    if (IsValid(this))
     8    {
     9        UWorld* World = GetWorld();
    10
    11        // Automatically add Controller to AI Pawns if we are allowed to.
    12        if (AutoPossessPlayer == EAutoReceiveInput::Disabled
    13            && AutoPossessAI != EAutoPossessAI::Disabled && Controller == nullptr && GetNetMode() != NM_Client
    14#if WITH_EDITOR
    15            && (GIsEditor == false || World->IsGameWorld())
    16#endif // WITH_EDITOR
    17            )
    18        {
    19            const bool bPlacedInWorld = (World->bStartup);
    20            if ((AutoPossessAI == EAutoPossessAI::PlacedInWorldOrSpawned) ||
    21                (AutoPossessAI == EAutoPossessAI::PlacedInWorld && bPlacedInWorld) ||
    22                (AutoPossessAI == EAutoPossessAI::Spawned && !bPlacedInWorld))
    23            {
    24                SpawnDefaultController();
    25            }
    26        }
    27
    28        // update movement component's nav agent values
    29        UpdateNavAgent();
    30    }
    31}

在代码中设置AutoPossessAI和WantsPlayerState的方法

  1. AutoPossessAI

    ShootThemUp: AI/STUAICharacter.cpp

    1// 构造函数
    2
    3AutoPossessAI = EAutoPossessAI::Disabled;
  2. WantsPlayerState

    ShootThemUp: AI/STUAIController.cpp

    1// 构造函数
    2
    3bWantsPlayerState = true;