我们以 Lyra 的架构为基础,创建一个名为 "Aura" 的英雄并实现发射火球技能。我会详细拆解每一步所需的代码,并添加详细注释。
整体架构概览
在 Lyra 中实现这个功能需要以下核心组件:
-
AuraHero- 英雄角色类 -
GA_AuraFireball- 火球技能 Gameplay Ability -
GE_FireballDamage- 火球伤害 Gameplay Effect -
AuraInputConfig- 输入配置数据资产 -
AuraHeroComponent- 英雄组件处理输入
1. 英雄角色类 (AuraHero.h/.cpp)
AuraHero.h
cpp
#pragma once
#include "GameFramework/Character.h"
#include "AbilitySystemInterface.h"
#include "LyraGameplayAbilitySet.h"
#include "AuraHero.generated.h"
class UAbilitySystemComponent;
class ULyraHeroComponent;
class ULyraHealthComponent;
UCLASS()
class AAuraHero : public ACharacter, public IAbilitySystemInterface
{
GENERATED_BODY()
public:
AAuraHero();
// 实现 IAbilitySystemInterface 接口
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
// 初始化英雄的属性和能力
UFUNCTION(BlueprintCallable, Category = "Aura|Abilities")
void InitializeAbilities();
protected:
virtual void BeginPlay() override;
// 能力系统组件 - 负责管理所有 Gameplay Abilities 和 Effects
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Aura|Components")
TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;
// Lyra 英雄组件 - 处理输入和相机等
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Aura|Components")
TObjectPtr<ULyraHeroComponent> HeroComponent;
// 生命值组件
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Aura|Components")
TObjectPtr<ULyraHealthComponent> HealthComponent;
// 能力集合 - 定义英雄初始拥有的能力
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Aura|Abilities")
TObjectPtr<ULyraGameplayAbilitySet> DefaultAbilitySet;
// 属性集 - 定义英雄的属性(生命值、魔法值等)
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Aura|Attributes")
TSubclassOf<UAttributeSet> DefaultAttributeSet;
};
AuraHero.cpp
cpp
#include "AuraHero.h"
#include "AbilitySystemComponent.h"
#include "LyraHeroComponent.h"
#include "LyraHealthComponent.h"
#include "GameplayAbilitySet.h"
AAuraHero::AAuraHero()
{
// 创建 Ability System Component
AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
// 创建 Lyra Hero Component - 处理输入和相机
HeroComponent = CreateDefaultSubobject<ULyraHeroComponent>(TEXT("HeroComponent"));
// 创建生命值组件
HealthComponent = CreateDefaultSubobject<ULyraHealthComponent>(TEXT("HealthComponent"));
// 设置 AbilitySystemComponent 的复制模式
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
}
void AAuraHero::BeginPlay()
{
Super::BeginPlay();
// 初始化属性和能力
InitializeAbilities();
}
UAbilitySystemComponent* AAuraHero::GetAbilitySystemComponent() const
{
return AbilitySystemComponent;
}
void AAuraHero::InitializeAbilities()
{
if (!AbilitySystemComponent)
return;
// 如果指定了默认属性集,则初始化属性
if (DefaultAttributeSet)
{
AbilitySystemComponent->InitStats(DefaultAttributeSet, nullptr);
}
// 如果指定了默认能力集,则授予这些能力
if (DefaultAbilitySet)
{
DefaultAbilitySet->GiveToAbilitySystem(AbilitySystemComponent, nullptr);
}
// 初始化 ASC
AbilitySystemComponent->RefreshAbilityActorInfo();
}
2. 火球技能 Gameplay Ability (GA_AuraFireball.h/.cpp)
GA_AuraFireball.h
cpp
#pragma once
#include "GameplayAbility.h"
#include "LyraGameplayAbility.h"
#include "GA_AuraFireball.generated.h"
class AProjectile;
// 火球技能类
UCLASS()
class UGA_AuraFireball : public ULyraGameplayAbility
{
GENERATED_BODY()
public:
UGA_AuraFireball();
protected:
// 当技能被激活时调用
virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle,
const FGameplayAbilityActorInfo* ActorInfo,
const FGameplayAbilityActivationInfo ActivationInfo,
const FGameplayEventData* TriggerEventData) override;
// 生成火球投射物
UFUNCTION(BlueprintCallable, Category = "Aura|Fireball")
void SpawnFireball();
// 火球投射物类
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Aura|Fireball")
TSubclassOf<AProjectile> FireballProjectileClass;
// 火球发射速度
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Aura|Fireball")
float FireballSpeed = 2000.0f;
// 魔法值消耗
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Aura|Fireball")
float ManaCost = 25.0f;
};
GA_AuraFireball.cpp
cpp
#include "GA_AuraFireball.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "AbilitySystemComponent.h"
#include "AbilitySystemGlobals.h"
#include "LyraGameplayAbility.h"
UGA_AuraFireball::UGA_AuraFireball()
{
// 设置此技能的实例化策略 - 每个激活都有独立实例
InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor;
// 设置网络执行策略 - 在本地预测,在服务器执行
NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::LocalPredicted;
}
void UGA_AuraFireball::ActivateAbility(const FGameplayAbilitySpecHandle Handle,
const FGameplayAbilityActorInfo* ActorInfo,
const FGameplayAbilityActivationInfo ActivationInfo,
const FGameplayEventData* TriggerEventData)
{
// 首先调用父类实现
Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData);
// 检查是否有可用的 Avatar Actor(施法者)
if (!ActorInfo || !ActorInfo->AvatarActor.IsValid())
{
EndAbility(Handle, ActorInfo, ActivationInfo, true, true);
return;
}
// 检查魔法值是否足够(这里简化处理,实际应该检查属性集)
// 在实际项目中,你会通过 Gameplay Effect 来消耗魔法值
// 生成火球
SpawnFireball();
// 技能执行完成,结束技能
EndAbility(Handle, ActorInfo, ActivationInfo, true, false);
}
void UGA_AuraFireball::SpawnFireball()
{
// 获取当前技能的 Actor 信息
const FGameplayAbilityActorInfo* ActorInfo = GetCurrentActorInfo();
if (!ActorInfo || !ActorInfo->AvatarActor.IsValid())
return;
// 获取施法者(Aura)
AActor* OwnerActor = ActorInfo->AvatarActor.Get();
if (!OwnerActor)
return;
// 检查火球类是否有效
if (!FireballProjectileClass)
{
UE_LOG(LogTemp, Error, TEXT("FireballProjectileClass is not set!"));
return;
}
// 计算生成位置和方向
FVector SpawnLocation = OwnerActor->GetActorLocation() + OwnerActor->GetActorForwardVector() * 100.0f;
FRotator SpawnRotation = OwnerActor->GetActorRotation();
// 设置生成参数
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = OwnerActor; // 设置所有者
SpawnParams.Instigator = Cast<APawn>(OwnerActor); // 设置触发者
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
// 在世界中生成火球
if (AProjectile* Fireball = GetWorld()->SpawnActor<AProjectile>(FireballProjectileClass, SpawnLocation, SpawnRotation, SpawnParams))
{
// 设置火球速度
if (UProjectileMovementComponent* MovementComp = Fireball->FindComponentByClass<UProjectileMovementComponent>())
{
MovementComp->Velocity = OwnerActor->GetActorForwardVector() * FireballSpeed;
}
// 这里可以设置火球的伤害值,通过 Gameplay Effect 应用
// 通常会通过接口或组件传递伤害信息
}
}
3. 火球投射物类 (AuraFireballProjectile.h/.cpp)
AuraFireballProjectile.h
cpp
#pragma once
#include "GameFramework/Actor.h"
#include "GameplayEffect.h"
#include "AuraFireballProjectile.generated.h"
class USphereComponent;
class UProjectileMovementComponent;
class UParticleSystemComponent;
UCLASS()
class AAuraFireballProjectile : public AActor
{
GENERATED_BODY()
public:
AAuraFireballProjectile();
protected:
virtual void BeginPlay() override;
// 碰撞组件
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
TObjectPtr<USphereComponent> CollisionComponent;
// 投射物移动组件
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
TObjectPtr<UProjectileMovementComponent> ProjectileMovement;
// 粒子效果组件
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
TObjectPtr<UParticleSystemComponent> ParticleComponent;
// 碰撞处理函数
UFUNCTION()
void OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
// 造成伤害的 Gameplay Effect 类
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Fireball")
TSubclassOf<UGameplayEffect> DamageEffect;
// 基础伤害值
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Fireball")
float BaseDamage = 30.0f;
};
AuraFireballProjectile.cpp
cpp
#include "AuraFireballProjectile.h"
#include "Components/SphereComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Particles/ParticleSystemComponent.h"
#include "AbilitySystemComponent.h"
#include "AbilitySystemGlobals.h"
AAuraFireballProjectile::AAuraFireballProjectile()
{
// 创建根组件 - 碰撞组件
CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("CollisionComponent"));
RootComponent = CollisionComponent;
CollisionComponent->InitSphereRadius(20.0f);
CollisionComponent->SetCollisionProfileName(TEXT("Projectile"));
// 绑定碰撞事件
CollisionComponent->OnComponentHit.AddDynamic(this, &AAuraFireballProjectile::OnHit);
// 创建粒子效果组件
ParticleComponent = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("ParticleComponent"));
ParticleComponent->SetupAttachment(RootComponent);
// 创建投射物移动组件
ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovement"));
ProjectileMovement->UpdatedComponent = CollisionComponent;
ProjectileMovement->InitialSpeed = 2000.0f;
ProjectileMovement->MaxSpeed = 2000.0f;
ProjectileMovement->bRotationFollowsVelocity = true;
ProjectileMovement->bShouldBounce = false;
// 设置生命周期,避免火球永远存在
InitialLifeSpan = 5.0f;
}
void AAuraFireballProjectile::BeginPlay()
{
Super::BeginPlay();
}
void AAuraFireballProjectile::OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
// 忽略与施法者的碰撞
if (OtherActor == GetOwner())
return;
// 如果碰撞到有效目标,应用伤害
if (OtherActor && OtherActor != this)
{
// 获取目标的 Ability System Component
UAbilitySystemComponent* TargetASC = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(OtherActor);
UAbilitySystemComponent* OwnerASC = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(GetOwner());
if (TargetASC && OwnerASC && DamageEffect)
{
// 创建伤害效果的上下文
FGameplayEffectContextHandle EffectContext = OwnerASC->MakeEffectContext();
EffectContext.AddSourceObject(this); // 设置伤害来源
// 创建 Gameplay Effect 规格
FGameplayEffectSpecHandle SpecHandle = OwnerASC->MakeOutgoingSpec(DamageEffect, 1.0f, EffectContext);
if (SpecHandle.IsValid())
{
// 设置伤害值
SpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(TEXT("Attribute.Damage")), BaseDamage);
// 应用效果到目标
OwnerASC->ApplyGameplayEffectSpecToTarget(*SpecHandle.Data.Get(), TargetASC);
}
}
// 在这里可以添加爆炸效果、音效等
// 销毁火球
Destroy();
}
}
4. 输入配置 (AuraInputConfig)
在编辑器中创建数据资产:
cpp
// 这是一个数据资产,需要在编辑器中创建
// 位置: Content/Aura/Input/DA_AuraInputConfig
UCLASS()
class UAuraInputConfig : public UDataAsset
{
GENERATED_BODY()
public:
// 输入动作与 Gameplay Tag 的映射
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Meta = (TitleProperty = "InputAction"))
TArray<FLyraInputAction> InputActions;
}
配置示例:
-
添加一个输入动作
IA_Ability_Fireball -
映射到 Gameplay Tag:
InputTag.Ability.Fireball -
在 Input Mapping Context 中绑定到鼠标左键或某个按键
5. 在 LyraHeroComponent 中集成
在 AuraHeroComponent 中(继承自 LyraHeroComponent),确保输入绑定:
cpp
void UAuraHeroComponent::BindInputs(UEnhancedInputComponent* EnhancedInputComponent)
{
Super::BindInputs(EnhancedInputComponent);
// 绑定火球技能输入
if (FireballInputAction && EnhancedInputComponent)
{
EnhancedInputComponent->BindAction(FireballInputAction, ETriggerEvent::Triggered,
this, &UAuraHeroComponent::Input_AbilityInputTagPressed,
FGameplayTag::RequestGameplayTag(TEXT("InputTag.Ability.Fireball")));
}
}
总结实现流程
-
输入触发:玩家按下绑定键 → Enhanced Input 系统触发
-
标签转换 :
AuraInputConfig将输入动作转换为InputTag.Ability.Fireball -
技能激活 :
AuraHeroComponent接收输入标签,通知AbilitySystemComponent -
执行技能 :
AbilitySystemComponent找到并激活GA_AuraFireball -
生成投射物 :技能生成
AuraFireballProjectile -
碰撞检测 :火球碰撞到敌人,触发
OnHit事件 -
应用伤害 :通过
GameplayEffect对敌人造成伤害
这个实现完全遵循 Lyra 的架构模式,保持了模块化和可扩展性。在实际项目中,你还需要添加更多的功能,如魔法值消耗、冷却时间、技能升级等,这些都可以通过 Gameplay Ability System 的强大功能来实现。