UE4自带的Actor伤害系统

关注
UE4自带的Actor伤害系统www.shan-machinery.com简介

伤害(Damage)是游戏中常见的概念,几乎所有的游戏都具有伤害,难怪UE在Actor中自带了承担伤害的方法。

如果让开发者自己写一套伤害系统,第一个想法应该是通过Interface或Actor组件去承担伤害逻辑,不会想到UE中已经提供了一个简单的伤害逻辑。

伤害的代码主要在Actor.h中,此外GameplayStatics.h封装了Actor的TakeDamage方法。没想到的是,Controller和PrimitiveComponent中也有Damage相关的代码,Controller可获得造成伤害时的事件,PrimitiveComponent可在接受伤害时应用冲击力。

对Actor应用伤害的方法// Actor.hvirtual float TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, AActor* DamageCauser);// GameplayStatics.hstatic bool ApplyRadialDamage(const UObject* WorldContextObject, float BaseDamage, const FVector& Origin, float DamageRadius, TSubclassOf DamageTypeClass, const TArray& IgnoreActors, AActor* DamageCauser = NULL, AController* InstigatedByController = NULL, bool bDoFullDamage = false, ECollisionChannel DamagePreventionChannel = ECC_Visibility);static bool ApplyRadialDamageWithFalloff(const UObject* WorldContextObject, float BaseDamage, float MinimumDamage, const FVector& Origin, float DamageInnerRadius, float DamageOuterRadius, float DamageFalloff, TSubclassOf DamageTypeClass, const TArray& IgnoreActors, AActor* DamageCauser = NULL, AController* InstigatedByController = NULL, ECollisionChannel DamagePreventionChannel = ECC_Visibility);static float ApplyPointDamage(AActor* DamagedActor, float BaseDamage, const FVector& HitFromDirection, const FHitResult& HitInfo, AController* EventInstigator, AActor* DamageCauser, TSubclassOf DamageTypeClass);static float ApplyDamage(AActor* DamagedActor, float BaseDamage, AController* EventInstigator, AActor* DamageCauser, TSubclassOf DamageTypeClass);

Actor中提供了蓝图实现的事件,可用于触发减少生命值、死亡等逻辑。//Actor.h/** Event when this actor takes ANY damage */void ReceiveAnyDamage(float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);/** Event when this actor takes RADIAL damage */void ReceiveRadialDamage(float DamageReceived, const class UDamageType* DamageType, FVector Origin, const struct FHitResult& HitInfo, class AController* InstigatedBy, AActor* DamageCauser);/** Event when this actor takes POINT damage */void ReceivePointDamage(float Damage, const class UDamageType* DamageType, FVector HitLocation, FVector HitNormal, class UPrimitiveComponent* HitComponent, FName BoneName, FVector ShotFromDirection, class AController* InstigatedBy, AActor* DamageCauser, const FHitResult& HitInfo);

此外,Actor中还提供了对应的伤害委托事件,开发者可监听这些伤害事件。// Actor.h/** Called when the actor is damaged in any way. */UPROPERTY(BlueprintAssignable, Category="Game|Damage")FTakeAnyDamageSignature OnTakeAnyDamage;/** Called when the actor is damaged by point damage. */UPROPERTY(BlueprintAssignable, Category="Game|Damage")FTakePointDamageSignature OnTakePointDamage;/** Called when the actor is damaged by radial damage. */UPROPERTY(BlueprintAssignable, Category="Game|Damage")FTakeRadialDamageSignature OnTakeRadialDamage;

Actor中有两个变量与伤害相关,bCanBeDamaged决定该Actor是否可接受伤害,Instigator则用于伤害逻辑,后面介绍。// Actor.h/** Whether this actor can take damage. Must be true for damage events. */uint8 bCanBeDamaged:1;/** Pawn responsible for damage and other gameplay events caused by this actor. */class APawn* Instigator;伤害的几个重要概念DamageCauser

直接造成该次伤害的Actor,比如子弹、炸弹、刀等。如果是喝了毒药掉血,可以把DamageCauser设为自己,也可以设为null。

Insigator

Instigator(伤害发起者),可用于伤害逻辑,表示该Actor对其它Actor照成伤害后,负责该次伤害的Pawn。

比如该Actor是一颗子弹,则需要将子弹的Instigator设置为枪的拥有者,对方Actor在接受到伤害后就知道该伤害是由谁负责,所以Instigator的类型是Pawn。这是最常见的一种情况,想一下如果没有Instigator,在做FPS游戏的时候,查找击杀者可能就有些绕了。

调用ApplyDamage方法,需要传递的是Insigator的Controller而不是Insigator。DamageType(伤害类型)

DamageType表示该伤害的类型,比如火属性、冰属性等。

一般游戏中可能会使用枚举来表示不同的伤害类型,而UE中则使用了单独的类来作为伤害类型,麻烦之处在于有多少个伤害类型就要创建多少个UDamageType的蓝图(不推荐直接使用C++类),优点则是可以创建复杂的伤害类型。

记住,直接使用的是UDamageType的class而不是对象,不应该为DamageType创建实例,一个class只能表示一种伤害类型。

3种不同的伤害种类

UE提供了3中不同的伤害事件,PointDamage (点伤害)、RadialDamage(辐射伤害)、Damage(普通伤害)。

PointDamage (点伤害)

点伤害算是最常见的伤害事件,一般通过Overlap或Hit事件触发,比如子弹击中、武器攻击、陷阱等,都可使用PointDamage来应用伤害。

HitFromDirection:该次击打的方向,比如子弹运动方向。HitInfo:FHitResult,Overlap或Hit事件的HitResult。RadialDamage(辐射伤害)

辐射伤害主要用于处理爆炸物的伤害,它有一个点和半径,生成一个球并检测该球内所有的Overlap物体,判断物体是否可接受伤害(是否被其它物体阻挡、是否在IgnoreActors内)并应用辐射伤害,所以ApplyRadialDamage没有固定的DamagedActor参数。

Origin:球心DamageRadius:球半径IgnoreActors:忽略这些Actor,不对其照成伤害DoFullDamage:如果为true,则伤害不会衰减DamagePreventionChannel:用于物体到球心的射线检测,Visibility表示可见的物体会阻挡这次伤害MinimunDamage:即使应用衰减,伤害也不会低于此值DamageInnerRadius:伤害衰减起始半径DamageOuterRadius:伤害衰减终止半径,也是最大半径DamageFallOff:伤害衰减指数,为0表示不衰减Damage(普通伤害)

普通伤害不会附带HitResult等额外信息,可用于中毒、出血等buff带来的简单伤害,通常在伤害不会造成物理表现的时候使用。

与伤害有关的其它类Controller

在Controller控制的Pawn作为Instigator向其它Actor应用伤害时,该Controller也会得到一个事件通知。

// Controller.cppvoid AController::InstigatedAnyDamage(float Damage, const class UDamageType* DamageType, class AActor* DamagedActor, class AActor* DamageCauser){ReceiveInstigatedAnyDamage(Damage, DamageType, DamagedActor, DamageCauser);OnInstigatedAnyDamage.Broadcast(Damage, DamageType, DamagedActor, DamageCauser);}PrimitiveComponent

物理组件在bApplyImpulseOnDamage设为true时,受到点伤害或辐射伤害时会添加一个冲击力的表现。

// PrimitiveComponent.huint8 bApplyImpulseOnDamage : 1;// PrimitiveComponent.cppvoid UPrimitiveComponent::ReceiveComponentDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser){if (bApplyImpulseOnDamage){UDamageType const* const DamageTypeCDO = DamageEvent.DamageTypeClass ? DamageEvent.DamageTypeClass->GetDefaultObject() : GetDefault();if (DamageEvent.IsOfType(FPointDamageEvent::ClassID)){FPointDamageEvent* const PointDamageEvent = (FPointDamageEvent*)&DamageEvent;if ((DamageTypeCDO->DamageImpulse > 0.f) && !PointDamageEvent->ShotDirection.IsNearlyZero()){if (IsSimulatingPhysics(PointDamageEvent->HitInfo.BoneName)){FVector const ImpulseToApply = PointDamageEvent->ShotDirection.GetSafeNormal() * DamageTypeCDO->DamageImpulse;AddImpulseAtLocation(ImpulseToApply, PointDamageEvent->HitInfo.ImpactPoint, PointDamageEvent->HitInfo.BoneName);}}}else if (DamageEvent.IsOfType(FRadialDamageEvent::ClassID)){FRadialDamageEvent* const RadialDamageEvent = (FRadialDamageEvent*)&DamageEvent;if (DamageTypeCDO->DamageImpulse > 0.f){AddRadialImpulse(RadialDamageEvent->Origin, RadialDamageEvent->Params.OuterRadius, DamageTypeCDO->DamageImpulse, RIF_Linear, DamageTypeCDO->bRadialDamageVelChange);}}}}

https://www.shan-machinery.com