这个游戏基本上是建立在JavaScript模块化的开发基础上进行封装的,对游戏里面需要使用到的游戏场景进行了封装,分别实现了Game,Sprite,enemy,player, base,Animation 等游戏类,后续代码还可以继续优化,最终实现的效果如下:

其他的所有核心代码已经开源:https://github.com/xiugangzhang/SuperMarioGame

在线预览游戏效果:

http://htmlpreview.github.io/?https://github.com/xiugangzhang/SuperMarioGame/blob/master/index.html

感兴趣的朋友可以对其进行继续优化,加上后续的其他功能!

其中游戏Game类代码如下:

  1. // 完成Game类的封装
  2. function Game(cfg) {
  3. for (var attr in cfg) {
  4. // 这里的this指的就是Game的对象
  5. this[attr] = cfg[attr];
  6. }
  7. }
  8. // 定义原型方法和属性
  9. Game.prototype = {
  10. constructor: Game,
  11. // 游戏画布的初始化(这个是游戏画布的默认宽度和高度)
  12. width: 640,
  13. height: 480,
  14. // 画布canvas和绘图句柄gc(在构造函数中已经完成了初始化)
  15. canvas: null,
  16. gc: null,
  17. // 帧速率和时间间隔
  18. FPS: 40,
  19. sleep: 0,
  20. // 游戏中的精灵
  21. sprites: null,
  22. // 游戏中的运动的背景
  23. skyOffset: 0,
  24. grassOffset: 0,
  25. treeOffset: 0,
  26. nearTreeoffset: 0,
  27. TREE_VELOCITY: 20,
  28. FAST_TREE_VELOCITY: 40,
  29. SKY_VELOCITY: 8,
  30. GRASS_VELOCITY: 75,
  31. lastTime: 0,
  32. lastUpdateFPS: 0,
  33. lastUpdateTime: 0,
  34. // 游戏场景的初始化(主要场景参数的初始化处理)
  35. init: function () {
  36. // 直接手动创建canvas元素
  37. this.canvas = document.createElement("canvas");
  38. this.canvas.width = this.width;
  39. this.canvas.height = this.height;
  40. document.body.appendChild(this.canvas);
  41. // 设置我的绘图句柄
  42. this.gc = this.canvas.getContext("2d");
  43. // 初始化键盘的事件
  44. this.initEvent();
  45. // 帧速率不为空,设置我的间隔时间
  46. if (this.FPS) {
  47. this.sleep = Math.floor(1000 / this.FPS);
  48. }
  49. // 当前的精灵(要么是自己, 要么是一个空数组)
  50. this.sprites = this.sprites || [];
  51. // 对每一个精灵完成初始化
  52. for (var i = 0, len = this.sprites.length; i < len; i++) {
  53. this.sprites[i].init();
  54. }
  55. },
  56. // 初始化键盘的事件
  57. initEvent: function () {
  58. // 按下按键
  59. document.addEventListener("keydown", function (ev) {
  60. keyState[ev.keyCode] = true;
  61. console.log(keyState);
  62. }, true);
  63. // 松开按键
  64. document.addEventListener("keyup", function (ev) {
  65. keyState[ev.keyCode] = false;
  66. console.log(keyState);
  67. }, true);
  68. },
  69. // 游戏开始, 就进入到主循环
  70. start: function () {
  71. // this指向的是Game这个对象
  72. var Me = this;
  73. // 记录一下,游戏开始的时间
  74. Me.startTime = Date.now();
  75. // 主循环
  76. this.mainLoop = setInterval(function () {
  77. // 距离上一次间隔的时间
  78. var deltaTime = Me.sleep;
  79. // 在主循环的执行过程中来实现碰撞检测的功能(一直在不断地检测是否发生了碰撞)
  80. Me.run(deltaTime);
  81. }, Me.sleep);
  82. },
  83. // 主循环中需要执行的操作
  84. run: function (deltaTime) {
  85. // 显示当前游戏持续进行的时间(玩家在这个游戏中持续的时间就是他的分数)
  86. var playedTime = Date.now() - this.startTime;
  87. // 在主界面上面显示时间(span标签)
  88. document.getElementById("timeCount").innerHTML = playedTime.toString();
  89. document.getElementById("lifeCount").innerHTML = this.sprites[0].HP.toString();
  90. // 开始碰撞检测
  91. var coll = this.checkCollide();
  92. // 只要coll不为空, 就说明有其他玩家和我发生了碰撞
  93. if (coll) {
  94. // 如果发生敌人和玩家的碰撞, 就结束游戏(有三个生命值)
  95. if (this.sprites[0].HP > 0) {
  96. this.sprites[0].HP--;
  97. }
  98. }
  99. // 我是精灵角色中的第0个角色,直接得到我的生命值并显示
  100. document.getElementById("lifeCount").innerHTML = this.sprites[0].HP.toString();
  101. if (this.sprites[0].HP == 0) {
  102. // 1. 清空主循环中的定时器
  103. clearInterval(this.mainLoop);
  104. alert("Game Over.\n Your score : " + playedTime);
  105. // 2.直接退出程序
  106. return;
  107. }
  108. // 更新画布
  109. this.update(deltaTime);
  110. // 清空画布
  111. this.clear(deltaTime);
  112. // 重绘画布
  113. this.draw(deltaTime);
  114. // 进入主循环之后, 还要不断地处理接收键盘事件
  115. this.handleInput();
  116. },
  117. // 开始实现碰撞的检测, 返回true就表示发生了玩家和敌人的碰撞
  118. checkCollide: function () {
  119. // 1.拿到我的玩家这个对象
  120. var player = this.sprites[0];
  121. // 注意这里是从第一个场景中的人物和我来逐一检测(我是第0个人物, 其他的都是敌人)
  122. for (var i = 1, len = this.sprites.length; i < len; i++) {
  123. var sprite = this.sprites[i];
  124. // 对于游戏场景中的除了自己的其他所有的精灵和我一一进行碰撞检测
  125. var coll = sprite.collideWidthOther(player);
  126. if (coll) {
  127. return coll;
  128. }
  129. }
  130. return false;
  131. },
  132. // 更新精灵的状态
  133. update: function (deltaTime) {
  134. for (var i = 0, len = this.sprites.length; i < len; i++) {
  135. var sprite = this.sprites[i];
  136. // 开始更新每一个精灵的坐标状态(运动状态信息)
  137. sprite.update(deltaTime);
  138. }
  139. },
  140. // 清空画布信息
  141. clear: function () {
  142. // 清空画布
  143. this.gc.clearRect(0, 0, this.canvas.width, this.canvas.height);
  144. var fps = this.caculateFPS();
  145. this.fps = fps;
  146. // 显示帧速率到画布上面
  147. var now = Date.now();
  148. if (now - this.lastUpdateTime > 1000) {
  149. this.lastUpdateTime = now;
  150. this.lastUpdateFPS = fps;
  151. document.getElementById("fps").innerText = this.lastUpdateFPS.toFixed();
  152. }
  153. this.initGameMap();
  154. },
  155. // 绘制背景地图
  156. initGameMap: function () {
  157. var fps = this.fps;
  158. // 实现移动的位移量
  159. this.skyOffset = this.skyOffset < this.canvas.width ?
  160. this.skyOffset + this.SKY_VELOCITY / fps : 0;
  161. this.grassOffset = this.grassOffset < this.canvas.width ?
  162. this.grassOffset + this.GRASS_VELOCITY / fps : 0;
  163. this.treeOffset = this.treeOffset < this.canvas.width ?
  164. this.treeOffset + this.TREE_VELOCITY / fps : 0;
  165. this.nearTreeOffset = this.nearTreeOffset < this.canvas.width ?
  166. this.nearTreeOffset + this.FAST_TREE_VELOCITY / fps : 0;
  167. var sky = ImgCache["sky"],
  168. tree = ImgCache["tree-twotrunks"],
  169. nearTree = ImgCache["smalltree"],
  170. grass = ImgCache["grass"],
  171. grass2 = ImgCache["grass2"];
  172. this.gc.save();
  173. this.gc.translate(-this.skyOffset, 0);
  174. this.gc.drawImage(sky, 0, 0);
  175. this.gc.drawImage(sky, sky.width - 2, 0);
  176. this.gc.restore();
  177. this.gc.save();
  178. this.gc.translate(-this.treeOffset, 0);
  179. this.gc.drawImage(tree, 100, 240);
  180. this.gc.drawImage(tree, 1100, 240);
  181. this.gc.drawImage(tree, 400, 240);
  182. this.gc.drawImage(tree, 1400, 240);
  183. this.gc.drawImage(tree, 700, 240);
  184. this.gc.drawImage(tree, 1700, 240);
  185. this.gc.restore();
  186. this.gc.save();
  187. this.gc.translate(-this.nearTreeOffset, 0);
  188. this.gc.drawImage(nearTree, 250, 240);
  189. this.gc.drawImage(nearTree, 1250, 240);
  190. this.gc.drawImage(nearTree, 800, 240);
  191. this.gc.drawImage(nearTree, 1800, 240);
  192. this.gc.restore();
  193. this.gc.save();
  194. this.gc.translate(-this.grassOffset, 0);
  195. this.gc.drawImage(grass, 0, this.canvas.height - grass.height);
  196. this.gc.drawImage(grass, grass.width - 5,
  197. this.canvas.height - grass.height);
  198. this.gc.drawImage(grass2, 0, this.canvas.height - grass2.height);
  199. this.gc.drawImage(grass2, grass2.width,
  200. this.canvas.height - grass2.height);
  201. this.gc.restore();
  202. },
  203. // 绘制背景滚动的效果
  204. caculateFPS: function (now) {
  205. if (now == undefined) {
  206. now = Date.now();
  207. }
  208. var fps = 1000 / (now - this.lastTime);
  209. this.lastTime = now;
  210. return fps;
  211. },
  212. // 开始重新绘制精灵
  213. draw: function (deltaTime) {
  214. for (var i = 0, len = this.sprites.length; i < len; i++) {
  215. var sprite = this.sprites[i];
  216. // 开始绘制
  217. sprite.draw(this.gc);
  218. }
  219. },
  220. // 游戏中的处理用户的输入
  221. handleInput: function () {
  222. for (var i = 0, len = this.sprites.length; i < len; i++) {
  223. var sprite = this.sprites[i];
  224. // 先判断一下,这个精灵有没有handleInput属性
  225. if (sprite.handleInput) {
  226. // 如果这个精灵有这个属性或者方法的话, 就去调用精灵自己的处理函数
  227. sprite.handleInput();
  228. }
  229. }
  230. }
  231. }

鉴于代码太多,已经将SuperMario的所有核心代码开源:

开源地址:https://github.com/xiugangzhang/SuperMarioGame

【JavaScript游戏开发】使用HTML5+Canvas+JavaScript 封装的一个超级马里奥游戏(包含源码)的更多相关文章

  1. 高性能动画!HTML5 Canvas JavaScript框架KineticJS

    高性能动画!HTML5 Canvas JavaScript框架KineticJS KineticJS是一款开源的HTML5 Canvas JavaScript框架,能为桌面和移动应用提供高性能动画,并 ...

  2. HTML5 Canvas中实现绘制一个像素宽的细线

    正统的HTML5 Canvas中如下代码 ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(10, 100); ctx.lineTo(300,100); c ...

  3. Unity 2D游戏开发高速入门第1章创建一个简单的2D游戏

    Unity 2D游戏开发高速入门第1章创建一个简单的2D游戏 即使是如今,非常多初学游戏开发的同学.在谈到Unity的时候.依旧会觉得Unity仅仅能用于制作3D游戏的. 实际上.Unity在2013 ...

  4. 【JavaScript游戏开发】JavaScript+HTML5封装的苏拉卡尔塔游戏(包含源码)

    /** 苏拉克尔塔游戏 * 思路: * 1.棋盘设置:使用HTML5的canvas标签绘制整个棋盘 * 2.点击事件:当页面被点击时,获取点击的x,y像素点,根据此像素点进行判断,再在合适位置绘制黑红 ...

  5. 【原创】使用HTML5+canvas+JavaScript开发的原生中国象棋游戏及源码分享

    目前已经实现的功能: V1.0 : 实现棋子的布局,画布及游戏场景的初始化V2.0 : 实现棋子的颜色改变V3.0 :实现所有象棋的走棋规则V4.0 : 实现所有棋子的吃子功能 GItHub源码下载地 ...

  6. 自己写的HTML5 Canvas + Javascript五子棋

    看到一些曾经只会灌水的网友,在学习了前端之后,已经能写出下载量几千几万的脚本.样式,帮助大众,成为受欢迎的人,感觉满羡慕的.我也想学会前端技术,变得受欢迎呀.于是心血来潮,开始学习前端知识,并写下了这 ...

  7. 【读书笔记《Android游戏编程之从零开始》】12.游戏开发基础(Canvas 画布)

    1.Canvas 画布 画布类 Canvas 封装了图形和图片绘制等内容,此类常用的函数说明如下: drawColor(int color) 作用:绘制颜色覆盖画布,常用于刷屏 参数:颜色值,也可用十 ...

  8. 使用PhoneGap开发基于Html5应用二:第一个PhoneGap应用:百度

    上一篇博文使用PhoneGap开发基于Html5应用一:PhoneGap简单介绍 中我介绍了怎样从phonegap官网上下载源代码并启动第一个应用,今天我们把phonegap的应用略微改一下,让他实现 ...

  9. 如何实现一个HTTP请求库——axios源码阅读与分析 JavaScript

    概述 在前端开发过程中,我们经常会遇到需要发送异步请求的情况.而使用一个功能齐全,接口完善的HTTP请求库,能够在很大程度上减少我们的开发成本,提高我们的开发效率. axios是一个在近些年来非常火的 ...

随机推荐

  1. Hard模式题目

    先过一下Hard模式的题目吧.   # Title Editorial Acceptance Difficulty Frequency   . 65 Valid Number     12.6% Ha ...

  2. 查看编译器的默认include 路径

    echo | gcc -v -x c++ -E - echo | g++ -v -x c++ -E - `gcc -print-prog-name=cc1plus` -v `g++ -print-pr ...

  3. CSDN处理问题神速,顶你,为你点32个赞!

    今天10点左右发表了一篇文章,发表之后.文章状态待审核,博文首页不能显示文章,例如以下图所看到的: 于是果断给官网发了第一封Email.10点19分.CSDN给予回复,内容例如以下: 尊敬的用户您好: ...

  4. 瀑布流 ajax 预载入 json

    pbl.json[模拟后台json数据]: [     {         "id": "511895",         "title": ...

  5. UVALive - 6910 (离线逆序并查集)

    题意:给处编号从1~n这n个节点的父节点,得到含有若干棵树的森林:然后再给出k个操作,分两种'C x'是将节点x与其父节点所连接的支剪短:'Q a b'是询问a和b是否在同一棵树中. 题解:一开始拿到 ...

  6. ScheduleJobFactory

    /* * file name: ScheduleJobFactory.java * copyright: Unis Cloud Information Technology Co., Ltd. Cop ...

  7. linux下apache+openssl配置记录

    软件环境 Apache Httpd 2.2.29 (http://httpd.apache.org ) OpenSSL 1.0.1h (http://www.openssl.org/source ) ...

  8. Harry Potter and the Order of the Phoenix

    书名:Harry Potter and the Order of the Phoenix 作者:J.K. Rowling 篇幅: 870P 蓝思值:950L 用时: 22天 工具: 有道词典 [透析成 ...

  9. FreeBSD内核之中的一个 ALQ机制的使用

    背景: 笔者由于一个项目,这段时间在使用FreeBSD进行内核模块的编程. 之前做过一段时间的Linux下驱动模块编程.对Linux下的模块编程还算熟悉. 如今突然转到FreeBSD下.尽管Linux ...

  10. bzoj3224: Tyvj 1728 普通平衡树(平衡树)

    bzoj3224: Tyvj 1728 普通平衡树(平衡树) 总结 a. cout<<(x=3)<<endl;这句话输出的值是3,那么对应的,在splay操作中,当父亲不为0的 ...