虚幻插件GAS分析02-1 技能的网络同步之预测与结束

虚幻插件GAS分析02-1 技能的网络同步之预测与结束

https://zhuanlan.zhihu.com/p/419741087

大家好,我是阿棍儿。上一篇如下,本篇总结一下GAS激活技能的预测机制与结束技能的网络同步流程。

技术宅阿棍儿:虚幻插件GAS分析02-0 技能网络同步之激活22 赞同 · 6 评论文章​编辑

什么是预测?

代码中常出现Predict字样的东西,带这个字样基本会跟预测有关。

GAS中的技能激活预测(仅LocalPredicted策略支持)大致步骤是:

  1. Client可以先激活技能,同时请求Server激活技能
  2. Server会判断一下激活技能合不合理,如果合理,Server也激活,否则Server就不激活
  3. Server会把判断结果回复给Client
  4. Client根据Server的判断结果处理,如果不合理,就结束技能

也就是说,预测的通用概念是,Client先做事,同时请Server也做,Server决定做不做,并执行决定,同时Server把结果反馈给Client,Client根据Server的决定修正自己,以求与Server保持一致。

修正针对的是副作用(side effect),在Server反馈到达Client后,副作用应该被移除。本文只考虑技能激活,所以移除副作用的方法就是立即结束技能。

激活技能的预测机制的实现

预测机制的基础数据结构主要在Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/GameplayPrediction.h中定义,里面还有大量注释,是非常重要的参考

为了尽量画到一张图,有些扭曲请见谅

预测Key

Server和Client的执行是跨越时空的,被发起和被同步发起的技能必须有匹配的方法,才能正确地修正副作用。例如,两端都在连续释放相同的攻击技能,在时间上,两端的技能可能是错位的,Client可能在打第5下,Server可能在打第1下,如果Server要拒绝第1下的执行,该怎么找到Client的第1个技能实例(假设实例化策略是Per Execution)呢?

预测Key的一个功能就是可以给两端对应的技能实例发相同的Unique ID。Server连同此ID一同反馈,Client就知道是哪个技能实例被拒绝了。

Key的创建

Client的激活过程

上图中的流程大致就是创建Key,传递Key,激活技能。

具体代码流程如下,FScopedPredictionWindow是一个“窗口”,在构造时窗口打开,同时生成新的预测Key,在析构时窗口关闭。在此期间,FPredictionKey UAbilitySystemComponent::ScopedPredictionKey是有效Key,可以用来预测。如上图,生成的Key连同激活请求会一起传到Server。

#Client UAbilitySystemComponent
    TryActivateAbilityByClass->TryActivateAbility->InternalTryActivateAbility
    {
        { // 用花括号建立局部作用域
            FScopedPredictionWindow ScopedPrediction(this, true);
            CallServerTryActivateAbility->ServerTryActivateAbility->#Server ...
            UGameplayAbility::CallActivateAbility
        } // ScopedPrediction析构
    }

技能预测失败

Server反馈Fail的过程

如上图,当Server因某种原因拒绝激活技能时,就会通知Client激活失败,同时将对应的预测Key传给Client,Client根据Key找到对应的技能并结束之。

/*----------- RPC Client->Server---------*/
#Server UAbilitySystemComponent
    ServerTryActivateAbility_Implementation->InternalServerTryActivateAbility
    ->ClientActivateAbilityFailed
    /*----------- RPC Server->Client---------*/
    #Client ClientActivateAbilityFailed_Implementation
    UGameplayAbility::K2_EndAbility

结束技能的网络同步

CancelAbility VS EndAbility

先辨析一下这2个概念。

两者都可以结束技能,有什么区别呢?代码中注释如下:

// CancelAbility() - Interrupts the ability (from an outside source).
//
// EndAbility() - The ability has ended. This is intended to be called by the ability to end itself.

简单说就是Cancel倾向在技能外使用,EndAbility倾向在技能内使用。在实际功能上,Cancel会调用CanBeCancel来确定是否能被Cancel,所以调用Cancel有机会阻止技能结束。

两者在流程上差不多,最终都是调用底层的EndAbility,所以为了简化问题,只分析EndAbility。流程分析的起点是UGameplayAbility::K2_EndAbility(EndAbility蓝图节点),终点是UGameplayAbility::EndAbility

EndAbility流程

技能结束的示意图看起来相对简单一些,不过,在简化掉目前没涉及到的概念的基础上,才能画得这么简单。

  • 代码流程

Server:

UGameplayAbility::K2_EndAbility
EndAbility
UAbilitySystemComponent::ReplicateEndOrCancelAbility
if (LocalPredicted 或 ServerInitiated)
{
    if (!Controlled)
    {
        ClientEndAbility
        /*----------- RPC Server->Client---------*/
        #Client ClientEndAbility_Implementation
        RemoteEndOrCancelAbility
        UGameplayAbility::EndAbility
   }
}

Client:

UGameplayAbility::K2_EndAbility
EndAbility
UAbilitySystemComponent::ReplicateEndOrCancelAbility
if (LocalPredicted 或 ServerInitiated)
{
    if (!Authority)
    {
        CallServerEndAbility
        ServerEndAbility
        /*----------- RPC Client->Server---------*/
        #Server ServerEndAbility_Implementation
        RemoteEndOrCancelAbility
        UGameplayAbility::EndAbility
   }
}

技术宅阿棍儿:虚幻插件GAS分析03-0 GameplayEffect标签面面观-标签栏31 赞同 · 21 评论文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值