ASC学习笔记0008:用于注册能力按键输入的回调

中文注释:UrealEngine-5.2.1源码-AbilitySystemComponent.h

学习内容:

/** 用于注册能力按键输入的回调 */
	DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAbilityAbilityKey, /*UGameplayAbility*, Ability, */int32, InputID);

一. 这是一个 Unreal Engine 中的宏定义,用于声明一个动态多播委托。让我为您详细解释这个声明:

语法分析

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(
    FAbilityAbilityKey,     // 委托类型名称
    /*UGameplayAbility*, Ability, */  // 被注释掉的参数
    int32, InputID          // 实际的参数类型和名称
);

实际效果

这个宏会生成一个名为 FAbilityAbilityKey 的动态多播委托,其签名相当于:

// 生成的委托签名(概念上)
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAbilityAbilityKey, int32, InputID);

// 对应的函数签名
void Broadcast(int32 InputID);

在 Gameplay Ability System 中的用途

这个委托通常用于处理能力系统的输入绑定:

// 在 Ability System Component 中
UPROPERTY(BlueprintAssignable, Category = "Abilities")
FAbilityAbilityKey AbilityKeyPressed;

// 绑定回调
AbilitySystemComponent->AbilityKeyPressed.AddDynamic(this, &AMyCharacter::OnAbilityKeyPressed);

// 回调函数实现
UFUNCTION()
void OnAbilityKeyPressed(int32 InputID) {
    // 处理对应 InputID 的能力激活
    UE_LOG(LogTemp, Warning, TEXT("Ability key pressed: %d"), InputID);
}

注意事项

  1. 注释掉的参数:原本可能设计为传递 UGameplayAbility* 和能力,但最终只保留了 InputID

  2. 命名重复:委托名称 FAbilityAbilityKey 中的 "Ability" 重复了,可能是笔误

  3. 建议的改进命名

    DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAbilityInput, int32, InputID);

这个委托让您可以在蓝图中或 C++ 中绑定回调,当玩家按下绑定的能力按键时触发相应的游戏能力。

二. 在真实的游戏项目中,这种能力按键委托在能力系统中有着广泛而核心的应用。以下是一些具体的实际应用场景:

1. 玩家输入与能力绑定

输入组件中的绑定

// 在玩家控制器或角色中
void AMyPlayerController::SetupInputComponent()
{
    Super::SetupInputComponent();
    
    InputComponent->BindAction("Ability1", IE_Pressed, this, &AMyPlayerController::OnAbility1Pressed);
    InputComponent->BindAction("Ability2", IE_Pressed, this, &AMyPlayerController::OnAbility2Pressed);
}

void AMyPlayerController::OnAbility1Pressed()
{
    // 触发能力按键委托,InputID 对应能力槽位
    AbilitySystemComponent->OnAbilityKeyPressed.Broadcast(1);
}

void AMyPlayerController::OnAbility2Pressed()
{
    AbilitySystemComponent->OnAbilityKeyPressed.Broadcast(2);
}
 

2. 能力系统组件中的处理

// 在自定义 AbilitySystemComponent 中
UMyAbilitySystemComponent::UMyAbilitySystemComponent()
{
    // 绑定委托
    OnAbilityKeyPressed.AddDynamic(this, &UMyAbilitySystemComponent::HandleAbilityInput);
}

void UMyAbilitySystemComponent::HandleAbilityInput(int32 InputID)
{
    if (!OwnerActor || OwnerActor->IsPendingKill())
        return;
    
    // 查找对应输入ID的能力
    FGameplayAbilitySpec* AbilitySpec = FindAbilitySpecFromInputID(InputID);
    if (AbilitySpec && AbilitySpec->Ability)
    {
        // 尝试激活能力
        TryActivateAbility(AbilitySpec->Handle);
    }
}
 

3. UI 技能栏集成

技能栏按钮点击

// 技能栏UI组件
void USkillBarWidget::OnSkillButtonClicked(int32 SlotIndex)
{
    if (APlayerController* PC = GetOwningPlayer())
    {
        if (UMyAbilitySystemComponent* ASC = GetAbilitySystemComponent())
        {
            // 通过委托通知能力系统
            ASC->OnAbilityKeyPressed.Broadcast(SlotIndex);
        }
    }
}
 

4. 复杂的输入上下文系统

// 支持不同上下文下的输入重映射
void UInputContextManager::HandleGlobalAbilityInput(int32 InputID)
{
    // 根据当前上下文获取映射后的能力ID
    int32 MappedAbilityID = GetMappedAbilityID(CurrentContext, InputID);
    
    // 广播映射后的ID
    AbilitySystem->OnAbilityKeyPressed.Broadcast(MappedAbilityID);
}
 

5. 连招系统集成

// 连招管理器
UComboManager::UComboManager()
{
    // 监听能力按键
    OnAbilityKeyPressed.AddDynamic(this, &UComboManager::OnAbilityInputReceived);
}

void UComboManager::OnAbilityInputReceived(int32 InputID)
{
    // 记录输入时间戳
    FInputRecord NewRecord;
    NewRecord.InputID = InputID;
    NewRecord.Timestamp = GetWorld()->GetTimeSeconds();
    
    InputHistory.Add(NewRecord);
    
    // 检测连招
    CheckForCombo();
}
 

6. 平台特定适配

// 处理不同平台的输入差异
void UCrossPlatformInputHandler::HandleTouchInput(int32 TouchIndex, FVector2D Location)
{
    // 将触摸位置转换为能力ID
    int32 AbilityID = ConvertTouchToAbilityID(Location);
    
    if (AbilityID != INDEX_NONE)
    {
        OnAbilityKeyPressed.Broadcast(AbilityID);
    }
}

void UCrossPlatformInputHandler::HandleControllerInput(FKey Key)
{
    // 手柄按键映射到能力ID
    int32 AbilityID = ControllerMapping.FindRef(Key);
    if (AbilityID != INDEX_NONE)
    {
        OnAbilityKeyPressed.Broadcast(AbilityID);
    }
}
 

7. AI 指令系统

// AI控制器下达能力使用指令
void AAIAbilityController::IssueAbilityCommand(int32 AbilityID, AActor* Target)
{
    // AI也可以通过相同的接口触发能力
    if (UAbilitySystemComponent* ASC = Pawn->FindComponentByClass<UAbilitySystemComponent>())
    {
        ASC->OnAbilityKeyPressed.Broadcast(AbilityID);
        
        // 记录AI决策
        LogAIAbilityUsage(AbilityID, Target);
    }
}
 

8. 回放和调试系统

// 能力输入记录和回放
void UAbilityReplaySystem::RecordAbilityInput(int32 InputID)
{
    FReplayFrame Frame;
    Frame.InputID = InputID;
    Frame.Timestamp = GetWorld()->GetTimeSeconds();
    ReplayData.Add(Frame);
    
    // 同时广播给实时系统
    OnAbilityKeyPressed.Broadcast(InputID);
}

void UAbilityReplaySystem::ReplayInputs()
{
    for (const FReplayFrame& Frame : ReplayData)
    {
        // 在回放时模拟输入
        GetWorld()->GetTimerManager().SetTimerForNextTick([this, Frame]() {
            OnAbilityKeyPressed.Broadcast(Frame.InputID);
        });
    }
}
 

实际项目中的优势

  1. 解耦设计 - 输入系统不需要了解具体能力实现

  2. 统一接口 - 键盘、鼠标、手柄、触摸屏都使用相同的委托

  3. 扩展性 - 轻松添加新的输入源或能力类型

  4. 调试友好 - 可以集中监控所有能力输入事件

  5. 网络同步 - 在多人游戏中容易同步输入事件

这种模式在《堡垒之夜》、《英雄联盟》等大型游戏中得到了广泛应用,是构建复杂能力系统的核心架构之一。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值