六一的部落格


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



Player State


说明

本节会为游戏角色划分阵营, 通过游戏角色模型材质的颜色标记

关于文章结构:

  1. 之前都是由小及大, 即使课程里先给出框架, 也会从叶子的实现一点点与分支处接轨

    这在讲解中是没有问题的, 但在实际开发中, 叶子实现完测试, 仍旧需要构建框架. 对细枝末节的把握, 也不会总是那么准确
  2. 本节会尝试从大的框架, 延伸到枝叶的实现

创建C++类: STUPlayerState

可以在 Common Classess 中找到


-
基类 PlayerState
路径 Player
名称 STUPlayerState
属性 Public

每个游戏角色都拥有一个PlayerState, 其存在和Controller一样, 贯穿整个游戏

游戏角色死亡被销毁时, PlayerState依然存在. 因此, PlayerState是用来存储和游戏角色并非直接相关的各种数据的不二选择

通常是一些统计数据: 游戏角色死亡次数, 击杀敌人数目, 游戏角色名称, 阵营标识等


添加数据成员

-
TeamID 阵营标识
TeamColor 阵营颜色

private

ShootThemUp: Player/STUPlayerState.h

1int32 TeamID;
2FLinearColor TeamColor;

添加接口

TeamID和TeamColor的Get和Set接口

public

ShootThemUp: Player/STUPlayerState.h

1void SetTeamID(int32 ID) { TeamID = ID; }
2void SetTeamColor(const FLinearColor &Color) { TeamColor = Color; }
3
4int32 GetTeamID() const { return TeamID; }
5FLinearColor GetTeamColor() const { return TeamColor; }

补充游戏规则

-
TeamColors 阵营颜色列表, 使用TeamID匹配
DefaultTeamColor 阵营默认颜色. 未设置TeamColors时使用

ShootThemUp: STUCoreTypes.h

1// FGameData
2
3UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
4FLinearColor DefaultTeamColor = FLinearColor::Red;
5
6UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
7TArray<FLinearColor> TeamColors;

关卡中搭建框架

STUGameModeBase

-
CreateTeamsInfo 游戏开始时调用, 为游戏角色分组
DetermineColorByTeamID 通过TeamID查询颜色
SetPlayerColor 通过Controller设置游戏角色阵营颜色

private

ShootThemUp: STUGameModeBase.h

1void CreateTeamsInfo();
2FLinearColor DetermineColorByTeamID(int32 TeamID) const;
3void SetPlayerColor(AController *Controller);

设置PlayerState类型

ShootThemUp: STUGameModeBase.cpp

1#include "Player/STUPlayerState.h"
2
3// 构造函数
4PlayerStateClass = ASTUPlayerState::StaticClass();

在StartPlay中为游戏角色分组

ShootThemUp: STUGameModeBase.cpp

1// StartPlay
2
3SpawnBots();
4CreateTeamsInfo();

实现分组逻辑

  • 可以通过Controller获取PlayerState: 迭代Controller, 设置PlayerState
  • 控制器个数对应游戏角色个数, 分组时, 轮流为每个组增加队员; 当前两个组, 所以会容易些
  • 设置颜色后, 需要应用颜色: 调用SetPlayerColor

ShootThemUp: STUGameModeBase.cpp

 1void ASTUGameModeBase::CreateTeamsInfo()
 2{
 3    if (!GetWorld()) return;
 4    int32 TeamID = 1;
 5    for (auto It = GetWorld()->GetControllerIterator(); It; ++It)
 6    {
 7        const auto Controller = It->Get();
 8        if (!Controller) continue;
 9
10        const auto PlayerState = Cast<ASTUPlayerState>(Controller->PlayerState);
11        if (!PlayerState) continue;
12
13        PlayerState->SetTeamID(TeamID);
14        PlayerState->SetTeamColor(DetermineColorByTeamID(TeamID));
15        SetPlayerColor(Controller);
16
17        TeamID = TeamID == 1 ? 2 : 1;
18    }
19}

根据TeamID获取阵营颜色

阵营编号类型为 int32 , 当然存在为负数的情况. 至于要不要检测, 调用处TeamID是合法的, 没有别的情况暂不检查

ShootThemUp: STUGameModeBase.cpp

 1FLinearColor ASTUGameModeBase::DetermineColorByTeamID(int32 TeamID) const
 2{
 3    if (TeamID - 1 < GameData.TeamColors.Num())
 4    {
 5        return GameData.TeamColors[TeamID - 1];
 6    }
 7    UE_LOG(LogSTUGameModeBase, Warning, TEXT("No color for team id %i, set to default: %s"), TeamID, *GameData.DefaultTeamColor.ToString());
 8
 9    return GameData.DefaultTeamColor;
10}

通过控制器修改游戏角色颜色

  1. 初始化时需要设置
  2. 重建游戏角色时, 需要再次设置

    ShootThemUp: STUGameModeBase.cpp
    1// ResetOnePlayer
    2
    3RestartPlayer(Controller);
    4SetPlayerColor(Controller);
  3. CreateTeamsInfo中调用SetPlayerColor, 可以传入颜色; 而由ResetOnePlayer调用时, 无法传入颜色

    当然, 我们可以在ResetOnePlayer获取PlayerState, 再传入颜色, 可这和代码可读性矛盾

    因此, 我们选择在SetPlayerColor中获取PlayerState. 这其实不会影响性能, 因为CreateTeamsInfo只调用一次
  4. 之后再在STUBaseCharacter中实现SetPlayerColor逻辑

ShootThemUp: STUGameModeBase.cpp

 1void ASTUGameModeBase::SetPlayerColor(AController *Controller)
 2{
 3    if (!Controller) return;
 4
 5    const auto Character = Cast<ASTUBaseCharacter>(Controller->GetPawn());
 6    if (!Character) return;
 7
 8    const auto PlayerState = Cast<ASTUPlayerState>(Controller->PlayerState);
 9    if (!PlayerState) return;
10
11    Character->SetPlayerColor(PlayerState->GetTeamColor());
12}

查看游戏角色材质

HeroTPP

打开方式: 双击打开 BP_STUAICharacter , 双击 Mesh > Materials > Element 0 > HeroTPP

左侧下方 Paint Color 属性, 对应材质上的橘色: 该颜色将与阵营相关联



实现修改游戏角色材质颜色逻辑

public

ShootThemUp: Player/STUBaseCharacter.h

1void SetPlayerColor(const FLinearColor &Color);

添加属性: 材质参数名称

protected

ShootThemUp: Player/STUBaseCharacter.h

1UPROPERTY(EditDefaultsOnly)
2FName MaterialColorName = "PaintColor";

实现

ShootThemUp: Player/STUBaseCharacter.cpp

1#include "Materials/MaterialInstanceDynamic.h"
2
3void ASTUBaseCharacter::SetPlayerColor(const FLinearColor &Color)
4{
5    const auto MaterialInst = GetMesh()->CreateAndSetMaterialInstanceDynamic(0);
6    if (!MaterialInst) return;
7
8    MaterialInst->SetVectorParameterValue(MaterialColorName, Color);
9}

查看

  1. 使用阵营默认颜色, 有告警


  2. 设置阵营颜色数组和玩家总数4

    • 两个元素: 绿色和蓝色

      - Hex Linear
      绿色 3AE90000
      蓝色 001BD600
    • 玩家总数

      -
      Players Num 4



划分阵营


Player State


说明

本节会为游戏角色划分阵营, 通过游戏角色模型材质的颜色标记

关于文章结构:

  1. 之前都是由小及大, 即使课程里先给出框架, 也会从叶子的实现一点点与分支处接轨

    这在讲解中是没有问题的, 但在实际开发中, 叶子实现完测试, 仍旧需要构建框架. 对细枝末节的把握, 也不会总是那么准确
  2. 本节会尝试从大的框架, 延伸到枝叶的实现

创建C++类: STUPlayerState

可以在 Common Classess 中找到


-
基类 PlayerState
路径 Player
名称 STUPlayerState
属性 Public

每个游戏角色都拥有一个PlayerState, 其存在和Controller一样, 贯穿整个游戏

游戏角色死亡被销毁时, PlayerState依然存在. 因此, PlayerState是用来存储和游戏角色并非直接相关的各种数据的不二选择

通常是一些统计数据: 游戏角色死亡次数, 击杀敌人数目, 游戏角色名称, 阵营标识等


添加数据成员

-
TeamID 阵营标识
TeamColor 阵营颜色

private

ShootThemUp: Player/STUPlayerState.h

1int32 TeamID;
2FLinearColor TeamColor;

添加接口

TeamID和TeamColor的Get和Set接口

public

ShootThemUp: Player/STUPlayerState.h

1void SetTeamID(int32 ID) { TeamID = ID; }
2void SetTeamColor(const FLinearColor &Color) { TeamColor = Color; }
3
4int32 GetTeamID() const { return TeamID; }
5FLinearColor GetTeamColor() const { return TeamColor; }

补充游戏规则

-
TeamColors 阵营颜色列表, 使用TeamID匹配
DefaultTeamColor 阵营默认颜色. 未设置TeamColors时使用

ShootThemUp: STUCoreTypes.h

1// FGameData
2
3UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
4FLinearColor DefaultTeamColor = FLinearColor::Red;
5
6UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
7TArray<FLinearColor> TeamColors;

关卡中搭建框架

STUGameModeBase

-
CreateTeamsInfo 游戏开始时调用, 为游戏角色分组
DetermineColorByTeamID 通过TeamID查询颜色
SetPlayerColor 通过Controller设置游戏角色阵营颜色

private

ShootThemUp: STUGameModeBase.h

1void CreateTeamsInfo();
2FLinearColor DetermineColorByTeamID(int32 TeamID) const;
3void SetPlayerColor(AController *Controller);

设置PlayerState类型

ShootThemUp: STUGameModeBase.cpp

1#include "Player/STUPlayerState.h"
2
3// 构造函数
4PlayerStateClass = ASTUPlayerState::StaticClass();

在StartPlay中为游戏角色分组

ShootThemUp: STUGameModeBase.cpp

1// StartPlay
2
3SpawnBots();
4CreateTeamsInfo();

实现分组逻辑

  • 可以通过Controller获取PlayerState: 迭代Controller, 设置PlayerState
  • 控制器个数对应游戏角色个数, 分组时, 轮流为每个组增加队员; 当前两个组, 所以会容易些
  • 设置颜色后, 需要应用颜色: 调用SetPlayerColor

ShootThemUp: STUGameModeBase.cpp

 1void ASTUGameModeBase::CreateTeamsInfo()
 2{
 3    if (!GetWorld()) return;
 4    int32 TeamID = 1;
 5    for (auto It = GetWorld()->GetControllerIterator(); It; ++It)
 6    {
 7        const auto Controller = It->Get();
 8        if (!Controller) continue;
 9
10        const auto PlayerState = Cast<ASTUPlayerState>(Controller->PlayerState);
11        if (!PlayerState) continue;
12
13        PlayerState->SetTeamID(TeamID);
14        PlayerState->SetTeamColor(DetermineColorByTeamID(TeamID));
15        SetPlayerColor(Controller);
16
17        TeamID = TeamID == 1 ? 2 : 1;
18    }
19}

根据TeamID获取阵营颜色

阵营编号类型为 int32 , 当然存在为负数的情况. 至于要不要检测, 调用处TeamID是合法的, 没有别的情况暂不检查

ShootThemUp: STUGameModeBase.cpp

 1FLinearColor ASTUGameModeBase::DetermineColorByTeamID(int32 TeamID) const
 2{
 3    if (TeamID - 1 < GameData.TeamColors.Num())
 4    {
 5        return GameData.TeamColors[TeamID - 1];
 6    }
 7    UE_LOG(LogSTUGameModeBase, Warning, TEXT("No color for team id %i, set to default: %s"), TeamID, *GameData.DefaultTeamColor.ToString());
 8
 9    return GameData.DefaultTeamColor;
10}

通过控制器修改游戏角色颜色

  1. 初始化时需要设置
  2. 重建游戏角色时, 需要再次设置

    ShootThemUp: STUGameModeBase.cpp
    1// ResetOnePlayer
    2
    3RestartPlayer(Controller);
    4SetPlayerColor(Controller);
  3. CreateTeamsInfo中调用SetPlayerColor, 可以传入颜色; 而由ResetOnePlayer调用时, 无法传入颜色

    当然, 我们可以在ResetOnePlayer获取PlayerState, 再传入颜色, 可这和代码可读性矛盾

    因此, 我们选择在SetPlayerColor中获取PlayerState. 这其实不会影响性能, 因为CreateTeamsInfo只调用一次
  4. 之后再在STUBaseCharacter中实现SetPlayerColor逻辑

ShootThemUp: STUGameModeBase.cpp

 1void ASTUGameModeBase::SetPlayerColor(AController *Controller)
 2{
 3    if (!Controller) return;
 4
 5    const auto Character = Cast<ASTUBaseCharacter>(Controller->GetPawn());
 6    if (!Character) return;
 7
 8    const auto PlayerState = Cast<ASTUPlayerState>(Controller->PlayerState);
 9    if (!PlayerState) return;
10
11    Character->SetPlayerColor(PlayerState->GetTeamColor());
12}

查看游戏角色材质

HeroTPP

打开方式: 双击打开 BP_STUAICharacter , 双击 Mesh > Materials > Element 0 > HeroTPP

左侧下方 Paint Color 属性, 对应材质上的橘色: 该颜色将与阵营相关联



实现修改游戏角色材质颜色逻辑

public

ShootThemUp: Player/STUBaseCharacter.h

1void SetPlayerColor(const FLinearColor &Color);

添加属性: 材质参数名称

protected

ShootThemUp: Player/STUBaseCharacter.h

1UPROPERTY(EditDefaultsOnly)
2FName MaterialColorName = "PaintColor";

实现

ShootThemUp: Player/STUBaseCharacter.cpp

1#include "Materials/MaterialInstanceDynamic.h"
2
3void ASTUBaseCharacter::SetPlayerColor(const FLinearColor &Color)
4{
5    const auto MaterialInst = GetMesh()->CreateAndSetMaterialInstanceDynamic(0);
6    if (!MaterialInst) return;
7
8    MaterialInst->SetVectorParameterValue(MaterialColorName, Color);
9}

查看

  1. 使用阵营默认颜色, 有告警


  2. 设置阵营颜色数组和玩家总数4

    • 两个元素: 绿色和蓝色

      - Hex Linear
      绿色 3AE90000
      蓝色 001BD600
    • 玩家总数

      -
      Players Num 4