六一的部落格


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



Are Enemies?


说明

  1. 判断阵营的方法

    PlayerState中的TeamID
  2. 行为树中, NPC锁定攻击对象时, 排除队友
  3. 判断阵营的位置
    • 方案一: 造成伤害前进行判断; 造成伤害的所有情形都要考虑到, 全部都要添加阵营判断
    • 方案二: 游戏角色受到伤害时, 判断伤害来源阵营

      要求伤害来源信息正确

      Controller信息设置敌方, Actor信息用来存放武器
  4. 游戏角色造成伤害的情形
    • 步枪击中游戏角色
    • 游戏角色在榴弹爆炸范围内

添加判断函数

  • 静态成员函数
  • 参数的设定, 考虑到调用处容易给出
  • 当且仅当二者阵营不同时返回true, 其他无效情况均返回false

public

ShootThemUp: STUUtils.h

 1#include "Player/STUPlayerState.h"
 2#include "GameFramework/Controller.h"
 3
 4bool static AreEnemies(AController *Controller1, AController *Controller2)
 5{
 6    if (!Controller1 || !Controller2 || Controller1 == Controller2) return false;
 7
 8    const auto PlayerState1 = Cast<ASTUPlayerState>(Controller1->PlayerState);
 9    const auto PlayerState2 = Cast<ASTUPlayerState>(Controller2->PlayerState);
10
11    return PlayerState1 && PlayerState2 && PlayerState1->GetTeamID() != PlayerState2->GetTeamID();
12}

在感知组件中排除同阵营游戏角色


获取感知Actor的控制器

1const auto PercievePawn = Cast<APawn>(PercieveActor);
2PercievePawn && PercievePawn->Controller;

完整实现

ShootThemUp: Components/STUAIPerceptionComponent.cpp

 1// GetClosestEnemy
 2
 3for (const auto PercieveActor : PercieveActors)
 4{
 5    // ...
 6
 7    const auto PercievePawn = Cast<APawn>(PercieveActor);
 8    const auto AreEnemies = PercievePawn && STUUtils::AreEnemies(Controller, PercievePawn->Controller);
 9
10    if (HealthComponent && !HealthComponent->IsDead() && AreEnemies)
11    {
12        // ...
13    }
14}

受到伤害处判断阵营


修改步枪造成伤害传入的伤害源信息

  • NPC无PlayerController
  • 伤害源本就为武器

ShootThemUp: Weapon/STURifleWeapon.cpp

1// DamagedActor->TakeDamage(DamageAmount, FDamageEvent(), GetPlayerController(), this);
2
3const auto Pawn = Cast<APawn>(GetOwner());
4const auto Controller = Pawn->Controller;
5
6DamagedActor->TakeDamage(DamageAmount, FDamageEvent(), Controller, this);

修改榴弹造成伤害时传入的伤害源信息

  • Controller正确
  • 伤害源本就为武器, 无修改

ShootThemUp: Weapon/STUProjectile.cpp

1UGameplayStatics::ApplyRadialDamage(GetWorld(), DamageAmount, GetActorLocation(), DamageRadius, UDamageType::StaticClass(),
2                                    {GetOwner()}, this, GetController(), DoFullDamage);

受伤时判断阵营

ShootThemUp: Components/STUHealthComponent.cpp

1#include "STUUtils.h"
2
3// OnTakeAnyDamage
4
5const auto Pawn = Cast<APawn>(GetOwner());
6if (!Pawn) return;
7
8const auto AreEnemies = STUUtils::AreEnemies(Pawn->Controller, InstigatedBy);
9if (!AreEnemies) return;

查看

  1. 回合时长设置为120s
  2. 玩家数设为3

队友捕捉到2个游戏角色, 但只攻击敌人



在造成伤害处判断阵营

不推荐这种方法


步枪造成伤害

ShootThemUp: Weapon/STURifleWeapon.cpp

 1#include "STUUtils.h"
 2
 3void ASTURifleWeapon::MakeDamage(const FHitResult &HitResult)
 4{
 5    const auto HitActor = HitResult.GetActor();
 6    if (!HitActor) return;
 7
 8    const auto OwnerPawn = Cast<APawn>(GetOwner());
 9    const auto HitPawn = Cast<APawn>(HitActor);
10
11    bool AreEnemies = OwnerPawn && HitPawn && STUUtils::AreEnemies(OwnerPawn->Controller, HitPawn->Controller);
12    if (AreEnemies)
13    {
14        UE_LOG(LogTemp, Warning, TEXT("Shooting enemy"));
15        HitActor->TakeDamage(DamageAmount, FDamageEvent(), OwnerPawn->Controller, this);
16    }
17    else
18    {
19        UE_LOG(LogTemp, Warning, TEXT("Shooting cancel"));
20    }
21}        

榴弹造成伤害

仅供参考


GameModeBase存放阵营信息

private

ShootThemUp: STUGameModeBase.h

1TArray<TArray<AActor *>> TeamMemberList;

初始化阵营信息

ShootThemUp: STUGameModeBase.cpp

 1// 构造函数
 2TeamMemberList.SetNum(2);
 3
 4// CreateTeamsInfo
 5
 6for (auto It = GetWorld()->GetControllerIterator(); It; ++It)
 7{
 8    // ...
 9
10    const auto Actor = Cast<AActor>(Controller->GetPawn());
11    if (!Actor) continue;
12
13    // ...
14
15    SetPlayerColor(Controller);
16    TeamMemberList[TeamID - 1].Push(Actor);
17
18    // ...
19}

队友信息没放在PlayerState中有两个原因:

  1. 同阵营游戏角色保存的是相同信息, 可以使用TeamID分组; 这种拷贝比较浪费
  2. 当前复用控制器遍历整理阵营成员信息. 如果要存放在PlayerState中, 需要再遍历一次控制器, 拷贝队友信息

造成半径伤害时忽略队友

如果之前为STUGameModeBase类设置Public属性, 此处包含头文件路径时不用去到上级目录

ShootThemUp: Weapon/STUProjectile.cpp

 1#include "../STUGameModeBase.h"
 2
 3// OnProjectileHit
 4
 5TArray<AActor *> IgnoreActors;
 6const auto GameModeBase = Cast<ASTUGameModeBase>(UGameplayStatics::GetGameMode(this));
 7if (GameModeBase && GameModeBase->GetTeamMemberByActor(GetOwner(), IgnoreActors))
 8{
 9    UE_LOG(LogTemp, Warning, TEXT("Ignore nums: %i"), IgnoreActors.Num());
10    UGameplayStatics::ApplyRadialDamage(GetWorld(), DamageAmount, GetActorLocation(), DamageRadius, UDamageType::StaticClass(),
11                                        IgnoreActors, this, GetController(), DoFullDamage);
12}
13else
14{
15    UGameplayStatics::ApplyRadialDamage(GetWorld(), DamageAmount, GetActorLocation(), DamageRadius, UDamageType::StaticClass(),
16                                        {GetOwner()}, this, GetController(), DoFullDamage);
17}

通过队员获取阵营成员列表

public

ShootThemUp: STUGameModeBase.h

1bool GetTeamMemberByActor(AActor *Actor, TArray<AActor *> &TeamMembers) const;

ShootThemUp: STUGameModeBase.cpp

 1bool ASTUGameModeBase::GetTeamMemberByActor(AActor *Actor, TArray<AActor *> &TeamMembers) const         
 2{
 3    const auto Pawn = Cast<APawn>(Actor);
 4    if (!Pawn) return false;
 5
 6    const auto Controller = Pawn->Controller;
 7    if (!Controller) return false;
 8
 9    const auto PlayerState = Cast<ASTUPlayerState>(Controller->PlayerState);
10    if (!PlayerState) return false;
11
12    const auto TeamID = PlayerState->GetTeamID();
13    if (TeamID <= TeamMemberList.Num())
14    {
15        TeamMembers = TeamMemberList[TeamID - 1];
16        return true;
17    }
18
19    return false;
20}

造成伤害时判断阵营


Are Enemies?


说明

  1. 判断阵营的方法

    PlayerState中的TeamID
  2. 行为树中, NPC锁定攻击对象时, 排除队友
  3. 判断阵营的位置
    • 方案一: 造成伤害前进行判断; 造成伤害的所有情形都要考虑到, 全部都要添加阵营判断
    • 方案二: 游戏角色受到伤害时, 判断伤害来源阵营

      要求伤害来源信息正确

      Controller信息设置敌方, Actor信息用来存放武器
  4. 游戏角色造成伤害的情形
    • 步枪击中游戏角色
    • 游戏角色在榴弹爆炸范围内

添加判断函数

  • 静态成员函数
  • 参数的设定, 考虑到调用处容易给出
  • 当且仅当二者阵营不同时返回true, 其他无效情况均返回false

public

ShootThemUp: STUUtils.h

 1#include "Player/STUPlayerState.h"
 2#include "GameFramework/Controller.h"
 3
 4bool static AreEnemies(AController *Controller1, AController *Controller2)
 5{
 6    if (!Controller1 || !Controller2 || Controller1 == Controller2) return false;
 7
 8    const auto PlayerState1 = Cast<ASTUPlayerState>(Controller1->PlayerState);
 9    const auto PlayerState2 = Cast<ASTUPlayerState>(Controller2->PlayerState);
10
11    return PlayerState1 && PlayerState2 && PlayerState1->GetTeamID() != PlayerState2->GetTeamID();
12}

在感知组件中排除同阵营游戏角色


获取感知Actor的控制器

1const auto PercievePawn = Cast<APawn>(PercieveActor);
2PercievePawn && PercievePawn->Controller;

完整实现

ShootThemUp: Components/STUAIPerceptionComponent.cpp

 1// GetClosestEnemy
 2
 3for (const auto PercieveActor : PercieveActors)
 4{
 5    // ...
 6
 7    const auto PercievePawn = Cast<APawn>(PercieveActor);
 8    const auto AreEnemies = PercievePawn && STUUtils::AreEnemies(Controller, PercievePawn->Controller);
 9
10    if (HealthComponent && !HealthComponent->IsDead() && AreEnemies)
11    {
12        // ...
13    }
14}

受到伤害处判断阵营


修改步枪造成伤害传入的伤害源信息

  • NPC无PlayerController
  • 伤害源本就为武器

ShootThemUp: Weapon/STURifleWeapon.cpp

1// DamagedActor->TakeDamage(DamageAmount, FDamageEvent(), GetPlayerController(), this);
2
3const auto Pawn = Cast<APawn>(GetOwner());
4const auto Controller = Pawn->Controller;
5
6DamagedActor->TakeDamage(DamageAmount, FDamageEvent(), Controller, this);

修改榴弹造成伤害时传入的伤害源信息

  • Controller正确
  • 伤害源本就为武器, 无修改

ShootThemUp: Weapon/STUProjectile.cpp

1UGameplayStatics::ApplyRadialDamage(GetWorld(), DamageAmount, GetActorLocation(), DamageRadius, UDamageType::StaticClass(),
2                                    {GetOwner()}, this, GetController(), DoFullDamage);

受伤时判断阵营

ShootThemUp: Components/STUHealthComponent.cpp

1#include "STUUtils.h"
2
3// OnTakeAnyDamage
4
5const auto Pawn = Cast<APawn>(GetOwner());
6if (!Pawn) return;
7
8const auto AreEnemies = STUUtils::AreEnemies(Pawn->Controller, InstigatedBy);
9if (!AreEnemies) return;

查看

  1. 回合时长设置为120s
  2. 玩家数设为3

队友捕捉到2个游戏角色, 但只攻击敌人



在造成伤害处判断阵营

不推荐这种方法


步枪造成伤害

ShootThemUp: Weapon/STURifleWeapon.cpp

 1#include "STUUtils.h"
 2
 3void ASTURifleWeapon::MakeDamage(const FHitResult &HitResult)
 4{
 5    const auto HitActor = HitResult.GetActor();
 6    if (!HitActor) return;
 7
 8    const auto OwnerPawn = Cast<APawn>(GetOwner());
 9    const auto HitPawn = Cast<APawn>(HitActor);
10
11    bool AreEnemies = OwnerPawn && HitPawn && STUUtils::AreEnemies(OwnerPawn->Controller, HitPawn->Controller);
12    if (AreEnemies)
13    {
14        UE_LOG(LogTemp, Warning, TEXT("Shooting enemy"));
15        HitActor->TakeDamage(DamageAmount, FDamageEvent(), OwnerPawn->Controller, this);
16    }
17    else
18    {
19        UE_LOG(LogTemp, Warning, TEXT("Shooting cancel"));
20    }
21}        

榴弹造成伤害

仅供参考


GameModeBase存放阵营信息

private

ShootThemUp: STUGameModeBase.h

1TArray<TArray<AActor *>> TeamMemberList;

初始化阵营信息

ShootThemUp: STUGameModeBase.cpp

 1// 构造函数
 2TeamMemberList.SetNum(2);
 3
 4// CreateTeamsInfo
 5
 6for (auto It = GetWorld()->GetControllerIterator(); It; ++It)
 7{
 8    // ...
 9
10    const auto Actor = Cast<AActor>(Controller->GetPawn());
11    if (!Actor) continue;
12
13    // ...
14
15    SetPlayerColor(Controller);
16    TeamMemberList[TeamID - 1].Push(Actor);
17
18    // ...
19}

队友信息没放在PlayerState中有两个原因:

  1. 同阵营游戏角色保存的是相同信息, 可以使用TeamID分组; 这种拷贝比较浪费
  2. 当前复用控制器遍历整理阵营成员信息. 如果要存放在PlayerState中, 需要再遍历一次控制器, 拷贝队友信息

造成半径伤害时忽略队友

如果之前为STUGameModeBase类设置Public属性, 此处包含头文件路径时不用去到上级目录

ShootThemUp: Weapon/STUProjectile.cpp

 1#include "../STUGameModeBase.h"
 2
 3// OnProjectileHit
 4
 5TArray<AActor *> IgnoreActors;
 6const auto GameModeBase = Cast<ASTUGameModeBase>(UGameplayStatics::GetGameMode(this));
 7if (GameModeBase && GameModeBase->GetTeamMemberByActor(GetOwner(), IgnoreActors))
 8{
 9    UE_LOG(LogTemp, Warning, TEXT("Ignore nums: %i"), IgnoreActors.Num());
10    UGameplayStatics::ApplyRadialDamage(GetWorld(), DamageAmount, GetActorLocation(), DamageRadius, UDamageType::StaticClass(),
11                                        IgnoreActors, this, GetController(), DoFullDamage);
12}
13else
14{
15    UGameplayStatics::ApplyRadialDamage(GetWorld(), DamageAmount, GetActorLocation(), DamageRadius, UDamageType::StaticClass(),
16                                        {GetOwner()}, this, GetController(), DoFullDamage);
17}

通过队员获取阵营成员列表

public

ShootThemUp: STUGameModeBase.h

1bool GetTeamMemberByActor(AActor *Actor, TArray<AActor *> &TeamMembers) const;

ShootThemUp: STUGameModeBase.cpp

 1bool ASTUGameModeBase::GetTeamMemberByActor(AActor *Actor, TArray<AActor *> &TeamMembers) const         
 2{
 3    const auto Pawn = Cast<APawn>(Actor);
 4    if (!Pawn) return false;
 5
 6    const auto Controller = Pawn->Controller;
 7    if (!Controller) return false;
 8
 9    const auto PlayerState = Cast<ASTUPlayerState>(Controller->PlayerState);
10    if (!PlayerState) return false;
11
12    const auto TeamID = PlayerState->GetTeamID();
13    if (TeamID <= TeamMemberList.Num())
14    {
15        TeamMembers = TeamMemberList[TeamID - 1];
16        return true;
17    }
18
19    return false;
20}