javascript高仿热血传奇游戏实现代码

前言

游戏的第一个版本开发于14年,浏览器端使用html+css+js,服务端使用asp+php,通讯采用ajax,数据存储使用access+mySql。不过由于一些问题(当时还不会用node,用asp写复杂的逻辑真的会写吐;当时对canvas写的也少,dom渲染很容易达到性能瓶颈),已经废弃。后来用canvas重制了一版。本文写于18年。

1.开发前的准备

为什么要用Javascript来实现一款比较复杂的PC端游戏

1.js实现PC端网游是可行的。随着PC、手机硬件配置的升级和浏览器的更新换代,以及H5各种库的发展,js实现一款网游的难度越来越低。这里的难度主要是两方面:浏览器的性能;js代码是否足够易于扩展,以满足于一款逻辑极其复杂的游戏的迭代。

2.现阶段的js游戏里,很少有规模较大的可供参考。涉及到多人联机、服务端数据存储、复杂交互的游戏,大多数(几乎全部)都是用flash开发的。但是flash毕竟在衰落,而js发展迅速,并且只要有浏览器就可以运行。

为什么选择了一款2001年的热血传奇游戏

第一个原因是对老游戏的情怀; 当然更重要的另一个原因是,别的游戏要么我不会玩、要么我会玩但没有素材(图片、音效等)。花很大精力去收集一个游戏的地图、人物怪物模型、物品和装备图,然后去处理、解析一遍再用于js开发,我觉得是浪费时间。

由于我以前搜集了一些传奇游戏的素材,并且幸运地找到了提取热血传奇客户端资源文件的方法( github地址 ),所以可以直接开始写码,省去了一些准备时间。

可能的困难

1.浏览器的运行性能:这个应该是最困难的一点。假如游戏要保持40帧,那么每帧只有25ms的时间留给js计算。并且由于渲染通常比计算耗性能,实际上留给js的时间只有10毫秒左右。

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

2.整体设计

浏览器端

画面渲染使用canvas。

相比dom(div)+css,canvas可以处理比较复杂的场景渲染和事件管理,例如下面这个场景,涉及了四张图片:玩家、动物、地上的物品、最下层的地图图片。(实际还有地上的影子,鼠标指向人物、动物、物品时出现的相应名称,以及地面上的阴影。为了方便读懂,先不考虑这么多内容。)

这时,如果希望实现“点击动物、攻击动物;点击物品、捡起物品”的效果,那么需要对动物和物品进行事件监听。如果采用dom的方式,那么会出现几点难于处理的问题:

a.渲染的顺序和事件处理的顺序不同(有时候z-index小的需要先处理事件),需要额外处理。例如这个上面的例子里:点击怪物、点击物品的时候也容易点到人物,那么需要给人物做“点击事件穿透”的处理。而且事件处理的顺序不固定:假如我有一个技能(例如游戏里的治疗)需要点人物才可以释放,那么这时人物又需要有事件监听。所以一个元素是否需要处理事件、处理事件的先后,是随着游戏状态的不同而变化的,而 dom的事件绑定已经不能满足需要 。

b.有关联的元素难以放在同一个dom节点中:例如玩家的模型、玩家的名字和玩家身上的技能画效,理想情况下是放在一个 <div> 或者 <section> 容器里,便于管理(这样几个元素的定位就可以继承父元素,不用分别处理位置了)。但是这样,z-index会很难处理。例如玩家A在玩家B的上面,那么A会被B遮挡,因此需要A的z-index小一些,但是又需要让玩家A的名字不会被B的名字或者影子遮挡,就无法实现。简单点说, dom结构的可维护性会牺牲画面展示的效果,反之亦然 。

c.性能问题。即使牺牲了效果,用dom渲染,势必出现很多嵌套关系,所有元素的style都在频繁变化,连续触发浏览器的repaint甚至reflow。

canvas渲染逻辑与项目逻辑分离

如果将canvas的各种渲染操作(如 drawImage 、 fillText 等)与项目代码放在一起,那么势必导致项目后期无法维护。翻了一下几款现有的canvas库,结合vue的数据绑定+调试工具的方式,搞了一个全新的canvas库Easycanvas( github地址 ),并且像vue一样支持通过一个插件来调试canvas中的元素。

这样,整个游戏的渲染部分就容易很多,只需要管理游戏当前的状态、并且根据服务端从socket传回来的数据去更新数据就可以。 “数据的变化引起视图的变化”这个环节由Easycanvas负责 。例如下图的玩家包裹物品的实现,我们只需要给出包裹容器的位置、背包里每个元素的排布规则,然后将每个包裹的物品绑定到一个array上,然后去管理这个array即可(数据映射到画面的过程由Easycanvas负责)。

例如,5行8列共计40个物品的样式可以通过如下的形式传递给Easycanvas(index为物品索引,物品x方向间距36,y方向间距32)。而这个逻辑是一成不变的,无论物品的数组怎样变化、包裹被拖拽到什么位置,每个物品的相对位置都是固定的。至于canvas上的渲染则完全不需要项目本身来考虑,所以可维护性较好。

style: {
 tw: 30, th: 30,
 tx: function () {
 return 40 + index % 8 * 36;
 },
 ty: function () {
 return 31 + Math.floor(index / 8) * 32;
 }
}

canvas分层渲染

假设:游戏需要保持40帧,浏览器宽800高600,面积48万(后面称48万为1个屏幕面积)。

如果用同一个canvas来呈现,那么这个canvas的帧数40,每秒至少需要绘制40个屏幕面积。但是同一个坐标点很可能出现多个元素重叠的情况,例如底部的UI、血条、按钮就是重叠放置,他们又共同遮挡了场景地图。所以这些加在一起,每秒浏览器的绘制量很容易达到100个屏幕面积以上。

这个绘制是很难优化的,因为整个canvas画布的任何一处都在进行视图的更新:可能是玩家和动物的移动、可能是按钮的特效、可能是某个技能效果的变化。这样的话,即使玩家不动,由于衣服“随风飘飘”的效果(其实是精灵动画播放到下一张图),或者是地面上出现了一瓶药水,都要引起整个canvas的重绘。因为 游戏中几乎不可能出现某一帧的画面与上一帧毫无区别的情况,即使是游戏画面的一个局部,也很难保持不变 。整个游戏的画面永远在更新。

因为 游戏中几乎不可能出现某一帧的画面与上一帧毫无区别的情况 ,画面永远在更新。

因此,这次我采用了3个canvas重叠排布的方式。由于Easycanvas的事件处理支持传递,因此即使点到了最上面的canvas,如果没有任何元素结束了某一次点击,后面的canvas也可以接到这次事件。3个canvas分别负责UI、地面(地图)、精灵(人物、动物、技能特效等):

这样分层的好处是,每层最大帧数可以根据需要来调整:

例如UI层,因为很多UI平时是不动的,即使动也不会需要太精密的绘制,所以可以适当降低帧数,例如降低到20。这样假如玩家的体力从100降低到20,那么可以在50ms内更新视图,而50ms的切换是玩家感觉不出来的。因为像体力这种 UI层数据的变化很难在很短的时间内连续变化多次,而50ms的延迟是人很难感知的,所以不需要频繁的绘制 。假如我们每秒节约了20帧,那么很可能可以节约10个屏幕面积的绘制。

再如地面,只有玩家移动的时候,地图才会变化。这样,如果玩家不动,那么每帧可以省去1个屏幕面积。由于需要保证玩家移动时的流畅感,地面的最大帧数不宜太低。假如地面为30帧,那么玩家不动时,每秒就可以节约30个屏幕面积的绘制(这个项目中,地图是几乎绘满屏幕的)。而且其它玩家、动物的移动不会改变地面,也不需要重绘地面这一层。

精灵层最大帧数不能降低,这层会展示游戏的人物动作等核心部分,所以最大帧数设置为40.

这样,每秒绘制的面积,玩家移动时可能是80~100个屏幕面积,而玩家不移动时可能只有50个屏幕面积。游戏中,玩家停下来打怪、打字、整理物品、释放技能都是站立不动的,因此 大量的时间里都不会触发地面的绘制,对性能的节约很大 。

服务器端

由于目标是js实现一款多人网游,所以服务端使用Node,使用socket与浏览器通讯。这样做还有一个好处,就是一些 公用的逻辑可以在两端复用 ,例如判断地图上某个坐标点是否存在障碍物。

Node端的玩家、场景等游戏相关数据全部存储与内存中,定期同步至文件。每次Node服务启动时,将数据从文件读取至内存。这样可以玩家较多时,文件读写的频率成指数级上升,从而引发的性能问题。(后来为了提高稳定,为文件读写增加了一个缓冲,“内存-文件-备份”的方式,以免读写过程中服务器重启导致的文件损坏)。

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

3.场景地图的实现

地图场景

下面开始介绍地图场景部分,仍然是依赖 Easycanvas 进行渲染。

思考

由于玩家是始终固定在屏幕中心的,所以玩家的移动,实际上是地图的移动。例如玩家像左跑,地图就向右平移即可。刚才已经提到,玩家处于3个canvas中的中间一层,而地图属于底层,因此玩家一定遮挡地图。

这样看起来是合理的,但是假如地图中有一棵树,那么“玩家的层次始终高于树”就不对了。这时,有2种大的解决方案:

地图分层,“地面”与“地上”拆开。将玩家处于两层之间,例如下图,左侧是地上、右侧是地面,然后重叠绘制,把人物夹在中间:

这样看似解决了问题,其实引入了2个新的问题:第一个是,玩家有时可能会被“地上”的东西遮挡(例如一棵树),有时又需要能够遮挡“地上”的东西(例如站在这棵树的下方,头部会遮挡住树)。另一个问题是渲染的性能消耗会增加。由于玩家是时刻在变的,“地上”这一层需要频繁重绘。这样做也打破了最初的设计——尽量节约地面大地图的渲染,从而导致canvas的分层更加复杂。

地图不分层,“地面”与“地上”在一起绘制。当玩家处于树后的时候,将玩家的透明度设置为0.5,例如下图:

这样做只有一个坏处:玩家的身体要么都不透明、要么都半透明(怪物在地图上行走也会有这个效果),不会完全真实。因为理想的效果是存在玩家的身体被遮挡住一部分的场景的。但是这样做对性能友好,并且代码易于维护,目前我也采用了这个方案。

那么如何判断“地图”这张图片哪些地方是树呢?游戏通常会有一个大的地图描述文件(其实就是一个Array),通过0、1、2这样的数字来标识哪些地方可以通过、哪些地方存在障碍物、哪些地方是传送点等等。热血传奇中的这个“描述文件”就是48x32为最小单位进行描述的,所以玩家在传奇中的行动会有一种“棋盘”的感觉。单位越小越流畅,但是占用的体积越大、生成这个描述的过程也就越耗时。

下面开始正题。

实现

我找了一个朋友帮我导出热血传奇客户端中“比奇省”的地图,宽33600、高22400,是我电脑的几百倍大。为了避免电脑爆炸,需要拆分成多块加载。由于传奇的最小单元是48x32,我们以480x320将地图拆成了4900(70x70)个图片文件。

canvas的尺寸我们设定为800x600,这样玩家只需要加载3x3共计9张图片就可以铺满整个画布。800/480=1.67,那么为什么不是2x2?因为有可能玩家当前的位置正好导致有的图片只展示了一部分。如下图:

总结

以上所述是小编给大家介绍的javascript高仿热血传奇游戏实现代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

您可能感兴趣的文章:

  • JavaScript实现打地鼠小游戏
  • js实现一个猜数字游戏
  • js实现九宫格拼图小游戏
  • js实现打地鼠小游戏
  • js实现贪吃蛇小游戏(容易理解)
(0)

相关推荐

  • js实现九宫格拼图小游戏

    效果如下: 代码如下: <!doctype html> <html> <head> <meta charset="UTF-8"> <title>九宫格拼图</title> <style> *{ padding: 0; margin: 0; border: 0; } /* *是通配符,给所有的元素去掉默认样式,因为有的浏览器会默认加上一些样式,这可能会给布局带来问题 */ body{ width: 100

  • js实现打地鼠小游戏

    话不多说,请看代码: <!doctype html> <html> <head> <meta charset="utf-8"> <title>打地鼠</title> <style type="text/css"> #content { width:960px; margin:0 auto; text-align:center; margin-top:40px; } #form1 {

  • js实现贪吃蛇小游戏(容易理解)

    话不多说,请看代码: <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>贪吃蛇</title> <link rel="stylesheet" href="style.css"> <script src="style.js" >

  • js实现一个猜数字游戏

    看你需要猜几次才能猜到那个正确的数字! 效果图: 代码如下: <!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <title>猜数字游戏</title> <script type="text/javascript" cha

  • JavaScript实现打地鼠小游戏

    在写这个打地鼠的游戏开始首先要清楚每一步该做什么 然后再一步一步搭好框架再完善功能. 1.创建table三行三列,用来存放坑(图片) 2.获得所有的图片标签 3.老鼠的动作有①.冒出来 4.②. 老鼠跑了 5.③.老鼠被打死了 6.完善功能 一.首先实现第一步,用table创建一个三行三列的坑 <div id = "content"> <h2>打地鼠</h2><br/> <table id = "background&qu

  • javascript高仿热血传奇游戏实现代码

    前言 游戏的第一个版本开发于14年,浏览器端使用html+css+js,服务端使用asp+php,通讯采用ajax,数据存储使用access+mySql.不过由于一些问题(当时还不会用node,用asp写复杂的逻辑真的会写吐:当时对canvas写的也少,dom渲染很容易达到性能瓶颈),已经废弃.后来用canvas重制了一版.本文写于18年. 1.开发前的准备 为什么要用Javascript来实现一款比较复杂的PC端游戏 1.js实现PC端网游是可行的.随着PC.手机硬件配置的升级和浏览器的更新换

  • Android高仿2048小游戏实现代码

    刚开始进入Splash界面: 1.SplashActivity.Java(两秒后进入开始界面,Splash界面的布局只有一个图片,在博客后,会展示给大家看) public class SplashActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.a

  • JavaScript高仿支付宝倒计时页面及代码实现

    实现目标 一,页面在图一时开始进行倒计时(可以点击取消订单按钮,支付页面消失). 二,倒计时完毕,出现删除订单. 三,单击删除订单,弹出弹框,询问是否要真正删除订单. 四,单击确定,即可删除订单. 如上图所示效果展示,这里给出所有的源代码(如需运行,只需要修改EJS文件中引入的CSS路径和JS路径即可). 这个页面其实是自己业余时间,写的东西. 但是里面涉及到,倒计时,弹框,以及字体图(可以参照"如何制作字体图"章节)的相关知识. 这里分享出来,希望大家能从中学到自己想要的知识. 代码

  • iOS高仿微信表情输入功能代码分享

    最近项目需求,要实现一个类似微信的的表情输入,于是把微信的表情扒拉出来,实现了一把.可以从这里下载源码.看起来表情输入没有多少东西,不外乎就是用NSTextAttachment来实现图文混排,结果在实现的过程中遇到了很多小问题,接下来会一一介绍遇到过的坑.先上一张效果图: 一.实现表情选择View(WKExpressionView) 具体的实现就不细说了,主要功能就是点击表情时,将对应表情的图片名称通知给delegate. 二.实现表情textView(WKExpressionTextView)

  • 纯JavaScript 实现flappy bird小游戏实例代码

    前言: <flappy bird>是一款由来自越南的独立游戏开发者Dong Nguyen所开发的作品,游戏于2013年5月24日上线,并在2014年2月突然暴红.2014年2月,<Flappy Bird>被开发者本人从苹果及谷歌应用商店撤下.2014年8月份正式回归APP STORE,正式加入Flappy迷们期待已久的多人对战模式.游戏中玩家必须控制一只小鸟,跨越由各种不同长度水管所组成的障碍. 正文: 接下来就是一步一步来实现它 步骤1:页面布局,这儿就不多说了,页面内容如下:

  • javascript实现仿腾讯游戏选择

    到我们玩腾讯游戏的时候,会有很多选择,比如选择什么区,什么人物等.下面简单制作腾讯游戏选择. <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="Generator" content="EditPlus®"> <meta name="Author"

  • 使用Javascript写的2048小游戏

    最近心血来潮,项目结束了,使用javascript写个小游戏,练练收吧,写的不好还请各位大侠给出批评建议. HTML代码如下 <!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8" /> <link rel="stylesheet" href="2048.css"/> <scri

  • javascript 打字游戏实现代码

    效果如图所示:下面是核心代码 复制代码 代码如下: GAME = { //随机产生字母 randLetter: function() { var arrLetter = new Array("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K&qu

  • 基于JavaScript+HTML5 实现打地鼠小游戏逻辑流程图文详解(附完整代码)

    随着html5的兴起,那些公司对大型游戏的开发正在慢慢疏远,一.开发周期长:二.运营花费高:他们正找一些能够克服这些缺点的替代品.正好,html5的出现可以改变这些现状,在淘宝.京东等一些大型电商网站.QQ.微信等聊天软件都出现了html5的小游戏,这说明html5越来越受到大家的青睐.接下来我用javascript实现一个小型游戏---打地鼠. 一.游戏简介 打地鼠这个游戏相信大家都不陌生,也是童年时候一款经典的游戏.本次游戏的编写是以html文件形式完成的,并且使用HBulider软件进行编

  • Android高仿微信聊天界面代码分享

    微信聊天现在非常火,是因其界面漂亮吗,哈哈,也许吧.微信每条消息都带有一个气泡,非常迷人,看起来感觉实现起来非常难,其实并不难.下面小编给大家分享实现代码. 先给大家展示下实现效果图: OK,下面我们来看一下整个小项目的主体结构: 下面是Activity的代码: package com.way.demo; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import jav

随机推荐