击中游戏角色身体不同部位造成伤害值不同
2023年12月13日 2024年1月11日
Headshot
回顾打出伤害
有两种伤害:
- 榴弹爆炸
- 子弹击中
榴弹爆炸伤害
STUProjectile
- 在BeginPlay注册委托, 发生碰撞时调用OnProjectileHit
- OnProjectileHit中调用ApplyRadialDamage, ApplyRadialDamage调用ApplyRadialDamageWithFalloff
- ApplyRadialDamageWithFalloff将爆炸波及者整理, 依次对其调用TakeDamage
1Victim->TakeDamage(BaseDamage, DmgEvent, InstigatedByController, DamageCauser);
子弹击中伤害
STURifleWeapon
若有击中对象, 对其调用TakeDamage
1DamagedActor->TakeDamage(DamageAmount, FDamageEvent(), GetController(), this);
AActor::TakeDamage
- 会根据DamageEvent类型造成不同伤害, 有三种:
- 点伤害
- 半径伤害
- 其他类型伤害
- 点伤害
- 目前, 子弹击中伤害为其他类型伤害, 榴弹爆炸伤害为半径伤害; 但计算伤害走的均是其他类型伤害
- 三种伤害对应委托成员
- 点伤害 AActor::OnTakePointDamage 半径伤害 AActor::OnTakeRadialDamage 其他类型伤害 AActor::OnTakeAnyDamage - 只要伤害值不为0, 均会触发OnTakeAnyDamage的回调函数; 处理特定类型伤害, 需注册对应类型委托
子弹击中伤害类型应该为点伤害
点伤害的处理函数形参列表中, 包含击中部位, 即骨骼网格体中骨骼名称
榴弹爆炸伤害类型为半径伤害时
计算伤害逻辑应该注册半径伤害对应委托
迁移受伤逻辑
- 将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}
- 在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;
为榴弹爆炸伤害注册半径伤害委托
- 添加处理函数
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}
- 注册委托处理函数
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);
注册委托
- 添加属性: 物理材质和伤害值关联
protected
ShootThemUp: Components/STUHealthComponent.h
1class UPhysicalMaterial; 2 3UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 4TMap<UPhysicalMaterial *, float> DamageModifiers;
- 添加接口: 返回伤害系数
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}
- 添加处理函数
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}
- 注册委托处理函数
ShootThemUp: Components/STUHealthComponent.cpp
1// BeginPlay 2 3TheOwner->OnTakePointDamage.AddDynamic(this, &USTUHealthComponent::OnTakePointDamage);
调整游戏角色身体物理材质
当前游戏角色身体拥有2种物理材质
Content/PhysMaterials | |
---|---|
头部 | PhysMat_Head |
身体 | PhysMat_Body |
-
新增物理材质
Content/PhysMaterials 手部 PhysMat_Hand 腿部 PhysMat_Leg -
应用新增物理材质
HeroTPP_Physics
善用属性拷贝粘贴
查看
-
为游戏角色设置子弹击中伤害系数
善用属性拷贝粘贴伤害系数 头部 百倍伤害, 击杀 身体 1.0 腿部 1.1 手部 0.8 -
玩家操控的游戏角色
-
NPC
-
-
暂停NPC行为树
BP_STUCharacter
-
将回合时长设置为300s
BP_STUGameModeBase
-
日志
依次击中腿部, 手部, 身体, 头部
说明
爆头应该直接导致死亡, 即检测到击中部位物理材质为PhysMat_Head, 直接设置生命值为0