[概述]

  对于玩家自身而言, 场景中的角色分两种:自己,别的生物(包括别的玩家,monster, npc等)。而生物本身是一个集合{属性数据(状态), 行为(动作表现)}。

  站在玩家自身的角度来看, 对于别的生物, 他们的所有数据和行为都是来自服务器, 客户端要做的相当于播放录像的功能, 根据服务器送过来的指令对他们进行控制和表现。

  而玩家自身, 其行为来自两个地方:1、操作模块(比如说客户端的io操作, 程序发出的操作指令等, 操作模块内部封装了所有的操作指令, 对玩家对象进行控制), 2、服务器指令。操作模块的指令会向两个路径分发:1、向服务器发送请求,2、向玩家自身发送指令.

[角色行为]

和角色行为相关的变量:

1、  完成条件(即条件满足前不能进行其他行为)。

2、  位置(产生行为的角色位置)。

3、  方向。

4、  状态(此行为对应的角色状态)。

5、  动作表现。

[行为分类]

1、walk.  终点位置, 速度

2、run. 终点位置, 速度

3、攻击(施法). 位置,方向

4、受伤(被攻击).

5、死亡. 位置

6、冲撞、被冲撞、死亡击飞等运动轨迹相关. 终点位置

[玩家自身]

[一] 连续移动

1、客户端角色Me每预先跑一步A-->B, 都要等待服务器的返回(失败或者成功)。在服务器返回响应之前(Me有可能在移动过程中, 也有可能已经到达B点等候),客户端有可能存在以下操作:

a、移动操作, 即点击场景其他地方C(即玩家想下一步移动到C),此时应该把C立即加入到移动队列中。

如果服务器返回成功, 则继续处理移动队列;如果服务器返回失败, 则将Me拉回到A点, 并清空移动队列。

b、其他操作(如攻击)

如果服务器返回成功, 则清空移动队列, 并处理操作队列;如果服务器返回失败, 则将Me拉回到A点, 并清空移动队列, 操作队列。

[二] 攻击

客户端角色Me的位置CP, 服务器位置SP

1、CP与SP位置一致, 则立即发送attack指令到服务器, 并立即切换到attack状态,表现攻击动作。

2、CP与SP位置不一致(有可能正在移动过程中, 即已经发送move指令到服务器), 则立即发送attack指令到服务器(预先攻击)

a、如果attack指令返回成功, 则等待CP与SP相同时则前端进行攻击行为流程。

b、如果attack指令返回失败, 则清除标志, 并等待下一步操作。

3、尝试释放技能的时机

  a、任务开始时在当前位置判断能否释放。

  (有可能攻击失败, 因为玩家位置已变, 延时通知)

  b、开始一次新的移动,先向服务请求移动,前端预移动,并且判断能否释放, 如果可以则预释放。服务器返回移动请求和释放请求成功后,等到达目的点之后表现攻击动作和特效,否则清除预攻击状态。

  (有可能攻击失败, 因为玩家位置已变, 延时通知。这时候是否需要根据当前玩家移动的时间做一个判断策略?一次移动的时间间隔是560ms, 正常的网络传输延时极限(玩家移动广播到达之前+自己发送攻击请求到服务器延时)3个传输延时和2个服务器处理延时)。

  c、收到攻击对象位置变化广播时,判断能否释放,如果可以, 则根据自己当前的状态进行释放或者预释放。

  (这个时候成功率最高, 因为玩家位置延时最短。)

  以上三个时机形成了开始一个攻击任务整个阶段的闭环。

[其它场景对象]

这里的处理模型, 主要用于处理行为队列中存在多个行为的情况(大部分原因是网络延迟引起的)

[一] 移动

1、  行为队列全部是移动行为, 两种处理方式:

a、  依次处理每个移动行为。

b、  根据队列长度,直接处理队尾的移动行为,并清空行为队列。

2、  行为队列最后一个行为是攻击行为, 同样是两种处理方式:

a、  依次处理每个移动行为。

b、根据队列长度,直接处理队尾的攻击行为,并清空行为队列。

[二] 攻击

处理模型同上。

[三] 服务器强制同步行为

服务器有时候会强制同步行为, 如瞬移等。 则立即清空行为队列, 并执行强制同步行为。

[操作模块]

操作模块是针对玩家自身的操作(io操作、自动挂机产生的操作等),将其转换为控制玩家对象的一个逻辑控制任务, 这里用operation表示,比如移动、释放技能、访问npc等都可称之为operation。

1、创建一个operation之前, 要先判断能否创建这个operation,不能则不响应玩家操作;若能, 则创建一个operation,并且插入到operation队列中。

  例如玩家当前处于摆摊或则死亡等不能移动的状态, 如果这时候玩家操作想移动, 则直接返回不响应。

  例如技能cd没到或魔法值不够等处于不能释放技能的状态, 如果这时候玩家操作想释放技能, 则直接返回不响应。

2、若当前正在执行的operation失败, 则要判断失败原因, 判断是否要重新尝试。

  例如移动任务,在移动过程中遇到阻挡点或则服务器返回移动失败, 则重新寻路。寻路成功,则继续执行;失败,则放弃任务。

  例如释放技能(有可能会创建一个移动的子任务),在执行过程中发现魔法值不足等问题, 则直接放弃任务。若是移动失败,则尝试重新寻路。

  

[行为队列模式(FIFO)]

无论是某个具体的动作行为,还是某个改变状态的操作, 都可以抽象为一个action cmd。

每个entity持有一个ActionModel, 每个ActionModel维护一个action list。

[一]实现细节

1、因为要频繁产生action cmd, 如何防止碎片化问题, 在解决这个问题的时候, 不能影响可维护性和扩展性, 尤其是扩展性。

2、action list: a1 a2 a3, 在表现上, 由于a2是一个持续性过程, a3有可能会影响a2的表现, 也就是a3可以在a2的执行过程中直接处理, 从而达到并发性表现(并发性的cmd可以使得表现更为丰富)。据此可以抽象, action cmd可以分为:互斥性action和并发性action。

action list的处理流程:

         if(curAction.isEnd)
{
curAction = list.empty ? action_idle : list.pop_front;
} while(!list.front.isMutex(curAction))
{
Process(list.pop_front);
} curAction.update;

[事件与状态]

1、移动

{run, walk} --> {move}

failed:

拉回原地

2、攻击

{物理攻击} --> {物理攻击状态}

{魔法攻击} --> {魔法攻击状态}

failed:

只播放攻击动作, 不播放后续的特效

3、冲撞

{rush} --> {冲撞状态}

{rushed} --> {被冲撞状态}

4、受伤

{beHit} --> {hurt}

5、死亡

{die} --> {死亡状态}

6、采集

{gatherItem} --> {采集状态}

实时战斗系统问题锦集

1、人物动作处理策略
    由于某个原因(网络延迟等), 当前正在处理的动作尚未结束(比如移动尚未到达目的地), 此时又收到另外一个要处理的动作协议,这时候的处理策略。
    a、直接将人物状态设置到当前动作的目标状态, 然后开始新的动作。
    b、按正常流程处理当前动作, 并且把要处理的动作入队, 待当前动作完成, 再依次处理动作队列里的内容。
    
    问题衍生:如果把问题放大化, 即当前动作尚未结束, 后面收到一串待处理的动作, 此时的处理策略。

有可能出现的情况:
    a、正在移动, 网络送来攻击行为        -->位置不一致
    b、正在攻击, 网络送来移动行为        -->位置不一致
    c、正在攻击, 网络送来新的攻击行为    -->表现不一致
    
    处理原则:优先保证位置一致,其次保证表现上的一致性。
    
    处理策略(实际上这里的动作处理, 仅仅是影响人物的表现, 还有坐标位置):
    动作按处理的紧急:
    <1>可以延迟处理的,放入队列缓存
    <2>需要立即处理的(如冲撞, 不立即处理的话,冲撞和被冲撞的表现不一致了。如怪物正在攻击(表现攻击动作))
    
2、技能释放的处理策略
技能分类:
    按技能的释放方式:主动、被动
    按攻击目标的数量:群攻、单体
    按是否需要目标:需要、不需要
    按是否魔法锁定:锁定、不锁定
    按施法距离:近身、远程
    按作用目标:只能对自己、只能对队友、只能对敌人
    
技能释放的前提条件:
    cd、距离、魔法量、目标

技能释放的要素:
    释放人、目标、方向、 目标点
    <1>从当前位置释放,作用于目标(或目标点)(要判断施法距离)
    <2>在当前位置朝当前方向释放技能(战士技能)
    <3>在当前位置朝鼠标方向释放技能(shift+左键攻击或者两个比较特殊的技能(地狱冰封,极光电影))
    <4>施加于自身(可以合并到<1>, 只是不用判断施法距离)
    
    特殊案例:
    群体治疗术、群体隐身术(作用于当前鼠标位置)
    单体治疗术、单体隐身术(作用于当前鼠标所在的目标, 如果没有目标,作用于自身)
    
3、打击感的体现

4、鼠标和键盘按着不放进行操作的延时机制
如果按着鼠标或者键盘不放,一秒钟可以触发次数:
鼠标:由程序主线程控制, window系统不会主动触发
键盘:34次/s

如果窗口样式指定了CS_DBLCLKS, 则双击鼠标产生的事件:

down->up->double click->up
否则:

down->up->down->up,此时需要使用者根据一定的规则自己检测并生成双击事件。

客户端ARPG角色行为模型的更多相关文章

  1. Lodop客户端本地角色注册号常见误区

    之前写过一篇关于Lodop和c-lodop注册号的区别:LODOP.C-LODOP注册号的区别第一种角色客户端本地打印角色是最常见的角色,最常见的场景,关于c-lodop云打印,它的第一种角色是取代L ...

  2. GJM : 基于Actor模式的c#网络游戏服务器的实现和Unity游戏客户端的连接 [转载]

    感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...

  3. [lua] 游戏客户端逻辑使用lua协程

    我们的游戏有这样一种情景:客户端中角色需要用到一些公会的数据,但服务器不会在玩家(创角后)一进入到游戏里就推送给玩家,而是需要客户端自己在需要的时候向服务器请求公会的数据,之前的实现就是在请求消息的时 ...

  4. redis 系列19 客户端

    一. 概述 Redis服务器是可以与多个客户端建立网络连接,每个客户端可以向服务器发送命令请求,而服务器则接收并处理客户端发送的命令请求,并向客户端返回命令回复.通过使用I/O多路复用技术实现的文件事 ...

  5. Redis实现之客户端

    客户端 Redis服务器是典型的一对多服务器程序:一个服务器可以与多个客户端建立网络连接,每个客户端可以向服务器发送命令请求,而服务器则接收并处理客户端发送的命令请求,并向客户端返回命令回复.通过使用 ...

  6. redis学习笔记——客户端

    Redis服务器是典型的一对多服务器程序:一个服务器可以与多个客户端建立网络连接,每个客户端可以向服务器发送命令请求,而服务器则接收并处理客户端发送的命令请求,并向客户端返回命令回复. 对于每个与服务 ...

  7. redis 笔记03 RDB 持久化、AOF持久化、事件、客户端

    RDB 持久化 1. RDB文件用于保存和还原Redis服务器所有数据库中的所有键值对数据. 2. SAVE命令由服务器进程直接执行保存操作,所以该命令会阻塞服务器. 3. BGSAVE由子进程执行保 ...

  8. Blazor应用程序基于角色的授权

    原文:https://chrissainty.com/securing-your-blazor-apps-configuring-role-based-authorization-with-clien ...

  9. LODOP和C-LODOP注册与角色等简短问答【增强版】

    之前的简短问答:Lodop.c-lodop注册与角色简短问答.Lodop简短问答客户反馈篇 及排查步骤 及注册相关本文重新整理相关简短问答,调整问答顺序,增加相关的问答. 其他相关(非简短问答):LO ...

随机推荐

  1. map reduce filter

    三个函数比较类似,都是应用于序列的内置函数.常见的序列包括list.tuple.str.   1.map函数 map函数会根据提供的函数对指定序列做映射. map函数的定义: map(function ...

  2. tee 命令基本使用方法、输出到多个文件

    功能说明:读取标准输入的数据,并将其内容输出成文件.语  法:tee [-ai][--help][--version][文件...]补充说明:tee指令会从标准输入设备读取数据,将其内容输出到标准输出 ...

  3. JDBC连接属性

    JDBC连接属性 hibernate.connection.driver_classs属性:设置连接数据库的驱动: hibernate.connection.url属性:设置所需连接数据库的URL: ...

  4. Objective C 基础

    1,字符串加@: 不加@的是C语言字符串. 加@的代表的是OC的NSString http://www.jikexueyuan.com/course/71_2.html 2,setValuesForK ...

  5. ①创建项目testpackage ②在pack2.B中添加方法f ③在类A中添加如下三个成员变量:int型的私有变量i float型的变量f double型的公有变量d 在pack1.B的main方法中为对象a的成员变量f和d分别赋值为2和3 在pack2.C的main方法中为对象a的成员变量d赋值为3

    package pack1; public class A { private int i; float f; public double d; public float getF() { retur ...

  6. jquery动态样式操作

    获取与设置样式 获取class和设置class都可以使用attr()方法来完成.例如使用attr()方法来获取p元素的class,JQuery代码如下: 1 var p_class = $(" ...

  7. shift移动变量

    1.移动变量 脚本 sh05.sh #!/bin/bash # Program # Program shows the effect of shift function # History: # // ...

  8. [转载] 为 Key-Value 数据库实现 MVCC 事务

    http://mp.weixin.qq.com/s?__biz=MzA5ODM5MDU3MA==&mid=400086920&idx=1&sn=b8174184059e2886 ...

  9. 14 Using Indexes and Clusters

    do not build indexes unless necessary. 索引是非常占资源的To maintain optimal performance, drop indexes that a ...

  10. JS事件中的对象

    在触发任何事件时都会产生一个对象.如:DOM事件,window事件等,都会产生一个对象,该对象就是当前元素的事件对象. DOM事件对象 <body> <input type=&quo ...