收藏 分销(赏)

基于unity的回合制网游战斗系统的研究与实现.docx

上传人:人****来 文档编号:4935984 上传时间:2024-10-20 格式:DOCX 页数:51 大小:4.03MB 下载积分:14 金币
下载 相关 举报
基于unity的回合制网游战斗系统的研究与实现.docx_第1页
第1页 / 共51页
基于unity的回合制网游战斗系统的研究与实现.docx_第2页
第2页 / 共51页


点击查看更多>>
资源描述
摘要 本文主要阐述网络回合制战斗系统的实现,包括回合制战斗系统的整个流程(战斗开始、玩家下达战斗指令、战斗动画、战斗结束),和网络回合制战斗系统常见的主要功能(攻击、使用技能、使用物品)的实现,并且在游戏项目《PKer》中进行实践和测试。在开发环境方面,服务器采用国内IOCP高性能开源框架“HP-Socket”(Windows平台),并且连接到Mysql数据库,客户端采用近年比较火热的强大跨平台引擎Unity3D(2D,C#)并且使用Sqlite作为嵌入式数据库。除此以外,本文还对战斗系统实现所涉及到的相关技术如Unity引擎的协同程序、单例设计模式和分包算法进行简要介绍。 关键词 回合制战斗系统, 网络游戏,Unity3D,协同程序,游戏编程 ABSTRACT This article mainly expounds the implementation of network turn-based combat system, including the whole process of turn-based combat system (battle begins, the players fighting instructions, combat animation, battle ends), and the main function of common (attack, using skills, using items) implementation, and in the game "PKer" in practice and test of the project. In the aspect of development environment, C++ IOCP server with Windows platform and connect to the Mysql database, the client uses Unity3D (2D, C#) and uses Sqlite as an embedded database. In addition to this, this article also involved to combat system related technologies such as the Unity engine coroutines, singleton design pattern and the subcontract algorithm are briefly introduced. KEYWORDS Turn-based combat system, online game, Unity3D, Coroutine, Singleton design pattern 目 录 前 言 1 第1章 绪论 2 1.1 回合制游戏简介 2 1.1.1 广义上的回合制游戏 2 1.1.2 狭义上的回合制游戏 2 1.2 回合制战斗系统简介 2 1.3 实践项目《PKer》简介 2 第2章 开发环境和部分涉及技术简介 4 2.1 开发环境简介 4 2.2 Unity协同程序 4 2.2.1 Unity协同程序简介 4 2.2.2 Unity协同程序的运用 4 2.3 单例设计模式 5 2.3.1 单例设计模式 5 2.3.2 单例设计模式的运用 6 2.3.3 单例设计模式在Unity中的运用 7 2.4 分包算法 7 2.4.1 分包的原因 7 2.4.2 环形缓冲区(Circular Buffer) 7 2.4.3 分包算法流程图 9 第3章 战斗动画实现方案的研究与对比 11 3.1 研究背景 11 3.2 实现方案一:有限状态机 11 3.3 实现方案二:协同程序(Coroutine) 12 3.4 方案抉择结论 13 第4章 回合制网游战斗系统设计 14 4.1 回合制网游战斗系统设计 14 4.2 回合制网游战斗系统服务器实现思路 15 4.3 回合制网游战斗系统客户端实现思路 15 4.4 战斗系统框架结构(以实践项目《PKer》为例) 16 第5章 回合制战斗系统实现 18 5.1 战斗系统相关消息结构体的定义 18 5.2 服务器战斗系统的实现 20 5.2.1 服务器战斗系统相关结构体 20 5.2.2 初始化一个战局 23 5.2.3 接收客户端的战斗指令消息 23 5.2.4 处理战斗指令并发送动画消息 24 5.2.5 说明 27 5.3 客户端战斗系统的实现 27 5.3.1 客户端战斗系统功能模块 28 5.3.2 客户端战斗系统相关数据类型 28 5.3.3 进入战斗后初始化战斗角色 29 5.3.4 下达战斗指令并发送战斗指令消息 29 5.3.5 接收战斗动画消息 30 5.3.6 收到战斗动画播放消息并播放战斗动画 30 5.3.7 战斗动画实现细述 35 第6章 项目测试 39 6.1 进入战斗测试 39 6.2 普通攻击功能测试 39 6.3 使用技能功能测试 40 6.4 使用物品功能测试 41 结论 43 参考文献 44 致谢 45 - 47 - 前 言 随着网络的普及与迅速发展,网络游戏已经成为电子游戏中的主流。网络游戏的战斗系统主要可分为即时制和回合制两种模式。即时制战斗系统侧重于刺激、反应、操作性,战斗节奏快。而回合制战斗系统侧重于休闲、战略、配合,战斗节奏慢。早期的电子游戏由于设备硬件条件有限,大多采用回合制战斗系统。 随着科技的发展和设备硬件的提升,如今回合制游戏的数量比例有所下降,但回合制游戏在国内游戏市场依旧占据着想当大的份额,仍有大量玩家热衷这种战斗模式,几款众所周知的国产单机游戏如《轩辕剑》系列、《仙剑奇侠传》系列、《古剑奇谭》系列以及国内在线人数最多的MMORPG《梦幻西游》均是采用回合制战斗系统的游戏。 本人也是热衷于回合制战斗系统的玩家之一,可纵观近年来国内回合制游戏的发展,国内回合制游戏一直止步不前,《轩辕剑》系列、《仙剑奇侠传》系列、《古剑奇谭》销量下降,MMORPG《梦幻西游》也有降温迹象,甚至国内大多回合制网游只是复制在《梦幻西游》或“换皮”,缺少创新和突破。本人希望在研究和实现回合制战斗系统的基础上,能够找到突破和创新点。同时在游戏编程方面,即时制逻辑较为简单明了清晰,而回合制则比较繁琐复杂,而且在Unity引擎上实现回合制战斗系统的相关资料较少。综上原因,本人撰写本文,希望能对志同道合者有所启发,同时也寻求学术交流。 项目将在2017年6月开源,开源地址 By 江正觊 2016.6 第1章 绪论 1.1 回合制游戏简介 1.1.1 广义上的回合制游戏 凡“我方与敌方在单个回合内轮流行动,只有轮到我方(自己)的回合或者是新的回合开始时,才可进行行动”的游戏,都可归类为广义上的回合制游戏,而且绝大大多数情况下,单个回合内敌我双方行动次数相同。从广义上来说,回合制游戏范围非常广,棋牌、卡牌、战棋策略、回合制战斗模式都能归为此类。 1.1.2 狭义上的回合制游戏 狭义上的回合制游戏,是从广义回合制游戏中细分,特指回合制战斗模式的游戏,与即时制战斗模式相对。 1.2 回合制战斗系统简介 在回合制战斗模式下,每个回合开始时,敌我双方各自为双方战斗角色下达战斗指令,待双方所有角色下达完战斗指令或超过时限(回合制网络游戏均会设置下达战斗指令的时间限制)后双方所有角色开始行动,一般根据角色自身“速度(敏捷)”的属性数值轮流行动,期间若果某一方符合战败条件(某方全体阵亡或全体人物阵亡)则战斗结束,如所有角色行动完后敌我双方均没符合战败条件,则进入下回合,如此循环。 1.3 实践项目《PKer》简介 《PKer》是一个以回合制竞技和社交为卖点的PC和移动端跨平台网络游戏,是游戏与社交APP的融合体。游戏主要玩法是玩家与玩家之间的回合制战斗PK。 通过移动设备GPS定位功能(PC端无法使用),能够快速搜索在你身边的游戏房间和玩家,与其开展战斗。 游戏提供一定数量的基础装备和宠物让玩家任意领取,故玩家可以随时更换装备和宠物,新玩家也能以此为基础投入到战斗中。更强的装备和稀有的宠物通过合成、付费租用等渠道获得。角色的属性和职业也可以随时更改,以便随时改变战术和改变在队伍里中的定位。 第2章 开发环境和部分涉及技术简介 2.1 开发环境简介 本文实践项目的服务器采用国内IOCP开源框架“HP-Socket”,并且连接到Mysql数据库,客户端采用近年比较火热的强大跨平台引擎Unity3D(2D,C#)并且使用Sqlite作为嵌入式数据库。 2.2 Unity协同程序 2.2.1 Unity协同程序简介 协同程序(Coroutine),通常简称“协程”,顾名思义,是一段协助的程序(方法),很多人以为它是另开一个线程执行一段程序,其不然,实际上它是从主线程每帧或每隔一定时间调用的程序。当协程创建后,主线程中创建协程的语句后面的代码块会“挂起”,直到协同程序结束后,才会继续执行创建协程语句后面的代码。当协程中的代码执行完或者使用yield break语句时,协程才会结束,并且返回到主线程中的创建该协程的语句的位置,继续执行后面的代码。协程中使用yield return 帧数/new WaitForSeconds(秒数)语句可以实现隔多少帧或多少秒后再执行后续代码。另外协程可以嵌套协程,利用协程的特点和嵌套,可以实现很多复杂和有趣的功能,十分强大。 2.2.2 Unity协同程序的运用 协程广泛地运用在计时、延迟、控制物体运动、等待物体状态的改变、有顺序地让物体执行一系列动作等方面上。 根据协程的特点,协程中可以使用“循环+条件判断+yield return 帧数/new WaitForSeconds(秒数)”实现每隔多少帧或者多少秒后再次执行条件判断语句,当判断语句满足跳出循环,从而让协程代码执行完结束,回到主线程继续执行后续代码,利用此功能,可以很方便地实现某物体达到某状态后再执行程序。 示例代码: 主线程代码 StartCoroutine(actor.Move(getAttackPosition(nTargetIndex))); Int i=0; 协同程序Move方法 public IEnumerator Move(Vector3 destPos) { while (transform.localPosition != destPos) { transform.localPosition = Vector3.MoveTowards(transform.localPosition, destPos, GlobalData.BATTLE_SPRITE_MOVE_SPEED * Time.deltaTime); yield return 0; } } 上述代码中,主线程StartCoroutine方法创建一个协程Move,待协程Move执行完成返回后,主线程才执行i=0语句。可是协程Move的返回条件有点特殊,yield return 0语句表示协程运行到此处挂起,等下一帧再从本语句继续运行,由于yield return 0在while循环内,所以不管yield return 0多少次,都依然在while循环内,而且while循环每一帧循环一次。直到while不满足循环条件(即transform.localPosition == destPos),则协程能够完成使命执行完成并返回。可以猜测到,所控制的物体可能通过Update方法或者其他方法每帧在移动,直到移动到目标地点,才执行主线程后面的代码。 2.3 单例设计模式 2.3.1 单例设计模式 单例设计模式是常见和简单的一种软件设计模式,单例模式下的类能确保在整个工程项目中只允许存在最多一个实例对象。单例模式下的类通常不能直接通过构造函数new出一个实例,而且构造函数通常会设置成私有,外部不可访问。只有通过类的一个静态方法可以创建或获取有且只有唯一一个的对象实例。 2.3.2 单例设计模式的运用 单例设计模式下的类往往具有通用性或全局性,用于保存全局数据,需要访问其变量的外部类只需要通过单例类的静态方法即可获得其唯一的实例,从而访问其内部变量。 示例代码: public class ClientSocket//单例类ClientSocket { private static ClientSocket _client_socket;//保存本类的唯一实例 private static MessageManager _msg_mgr; private static Socket _socket; private string _web = ""; private IPAddress _ip; private int _port=8000; private byte[] _buf=new byte[1024]; private CircularBuffer _circularBuf = new CircularBuffer(4096); private ClientSocket() { } public static ClientSocket getInstance() { if (_client_socket == null)//若实例不存在则创建,实例已存在则直接返回 { _client_socket = new ClientSocket(); _msg_mgr = MessageManager.getInstance(); } return _client_socket; } …… } 2.3.3 单例设计模式在Unity中的运用 在Unity中使用单例设计模式,有时会有额外的意义。在Unity中,通常一个类会作为一个组件挂载到一个物体上,可是如果进行了场景切换,当前场景所有物体以及物体所挂载的所有组件都会被销毁,这时如果想要所有场景都能访问一个类的实例以及其变量,则需要把该类设置成单例类,并且不能继承MonoBehaviour,而且不能挂载到任何一个物体上,这样就可以保证整个游戏中不管场景怎么切换,该类的实例一直存在,而且只有唯一一个实例,所有场景均可通过静态方法获取实例并且访问。 2.4 分包算法 2.4.1 分包的原因 TCP在发送消息时,如果发送间隔太短,发送方有可能出现一次发送多个消息的情况,而接收方便会一次接收到多个消息,另外,很多时候网络情况较差或不稳定,接收方迟迟收不到消息,过一段时间有可能一次过接收到此前该接收的多个消息,这些现象就是粘包。 由于粘包现象的存在,如果不进行分包,接收方可能只能读取到粘包数据的头一个消息,后面的消息将会丢失,甚至影响后续接收的消息,严重影响程序的运行。因此,我们需要一个分包算法来检测粘包情况并进行分包,以便正确读取所有来自发送方的消息。 2.4.2 环形缓冲区(Circular Buffer) 在分包算法中,我们需要用到一个数据结构——环形缓冲区。之所以要用到环形缓冲区,是因为分包前我们要把接收到的所有数据存放在一个缓冲区中,分包时根据消息的长度从缓冲区里取数据,取出后缓冲区相应地移除对应的数据,这样反复频繁进行添加和删除操作如果只用普通的缓冲区(byte数组/char数组)的话,则要频繁地移动数据存放的位置,十分影响程序运行效率,为此需要引入环形缓冲区。 下面是环形缓冲区(CricularBuffer)类的UML类图(C++): 图 2-1 环形缓冲区UML类图 说明: buf:指向缓冲区的指针 front:缓冲区的数据的起始位置 rear:缓冲区的数据的尾部位置 lock:用于多线程同步的临界区,防止多个线程访问修改缓冲区数据 size:缓冲区数据的长度 capacity:缓冲区的容量 CircularBuffer:构造函数,创建一个指定容量为参数大小的缓冲区 ~CircularBuffer:析构函数,释放缓冲区 clear:清空缓冲区 pushBuf:往缓冲区尾部添加数据 popBuf:从缓冲区头部得到数据,并且缓冲区移除相应数据 getBuf:从缓冲区头部得到数据,但缓冲区不移除数据 isEmpty:判断缓冲区是否为空 由于环形缓冲区使用front起始位置和rear尾部位置记录数据存放的位置,添加或移除数据不需要移动数据的储存位置,运行效率高。 2.4.3 分包算法流程图 图2-2 分包算法流程图 说明: (1) 定义或获得一个容量足够大的环形缓冲区(具体大小请根据实际情况考虑); (2) 接收到数据后,往环形缓冲区尾部添加数据; (3) 判断缓冲区的数据长度是否大于等于4(消息头的长度),本文实践项目的结构体消息头由消息类型(ushort)和消息长度(ushort)组成,共4字节。若缓冲区数据长度小于消息头长度,则无法获取第一条消息的长度,也无法分包,也表明数据接收不全,回到(2)继续接收数据,不进行任何操作;若缓冲区数据长度大于等于消息头长度,则开始进行分包; (4) 从缓冲区头部getBuf获得4字节数据(消息头),不改变缓冲区数据,从消息头得到第一包消息的长度; (5) 判断缓冲区数据长度,若缓冲区数据长度大于第一包消息的长度,则第一包消息接收完并可取,根据第一包消息的长度popBuf得到第一包消息数据,并且缓冲区移除相应数据,处理该消息;若缓冲区数据长度小于第一包消息长度(此时缓冲区数据长度大于等于4,但是小于第一包消息的长度),则说明第一包消息还没接收完,回到(2)继续接收,不进行任何操作; (6) 回到(4),一直循环。 第3章 战斗动画实现方案的研究与对比 3.1 研究背景 笔者认为回合制战斗系统实现的最大难点,那无疑就是客户端战斗动画的实现。回合制战斗系统的战斗动画,不是平常我们说的“动画”,它是由数据转换过来的动态动画,动画内容由数据决定。例如动画数据是“某个角色攻击某个角色”,动画内容便是“攻击者移动到目标面前并播放攻击动作动画,目标播放受伤动作动画,显示扣血,攻击者返回到本来的位置”;再例如动画数据是“某个角色使用技能攻击某个角色”,动画内容便是“施法者播放施法动作动画,然后在目标身上播放技能特效,等到特效播放到某一帧,目标播放受伤动作动画,显示扣血”。 上述例子已经是回合制战斗系统最基本的战斗动画,更不用说多次攻击或者多次施法,可谓难上加难。更何况,战斗动画的质量极大影响回合制游戏的可玩性,而且回合制战斗除了玩家下达战斗指令外,其余时间全部都在播放战斗动画上。 所以,笔者认为客户端的战斗动画实现是整个回合制战斗系统的重点和难点,为了正确谨慎地抉择实现方案,少走弯路,额外对战斗动画的实现展开了研究。 3.2 实现方案一:有限状态机 这是笔者最先想到的方案,毕竟有限状态机在游戏编程中实在极为常用,相信不少读者也会率先联想到这种方式。 有限状态机代码示例: void Update () { switch(state) { case (int)State.Standby: …… break; case (int)State.Attack: …… break; case (int)State.Magic: …… break; …… } …… } 有限状态机方案下,Update方法每帧检测记录状态的变量state,从而决定究竟要执行何种行为,当行为执行完或者某个事件触发时,通过修改状态变量state,改变执行的行为。 优点: (1)灵活可变,随时随地都可以改变状态,从而改变执行的行为 缺点: (1) 每帧都需要检测状态变量,影响程序运行效率; (2) 如果要实现一系列复杂的行为,代码量大而且凌乱,逻辑复杂,例如要实现“攻击者移动到目标面前,攻击者播放攻击动画,目标播放受伤动画,显示扣血,攻击者返回原本位置”这样的功能,可能需要不止一个状态机; (3) Update方法变得臃肿,很多时候Update还需要做其他事情; 3.3 实现方案二:协同程序(Coroutine) 在感觉方案一可行性不高的情况下,本人开始寻求其他实现方案,直到发现协同程序(Coroutine)。若读者不了解协程可阅本文第二章,协同程序在本文第2章有简略介绍,此处不再作介绍。个人感觉Unity的协同程序跟Cocos2d的CCSequence动作有点像,都能很方便实现按顺序执行一系列动作,而且协同程序比CCSequence更灵活。 协同程序示例代码: IEnumerator Attack(BATTLE_ANIM battleAnim) { BattleSprite sprite = battleSprites[battleAnim.nActorIndex - 1]; yield return StartCoroutine(sprite.Move(getAttackPosition(nTargetIndex))); yield return StartCoroutine(sprite.AttackAnim()); yield return StartCoroutine(sprite.Back(actor.vOrigPos)); } 协程Attack()能够简单清晰实现“控制某个物体移动到某个地点,完成后进行攻击,攻击完成后返回到某个地点”,一步完成之后再进行下一步。 优点: (1) 不需要在Update每帧调用,协程开启后根据代码逻辑等待(每)n帧/n秒自动调用; (2) 代码逻辑清晰 (3) 能够很好实现按顺序执行一系列复杂的行为 缺点: (1) 如要中途终止协程,有一定的终止条件 (2) 没状态机灵活,难以临时转变行为 3.4 方案抉择结论 显而易见,经过笔者的研究和深思熟虑,本文采用了协同程序方案。理由很简单,回合制战斗动画几乎全避开了协程的缺点,体现了协程的优点。因为回合制战斗动画是由服务器发来的动画数据转换的,数据自始至终没有改变,客户端只负责播放动画效果,并没有修改数据,那么从数据转化为动画那刻开始,动画就不可能有变化或者中途终止,并不需要程序的灵活性。这么看来,协程用在回合制战斗动画的播放很是适合。 另外,读者也可考虑行为树方案,由于笔者时间和能力有限,截至目前未能深入学习研究行为树,不知是否能比协程更好地实现战斗动画,所以本文未能提及,恳请体谅。 第4章 回合制网游战斗系统设计 4.1 回合制网游战斗系统设计 我们从回合制网游中不难发现,在客户端面前,我们可以看到一场回合制战斗可划分为以下阶段:进入战斗-玩家下达战斗指令(很多情况下需要下达人物和宠物两个战斗指令)-战斗动画-战斗结束。经过本人对上述战斗过程的研究、思考和分析,得出个人的回合制战斗系统实现思路,战斗系统流程图如图: 图 4-1 回合制网游战斗系统流程图 4.2 回合制网游战斗系统服务器实现思路 (1)战斗开始发送“战斗初始化消息”给客户端,“战斗初始化消息”包含所有战斗角色(人物和宠物)的名字、图形及动画控制器ID、最大Hp、当前Hp、最大Mp、当前Mp等数据,客户端只关心需要显示的内容; (2)根据战斗角色的数量,等待接收并保存数量与角色数量想当的来自客户端的“战斗指令消息”,即等待所有战斗角色都下达完战斗指令; (3)接收到足够数量的“角色战斗指令消息”后,根据角色的“速度”属性数值,倒序(即速度高者先行动)处理每一个战斗指令所造成的影响,并更新服务器各个角色的数值,发送对应的“动画消息”给所有客户端,每处理一个“战斗指令消息”就发送一个对应的“动画消息”; (4)待所有“战斗指令消息”处理完并发送完对应的“动画消息”后,发送“动画播放消息”; (5)判断双方角色的阵亡情况,若一方符合战败条件(所有角色阵亡或者所有人物阵亡,具体根据游戏设定决定)则结束战斗,否则回到步骤(2)进入下一个回合,如此循环。 4.3 回合制网游战斗系统客户端实现思路 (1)若接收到“战斗初始化消息”,则进入战斗,并根据消息里的数据,初始化每一个战斗角色; (2)显示人物战斗指令菜单,根据玩家的操作,发送“战斗指令消息”,人物战斗指令菜单消失; (3)若没有参战宠物,直接跳到下一步,若有参战宠物,显示宠物战斗指令菜单,根据玩家的操作,发送“战斗指令消息”,宠物战斗指令菜单消失; (4)接收并保存每一个来自服务器的“动画消息”或“动画播放消息”; (5)收到“动画播放消息”后,读取并处理每一个“动画消息”,转化为对应等量的战斗动画类并用容器保存,遍历容器播放所有动画; (6)当前回合所有战斗动画播放完后,判断双方阵亡情况,若一方符合战败条件(所有角色阵亡或者所有人物阵亡,具体根据游戏设定决定)则结束战斗,否则回到步骤(2)进入下一个回合,如此循环。 4.4 战斗系统框架结构(以实践项目《PKer》为例) 虽然本文主要阐述战斗系统,但是由于战斗系统较为复杂,为了让读者更好的理解本文所述战斗系统的实现思路,以实践项目《PKer》为例,对游戏的战斗系统架构和相关功能架构进行简单的罗列和说明。 图4-2 实践项目《PKer》战斗系统及相关功能架构 说明: (1) 玩家从游戏大厅可以搜索加入房间或创建房间,与房间内其他玩家进行对战进入战斗(读者可根据自身游戏玩法考虑如何触发战斗,如常见的回合制游戏通过暗雷或明雷遇敌、玩家对点触发战斗); (2) 《PKer》中玩家可以直接在玩家属性配置界面中通过点选“宠物库”内的宠物即可获得对应宠物,并且通过设置“参战宠物”可让宠物在战斗中出战(读者可根据自身游戏玩法考虑宠物的获得途径,常见的途径有战斗捕捉、任务奖励等); (3) 《PKer》中玩家可以直接在玩家属性配置界面中通过点选“装备物品库”内的装备/物品即可获得对应装备/物品,装上装备将提高角色的属性使角色更具战斗力,放在物品栏的物品可以在战斗中使用回复Hp/Mp(读者可根据自身游戏玩法考虑装备物品的获得途径,常见的途径有战斗奖励、任务奖励等); (4) 《PKer》中玩家可以直接在玩家属性配置界面中更改职业,从而获得在战斗中使用的技能; (5) 进入战斗后,参战角色的属性由各个玩家配置(属性加点、装备、参战宠物、职业等)所决定; (6) 各玩家通过指令菜单下达指令从而控制自身角色和自身参战宠物(如有设置参战宠物)的行动。其中,使用技能能够选择使用自身职业的技能,使用物品能够选择使用物品栏的物品; (7) 战斗动画主要由多个协同程序组成来实现,根据各个参战人物/宠物的下达的战斗指令对应的播放其行为的动画,不同的行为动画内容不一样; (8) 而在技能动画中,为了增加回合制战斗的画面感和可玩性,对不同的技能配置了不同的特效播放方式,让特效动画更华丽更多元化,详细实现方式在本文5.3.6和5.3.7小节有阐述; 第5章 回合制战斗系统实现 5.1 战斗系统相关消息结构体的定义 结构体消息是服务器与客户端沟通的“共同语言”,结构体消息在客户端和服务器分别定义并且数据结构是相同,发送方填写结构体数据,接收方先获取消息头的消息类型ID和长度,再判断是哪种类型的消息并转换成该类型结构体,然后读取内容并进行对应的处理。 以下是Unity客户端(C#)中定义的相关结构体消息,服务器的定义与其一致,但由于服务器(C++)与客户端(C#)的编程语言不同所以定义语法有些许区别。 //战斗初始化消息 public struct MSG_BATTLE_INIT { public ushort nMsgID; //消息类型ID public ushort nLen; //结构体长度 public uint nBattleID; //战局ID [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] public BATTLE_SPRITE_INIT[] battleSprites;//所有战斗角色基本数据 }; //战斗角色(人物/宠物)基本数据消息 public struct BATTLE_SPRITE_INIT { public uint nID; //玩家/宠物ID(客户端用此值判断是否有战斗成员,可能还会用来标识自身和自身宠物) [MarshalAs(UnmanagedType.ByValArray, SizeConst = 22)] public byte[] strName; //战斗人物/宠物名字 public uint nImageID; //图形及动画控制器ID public short nHpMax; //最大HP public short nHp; //当前HP public short nMpMax; //最大MP public short nMp; //当前MP }; //战斗指令消息 public struct MSG_BATTLE_COMMAND { public ushort nMsgID; //消息类型ID public ushort nLen; //结构体长度 public ushort nActorType; //行动者类型(1=人物 2=宠物,由服务端赋值,客户端不需要赋值) public ushort nActorIndex; //行动者索引(由服务端赋值,客户端不需要赋值) public ushort nActionType; //行动的类型 public ushort nTargetIndex; //目标索引 public ushort nParam; //参数(根据行动类型而定,如使用技能则是技能ID,使用物品则是物品栏位置,切换战宠则是宠物栏位置) }; //战斗动画播放消息(客户端收到此消息后开始播放战斗动画) public struct MSG_BATTLE_ANIM_BEGIN { public ushort nMsgID; //消息类型ID public ushort nLen; //结构体长度 }; //战斗动画消息 public struct MSG_BATTLE_ANIM { public ushort nMsgID; //消息类型ID public ushort nLen; //结构体长度 public ushort nActorIndex; //行动者索引 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] public ushort[] nTargetIndex; //目标索引(初始值为0,0则无目标) public ushort action_type; //行动类型 public ushort nParam; //参数(根据行动类型而定,如使用技能则是技能ID,使用物品则是物品栏位置) [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] public short[] nActorHp; //行动者Hp影响(<0:扣血,> 0:加血,0=无任何影响) [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] public short[] nActorMp; //行动者Mp影响 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] public short[] nTargetHp; //目标Hp影响(<0:扣血,>0:加血,0=无任何影响) [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] public short[] nTargetMp; //目标Mp影响 }; 5.2 服务器战斗系统的实现 5.2.1 服务器战斗系统相关结构体 图5-1 服务器战斗系统的相关结构体的关系图 //战局结构体 struct BATTLE { UINT nBattleID; //战局ID BATTLE_SPRITE battleSprites[20]; //战斗角色(人物/宠物)数组 std::list<USHORT> team1_survivors_index; //队伍1存活者索引list std::list<USHORT> team2_survivors_index; //队伍2存活者索引list USHORT nSpriteTotal; //参战角色总数 std::multimap<float, MSG_BATTLE_COMMAND*> m_CommandMap;//玩家战斗指令字典,用于保存每回合接收的战斗指令消息,Key为角色速度数值 } 说明: battleSprites数组的索引0-4位置分别为队伍1人物1、队伍1人物2……,索引5-9位置分别为队伍1人物1的宠物、队伍1人物2的宠物……,类似的,索引10-14为队伍2人物,索引15-19为队伍2宠物,人物索引+5即该人物的宠物的索引,根据人物索引即可获得其宠物索引 //战斗角色(人物/宠物) struct BATTLE_SPRITE { BATTLE_SPRITE_DATA *pBattleData; //战斗数据(包含Hp、Mp、攻击力等) CLIENT_DATA *pClient; //客户端数据(CLIENT_DATA包含客户端IOCP完成键、套接字、玩家游戏数据等数据,本文不阐述,请读者根据自身游戏服务器架构设计) USHORT nType; //角色类型,1=人,2=宠物 USHORT bCommand; //是否已经下命令,0=没下
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 学术论文 > 其他

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服