├── Animation.js ├── Base.js ├── Game.js ├── README.md ├── Sprite.js ├── enemy.js ├── index.html ├── player.js ├── preview.jpg └── res ├── bg.png ├── enemy.png ├── grass.png ├── grass2.png ├── player.png ├── sky.png ├── smalltree.png ├── tree-twotrunks.png ├── tree.png ├── 地上.mp3 ├── 游戏结束.mp3 └── 踩敌人.mp3 /Animation.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 添加一个动画类 3 | * @param cfg object类型的参数集 4 | * @constructor 5 | */ 6 | // 每次在创建new Animation()这个动画对象的时候都会来调用这个构造函数 7 | function Animation(cfg) { 8 | for (var attr in cfg) { 9 | this[attr] = cfg[attr]; 10 | } 11 | } 12 | 13 | // 添加原型方法 14 | Animation.prototype = { 15 | constructor: Animation, 16 | 17 | // Animation包含的Frame, 类型数组 18 | frames: null, 19 | // 包含的frame数量 20 | frameCount: -1, 21 | // 所使用的图片ID(在ImgCache中存放的key),字符串类型 22 | img: null, 23 | currentFrame: null, 24 | currentFrameIndex: -1, 25 | currentFramePlayed: -1, 26 | 27 | 28 | // 初始化Animation 29 | init: function () { 30 | // 首先更具ID获得image对象 31 | this.img = ImgCache[this.img] || this.img; 32 | 33 | // 当前所有的帧存储在一个数组中 34 | this.frames = this.frames || []; 35 | this.frameCount = this.frames.length; 36 | 37 | // 缺省从第0帧开始播放 38 | /*this.currentFrameIndex = 0; 39 | this.currentFrame = this.frames[this.currentFrameIndex]; 40 | // 当前帧是否已经播放完毕 41 | this.currentFramePlayed = 0;*/ 42 | this.setFrame(0); 43 | }, 44 | 45 | // 设置当前帧 46 | // 一个frame里面的属性: 47 | /* 48 | duration:100 49 | h : 60 50 | w : 50 51 | x : 0 52 | y : 0 53 | * */ 54 | setFrame: function (index) { 55 | this.currentFrameIndex = index; 56 | this.currentFrame = this.frames[index]; 57 | this.currentFramePlayed = 0; 58 | }, 59 | 60 | 61 | // 更新Animation的状态, deltaTime表示时间的变化量 62 | update: function (deltaTime) { 63 | // 判断当前的Frame 是否已经播放完成 64 | if (this.currentFramePlayed >= this.currentFrame.duration) { 65 | 66 | //播放下一帧 67 | if (this.currentFrameIndex >= this.frameCount - 1) { 68 | // 如果当前是最后一帧, 就播放第0帧 69 | this.currentFrameIndex = 0; 70 | } else { 71 | // 播放下一帧 72 | this.currentFrameIndex++; 73 | } 74 | 75 | // 设置当前帧的信息 76 | this.currentFrame = this.frames[this.currentFrameIndex]; 77 | this.currentFramePlayed = 0; 78 | } else { 79 | // 增加当前帧的已经播放的时间 80 | this.currentFramePlayed += deltaTime; 81 | } 82 | }, 83 | 84 | // 绘制Animation 85 | draw: function (gc, x, y) { 86 | var f = this.currentFrame; 87 | // 开始绘制 88 | gc.drawImage(this.img, f.x, f.y, f.w, f.h, x, y, f.w, f.h); 89 | }, 90 | 91 | } -------------------------------------------------------------------------------- /Base.js: -------------------------------------------------------------------------------- 1 | /** 2 | * { } 大括号,表示定义一个对象,大部分情况下要有成对的属性和值,或是函数 3 | [ ]中括号,表示一个数组,也可以理解为一个数组对象 4 | * @param srcList 5 | * @param callback 6 | * @returns {{}} 7 | */ 8 | // 自定义一个图片加载的函数,callback为所有的图片加载完成之后的回调函数 9 | function loadImage(srcList, callback) { 10 | // 定义一个图片数组,用于返回结果,{}这种数据类型表示用来存储json数据格式 11 | var imgs = {}; 12 | // 求出传过来的图片数量 13 | var totalCount = srcList.length; 14 | // 已经加载完成的图片初始为1 15 | var loadedCount = 0; 16 | for (var i = 0; i < totalCount; i++) { 17 | var img = srcList[i]; 18 | // 创建图片对象 19 | var image = imgs[img.id] = new Image(); 20 | image.src = img.url; 21 | 22 | // 开始加载 23 | image.onload = function () { 24 | loadedCount++; 25 | } 26 | } 27 | 28 | // 开始处理回调函数 29 | if (typeof callback == "function") { 30 | // 这里的this实际上指的是this对象,是window对象 31 | var self = this; 32 | 33 | function check() { 34 | // 如果已经加载完毕 35 | if (loadedCount >= totalCount) { 36 | // callback 是一个函数,function startDemo(){} 37 | // self是函数体内对象的指向, 指向window对象 38 | callback.apply(self, arguments); 39 | } else { 40 | // 没有加载完毕 41 | setTimeout(check, 100); 42 | } 43 | } 44 | 45 | // 开始反复检查图片有么有加载完毕 46 | check(); 47 | } 48 | return imgs; 49 | } 50 | 51 | 52 | /** 53 | * 得到闭区间里面的一个随机数 54 | * @param lower 55 | * @param higher 56 | */ 57 | function genRandom(lower, higher) { 58 | lower = lower || 0; 59 | higher = higher || 9999; 60 | 61 | // 先来生成一个[0, higgher-lower+1)之间的随机数(整数) 62 | // 然后两端加一得:[0 , higher-lower+1+1), 也就是[0, higher-lower+1]之间的随机数 63 | return Math.floor((higher - lower + 1) * Math.random()) + lower; 64 | } 65 | 66 | 67 | // 定义一个全局变量 68 | var ImgCache = null; 69 | 70 | // 记录按键的状态 71 | var keyState = {}; -------------------------------------------------------------------------------- /Game.js: -------------------------------------------------------------------------------- 1 | // 完成Game类的封装 2 | function Game(cfg) { 3 | for (var attr in cfg) { 4 | // 这里的this指的就是Game的对象 5 | this[attr] = cfg[attr]; 6 | } 7 | } 8 | 9 | // 定义原型方法和属性 10 | Game.prototype = { 11 | constructor: Game, 12 | 13 | // 游戏画布的初始化(这个是游戏画布的默认宽度和高度) 14 | width: 640, 15 | height: 480, 16 | 17 | // 画布canvas和绘图句柄gc(在构造函数中已经完成了初始化) 18 | canvas: null, 19 | gc: null, 20 | 21 | // 帧速率和时间间隔 22 | FPS: 40, 23 | sleep: 0, 24 | 25 | // 游戏中的精灵 26 | sprites: null, 27 | 28 | // 游戏中的运动的背景 29 | 30 | skyOffset: 0, 31 | grassOffset: 0, 32 | treeOffset: 0, 33 | nearTreeoffset: 0, 34 | 35 | TREE_VELOCITY: 20, 36 | FAST_TREE_VELOCITY: 40, 37 | SKY_VELOCITY: 8, 38 | GRASS_VELOCITY: 75, 39 | 40 | lastTime: 0, 41 | 42 | lastUpdateFPS: 0, 43 | lastUpdateTime: 0, 44 | 45 | 46 | // 游戏场景的初始化(主要场景参数的初始化处理) 47 | init: function () { 48 | // 直接手动创建canvas元素 49 | this.canvas = document.createElement("canvas"); 50 | this.canvas.width = this.width; 51 | this.canvas.height = this.height; 52 | 53 | 54 | document.body.appendChild(this.canvas); 55 | 56 | 57 | // 设置我的绘图句柄 58 | this.gc = this.canvas.getContext("2d"); 59 | 60 | // 初始化键盘的事件 61 | this.initEvent(); 62 | 63 | // 帧速率不为空,设置我的间隔时间 64 | if (this.FPS) { 65 | this.sleep = Math.floor(1000 / this.FPS); 66 | } 67 | 68 | // 当前的精灵(要么是自己, 要么是一个空数组) 69 | this.sprites = this.sprites || []; 70 | // 对每一个精灵完成初始化 71 | for (var i = 0, len = this.sprites.length; i < len; i++) { 72 | this.sprites[i].init(); 73 | } 74 | 75 | }, 76 | 77 | // 初始化键盘的事件 78 | initEvent: function () { 79 | // 按下按键 80 | document.addEventListener("keydown", function (ev) { 81 | keyState[ev.keyCode] = true; 82 | console.log(keyState); 83 | }, true); 84 | 85 | // 松开按键 86 | document.addEventListener("keyup", function (ev) { 87 | keyState[ev.keyCode] = false; 88 | console.log(keyState); 89 | }, true); 90 | }, 91 | 92 | // 游戏开始, 就进入到主循环 93 | start: function () { 94 | // this指向的是Game这个对象 95 | var Me = this; 96 | 97 | // 记录一下,游戏开始的时间 98 | Me.startTime = Date.now(); 99 | 100 | 101 | // 主循环 102 | this.mainLoop = setInterval(function () { 103 | // 距离上一次间隔的时间 104 | var deltaTime = Me.sleep; 105 | 106 | // 在主循环的执行过程中来实现碰撞检测的功能(一直在不断地检测是否发生了碰撞) 107 | Me.run(deltaTime); 108 | 109 | 110 | }, Me.sleep); 111 | 112 | }, 113 | 114 | // 主循环中需要执行的操作 115 | run: function (deltaTime) { 116 | // 显示当前游戏持续进行的时间(玩家在这个游戏中持续的时间就是他的分数) 117 | var playedTime = Date.now() - this.startTime; 118 | // 在主界面上面显示时间(span标签) 119 | document.getElementById("timeCount").innerHTML = playedTime.toString(); 120 | document.getElementById("lifeCount").innerHTML = this.sprites[0].HP.toString(); 121 | 122 | // 开始碰撞检测 123 | var coll = this.checkCollide(); 124 | // 只要coll不为空, 就说明有其他玩家和我发生了碰撞 125 | if (coll) { 126 | // 如果发生敌人和玩家的碰撞, 就结束游戏(有三个生命值) 127 | if (this.sprites[0].HP > 0) { 128 | this.sprites[0].HP--; 129 | game.playSingleMusic('kill', './res/踩敌人.mp3'); 130 | } 131 | } 132 | // 我是精灵角色中的第0个角色,直接得到我的生命值并显示 133 | document.getElementById("lifeCount").innerHTML = this.sprites[0].HP.toString(); 134 | 135 | if (this.sprites[0].HP == 0) { 136 | // 1. 清空主循环中的定时器 137 | clearInterval(this.mainLoop); 138 | document.getElementById('music').innerHTML = ''; 139 | game.playSingleMusic('end', './res/游戏结束.mp3'); 140 | alert("Game Over.\n Your score : " + playedTime); 141 | document.getElementById('score').innerHTML = '您的最终得分:'+playedTime.toString()+''; 142 | // 2.直接退出程序 143 | return; 144 | } 145 | 146 | 147 | // 更新画布 148 | this.update(deltaTime); 149 | // 清空画布 150 | this.clear(deltaTime); 151 | // 重绘画布 152 | this.draw(deltaTime); 153 | 154 | 155 | // 进入主循环之后, 还要不断地处理接收键盘事件 156 | this.handleInput(); 157 | }, 158 | 159 | // 开始实现碰撞的检测, 返回true就表示发生了玩家和敌人的碰撞 160 | checkCollide: function () { 161 | // 1.拿到我的玩家这个对象 162 | var player = this.sprites[0]; 163 | // 注意这里是从第一个场景中的人物和我来逐一检测(我是第0个人物, 其他的都是敌人) 164 | for (var i = 1, len = this.sprites.length; i < len; i++) { 165 | var sprite = this.sprites[i]; 166 | // 对于游戏场景中的除了自己的其他所有的精灵和我一一进行碰撞检测 167 | var coll = sprite.collideWidthOther(player); 168 | if (coll) { 169 | return coll; 170 | } 171 | } 172 | return false; 173 | }, 174 | 175 | // 更新精灵的状态 176 | update: function (deltaTime) { 177 | for (var i = 0, len = this.sprites.length; i < len; i++) { 178 | var sprite = this.sprites[i]; 179 | // 开始更新每一个精灵的坐标状态(运动状态信息) 180 | sprite.update(deltaTime); 181 | } 182 | }, 183 | 184 | // 清空画布信息 185 | clear: function () { 186 | // 清空画布 187 | this.gc.clearRect(0, 0, this.canvas.width, this.canvas.height); 188 | 189 | var fps = this.caculateFPS(); 190 | this.fps = fps; 191 | 192 | // 显示帧速率到画布上面 193 | var now = Date.now(); 194 | if (now - this.lastUpdateTime > 1000) { 195 | this.lastUpdateTime = now; 196 | this.lastUpdateFPS = fps; 197 | 198 | document.getElementById("fps").innerText = this.lastUpdateFPS.toFixed(); 199 | } 200 | 201 | 202 | this.initGameMap(); 203 | 204 | }, 205 | 206 | // 绘制背景地图 207 | initGameMap: function () { 208 | var fps = this.fps; 209 | 210 | // 实现移动的位移量 211 | this.skyOffset = this.skyOffset < this.canvas.width ? 212 | this.skyOffset + this.SKY_VELOCITY / fps : 0; 213 | 214 | this.grassOffset = this.grassOffset < this.canvas.width ? 215 | this.grassOffset + this.GRASS_VELOCITY / fps : 0; 216 | 217 | this.treeOffset = this.treeOffset < this.canvas.width ? 218 | this.treeOffset + this.TREE_VELOCITY / fps : 0; 219 | 220 | this.nearTreeOffset = this.nearTreeOffset < this.canvas.width ? 221 | this.nearTreeOffset + this.FAST_TREE_VELOCITY / fps : 0; 222 | 223 | var sky = ImgCache["sky"], 224 | tree = ImgCache["tree-twotrunks"], 225 | nearTree = ImgCache["smalltree"], 226 | grass = ImgCache["grass"], 227 | grass2 = ImgCache["grass2"]; 228 | 229 | this.gc.save(); 230 | this.gc.translate(-this.skyOffset, 0); 231 | this.gc.drawImage(sky, 0, 0); 232 | this.gc.drawImage(sky, sky.width - 2, 0); 233 | this.gc.restore(); 234 | 235 | this.gc.save(); 236 | this.gc.translate(-this.treeOffset, 0); 237 | this.gc.drawImage(tree, 100, 240); 238 | this.gc.drawImage(tree, 1100, 240); 239 | this.gc.drawImage(tree, 400, 240); 240 | this.gc.drawImage(tree, 1400, 240); 241 | this.gc.drawImage(tree, 700, 240); 242 | this.gc.drawImage(tree, 1700, 240); 243 | this.gc.restore(); 244 | 245 | this.gc.save(); 246 | this.gc.translate(-this.nearTreeOffset, 0); 247 | this.gc.drawImage(nearTree, 250, 240); 248 | this.gc.drawImage(nearTree, 1250, 240); 249 | this.gc.drawImage(nearTree, 800, 240); 250 | this.gc.drawImage(nearTree, 1800, 240); 251 | this.gc.restore(); 252 | 253 | this.gc.save(); 254 | this.gc.translate(-this.grassOffset, 0); 255 | 256 | this.gc.drawImage(grass, 0, this.canvas.height - grass.height); 257 | 258 | this.gc.drawImage(grass, grass.width - 5, 259 | this.canvas.height - grass.height); 260 | 261 | this.gc.drawImage(grass2, 0, this.canvas.height - grass2.height); 262 | 263 | this.gc.drawImage(grass2, grass2.width, 264 | this.canvas.height - grass2.height); 265 | this.gc.restore(); 266 | }, 267 | 268 | // 绘制背景滚动的效果 269 | caculateFPS: function (now) { 270 | if (now == undefined) { 271 | now = Date.now(); 272 | } 273 | 274 | var fps = 1000 / (now - this.lastTime); 275 | this.lastTime = now; 276 | return fps; 277 | }, 278 | 279 | 280 | // 开始重新绘制精灵 281 | draw: function (deltaTime) { 282 | for (var i = 0, len = this.sprites.length; i < len; i++) { 283 | var sprite = this.sprites[i]; 284 | // 开始绘制 285 | sprite.draw(this.gc); 286 | } 287 | 288 | }, 289 | 290 | 291 | // 游戏中的处理用户的输入 292 | handleInput: function () { 293 | for (var i = 0, len = this.sprites.length; i < len; i++) { 294 | var sprite = this.sprites[i]; 295 | // 先判断一下,这个精灵有没有handleInput属性 296 | if (sprite.handleInput) { 297 | // 如果这个精灵有这个属性或者方法的话, 就去调用精灵自己的处理函数 298 | sprite.handleInput(); 299 | } 300 | } 301 | }, 302 | 303 | 304 | // 游戏音乐 305 | playMusic : function(id, url) { 306 | document.getElementById(id).innerHTML = ''; 310 | }, 311 | 312 | // 不循环 313 | playSingleMusic : function (id, url) { 314 | document.getElementById(id).innerHTML = ''; 318 | } 319 | } 320 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SuperMarioGame 2 | 3 | ## 试玩链接:http://htmlpreview.github.io/?https://github.com/xiugangzhang/SuperMarioGame/blob/master/index.html 4 | ## 备用链接:http://vip.52tech.tech/www/game/SuperMarioGame/index.html 5 | 6 |  7 | - V1.0 : 实现游戏场景的初始化 8 | - V2.0: 实现画布的不断刷新 9 | - V3.0: 实现了游戏中人物的运动 10 | - V4.0: 实现了js代码的分离 11 | - V5.0:实现键盘对人物移动的控制 12 | - V6.0:完成Game游戏类的封装 13 | - V7.0: 实现敌人角色的加入, player,enemy类的封装; 实现了我方角色和敌人角色的自由移动 14 | - V8.0: 实现精灵之间的碰撞检测功能 15 | - V9.0: 实现游戏中分数的统计, 输赢的判定 16 | - V10.0: 实现了游戏背景的滚动效果和场景的优化 17 | -------------------------------------------------------------------------------- /Sprite.js: -------------------------------------------------------------------------------- 1 | // 这是一个精灵类(主要用于创建场景中的人物) 2 | /** 3 | * 精灵对象 4 | * @param cfg 5 | * @constructor 6 | */ 7 | function Sprite(cfg) { 8 | for (var attr in cfg) { 9 | // 这里是对这个对象的属性参数逐个赋值 10 | this[attr] = cfg[attr]; 11 | } 12 | } 13 | 14 | // 添加原型方法和属性 15 | Sprite.prototype = { 16 | constructor: Sprite, 17 | 18 | // 精灵的坐标 19 | x: 0, 20 | y: 0, 21 | // 精灵的速度 22 | speedX: 0, 23 | speedY: 0, 24 | 25 | acceY: 0, 26 | 27 | // 生命值 28 | HP : 100, 29 | 30 | // 精灵的坐标区间 31 | minX: 0, 32 | maxX: 9999, 33 | minY: 0, 34 | maxY: 9999, 35 | 36 | // 精灵包含的所有Animation集合, Object类型,数据存放方式为:“id : animation” 37 | anims: null, 38 | // 默认的Animation的Id, string类型 39 | defaultAnimId: null, 40 | 41 | // 当前的Animation 42 | currentAnim: null, 43 | 44 | 45 | // 初始化方法 46 | init: function () { 47 | // 初始化所有的Animation, 先把所有的动画取出来 48 | for (var animId in this.anims) { 49 | var anim = this.anims[animId]; 50 | anim.id = animId; 51 | // 初始化,反复调用(Animation的init()方法) 52 | anim.init(); 53 | } 54 | 55 | // 设置当前的Animation 56 | this.setAnim(this.defaultAnimId); 57 | }, 58 | 59 | // 设置当前的Animation, 参数为Animatio的ID, string类型 60 | /** 61 | * 这里根据animId也就是walk-left,walk-right的任意一个,获得这个动画里面的额所有参数 62 | * @param animId 63 | */ 64 | setAnim: function (animId) { 65 | this.currentAnim = this.anims[animId]; 66 | 67 | // 重置Animation的状态, 设置为0帧 68 | this.currentAnim.setFrame(0); 69 | }, 70 | 71 | // 更新精灵当前的状态 72 | update: function (deltaTime) { 73 | // 每次循环,改变一下绘制的坐标 74 | this.x = this.x + this.speedX * deltaTime; 75 | 76 | 77 | // 物体竖直方向上抛运动, 根据速度 时间来计算位移 78 | // V = v0 + a*t 79 | var newSpeedY = this.speedY + this.acceY * deltaTime; 80 | // Math.round就是四舍五入, 速度*时间 = 距离 81 | this.y = Math.round(this.y + (this.speedY + newSpeedY) / 2 * deltaTime); 82 | // 更新速度 83 | this.speedY = newSpeedY; 84 | 85 | 86 | //this.y = this.y + this.speedY * deltaTime; 87 | //限定移动的范围 88 | this.x = Math.max(this.minX, Math.min(this.x, this.maxX)); 89 | this.y = Math.max(this.minY, Math.min(this.y, this.maxY)); 90 | 91 | // 如果当前的animation 不是空的 92 | if (this.currentAnim) { 93 | // 这是当前的帧, 让动画去更新 94 | this.currentAnim.update(deltaTime); 95 | } 96 | }, 97 | 98 | // 绘制精灵 99 | // 把绘制的句柄context传进来就可以了 100 | draw: function (gc) { 101 | if (this.currentAnim) { 102 | 103 | // 这里去调用这个动画的绘制函数 104 | this.currentAnim.draw(gc, this.x, this.y); 105 | } 106 | }, 107 | 108 | // 对这个精灵增加两个方法, 主要实现精灵之间的碰撞检测的 109 | //1.获得精灵的碰撞区域 110 | getCollideRect: function () { 111 | if (this.currentAnim) { 112 | // 先拿到当前的帧 113 | var f = this.currentAnim.currentFrame; 114 | // 把当前帧的区域返回出去(当前帧的那一个图片区域范围) 115 | return { 116 | x1: this.x, 117 | y1: this.y, 118 | x2: this.x + f.w, 119 | y2: this.y + f.h 120 | } 121 | } 122 | }, 123 | 124 | //2. 判断是否和另一个精灵发生了碰撞 125 | // 这里的sprite2 作为参数传进来的实际上是玩家本人palyer 126 | collideWidthOther: function (sprite2) { 127 | // 1.首先拿到其他每一个敌人的矩形区域 128 | var rect1 = this.getCollideRect(); 129 | // 2. 拿到玩家本人当前的矩形区域范围 130 | var rect2 = sprite2.getCollideRect(); 131 | 132 | if (rect1 && rect2) { 133 | // 开始判断 134 | if (rect1.x1 > rect2.x2 || rect1.y1 > rect2.y2 || rect1.x2 < rect2.x1 || rect1.y2 < rect2.y1) { 135 | // 说明没有碰撞 136 | return false; 137 | } else { 138 | // 其他所有的情况都是发生了碰撞 139 | return true; 140 | } 141 | } 142 | 143 | } 144 | 145 | 146 | }; 147 | -------------------------------------------------------------------------------- /enemy.js: -------------------------------------------------------------------------------- 1 | // 完成敌人角色的封装 2 | /** 3 | * 4 | * @returns {Sprite} 5 | */ 6 | function createEnemy() { 7 | 8 | // 先拿到一个0或者1的数字, 要么是0, 要么是1 9 | var r = genRandom(0, 1); 10 | var cfg = { 11 | img: "enemy", 12 | 13 | 14 | // 随机数如果是1, x坐标为500, 如果是0, x坐标为0 15 | // 实现敌人从场景的两边出现的效果 16 | x: r ? 500 : 0, 17 | y: Math.random() * 500, 18 | 19 | 20 | // 定义Xy坐标的最大值和最小值,用来限定移动的范围 21 | minX: 0, 22 | maxX: 1024, 23 | minY: 0, 24 | maxY: 512, 25 | 26 | 27 | // 处理键盘事件(实现敌人角色的左右移动) 28 | handleInput: function () { 29 | // 先生成一个-4到4之间的整数随机数 30 | var s = genRandom(-4, 4); 31 | // 定义移动速度(这个移动的速度是随机分配的) 32 | var moveSpeed = (150 + s * 10) / 1000; 33 | 34 | this.speedX = this.speedX || moveSpeed; 35 | // 如果已经移动到了左边的边界 36 | if (this.x <= this.minX) { 37 | this.x = this.minX; 38 | // 开始向右移动 39 | this.speedX = moveSpeed; 40 | } else if (this.x >= this.maxX) { 41 | this.x = this.maxX; 42 | // 开始向左移动 43 | this.speedX = -moveSpeed; 44 | } 45 | }, 46 | 47 | // 定义默认的动画 48 | defaultAnimId: "move", 49 | anims: { 50 | "move": new Animation({ 51 | img: "enemy", 52 | frames: [ 53 | {x: 0, y: 0, w: 50, h: 50, duration: 100}, 54 | {x: 50, y: 0, w: 50, h: 50, duration: 100}, 55 | {x: 100, y: 0, w: 50, h: 50, duration: 100}, 56 | {x: 150, y: 0, w: 50, h: 50, duration: 100} 57 | ] 58 | }) 59 | } 60 | }; 61 | 62 | 63 | // 把新建的这个敌人角色添加进去 64 | return new Sprite(cfg); 65 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |