宝哥软件园

javascript高仿传奇的mir游戏实现代码

编辑:宝哥软件园 来源:互联网 时间:2021-09-01

前言

游戏的第一个版本是14年开发的。浏览器使用html css js,服务器使用asp php,通讯使用ajax,数据存储使用access mySql。但是由于一些问题(当时没有使用node,用asp写复杂逻辑真的会吐;当时画布上的文字很少,dom渲染很容易达到性能瓶颈),被放弃了。后来,它被复制在画布上。这篇文章写于18年前。

1.开发前的准备

为什么要用Javascript来实现一个更复杂的PC端游戏

1.1.js在PC上实现网络游戏是可行的。随着PC和手机硬件配置的升级、浏览器的升级以及H5图书馆的发展,js实现一款网络游戏的难度越来越大。这里的难点主要在两个方面:浏览器的性能;js代码是否足够容易扩展以满足逻辑极其复杂的游戏的迭代。

2.目前很少有大规模的js游戏可供参考。大多数(几乎所有)涉及多人在线、服务器端数据存储和复杂交互的游戏都是用flash开发的。不过flash毕竟在走下坡路,而js发展很快,只要有浏览器就能运行。

你为什么选择一款2001年的传奇mir游戏

第一个原因是老游戏的情怀;当然,更重要的另一个原因是,其他游戏要么我不会玩,要么我会玩但没有素材(图片、音效等)。).花大量的精力去收集一张游戏地图、人物怪物模型、物品装备地图,然后再进行加工分析,进行js开发,这是浪费时间。

因为之前收集了一些传奇游戏素材,幸运的是找到了提取mir客户端资源文件(github地址)传奇的方法,可以直接开始写代码,节省了一些准备时间。

可能的困难

1.浏览器性能:这应该是最难的一点。如果游戏保持40帧,那么js计算每一帧只剩下25毫秒。因为渲染通常比计算消耗更多的性能,所以留给js的时间实际上只有10毫秒左右。

2.防作弊:如何避免用户直接调用接口或者篡改网络请求数据?因为目标是用js实现复杂的游戏,而任何网游都需要考虑这一点,才会有相对成熟的解决方案。这不是本文的重点。

2.总设计

浏览器端

画布用于图片渲染。

与dom(div) css相比,canvas可以处理更复杂的场景渲染和事件管理。例如,下面的场景涉及四张图片:玩家、动物、地面物体和最低的地图图片。(其实地面有阴影,鼠标指向人、动物、物体时对应的名字,地面有阴影。为了阅读方便,先不要考虑那么多内容。)

这时,如果想实现“点击动物并攻击动物;点击物品,拿起物品”,那么就要监控动物和物品的事件。如果采用dom,将有几个难题需要处理:

A.渲染的顺序与事件处理的顺序不同(有时如果z-index很小,则需要先处理事件),这需要额外的处理。比如上面的例子,在点击怪物和物品的时候很容易指向人,所以需要对人做“点击事件穿透”。而且事件处理的顺序也不是固定的:如果我有一个技能(比如游戏中的治疗)需要有人释放,那么这个人就需要有事件监控。所以一个元素是否需要处理事件,事件的顺序会随着游戏状态的不同而变化,dom的事件绑定已经不能满足需求。

b相关的元素很难放在同一个dom节点中:比如玩家的模型、玩家的名字和对玩家的技能绘制效果,理想情况下放在div或者section容器中便于管理(这样几个元素的定位可以继承父元素,不需要单独处理位置)。但这样的话,z指数会很难处理。比如A玩家在B玩家之上,那么A就会被B挡住,所以A的z指数要小一些,但是A玩家的名字不可能被B的名字或者影子挡住。简单来说,dom结构的可维护性会牺牲显示效果,反之亦然。

C.性能问题。即使牺牲效果用dom渲染,也会有很多嵌套关系,所有元素的样式都会频繁变化,不断触发浏览器的重画甚至回流。

画布渲染逻辑与项目逻辑是分离的

如果各种画布渲染操作(如drawImage、fillText等。)都与项目代码放在一起,这必然会导致项目的后期维护。纵观现有的几个canvas库,结合vue的数据绑定调试工具,创建了一个全新的canvas库Easycanvas(github地址),和vue一样,它支持通过插件调试canvas中的元素。

这样整个游戏的渲染部分就轻松多了,只需要管理游戏的当前状态并根据服务器从socket发回的数据更新数据即可。Easycanvas负责“数据变化引起的视图变化”的链接。比如下图中,我们只需要给出包裹容器的位置和背包中每个元素的排列规则,然后将每个包裹项绑定到一个数组,然后管理这个数组(Easycanvas负责数据映射到屏幕的过程)。

比如5行8列共40篇文章的风格可以传到Easycanvas(索引如下表(index为文章索引,X方向文章间距为36,Y方向文章间距为32)。而且这个逻辑是不变的,不管项目数组如何变化,把包拖到哪里,每个项目的相对位置都是固定的。至于画布上的渲染,项目本身完全不用考虑,可维护性更好。

style: { tw: 30,th: 30,tx : function(){ return 40 index % 8 * 36;},ty: function(){ return 31 math . floor(index/8)* 32;} }画布分层渲染

假设:游戏需要保留40帧,浏览器宽度800,高度600,面积48万(以下称48万为屏幕面积)。

如果使用同一个画布进行渲染,这个画布的帧数是40,每秒至少需要绘制40个屏幕区域。但是,多个元素可能在同一坐标点重叠。比如底部的UI、血条、按钮重叠,共同遮挡了场景图。因此,当这些加在一起时,浏览器很容易每秒绘制100多个屏幕。

这个图很难优化,因为视图在整个画布上到处都在更新:可能是玩家和动物的移动,也可能是按钮的特效,也可能是某个技能效果的变化。在这种情况下,即使玩家不动,也会因为衣服随风飘动的效果而重画整个画布(其实是把精灵动画播放到下一张图片),或者地上出现一瓶药水。因为游戏中某一帧几乎不可能和前一帧区分开来,甚至游戏画面的一部分也很难保持不变。整个游戏的画面总是在更新。

因为一帧几乎不可能和游戏中的前一帧区分开来,所以画面总是会更新。

所以这次我采用了三块画布重叠的方式。因为Easycanvas的事件处理支持传输,即使点击了顶部的canvas,如果没有元素结束一次点击,后续的canvas也可以接收到事件。三个画布分别负责UI、地面(地图)、精灵(人物、动物、技能和特效等。):

这种分层的优点是,可以根据需要调整每层的最大帧数:

比如在UI层,因为很多UIS通常是不动的,即使动了也不需要画得太精确,所以帧数可以适当减少,比如减少到20帧。这样,如果玩家体力从100减少到20,视图可以在50毫秒内更新,50毫秒的切换玩家是察觉不到的。由于体力等UI层数据的变化很难在短时间内连续多次变化,50ms的延迟也很难让人感知,所以没有必要频繁绘制。如果我们每秒保存20帧,我们可能可以保存10个屏幕区域。

和地面一样,地图只有在玩家移动时才会改变。这样,如果玩家不移动,每帧可以保存一个屏幕区域。地面最大帧数不能太低,因为需要保证玩家移动时的流畅度。如果地面有30帧,玩家不移动时每秒可以节省30屏(在这个项目中,地图几乎全是屏)。而且其他玩家和动物的移动不会改变地面,也不需要重新绘制地面图层。

Elf层的最大帧数不能降低,这个层会显示游戏的核心部分,比如人物动作,所以最大帧数设置为40。

这样,当玩家移动时,每秒的绘图区域可能是80 ~ 100个屏幕区域,但当玩家不移动时,只有50个屏幕区域。游戏中玩家停下来打怪、打字、整理东西、释放技能,所以很多时候不会触发地面的绘制,节省了很多性能。

计算机网络服务器

因为目标是用js实现多人在线游戏,所以服务器使用Node和socket与浏览器进行通信。另一个优点是一些常见的逻辑可以在两端重用,比如判断地图上某个坐标点是否有障碍物。

节点端的玩家、场景等游戏相关数据都存储在内存中,并定期同步到文件中。每次节点服务启动时,数据都会从文件中读取到内存中。这样,当玩家多的时候,读写文件的频率就会呈指数级增长,从而导致性能问题。(后来为了提高稳定性,增加了一个缓冲区“内存-文件-备份”,用于文件读写,避免读写时服务器重启造成文件损坏。).

节点有多层接口、数据和实例。该接口负责与浏览器进行交互。“数据”是一些静态的数据,比如一种药物的名称和效果,一个怪物的速度和体力,这是游戏规则的一部分。“实例”是游戏中的当前状态,例如,玩家身上的某个毒品就是“毒品数据”的一个例子。再比如“鹿实例”有“当前血量”属性,鹿A可能是10,鹿B可能是14,鹿本身只有“初始血量”。

3.场景地图的实现

地图场景

下面开始介绍地图场景,依旧依赖Easycanvas进行渲染。

思考

因为玩家始终固定在屏幕中央,玩家的移动实际上就是地图的移动。例如,如果玩家向左跑,地图可以向右平移。如前所述,玩家在三个画布的中间层,地图属于底层,所以玩家必须屏蔽地图。

这看似合理,但如果地图中有树,那么“玩家的等级总是比树高”就错了。此时,有两大解决方案:

地图分层,“地”与“地”分离。将玩家放在两层之间,如下图,左侧是地面,右侧是地面,然后重叠绘制,将人物夹在中间:

这看似解决了问题,实际上却引入了两个新的问题:第一,玩家有时可能会被“地上”的东西挡住(比如一棵树),有时需要能挡住“地上”的东西(比如站在树下,头会挡住树)。另一个问题是渲染的性能消耗会增加。因为玩家不断变化,“地上”层需要频繁重绘。这也打破了——的原有设计,尽可能的保存了大型地面地图的渲染,导致画布的分层更加复杂。

地图没有分层,“地”和“地”画在一起。当播放器在树后面时,将播放器的透明度设置为0.5,如下图所示:

只有一个缺点:玩家的身体要么是不透明的,要么是半透明的(即使怪物在地图上行走),这并不是完全真实的。因为理想的效果是有一个玩家身体被部分覆盖的场景。然而,这是性能友好的,并且代码易于维护。目前,我也采用了这个方案。

那么如何判断图片“地图”的哪些部分是树呢?游戏通常有一个大的地图描述文件(事实上,它是一个数组)。0、1、2等数字用来标识哪些地方可以通过,哪些地方有障碍物,哪些地方是传播点。mir传奇中的这个“描述文件”是以48x32为最小单位来描述的,所以传奇中玩家的动作会有一种“棋盘”的感觉。单位越小,越平滑,但是占用的体积越大,生成这个描述就越耗时。

让我们言归正传。

实现

找了个朋友帮我导出了mir客户端传说中“山毛榉省”的地图,宽33600,高22400,是我电脑的几百倍。为了避免电脑爆炸,需要将其拆分成几块进行加载。由于传说中的最小单位是48x32,我们将地图分割成4900(70x70)个图片文件,其中480x320个。

我们将画布的大小设置为800x600,这样玩家只需要加载3x3和总共9张图片就可以展开整个画布。800/480=1.67,那么为什么不是2x2呢?因为有可能玩家当前的位置只是导致一些图片只显示了一部分。下图:

摘要

以上就是边肖介绍的javascript高仿传奇的mir游戏实现代码。希望对大家有帮助。如果你有任何问题,请给我留言,边肖会及时回复你。非常感谢您对我们网站的支持!

更多资讯
游戏推荐
更多+