微信网站建设公司,建c2c网站,哈尔滨建设网官方网站,网站搭建原理受击反馈HitReact
设置角色受击标签
(GameplayTag基本了解待补充) 角色监听标签并设置移动速度 创建一个受击技能#xff0c;并应用GE 实现设置角色的受击蒙太奇动画 实现角色受击时播放蒙太奇动画#xff0c;为了保证通用性#xff0c;将其设置为一个函数#xff0c;并…受击反馈HitReact
设置角色受击标签
(GameplayTag基本了解待补充) 角色监听标签并设置移动速度 创建一个受击技能并应用GE 实现设置角色的受击蒙太奇动画 实现角色受击时播放蒙太奇动画为了保证通用性将其设置为一个函数并设置到战斗接口中这样只需要在战斗接口中获取对应角色的蒙太奇即可。每个角色的受击动画不一定一样 UCLASS()
class AURA_API AAuraCharacterBase : public ACharacter, public IAbilitySystemInterface, public ICombatInterface
{GENERATED_BODY()public://HitReactvirtual UAnimMontage* GetHitReactMontage_Implementation() override;private:UPROPERTY(EditAnywhere, Category Combat)TObjectPtrUAnimMontage HitReactMontage;
};UAnimMontage* AAuraCharacterBase::GetHitReactMontage_Implementation()
{return HitReactMontage;
}
别忘了设置对应的HitReact_AM 激活受击技能
在CharacterClassInfo.h里增加一个参数用于设置创建敌人时所拥有的初始技能 在函数技能库里新增一个函数用于初始化角色技能
UCLASS()
class AURA_API UMyASBlueprintFunctionLibrary : public UBlueprintFunctionLibrary
{GENERATED_BODY()
public://初始化角色的技能UFUNCTION(BlueprintCallable, CategoryMyBlueprintFunctionLibrary|CharacterClassDefaults)static void GiveStartupAbilities(const UObject* WorldContextObject, UAbilitySystemComponent* ASC);};void UMyASBlueprintFunctionLibrary::GiveStartupAbilities(const UObject* WorldContextObject, UAbilitySystemComponent* ASC)
{//获取到当前关卡的GameMode实例const AAuraGameModeBase* GameMode CastAAuraGameModeBase(UGameplayStatics::GetGameMode(WorldContextObject));if(GameMode nullptr) return;//从实例获取到关卡角色的配置UCharacterClassInfo* CharacterClassInfo GameMode-CharacterClassInfo;//遍历角色拥有的技能数组for(const TSubclassOfUGameplayAbility AbilityClass : CharacterClassInfo-CommonAbilities){FGameplayAbilitySpec AbilitySpec FGameplayAbilitySpec(AbilityClass, 1); //创建技能实例ASC-GiveAbility(AbilitySpec); //只应用不激活}
}
在敌人基类里开始事件时我们调用函数库的初始化技能函数将技能应用到角色身上
void AAuraEnemy::BeginPlay()
{Super::BeginPlay();InitAbilityActorInfo();//初始化角色的技能UMyASBlueprintFunctionLibrary::GiveStartupAbilities(this, AbilitySystemComponent);...
}
在PostGameplayEffectExecute函数里之前设置血量下面有判断我们在角色没有被击杀时让其触发受击技能。
void UAuraAttributeSet::PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData Data)
{...//查看IncomingDamage修改的是否为元属性if(Data.EvaluatedData.Attribute GetIncomingDamageAttribute()){//获取到元属性的值备用并将属性集上的值设置为0等待下一次设置。const float LocalIncomingDamage GetIncomingDamage();SetIncomingDamage(0.f);if(LocalIncomingDamage 0.f)//伤害传入的时机{const float NewHealth GetHealth() - LocalIncomingDamage; //受到伤害后的新生命值SetHealth(FMath::Clamp(NewHealth, 0.f, GetMaxHealth())); //设置新的生命值const bool bFatal NewHealth 0.f; //血量小于等于0时角色将会死亡 致命的(Fatal)if(!bFatal) //在角色没有被击杀时让其触发受击技能。{FGameplayTagContainer TagContainer;TagContainer.AddTag(FMyGameplayTags::Get().Effects_HitReact); //添个眼 这里原本是Effects.HitReact 对应GA一样 我是修改了Props.TargetASC-TryActivateAbilitiesByTag(TagContainer); //根据tag标签激活技能}}} 接下来是蓝图中的设置已做修改 我们将使用标签激活技能所以还需要将受击标签设置给技能
我们不需要在每次激活时创建一个新的实例只需要一个角色生成一个实例重复利用即可。 由于它一个角色对应一个单例每次触发都是相同的实例所以我们需要在其播放完成后将其结束才可以重新触发然后在敌人的受击动画播放完成后我们需要将敌人身上的受击标签清楚如果GE添加的只需要将对应的GE清除标签也会随着清除并结束技能。 死亡效果Death/Dissolve 我们实现了敌人受到攻击后会播放受击动画并且还给角色设置了受击标签。并在角色受击时在角色身上挂上受击标签在c里如果挂载了此标签速度将降为0 。 受击有了接下来我们将实现角色的死亡逻辑角色血量为0或者小于0时我们将触发它的死亡功能
实现死亡
在战斗接口类里增加一个纯虚函数无法创建函数的实现必须在子类里面去覆写它。 virtual void Die() 0;
在角色基类里面覆写
virtual void Die() override;
接着我们增加一个在每个客户端上执行的函数被Die函数调用。 NetMulticast设置后这个函数被调用时将在服务器执行然后复制到每个客户端。和它对应的还有Server只在服务器运行Client只在调用此函数的客户端运行这种情况的函数实现需要在后面加上_Implementation Reliable: 这是一个传输属性表示该函数的数据应该以可靠的方式发送。 UFUNCTION(NetMulticast, Reliable)virtual void MulticastHandleDeath();void AAuraCharacterBase::Die()
{//将武器从角色身上分离(Detach)Weapon-DetachFromComponent(FDetachmentTransformRules(EDetachmentRule::KeepWorld, true));MulticastHandleDeath(); //NetMulticast在服务器执行然后复制到每个客户端()
}void AAuraCharacterBase::MulticastHandleDeath_Implementation()
{//开启武器物理效果Weapon-SetSimulatePhysics(true); //开启模拟物理效果Weapon-SetEnableGravity(true); //开启重力效果Weapon-SetCollisionEnabled(ECollisionEnabled::PhysicsOnly); //开启物理碰撞通道//开启角色物理效果GetMesh()-SetSimulatePhysics(true); //开启模拟物理效果GetMesh()-SetEnableGravity(true); //开启重力效果GetMesh()-SetCollisionEnabled(ECollisionEnabled::PhysicsOnly); //开启物理碰撞通道GetMesh()-SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block); //开启角色与静态物体产生碰撞//关闭角色碰撞体碰撞通道避免其对武器和角色模拟物理效果产生影响GetCapsuleComponent()-SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
在敌人基类里面也覆盖Die函数并在死亡时设置它的清除时间 UPROPERTY(EditAnywhere, BlueprintReadOnly, CategoryCombat)float LifeSpan 5.f; //设置死亡后的存在时间void AAuraEnemy::Die()
{SetLifeSpan(LifeSpan); //在Enemy死亡后尸体会存在五秒 对象不再需要时会自动销毁Super::Die();
}
接下来在AttributeSet的PostGameplayEffectExecute函数里增加Die函数调用的逻辑处理我们在死亡时调用即可
void UAuraAttributeSet::PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData Data)
{。。。//查看IncomingDamage修改的是否为元属性if(Data.EvaluatedData.Attribute GetIncomingDamageAttribute()){//获取到元属性的值备用并将属性集上的值设置为0等待下一次设置。const float LocalIncomingDamage GetIncomingDamage();SetIncomingDamage(0.f);if(LocalIncomingDamage 0.f)//伤害传入的时机{const float NewHealth GetHealth() - LocalIncomingDamage; //受到伤害后的新生命值SetHealth(FMath::Clamp(NewHealth, 0.f, GetMaxHealth())); //设置新的生命值const bool bFatal NewHealth 0.f; //血量小于等于0时角色将会死亡 致命的(Fatal)if(bFatal){//调用死亡函数ICombatInterface* CombatInterface CastICombatInterface(Props.TargetAvatarActor);if(CombatInterface){CombatInterface-Die();}}else{在角色没有被击杀时让其触发受击技能。FGameplayTagContainer TagContainer;TagContainer.AddTag(FMyGameplayTags::Get().Abilities_HitReact); //添个眼 这里原本是Effects.HitReact 对应GA一样 我是修改了Props.TargetASC-TryActivateAbilitiesByTag(TagContainer); //根据tag标签激活技能}}}
测试效果 溶解材质(目前渲染方面不做了解)
小怪死亡定时直接清除掉显得太突兀大部分游戏中的做法就是使用溶解效果来实现它的缓慢消失的效果。
实现溶解效果 实现从普通材质切换MI到溶解材质并实现通过程序修改Dissolve溶解的标量Scalar Parameter参数数值。步骤 增加两个变量用于设置角色和武器的溶解材质 增加一个溶解函数在角色死亡时调用 溶解是需要一个时间过程所以可以在蓝图里面实现时间轴所以增加一个蓝图可实现函数这个函数在代码里调用在蓝图实现。
// YanWei.UCLASS()
class AURA_API AAuraCharacterBase : public ACharacter, public IAbilitySystemInterface, public ICombatInterface
{GENERATED_BODY()public://--ICombatInterfacevirtual UAnimMontage* GetHitReactMontage_Implementation() override;//HitReactvirtual void Die() override; //Death OnServer 服务器端 UFUNCTION(NetMulticast, Reliable) //NetMulticast:在服务器执行然后复制到每个客户端virtual void MulticastHandleDeath();protected://--Dissolve Effect 实现思路:是将avatar和weapon的材质叫换成下面的//溶解函数void Dissolve(); //蓝图可实现事件UFUNCTION(BlueprintImplementableEvent)void StartDissolveTimeline(const TArrayUMaterialInstanceDynamic* DynamicMaterialInstance); //接受材质实例dyn 数组用于存储材质sUPROPERTY(EditAnywhere,BlueprintReadOnly)TObjectPtrUMaterialInstance DissolveMaterialInstance; //对应的材质实例UPROPERTY(EditAnywhere,BlueprintReadOnly)TObjectPtrUMaterialInstance WeaponDissolveMaterialInstance;//};//cppvoid AAuraCharacterBase::Dissolve()
{TArrayUMaterialInstanceDynamic* DynMatArray;if (DissolveMaterialInstance){UMaterialInstanceDynamic* DynamicMatInst UMaterialInstanceDynamic::Create(DissolveMaterialInstance, this);GetMesh()-SetMaterial(0, DynamicMatInst);DynMatArray.Add(DynamicMatInst);}if (WeaponDissolveMaterialInstance){UMaterialInstanceDynamic* DynamicMatInst UMaterialInstanceDynamic::Create(WeaponDissolveMaterialInstance, this);Weapon-SetMaterial(0, DynamicMatInst);DynMatArray.Add(DynamicMatInst);}StartDissolveTimeline(DynMatArray);
}void AAuraCharacterBase::Die()
{//将武器从角色身上分离(Detach)Weapon-DetachFromComponent(FDetachmentTransformRules(EDetachmentRule::KeepWorld, true));MulticastHandleDeath(); //NetMulticast在服务器执行然后复制到每个客户端()
}void AAuraCharacterBase::MulticastHandleDeath_Implementation()
{...//设置角色溶解Dissolve();
}打开UE在敌人基类里面覆写StartDissolveTimeline 增加线结点Add Reroute) 创建一个时间轴 Timeline 并点击它 Set Scalar Parameter Value 效果图
伤害值浮动FloatingText 实现技能或者攻击对敌人造成的伤害数值并直观的显示出来。接下来还要实现一个用户控件来设置伤害数值的UI表现。并且还需要一个用户控件的组件来修改伤害UI显示的数值。在创建完成这些以后我们还需要实现从AttributeSet里面实现在目标位置也就是受到伤害的角色位置创建组件并实现在每个客户端上面显示数值。
创建Damage用户控件
新建一个控件蓝图这里不需要其它脚本直接基于UserWidget创建即可。我们将其命名为WBP_DamageText 在控件里面增加一个覆层覆层下面添加一个文本显示文字 在控件尺寸这里我们使用自定义来设置一个固定的尺寸这样它的尺寸不会乱动不会在更新时频繁修改尺寸影响性能。 然后设置文本的样式并将其设置为变量方便后期修改其显示的内容 创建伤害文字动画 (视觉反馈很重要)
动画创建完成后在事件构造时调用动画播放 创建用户控件组件
用户控件创建完成我们还需要创建用户控件组件用于实现用户控件的实例化并播放动画。 所以我们接下来要创建一个用户组件的控件创建基于最基本的组件即可我们不需要从控制器获取内容只需要一个设置显示数字的方法即可。命名为DamageTextComponent 增加一个函数设置伤害的数值可以在蓝图内实现此函数并且可以在蓝图内调用 基于此类创建一个蓝图类命名为BP_DamageTextComponent 现在这个蓝图相当于一个未挂载在对象上面的组件类但是我们可以设置它的默认参数。我们将空间修改为屏幕空间并将创建的用户控件设置上去 在蓝图内我们实现SetDamageText将设置的类型转换为我们创建的用户组件类型调用它的更新数字方法然后在延迟一秒钟动画播放一秒钟后将自身销毁组件也会跟随销毁。 其中 UpdateDamageText 鼠标处设0是为了只显示整数部分
调用实现伤害显示
现在我们有了显示伤害数值的用户控件和组件接下来就要考虑实现如何调用并显示对应的内容。 现在有个问题就是我们是通过在AttributeSet里面通过元属性获取到当前的伤害的它是在服务器上运行的因此我们要实现在AttributeSet里面去调用函数生成并且还需要获取到玩家控制器因为你需要将数值显示给玩家查看得所以我们将数值在玩家控制器里面实现在每个客户端上面调用是一个很好的选择。 所以我们打开玩家控制器的基类在里面增加一个函数用于复制到每个客户端并且只会在客户端执行所以我们设置了Client 只在客户端执行 在函数内部实现在目标角色身上显示伤害数字。
制作一个客户端RPC函数如果在服务端调用它它将在服务端执行如果玩家控制器是本地Local的话但是如果玩家控制器是Remote远程的话这个函数将被执行在remote远程的客户端
// YanWei.class UDamageTextComponent;/*** */
UCLASS()
class AURA_API AAuraPlayerController : public APlayerController
{GENERATED_BODY()
public:AAuraPlayerController();virtual void PlayerTick(float DeltaTime) override;//客户端RPC函数UFUNCTION(Client, Reliable)void ShowDamageNumber(float DamageAmount, ACharacter* TargetCharacter); //在每个客户端显示伤害数值private:。。。 //DamageText 创建一个用于设置显示伤害数值的组件类后续可以使用它去实例化多个实例显示多个伤害数值UPROPERTY(EditDefaultsOnly)TSubclassOfUDamageTextComponent DamageTextComponentClass;};// 客户端的具体实现
void AAuraPlayerController::ShowDamageNumber_Implementation(float DamageAmount,ACharacter* TargetCharacter)
{if(IsValid(TargetCharacter) DamageTextComponentClass){//在内部实例化组件并注册在实例化时第一个参数相当于作为此组件的父类当PlayerController被销毁时它也会被销毁。UDamageTextComponent* DamageText NewObjectUDamageTextComponent(TargetCharacter, DamageTextComponentClass);DamageText-RegisterComponent(); //动态创建的组件需要调用注册DamageText-AttachToComponent(TargetCharacter-GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform); //先附加到角色身上使用角色位置DamageText-DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform); //然后从角色身上分离保证在一个位置播放完成动画DamageText-SetDamageText(DamageAmount); //设置显示的伤害数字}
}// YanWei./*** */
UCLASS()
class AURA_API UAuraAttributeSet : public UAttributeSet
{GENERATED_BODY()
protected://用于服务器端调用客户端RPC函数在客户端执行static void ShowFloatingText(const FEffectProperties Props, const float Damage);
};// 服务器调用客户端函数的实现
void UAuraAttributeSet::ShowFloatingText(const FEffectProperties Props, const float Damage)
{//调用显示伤害数字if(Props.SourceCharacter ! Props.TargetCharacter){if(AAuraPlayerController* PC CastAAuraPlayerController(UGameplayStatics::GetPlayerController(Props.SourceCharacter, 0))){//调用客户端RPC函数PC-ShowDamageNumber(Damage, Props.TargetCharacter); //调用显示伤害数字}}
}void UAuraAttributeSet::PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData Data)
{。。。if(LocalIncomingDamage 0.f)//伤害传入的时机{。。。//TODO::设置显示float伤害值的区域ShowFloatingText(Props, LocalIncomingDamage);// 服务端请求客户端执行}} 在BP_Controller控制器中设置bpCompDamageText 测试结果
每个客户端只能看到自己所造成的浮动伤害值其他客户端看不到别的客户端(也包括监听服务器端)造成浮动伤害值而(监听)服务端可以看到所有的浮动伤害值。