六一的部落格


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



Headshot


回顾打出伤害

有两种伤害:

  • 榴弹爆炸
  • 子弹击中

榴弹爆炸伤害

STUProjectile

  1. 在BeginPlay注册委托, 发生碰撞时调用OnProjectileHit
  2. OnProjectileHit中调用ApplyRadialDamage, ApplyRadialDamage调用ApplyRadialDamageWithFalloff
  3. ApplyRadialDamageWithFalloff将爆炸波及者整理, 依次对其调用TakeDamage
    1Victim->TakeDamage(BaseDamage, DmgEvent, InstigatedByController, DamageCauser);

子弹击中伤害

STURifleWeapon

若有击中对象, 对其调用TakeDamage

1DamagedActor->TakeDamage(DamageAmount, FDamageEvent(), GetController(), this);

AActor::TakeDamage

  1. 会根据DamageEvent类型造成不同伤害, 有三种:
    • 点伤害
    • 半径伤害
    • 其他类型伤害
  2. 目前, 子弹击中伤害为其他类型伤害, 榴弹爆炸伤害为半径伤害; 但计算伤害走的均是其他类型伤害
  3. 三种伤害对应委托成员
    -
    点伤害 AActor::OnTakePointDamage
    半径伤害 AActor::OnTakeRadialDamage
    其他类型伤害 AActor::OnTakeAnyDamage
  4. 只要伤害值不为0, 均会触发OnTakeAnyDamage的回调函数; 处理特定类型伤害, 需注册对应类型委托

子弹击中伤害类型应该为点伤害

点伤害的处理函数形参列表中, 包含击中部位, 即骨骼网格体中骨骼名称


榴弹爆炸伤害类型为半径伤害时

计算伤害逻辑应该注册半径伤害对应委托


迁移受伤逻辑

  1. 将OnTakeAnyDamage逻辑移动到ApplyDamage

    private

    ShootThemUp: Components/STUHealthComponent.h
    1void ApplyDamage(float Damage, class AController *InstigatedBy);
    ShootThemUp: Components/STUHealthComponent.cpp
     1void USTUHealthComponent::ApplyDamage(float Damage, class AController *InstigatedBy)
     2{
     3    const auto Pawn = Cast<APawn>(GetOwner());
     4    if (!Pawn) return;
     5
     6    const auto AreEnemies = STUUtils::AreEnemies(Pawn->Controller, InstigatedBy);
     7    if (!AreEnemies) return;
     8
     9    if (Damage <= 0.0f || IsDead()) return;
    10
    11    if (!IsHealthFull())
    12    {
    13        StopHealTimer();
    14    }
    15
    16    SetHealth(Health - Damage);
    17
    18    if (IsDead())
    19    {
    20        UE_LOG(LogTemp, Display, TEXT("character dead"));
    21        Killed(InstigatedBy);
    22        OnDeath.Broadcast();
    23    }
    24    else
    25    {
    26        StartHealTimer();
    27    }
    28
    29    PlayCameraShake();
    30}
  2. 在OnTakeAnyDamage输出伤害数值

    ShootThemUp: Components/STUHealthComponent.cpp
    1void USTUHealthComponent::OnTakeAnyDamage(AActor *DamagedActor, float Damage, const class UDamageType *DamageType,
    2                                          class AController *InstigatedBy, AActor *DamageCauser)
    3{
    4    UE_LOG(LogHealthComponent, Display, TEXT("On any damage: %f"), Damage);
    5}

API导航


FTakeRadialDamageSignature

半径伤害委托类型定义

1DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_SevenParams( FTakeRadialDamageSignature, AActor, OnTakeRadialDamage, AActor*, DamagedActor, float, Damage, const class UDamageType*, DamageType, FVector, Origin, FHitResult, HitInfo, class AController*, InstigatedBy, AActor*, DamageCauser );

处理函数签名

1void OnTakeRadialDamage(AActor* DamagedActor, float Damage, const class UDamageType* DamageType, FVector Origin, FHitResult HitInfo, class AController* InstigatedBy, AActor* DamageCauser);

FTakePointDamageSignature

点伤害委托类型定义

1DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_NineParams( FTakePointDamageSignature, AActor, OnTakePointDamage, AActor*, DamagedActor, float, Damage, class AController*, InstigatedBy, FVector, HitLocation, class UPrimitiveComponent*, FHitComponent, FName, BoneName, FVector, ShotFromDirection, const class UDamageType*, DamageType, AActor*, DamageCauser );

处理函数签名

1void OnTakePointDamage(AActor* DamagedActor, float Damage, class AController* InstigatedBy, FVector HitLocation, class UPrimitiveComponent* FHitComponent, FName BoneName, FVector ShotFromDirection, const class UDamageType* DamageType, AActor* DamageCauser);

FDamageEvent

多个构造函数

  • 默认构造函数
  • 拷贝构造函数: 传递伤害类型
    1FDamageEvent(FDamageEvent const& InDamageEvent)
    2    : DamageTypeClass(InDamageEvent.DamageTypeClass)
    3{ }
  • 接受伤害类型的构造函数
    1explicit FDamageEvent(TSubclassOf<UDamageType> InDamageTypeClass)
    2    : DamageTypeClass(InDamageTypeClass)
    3{ }

FPointDamageEvent::HitInfo

包含击中信息

1/** Describes the trace/location that caused this damage */
2UPROPERTY()
3FHitResult HitInfo;

FHitResult

击中信息

-
PhysMaterial 击中部位材质
BoneName 骨骼网格体骨骼名称
 1/**
 2 * Physical material that was hit.
 3 * @note Must set bReturnPhysicalMaterial on the swept PrimitiveComponent or in the query params for this to be returned.
 4 */
 5UPROPERTY()
 6TWeakObjectPtr<UPhysicalMaterial> PhysMaterial;
 7
 8/** Name of bone we hit (for skeletal meshes). */
 9UPROPERTY()
10FName BoneName;

UPrimitiveComponent::GetBodyInstance

传入骨骼网格体的骨骼名称, 获取骨骼对应身体部位

 1/**
 2 * Returns BodyInstance of the component.
 3 *
 4 * @param BoneName				Used to get body associated with specific bone. NAME_None automatically gets the root most body
 5 * @param bGetWelded				If the component has been welded to another component and bGetWelded is true we return the single welded BodyInstance that is used in the simulation
 6 * @param Index					Index used in Components with multiple body instances
 7 *
 8 * @return		Returns the BodyInstance based on various states (does component have multiple bodies? Is the body welded to another body?)
 9 */
10virtual FBodyInstance* GetBodyInstance(FName BoneName = NAME_None, bool bGetWelded = true, int32 Index = INDEX_NONE) const;

UBodyInstance::GetSimplePhysicalMaterial

返回该部位使用的物理材质

1/** Find the correct PhysicalMaterial for simple geometry on this body */
2UPhysicalMaterial* GetSimplePhysicalMaterial() const;

为榴弹爆炸伤害注册半径伤害委托

  1. 添加处理函数

    private

    ShootThemUp: Components/STUHealthComponent.h
    1UFUNCTION()
    2void OnTakeRadialDamage(AActor* DamagedActor, float Damage, const class UDamageType* DamageType, FVector Origin, FHitResult HitInfo, class AController* InstigatedBy, AActor* DamageCauser);
    ShootThemUp: Components/STUHealthComponent.cpp
    1void USTUHealthComponent::OnTakeRadialDamage(AActor* DamagedActor, float Damage, const class UDamageType* DamageType, FVector Origin, FHitResult HitInfo, class AController* InstigatedBy, AActor* DamageCauser)
    2{
    3    UE_LOG(LogHealthComponent, Display, TEXT("On radial damage: %f"), Damage);
    4    ApplyDamage(Damage, InstigatedBy);
    5}
  2. 注册委托处理函数

    ShootThemUp: Components/STUHealthComponent.cpp
    1// BeginPlay
    2
    3TheOwner->OnTakeRadialDamage.AddDynamic(this, &USTUHealthComponent::OnTakeRadialDamage);

为子弹击中伤害注册点伤害委托


传递伤害类型和击中信息

ShootThemUp: Weapon/STURifleWeapon.cpp

1#include "PhysicalMaterials/PhysicalMaterial.h"
2
3// MakeDamage
4
5FPointDamageEvent PointDamageEvent;
6PointDamageEvent.HitInfo = HitResult;
7UE_LOG(LogTemp, Display, TEXT("Rifle hit: bone %s, physical materials %s"), *HitResult.BoneName.ToString(), *HitResult.PhysMaterial->GetName());
8
9DamagedActor->TakeDamage(DamageAmount, PointDamageEvent, GetController(), this);

注册委托

  1. 添加属性: 物理材质和伤害值关联

    protected

    ShootThemUp: Components/STUHealthComponent.h
    1class UPhysicalMaterial;
    2
    3UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
    4TMap<UPhysicalMaterial *, float> DamageModifiers;
  2. 添加接口: 返回伤害系数

    private

    ShootThemUp: Components/STUHealthComponent.h
    1float GetPointDamageModifier(AActor *DamageActor, const FName &BoneName);
    ShootThemUp: Components/STUHealthComponent.cpp
     1// #include "GameFramework/Actor.h"
     2// #include "GameFramework/Pawn.h"
     3#include "GameFramework/Character.h"
     4#include "Components/SkeletalMeshComponent.h"
     5#include "PhysicalMaterials/PhysicalMaterial.h"
     6
     7float USTUHealthComponent::GetPointDamageModifier(AActor *DamageActor, const FName &BoneName)
     8{
     9    const auto Character = Cast<ACharacter>(DamageActor);
    10    if (!Character || //
    11        !Character->GetMesh() || //
    12        !Character->GetMesh()->GetBodyInstance(BoneName)) return 1.0f;
    13
    14    const auto PhysMaterial = Character->GetMesh()->GetBodyInstance(BoneName)->GetSimplePhysicalMaterial();
    15
    16    if (!PhysMaterial || !DamageModifiers.Contains(PhysMaterial)) return 1.0f;
    17
    18    UE_LOG(LogHealthComponent, Display, TEXT("Point damage: bone %s, physical materials %s"), *BoneName.ToString(), *PhysMaterial->GetName());
    19
    20    return DamageModifiers[PhysMaterial];
    21}
  3. 添加处理函数

    private

    ShootThemUp: Components/STUHealthComponent.h
    1UFUNCTION()
    2void OnTakePointDamage(AActor* DamagedActor, float Damage, class AController* InstigatedBy, FVector HitLocation, class UPrimitiveComponent* FHitComponent, FName BoneName, FVector ShotFromDirection, const class UDamageType* DamageType, AActor* DamageCauser);
    ShootThemUp: Components/STUHealthComponent.cpp
    1void USTUHealthComponent::OnTakePointDamage(AActor *DamagedActor, float Damage, class AController *InstigatedBy, FVector HitLocation,
    2                                            class UPrimitiveComponent *FHitComponent, FName BoneName, FVector ShotFromDirection,
    3                                            const class UDamageType *DamageType, AActor *DamageCauser)
    4{
    5    const auto FinalDamage = GetPointDamageModifier(DamagedActor, BoneName) * Damage;
    6
    7    UE_LOG(LogHealthComponent, Display, TEXT("On point damage: %f, final damage %f, bone: %s"), Damage, FinalDamage, *BoneName.ToString());
    8    ApplyDamage(FinalDamage, InstigatedBy);
    9}
  4. 注册委托处理函数

    ShootThemUp: Components/STUHealthComponent.cpp
    1// BeginPlay
    2
    3TheOwner->OnTakePointDamage.AddDynamic(this, &USTUHealthComponent::OnTakePointDamage);

调整游戏角色身体物理材质

当前游戏角色身体拥有2种物理材质

Content/PhysMaterials
头部 PhysMat_Head
身体 PhysMat_Body
  1. 新增物理材质

    Content/PhysMaterials
    手部 PhysMat_Hand
    腿部 PhysMat_Leg
  2. 应用新增物理材质

    HeroTPP_Physics

    善用属性拷贝粘贴



查看

  1. 为游戏角色设置子弹击中伤害系数

    善用属性拷贝粘贴

    伤害系数
    头部 百倍伤害, 击杀
    身体 1.0
    腿部 1.1
    手部 0.8
    • 玩家操控的游戏角色


    • NPC


  2. 暂停NPC行为树

    BP_STUCharacter

  3. 将回合时长设置为300s

    BP_STUGameModeBase

  4. 日志

    依次击中腿部, 手部, 身体, 头部



说明

爆头应该直接导致死亡, 即检测到击中部位物理材质为PhysMat_Head, 直接设置生命值为0


击中游戏角色身体不同部位造成伤害值不同


Headshot


回顾打出伤害

有两种伤害:

  • 榴弹爆炸
  • 子弹击中

榴弹爆炸伤害

STUProjectile

  1. 在BeginPlay注册委托, 发生碰撞时调用OnProjectileHit
  2. OnProjectileHit中调用ApplyRadialDamage, ApplyRadialDamage调用ApplyRadialDamageWithFalloff
  3. ApplyRadialDamageWithFalloff将爆炸波及者整理, 依次对其调用TakeDamage
    1Victim->TakeDamage(BaseDamage, DmgEvent, InstigatedByController, DamageCauser);

子弹击中伤害

STURifleWeapon

若有击中对象, 对其调用TakeDamage

1DamagedActor->TakeDamage(DamageAmount, FDamageEvent(), GetController(), this);

AActor::TakeDamage

  1. 会根据DamageEvent类型造成不同伤害, 有三种:
    • 点伤害
    • 半径伤害
    • 其他类型伤害
  2. 目前, 子弹击中伤害为其他类型伤害, 榴弹爆炸伤害为半径伤害; 但计算伤害走的均是其他类型伤害
  3. 三种伤害对应委托成员
    -
    点伤害 AActor::OnTakePointDamage
    半径伤害 AActor::OnTakeRadialDamage
    其他类型伤害 AActor::OnTakeAnyDamage
  4. 只要伤害值不为0, 均会触发OnTakeAnyDamage的回调函数; 处理特定类型伤害, 需注册对应类型委托

子弹击中伤害类型应该为点伤害

点伤害的处理函数形参列表中, 包含击中部位, 即骨骼网格体中骨骼名称


榴弹爆炸伤害类型为半径伤害时

计算伤害逻辑应该注册半径伤害对应委托


迁移受伤逻辑

  1. 将OnTakeAnyDamage逻辑移动到ApplyDamage

    private

    ShootThemUp: Components/STUHealthComponent.h
    1void ApplyDamage(float Damage, class AController *InstigatedBy);
    ShootThemUp: Components/STUHealthComponent.cpp
     1void USTUHealthComponent::ApplyDamage(float Damage, class AController *InstigatedBy)
     2{
     3    const auto Pawn = Cast<APawn>(GetOwner());
     4    if (!Pawn) return;
     5
     6    const auto AreEnemies = STUUtils::AreEnemies(Pawn->Controller, InstigatedBy);
     7    if (!AreEnemies) return;
     8
     9    if (Damage <= 0.0f || IsDead()) return;
    10
    11    if (!IsHealthFull())
    12    {
    13        StopHealTimer();
    14    }
    15
    16    SetHealth(Health - Damage);
    17
    18    if (IsDead())
    19    {
    20        UE_LOG(LogTemp, Display, TEXT("character dead"));
    21        Killed(InstigatedBy);
    22        OnDeath.Broadcast();
    23    }
    24    else
    25    {
    26        StartHealTimer();
    27    }
    28
    29    PlayCameraShake();
    30}
  2. 在OnTakeAnyDamage输出伤害数值

    ShootThemUp: Components/STUHealthComponent.cpp
    1void USTUHealthComponent::OnTakeAnyDamage(AActor *DamagedActor, float Damage, const class UDamageType *DamageType,
    2                                          class AController *InstigatedBy, AActor *DamageCauser)
    3{
    4    UE_LOG(LogHealthComponent, Display, TEXT("On any damage: %f"), Damage);
    5}

API导航


FTakeRadialDamageSignature

半径伤害委托类型定义

1DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_SevenParams( FTakeRadialDamageSignature, AActor, OnTakeRadialDamage, AActor*, DamagedActor, float, Damage, const class UDamageType*, DamageType, FVector, Origin, FHitResult, HitInfo, class AController*, InstigatedBy, AActor*, DamageCauser );

处理函数签名

1void OnTakeRadialDamage(AActor* DamagedActor, float Damage, const class UDamageType* DamageType, FVector Origin, FHitResult HitInfo, class AController* InstigatedBy, AActor* DamageCauser);

FTakePointDamageSignature

点伤害委托类型定义

1DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_NineParams( FTakePointDamageSignature, AActor, OnTakePointDamage, AActor*, DamagedActor, float, Damage, class AController*, InstigatedBy, FVector, HitLocation, class UPrimitiveComponent*, FHitComponent, FName, BoneName, FVector, ShotFromDirection, const class UDamageType*, DamageType, AActor*, DamageCauser );

处理函数签名

1void OnTakePointDamage(AActor* DamagedActor, float Damage, class AController* InstigatedBy, FVector HitLocation, class UPrimitiveComponent* FHitComponent, FName BoneName, FVector ShotFromDirection, const class UDamageType* DamageType, AActor* DamageCauser);

FDamageEvent

多个构造函数

  • 默认构造函数
  • 拷贝构造函数: 传递伤害类型
    1FDamageEvent(FDamageEvent const& InDamageEvent)
    2    : DamageTypeClass(InDamageEvent.DamageTypeClass)
    3{ }
  • 接受伤害类型的构造函数
    1explicit FDamageEvent(TSubclassOf<UDamageType> InDamageTypeClass)
    2    : DamageTypeClass(InDamageTypeClass)
    3{ }

FPointDamageEvent::HitInfo

包含击中信息

1/** Describes the trace/location that caused this damage */
2UPROPERTY()
3FHitResult HitInfo;

FHitResult

击中信息

-
PhysMaterial 击中部位材质
BoneName 骨骼网格体骨骼名称
 1/**
 2 * Physical material that was hit.
 3 * @note Must set bReturnPhysicalMaterial on the swept PrimitiveComponent or in the query params for this to be returned.
 4 */
 5UPROPERTY()
 6TWeakObjectPtr<UPhysicalMaterial> PhysMaterial;
 7
 8/** Name of bone we hit (for skeletal meshes). */
 9UPROPERTY()
10FName BoneName;

UPrimitiveComponent::GetBodyInstance

传入骨骼网格体的骨骼名称, 获取骨骼对应身体部位

 1/**
 2 * Returns BodyInstance of the component.
 3 *
 4 * @param BoneName				Used to get body associated with specific bone. NAME_None automatically gets the root most body
 5 * @param bGetWelded				If the component has been welded to another component and bGetWelded is true we return the single welded BodyInstance that is used in the simulation
 6 * @param Index					Index used in Components with multiple body instances
 7 *
 8 * @return		Returns the BodyInstance based on various states (does component have multiple bodies? Is the body welded to another body?)
 9 */
10virtual FBodyInstance* GetBodyInstance(FName BoneName = NAME_None, bool bGetWelded = true, int32 Index = INDEX_NONE) const;

UBodyInstance::GetSimplePhysicalMaterial

返回该部位使用的物理材质

1/** Find the correct PhysicalMaterial for simple geometry on this body */
2UPhysicalMaterial* GetSimplePhysicalMaterial() const;

为榴弹爆炸伤害注册半径伤害委托

  1. 添加处理函数

    private

    ShootThemUp: Components/STUHealthComponent.h
    1UFUNCTION()
    2void OnTakeRadialDamage(AActor* DamagedActor, float Damage, const class UDamageType* DamageType, FVector Origin, FHitResult HitInfo, class AController* InstigatedBy, AActor* DamageCauser);
    ShootThemUp: Components/STUHealthComponent.cpp
    1void USTUHealthComponent::OnTakeRadialDamage(AActor* DamagedActor, float Damage, const class UDamageType* DamageType, FVector Origin, FHitResult HitInfo, class AController* InstigatedBy, AActor* DamageCauser)
    2{
    3    UE_LOG(LogHealthComponent, Display, TEXT("On radial damage: %f"), Damage);
    4    ApplyDamage(Damage, InstigatedBy);
    5}
  2. 注册委托处理函数

    ShootThemUp: Components/STUHealthComponent.cpp
    1// BeginPlay
    2
    3TheOwner->OnTakeRadialDamage.AddDynamic(this, &USTUHealthComponent::OnTakeRadialDamage);

为子弹击中伤害注册点伤害委托


传递伤害类型和击中信息

ShootThemUp: Weapon/STURifleWeapon.cpp

1#include "PhysicalMaterials/PhysicalMaterial.h"
2
3// MakeDamage
4
5FPointDamageEvent PointDamageEvent;
6PointDamageEvent.HitInfo = HitResult;
7UE_LOG(LogTemp, Display, TEXT("Rifle hit: bone %s, physical materials %s"), *HitResult.BoneName.ToString(), *HitResult.PhysMaterial->GetName());
8
9DamagedActor->TakeDamage(DamageAmount, PointDamageEvent, GetController(), this);

注册委托

  1. 添加属性: 物理材质和伤害值关联

    protected

    ShootThemUp: Components/STUHealthComponent.h
    1class UPhysicalMaterial;
    2
    3UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
    4TMap<UPhysicalMaterial *, float> DamageModifiers;
  2. 添加接口: 返回伤害系数

    private

    ShootThemUp: Components/STUHealthComponent.h
    1float GetPointDamageModifier(AActor *DamageActor, const FName &BoneName);
    ShootThemUp: Components/STUHealthComponent.cpp
     1// #include "GameFramework/Actor.h"
     2// #include "GameFramework/Pawn.h"
     3#include "GameFramework/Character.h"
     4#include "Components/SkeletalMeshComponent.h"
     5#include "PhysicalMaterials/PhysicalMaterial.h"
     6
     7float USTUHealthComponent::GetPointDamageModifier(AActor *DamageActor, const FName &BoneName)
     8{
     9    const auto Character = Cast<ACharacter>(DamageActor);
    10    if (!Character || //
    11        !Character->GetMesh() || //
    12        !Character->GetMesh()->GetBodyInstance(BoneName)) return 1.0f;
    13
    14    const auto PhysMaterial = Character->GetMesh()->GetBodyInstance(BoneName)->GetSimplePhysicalMaterial();
    15
    16    if (!PhysMaterial || !DamageModifiers.Contains(PhysMaterial)) return 1.0f;
    17
    18    UE_LOG(LogHealthComponent, Display, TEXT("Point damage: bone %s, physical materials %s"), *BoneName.ToString(), *PhysMaterial->GetName());
    19
    20    return DamageModifiers[PhysMaterial];
    21}
  3. 添加处理函数

    private

    ShootThemUp: Components/STUHealthComponent.h
    1UFUNCTION()
    2void OnTakePointDamage(AActor* DamagedActor, float Damage, class AController* InstigatedBy, FVector HitLocation, class UPrimitiveComponent* FHitComponent, FName BoneName, FVector ShotFromDirection, const class UDamageType* DamageType, AActor* DamageCauser);
    ShootThemUp: Components/STUHealthComponent.cpp
    1void USTUHealthComponent::OnTakePointDamage(AActor *DamagedActor, float Damage, class AController *InstigatedBy, FVector HitLocation,
    2                                            class UPrimitiveComponent *FHitComponent, FName BoneName, FVector ShotFromDirection,
    3                                            const class UDamageType *DamageType, AActor *DamageCauser)
    4{
    5    const auto FinalDamage = GetPointDamageModifier(DamagedActor, BoneName) * Damage;
    6
    7    UE_LOG(LogHealthComponent, Display, TEXT("On point damage: %f, final damage %f, bone: %s"), Damage, FinalDamage, *BoneName.ToString());
    8    ApplyDamage(FinalDamage, InstigatedBy);
    9}
  4. 注册委托处理函数

    ShootThemUp: Components/STUHealthComponent.cpp
    1// BeginPlay
    2
    3TheOwner->OnTakePointDamage.AddDynamic(this, &USTUHealthComponent::OnTakePointDamage);

调整游戏角色身体物理材质

当前游戏角色身体拥有2种物理材质

Content/PhysMaterials
头部 PhysMat_Head
身体 PhysMat_Body
  1. 新增物理材质

    Content/PhysMaterials
    手部 PhysMat_Hand
    腿部 PhysMat_Leg
  2. 应用新增物理材质

    HeroTPP_Physics

    善用属性拷贝粘贴



查看

  1. 为游戏角色设置子弹击中伤害系数

    善用属性拷贝粘贴

    伤害系数
    头部 百倍伤害, 击杀
    身体 1.0
    腿部 1.1
    手部 0.8
    • 玩家操控的游戏角色


    • NPC


  2. 暂停NPC行为树

    BP_STUCharacter

  3. 将回合时长设置为300s

    BP_STUGameModeBase

  4. 日志

    依次击中腿部, 手部, 身体, 头部



说明

爆头应该直接导致死亡, 即检测到击中部位物理材质为PhysMat_Head, 直接设置生命值为0