游戏引擎mota-js-v3.0 施工记录
作者:互联网
前言
mota-js是一款用于做出魔塔类型游戏的HTML5 2d游戏引擎(github项目地址),目前最新的版本是v2.66,由于原主力开发已经工作,因此很长一段时间没有大版本的更新。
最近在用样板做一个游戏的时候,体会到了样板的一些限制。主要有:1. 地图尺寸受限,超过一定尺寸(30x30)会到达性能瓶颈,尤其是在手机上会很卡顿。2. 素材尺寸受限。贴图种类被限制在32x32或32x48,尺寸更大做起来会很麻烦,没有一个精灵系统。3. 实现一些特效很困难。样板中大量使用了dom来分割地图场景与状态栏UI,一些联动特效难以实现,并且使用dom对做游戏开发而非前端开发的人来说是个噩梦。
去年针对这些问题改了一版pre3.0的运行时系统,但现在回看感觉问题很多,首先是设计模式选取不当,造成理解困难,其次对原样板的耦合太多,被原有框架所限制,导致渲染引擎的能力没有发挥出来。因此这两天决定重启项目,解决之前的问题。
运行时系统简介
这里主要施工的部分是运行时,编辑器有另外一位大佬在施工。游戏引擎的运行时系统,如果是复杂的游戏,其系统规模将会是庞大的,如图是《游戏引擎架构》一书中对现代游戏引擎的系统架构描述。
这样的架构在unity、ue4等引擎中有完整的体现,但在我要施工的对象上不可能有这么多。
HTML5游戏是运行在浏览器上的,因此浏览器解决了A1-A3、B、C以及部分D的问题。其次因为是2d游戏,许多核心系统中的东西用不到。然后因为魔塔是棋盘类单机游戏,位置是确定的方格,因此也不需要碰撞检测、骨骼动画、在线多人等模块。最后,因为是在原样板基础上进行的二次开发,很多游戏算法都已经实现过了。因此算下来,借助成熟的第三方库,工作量并不大,对预期的架构图修剪如下,预期工期在15天左右。
施工日志
4-19
调研第三方库:
- 渲染引擎PIXI
- 动画库tweenjs
4-20
实现资产管理(AssetsManager)。
资产管理中存在的坑:
- PIXI的材质对象在载入图片时,如果图片超出一定尺寸(网上说是1024)会导致图片渲染失败,这是webgl存在的问题,无法解决。因此在导入材质之前,需要将原始图集进行手动裁剪,切分成一个个img元素。
4-21
实现动画管理(AnimationManager)、精灵管理(SpriteManager)的部分功能。
4-22
实现一个地图的原型:瓦片地图绘制与角色移动。
对系统框架的进一步总结:
4-23
- 实现一个UI的原型:对话框的绘制与控制消失
– 对话框基于窗口基类(WinBase),这是UI的基本单元,包含基本的坐标、长宽属性,并且可以有皮肤(WinSkin),具有聚合组件的功能。
– 组件(Components)是使能窗口的重要部分,可以接收消息控制窗口属性,如,在对话框窗体注册一个“点击响应”(tapScreen)组件,其功能为关闭当前窗口。
– 对话框文字格式控制。控制字符,比如"\r \n"之类的,对文字内容进行格式控制,重写了原样板的实现,可以通过注册实现任意格式控制,如字体变化、透明度变化等等。 - 实现寻路与路线绘制
– 寻路使用bfs,复用原样板的函数。路线绘制重写,功能性与原样板一致。
– 对控制器(Controller)概念思考:一个控制器,响应用户操作,控制一个对象,对象可以是角色、UI,也可以没有特定对象,表示实现某种操作(比如响应点击操作)。当有特定对象时,被控制的对象是完全“受控”于控制器的。举个例子:勇士在被控制移动时,使用了寻路功能,那么此时,寻路走动的过程中,会不断调用的move,而这个move,是勇士自身的方法,还是控制器的方法?如果注意了控制器的概念,那么这里就应该是调用控制器的方法,这样做的好处是,把操作与对象的行为分离了,便于后续实现录像系统——操作全部由控制系统管理,对象无需关心操作。
记录一个坑:
ES6中的箭头函数()=>{} 与 function有一个重要区别,那就是箭头函数中的this绑定的是函数体外的,也就是说在声明的时候就绑定了this,而不是像function一样在调用的时候才绑定。
这两部分需要后续补充的内容:
- 窗口的更多功能,包括不同位置显示,自适应大小,需要后续补充。
- 需要后续补充更多组件
- 寻路移动的路线目前没有UI显示,后续UI完善了在这里加上。
4-24
今天主要实现事件系统和消息管理的部分架构。
之前的进度到了勇士能在地图移动,但是没办法和地图上的事件进行交互,主要就对这块进行施工。
事件、角色的概念
事件,在传统角色扮演类游戏中一般特指会产生一系列剧情、动作的触发点或者npc,角色,通常指的是主角和npc一类会动会产生行为的对象。
在设计事件系统的架构的过程中,理论上可以把地图上能跑能动的有属性的对象都当成是角色,但纯图块(block)、和角色(带事件的块如npc)和勇士(玩家控制的对象)显然不属于同一种,单明显带有一种递增的关系:
block -> actor -> hero (具体的关系等之后施工完了再完善)
在实现中,如果把所有带事件的点,都当成是一个个“角色”,那么勇士触发事件就可以当作是与角色之间的【交互】,简单的例子比如碰撞事件。
实现碰撞事件过程如下:
- 载入当前地图后,把地图上事件层的图块都升级为actor(这是为了方便起见,实际上只需要对含有事件的块进行升级)
- 勇士移动过程中,如果前方不可移动,判断是否有碰撞角色,是就获取这个碰撞角色存在的事件,执行该事件。
这个过程实现的是【碰撞】这类交互,但是实际游戏中,不止碰撞,比如有的点是空点,有的点是【战后事件】、【拾取后事件】……对原样板有的或没有的总结有如下类型的事件:
事件类型:
- 碰撞类事件,包括与块碰撞、角色碰撞(@code: collision) !
- 到达地图点事件,(@code: arrive) !
- 离开地图点事件,(@code: leave)!
- 战后事件:战后触发(@code: afterBattle)!
- 战前事件:战前触发(@code: beforeBattle)!
- 拾取后事件(@code: getItem)!
- 开门后事件(@code: afteropendoor)!
- 开门前事件(@code: beforeopendoor)!
- 自动事件(@code: auto)
- 楼层转换(@code: changeFloor)!(@code: firstArrive)(@code: eachArrive)(@code: firstLeave)(@code: eachLeave)
- 并行事件(@code: tick) —— 慎用
其中打!的是在地图上定义的事件,这些可能还不是全部,那么问题来了,要对所有事件单独写代码去判断吗?原样板是这么做的,感觉工作量会很恐怖,我怕工期赶不及,所以有了下面的消息管理。
消息管理
消息管理(MessageManager)是一个处理行为产生的消息的模块。
这里定义一下“行为”的概念,一般来说,游戏中的对象都能产生行为,但不是所有行为都会产生消息。比如之前的控制器,是用户行为,但由于控制器是同步的,即时控制勇士,没有必要产生消息。但是勇士的行为会产生消息,因为勇士走出去触碰地图点,可能会产生诸如碰撞、到达、离开等一系列情况,这些情况是不由勇士自己处理的,必须交由消息中心处理。当勇士发送消息时,是一个生产者,消息管理中心的任务是找到一个消费者处理这个消息。
比如前面11类事件,就是11种以上的消息,这些消息会在对应的代码执行时,如果有消费者成功消费了这条消息,就会返回消费情况,让生产者来进行判断下一步的情况——这个过程中,生产者不需要知道自己的消息被谁消费。这样只需要在合适的地方加入消息生产,就可以比较快速灵活地实现以上事件的处理。
移动事件
在原样板中,事件与地图点是绑定的,这在写一些剧情的时候会很头疼,npc不能频繁移动,否则会满地都是事件点,真·移动事件可以解决这个问题。
在上面的事件实现中,所有事件点都会成为角色(Actor),角色可以包含数据,这个数据可以用于定位事件原点。这样移动事件本质上是移动角色,角色移动后,原点将不存在块,勇士不再触发事件,但触发移动后的角色时,就会触发其定位到原点的事件。
这一块刚开始做,需要对事件系统进行进一步的施工。
存在的问题
- 同类消息多次注册导致的冲突。当一个生产者有多个消费者时会出现这种问题,同样的问题也会出现在控制器,比如点击绑定了屏幕响应和勇者瞬移,一旦优先级不当,就有可能出现事件触发的瞬间就点下了响应。只能从设计上避免。
- 移动事件需要存储数据,但只有移动过的事件才需要,如果全部成为角色,很有可能没法区分,或者是需要一定开销去区分谁的数据需要进存档。(也许这不是问题,需要进一步测试)
标签:mota,code,游戏,角色,js,v3.0,事件,样板,勇士 来源: https://blog.csdn.net/u011602557/article/details/105672766