以 Lyra 的架构为基础,创建一个名为 “Aura“ 的英雄并实现发射火球技能

我们以 Lyra 的架构为基础,创建一个名为 "Aura" 的英雄并实现发射火球技能。我会详细拆解每一步所需的代码,并添加详细注释。

整体架构概览

在 Lyra 中实现这个功能需要以下核心组件:

  1. AuraHero - 英雄角色类

  2. GA_AuraFireball - 火球技能 Gameplay Ability

  3. GE_FireballDamage - 火球伤害 Gameplay Effect

  4. AuraInputConfig - 输入配置数据资产

  5. 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")));
    }
}
复制代码

总结实现流程

  1. 输入触发:玩家按下绑定键 → Enhanced Input 系统触发

  2. 标签转换AuraInputConfig 将输入动作转换为 InputTag.Ability.Fireball

  3. 技能激活AuraHeroComponent 接收输入标签,通知 AbilitySystemComponent

  4. 执行技能AbilitySystemComponent 找到并激活 GA_AuraFireball

  5. 生成投射物 :技能生成 AuraFireballProjectile

  6. 碰撞检测 :火球碰撞到敌人,触发 OnHit 事件

  7. 应用伤害 :通过 GameplayEffect 对敌人造成伤害

这个实现完全遵循 Lyra 的架构模式,保持了模块化和可扩展性。在实际项目中,你还需要添加更多的功能,如魔法值消耗、冷却时间、技能升级等,这些都可以通过 Gameplay Ability System 的强大功能来实现。

相关推荐
('-')2 小时前
《从根上理解MySQL是怎样运行的》第二张学习笔记
笔记·学习·mysql
xlq223222 小时前
16.17.list(上)
c++·list
d111111111d2 小时前
STM32外设学习-I2C通信(代码)--MPU6050--笔记
笔记·stm32·单片机·嵌入式硬件·学习
一 乐2 小时前
英语学习激励|基于java+vue的英语学习交流平台系统小程序(源码+数据库+文档)
java·前端·数据库·vue.js·学习·小程序
cpp_25012 小时前
P1765 手机
数据结构·c++·算法·题解·洛谷
未到结局,焉知生死3 小时前
PAT每日三题11-20
c++·算法
martian6653 小时前
第九章:如何学习和掌握BI?
大数据·数据仓库·学习·etl·bi
自小吃多3 小时前
正点原子-梯形加减速
笔记
musk12123 小时前
人工智能学习大纲,豆包深入研究版
人工智能·学习