js html5 游戏引擎 js游戏引擎开发_sed

1 /**
  2  * @description: 引擎的设计与实现
  3  * @user: xiugang
  4  * @time: 2018/10/01
  5  */
  6 
  7 /*
  8 * V1.0: 引擎实现的基本模块思路
  9 *       1.创建一个游戏引擎对象及精灵对象
 10 *       2.将精灵对象添加到引擎中去,并实现播放动画效果以及需要用到的回调方法
 11 *       3.启动引擎
 12 * */
 13 
 14 /*
 15 * V2.0: 实现游戏循环模块
 16 *       1.如果游戏暂停了,就跳过以下各步骤,并在100毫秒后再次执行游戏循环
 17 *       2.更新帧速率
 18 *       3.设置游戏时间
 19 *       4.清除屏幕内容
 20 *       5.在播放动画前,调用startAnimate的方法(可以进行碰撞检测)
 21 *       6.绘制精灵背后的内容(绘制背景)
 22 *       7.更新精灵
 23 *       8.绘制精灵
 24 *       9.绘制精灵前方的内容
 25 *       10.动画播放完毕之后,调用endAnimate方法
 26 *       11.请求浏览器播放下一帧动画
 27 *
 28 * */
 29 
 30 
 31 /**
 32  * V3.0: 实现在暂停状态与运行状态之间的切换togglePaused
 33  */
 34 
 35 /**
 36  * V4.0:实现基于时间的运动效果 :pixelPerFrame
 37  * 计算公式:(pixels / second) * (second / frame) = pixeld / second【单位:每一秒移动的像素数】
 38  */
 39 
 40 /**
 41  * V5.0: 实现加载图像的功能:
 42  * queueImage(imageUrl): 将图像放入到加载队列中去
 43  * loadImages(): 开发者需要持续调用该方法,知道返回100位置(方法的返回值表示图像加载完成的百分比)
 44  * getImage(imageUrl):返回图像对象, 只有咋loadImages()返回100之后,才可以调用该方法
 45  */
 46 
 47 /**
 48  * V6.0:实现同时播放多个声音的功能
 49  * canPlay(): 用于查询浏览器是否能够播放某种特定格式的声音文件
 50  * playSound():用于播放声音
 51  */
 52 
 53 
 54 /**
 55  * V7.0: 键盘事件的处理
 56  * addKeyListener(): 用于向游戏注册按键监听器
 57  */
 58 
 59 
 60 /**
 61  * V8.0: 高分榜的维护:游戏的高分榜数组以json格式存档在本地
 62  */
 63 
 64 /**
 65  * V9.0: 实现了一个比较完整的游戏引擎,开始使用这个简单的游戏引擎去制作一个小游戏
 66  * 需求分析:
 67  * 1.资源加载的画面
 68  * 2.游戏资源的管理
 69  * 3.声音的播放
 70  * 4.具有视差动画的滚动背景
 71  * 5.生命数量的显示
 72  * 6.高分榜的维护
 73  * 7.按键的监听与处理
 74  * 8.暂停功能与自动暂停机制实现
 75  * 9.游戏结束的流程处理
 76  */
 77 
 78 
 79 /**
 80  * 游戏类
 81  * @param gameName  游戏名称
 82  * @param canvasId  画布ID
 83  * @returns {Game}  游戏实例
 84  * @constructor
 85  */
 86 var Game = function (gameName, canvasId) {
 87     // 获取canvas画布
 88     var canvas = document.getElementById(canvasId);
 89     console.log(canvas);
 90     var self = this;
 91 
 92 
 93     //----------------------------------------基本属性
 94     this.context = canvas.getContext('2d');                 // 定义游戏中的基本需要的属性
 95     this.sprites = [];                                       // 游戏中的精灵对象
 96     this.gameName = gameName;                               // 游戏的名字
 97 
 98 
 99     //----------------------------------------时间管理
100     this.startTime = 0;                                     // 游戏开始时间
101     this.lastTime = 0;                                      // 游戏上一次的时间
102     this.gameTime = 0;                                      // 游戏总时间
103     this.fps = 0;                                          // 游戏帧速率(实时更新的)
104     this.STARTING_FPS = 60;                                 // 默认启动的时候的帧速率
105 
106     this.paused = false;                                     // 游戏是否暂停
107     this.startedPauseAt = 0;
108     this.PAUSE_TIMEOUT = 100;                               // 游戏暂停的持续时间
109 
110 
111     //---------------------------------------图像加载
112     this.imageLoadingProgressCallback;                         // 图像加载过程的回调函数
113     this.images = {};                                           // 图像对象
114     this.imageUrls = [];                                        // 图像的Urls
115     this.imagesLoaded = 0;                                      // 已加载完成的图像个数
116     this.imagesFailedToLoad = 0;                                // 加载失败的图像个数
117     this.imagesIndex = 0;                                       // 图像数组的下标, 从0开始的
118 
119 
120 
121     //-----------------------------------------声音加载播放
122     this.soundOn = true;
123     this.soundChannels = [];                                    // 初始化一个播放信道数组
124     this.audio = new Audio();                                   // 这里的Audio实际上是JavaScript内置的DOM对象, 不需要自己手动去创建一个Audio对象
125     this.NUM_SOUND_CHANNELS = 10;                               // 设置初始信道的数量
126 
127 
128 
129     //----------------------------------------键盘事件的监听
130     this.keyListeners = [];                                     // 用于存放keyandListener的键值对
131 
132     window.onkeypress = function (ev) {                         // 这里的对象处理的是DOM Window这个窗体对象,添加了一个监听事件
133         self.keyPressed(ev);
134     }
135     window.onkeydown = function (ev) {
136         self.keyPressed(ev);
137     }
138 
139 
140     //-----------------------------------------高分榜的维护
141     this.HIGH_SCORES_SUFFIX = '_highscores';                    // 后缀名字
142     this.highScores = [];                                       // 用于存储游戏分数的数组
143 
144 
145 
146     // 构造10个Audio对象,将其加入到数组中去, 当调用playSound()方法,游戏引擎会找出第一个未被占用的声道,并用它来播放指定的声音文件
147     for (var i = 0; i < this.NUM_SOUND_CHANNELS; i++){
148         var audio = new Audio();
149         this.soundChannels.push(audio);
150     }
151 
152     return this;                                            // 把当前的游戏对象返回
153 }
154 
155 
156 
157 
158 /**
159  * 游戏的成员方法
160  * @type {{start: Game.start, animate: Game.animate, tick: Game.tick, updateFrameRate: Game.updateFrameRate, clearScreen: Game.clearScreen, startAnimate: Game.startAnimate, paintUnderSprites: Game.paintUnderSprites, updateSprites: Game.updateSprites, paintSprites: Game.paintSprites, paintOverSprites: Game.paintOverSprites, endAnimate: Game.endAnimate}}
161  */
162 Game.prototype = {
163     // 游戏加载图像的模块-------------------------------------------------------
164     /**
165      * 通过图像的Url地址,获取这个图像(json格式对象取出值的方法)对象
166      * @param imageUrl
167      */
168     getImage : function (imageUrl) {
169         return this.images[imageUrl];
170     },
171 
172 
173 
174     /**
175      *  图像加载完成的回调函数
176      */
177     imageLoadedCallback : function (e) {
178         // 每次加载完成一个图像,次数加一
179         this.imagesLoaded++;
180     },
181 
182 
183 
184     /**
185      * 当一个图像加载失败的回调函数
186      */
187     imageLoadErrorCallback : function (e) {
188         this.imagesFailedToLoad++;
189     },
190 
191 
192     /**
193      * 正式加载一张图像
194      * @param imageUrl
195      */
196     loadImage : function (imageUrl) {
197         var self = this;
198         var image = new Image();
199 
200         image.src = imageUrl;
201 
202         // 图像加载完成的回调函数
203 
204         image.addEventListener("load", function (e) {
205             self.imageLoadedCallback(e);
206 
207             // 显示出来, 测试成功
208             //self.context.drawImage(image, 0, 0);
209         });
210 
211 
212         // 图像加载失败的回调函数
213         image.addEventListener("error", function (e) {
214             self.imageLoadErrorCallback(e);
215         });
216 
217 
218         // 把所有的加载的Images存起来
219         this.images[imageUrl] = image;
220     },
221 
222     /**
223      * 加载图像的过程中反复调用这个函数, 这个函数返回已经处理完成的图像百分比
224      * 当图像返回100%的时候, 表示所有的图像已经全部加载完毕
225      * @returns {number}
226      */
227     loadImages : function () {
228         //  如果还有图像没有加载【图像的url个数多余已经加载完成的图像下标】
229         if (this.imagesIndex < this.imageUrls.length){
230             // 再次把当前这个图像去加载(把没有加载的全部加载进来)
231             this.loadImage(this.imageUrls[this.imagesIndex]);
232             this.imagesIndex ++;
233         }
234 
235 
236         // 返回已经加载完成的图像百分比(加载成功的个数+加载失败的个数 占整个事先提供的所有URL个数的百分比)
237         var percentage = (this.imagesLoaded + this.imagesFailedToLoad) / this.imageUrls.length * 100;
238         console.log(percentage);
239         return (this.imagesLoaded + this.imagesFailedToLoad) / this.imageUrls.length * 100;
240     },
241 
242     /**
243      *  用于把所有的图像URL放在一个队列里面【数组】
244      * @param imageUrl
245      */
246     queueImage : function (imageUrl) {
247         this.imageUrls.push(imageUrl);
248     },
249 
250 
251 
252 
253 
254     // 游戏循环模块---------------------------------------------------------------
255     start: function () {
256         var self = this;
257 
258         this.startTime = +new Date();                  // 获取游戏当前的时间
259         console.log("游戏启动成功, 当前时间:", this.startTime);
260 
261 
262         // 开始游戏循环(这是一个系统实现的帧速率方法)
263         window.requestNextAnimationFrame(
264             function (time) {
265                 // self is the game, and this is the window
266                 console.log(self, this);
267                 // 每次把游戏实例对象的引用和当前的时间传过去
268                 self.animate.call(self, time);  // self is the game
269             }
270         );
271     },
272 
273     animate: function (time) {
274         // 这里的this 指向的是Game对象
275         var self = this;
276 
277         if (this.paused) {
278             // 如果用户暂停了游戏,然后每隔100ms的时间检查一次去看一下有没有开始循环
279             // (由于游戏暂停的情况不会频繁出现,因此使用setTimeout()方法就可以满足我们的需求, 每隔100ms去看一次)
280 
281             setTimeout(function () {
282                 self.animate.call(self, time);
283             }, this.PAUSE_TIMEOUT);
284         }
285         // 没有暂停的话
286         else {
287             this.tick(time);                    // 1.更新帧速率, 设置游戏时间
288             this.clearScreen();                 // 2.清空屏幕内容
289 
290             // 碰撞检测代码
291 
292             this.startAnimate(time);            // 3.开始游戏动画
293             this.paintUnderSprites();           // 4.绘制精灵后面的内容---背景
294 
295             this.updateSprites(time);           // 5.更新精灵的位置
296             this.paintSprites(time);            // 6.绘制精灵
297 
298             this.paintOverSprites();            // 7.绘制精灵前方的内容
299             this.endAnimate();                  // 8.动画结束
300 
301 
302             // 回调这个方法, 开始进入到下一帧动画
303             window.requestNextAnimationFrame(
304                 function (time) {
305                     console.log(self, this);
306                     // 注意这里不能直接传过去哈, 如果直接传过去的话,第一个参数就是就会把time 的指向修改为Game这个类
307                     // self.animate(self, time);
308                     // 第一个参数是用来校正animate函数内部的this的指向, 第二个参数是用来传递animate()函数执行需要的参数
309                     self.animate.call(self, time);
310                 }
311             );
312         }
313     },
314     togglePaused : function () {
315         // 这是一个游戏暂停的方法
316         var now = +new Date();                      // 获取游戏暂停的那个时间点
317 
318         this.paused = !this.paused;                 // 每次在暂停与不暂停之间来回切换
319 
320         if (this.paused){
321             // 如果游戏暂停了(暂停的那个时间点就是当前的时间)
322             this.startedPauseAt = now;
323         }else{
324             // 没有暂停的话:调整开始的时间, 使得游戏开始是从点击开始游戏之后就开始计时的
325             // this.startTime 记录的是:开始时间 + 当前时间 - 游戏上一次暂停的时间点
326             // now - this.startedPauseAt = 游戏暂停的时长, 然后再加上游戏开始的时候的时间,就能从原来的暂停位置处继续执行下去
327             this.startTime = this.startTime + now - this.startedPauseAt;
328             this.lastTime = now;
329         }
330     },
331     // 实现动画中需要实现的功能
332     /**
333      * // 1.更新帧速率(实现基于时间的运动效果)
334      * @param time
335      */
336     tick: function (time) {
337         // 1. 更新帧帧速率
338         this.updateFrameRate(time);
339 
340         // 2. 设置游戏时间(每一帧间隔的时间)
341         this.gameTime = (+new Date()) - this.startTime;
342         console.log("设置游戏的时间:" + this.gameTime);
343         this.lastTime = time;
344 
345     },
346     updateFrameRate: function (time) {
347         // 启动时候的帧速率
348         if (this.lastTime === 0) {
349             this.fps = this.STARTING_FPS;
350         }
351         else {
352             // 计算当前的帧速率(每秒执行的帧数)
353             this.fps = 1000 / (time - this.lastTime);
354             console.log("实时更新游戏当前的帧速率", this.fps);
355         }
356 
357     },
358     /**
359      * 实现基于时间的运动效果
360      * @param time
361      * @param velocity
362      */
363     pixelsPerFrame : function (time, velocity) {
364         // 是动画平滑地运动起来
365         // 计算公式:(pixels / second) * (second / frame) = pixeld / second【单位:每一秒移动的像素数】
366         return velocity / this.fps;
367     },
368 
369     /**
370      * // 2.清空屏幕内容
371      */
372     clearScreen: function () {
373         // 注意this.context.canvas.width, this.context.canvas.height 用于获取画布的宽度和高度
374         //        this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
375         this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height);
376         console.log("画布屏幕清空成功!");
377     },
378 
379 
380     /**
381      * // 3.开始游戏动画
382      * @param time
383      */
384     startAnimate: function (time) {
385         console.log(time, "开始游戏动画………………");
386     },
387     /**
388      * // 4.绘制精灵后面的内容
389      */
390     paintUnderSprites: function () {
391         console.log("绘制精灵后面的内容!");
392     },
393     /**
394      * // 5. 更新精灵的位置
395      * @param time
396      */
397     updateSprites: function (time) {
398         console.log("更新所有精灵的位置!");
399         for (var i = 0; i < this.sprites.length; i++) {
400             var sprite = this.sprites[i];
401             // 重新绘制精灵(调用每一个精灵自己的方法去绘制显示)
402             sprite.update(this.context, time);
403         }
404     },
405 
406     // 6.绘制所有可见的精灵对象
407     paintSprites: function (time) {
408         console.log("绘制所有可见的精灵对象");
409         for (var i = 0; i < this.sprites.length; i++) {
410             var sprite = this.sprites[i];
411             // 绘制之前需要先判断一下
412             if (sprite.visible) {
413                 sprite.paint(this.context);                         //绘制精灵的时候需要拿到绘制精灵的绘图句柄
414             }
415         }
416     },
417 
418     // 7. 绘制精灵前方的内容
419     paintOverSprites: function () {
420         console.log("绘制精灵前面的内容!");
421     },
422     // 8. 绘制动画结束
423     endAnimate: function () {
424         console.log("绘制动画结束!");
425     },
426 
427 
428 
429 
430     // 声音文件加载播放的模块----------------------------------------------------------
431     /**
432      * 浏览器是否支持ogg格式的文件
433      * @returns {boolean}
434      */
435     canPlayOggVorbis : function () {
436         // 只要返回的有内容,就说明浏览器支持这个文件格式
437         return "" != this.audio.canPlayType('audio/ogg; codecs="vorbis"');
438     },
439 
440     /**
441      * 浏览器是否支持MP3格式的音乐播放
442      * @returns {boolean}
443      */
444     canPlayMp3 : function () {
445         // 返回的内容不为空,说明支持
446         return "" != this.audio.canPlayType('audio/mpeg');
447     },
448 
449     /**
450      * 用于返回当前系统中可以使用的信道
451      * @returns {*}
452      */
453     getAvailableSoundChannel : function () {
454         var audio;
455 
456         // 遍历初始化中的所有信道
457         for (var i = 0; i < this.NUM_SOUND_CHANNELS; i++){
458             audio = this.soundChannels[i];
459             // 如果当前的audio信道已经开始播放了(而且已经播放的信道数量不为空)
460             if (audio.played && audio.played.length > 0){
461                 // 如果当前的信道已经播放完毕了音乐
462                 if (audio.ended){
463                     return audio;
464                 }
465             } else{
466                 // 如果当前的信道已经播放完毕音乐了, 就返回当前的这个audio对象
467                 if (!audio.ended)
468                     return audio;
469             }
470         }
471 
472         // 如果所有的信道都在使用的话,就返回undifined
473         return undefined;
474     },
475     /**
476      * 用于播放指定ID的音乐
477      * @param id
478      */
479     playSound : function (id) {
480         // 获取当前可以使用的一个信道
481         var track = this.getAvailableSoundChannel(),
482             element = document.getElementById(id);
483 
484 
485         // 如果不为空(undefined)
486         if (track && element){
487             // 获取当前选中的媒体资源的URL地址
488             track.src = element.src === '' ? element.currentSrc : element.src;
489 
490             // 加载并播放音乐
491             track.load();
492             track.play();
493 
494         }
495     },
496 
497 
498 
499     // 键盘事件的监听与处理操作---------------------------------------------
500     /**
501      * 把一个键值对添加到监听数组中去
502      * @param keyAndListener
503      */
504     addKeyListener : function (keyAndListener) {
505         this.keyListeners.push(keyAndListener);
506     },
507 
508     /**
509      * 通过key来查找相应的listener对象
510      * @param key
511      * @returns {undefined}
512      */
513     findKeyListener : function (key) {
514         var listener = undefined;
515 
516         // 遍历所有的keyListeners数组
517         for (var i = 0; i < this.keyListeners.length; i++){
518             // 拿到当前的键值监听对象及按键的key值
519             var keyAndListener = this.keyListeners[i],
520                 currentKey = keyAndListener.key;
521 
522             // 如果按下的按键是在我今天按下的所有keyAndListener中,就得到了这个listener
523             if (currentKey === key){
524                 listener = keyAndListener.listener;
525             }
526         }
527 
528         return listener;
529     },
530 
531     /**
532      * 键盘按下的回调事件
533      * @param e
534      */
535     keyPressed : function (e) {
536         var listener = undefined,
537             key = undefined;
538 
539         switch (e.keyCode){
540             // 添加一些常用的按键处理键值对
541             case 32:
542                 key = 'space';
543                 break;
544             case 65:
545                 key = 'a';
546                 break;
547             case 83:
548                 key = 's';
549                 break;
550             case 80:
551                 key = 'p';
552                 break;
553             case 87:
554                 key = 'w';
555                 break;
556             // 记忆:左上右下的顺序,依次为:37 38 39 40
557             case 37:
558                 key = 'left arrow';
559                 break;
560             case 39:
561                 key = 'right arrow';
562                 break;
563             case 38:
564                 key = 'up arrow';
565                 break;
566             case 40:
567                 key = 'down arrow';
568                 break;
569         }
570 
571         // 获取当前按下的按键的监听事件
572         listener = this.findKeyListener(key);
573         if (listener){
574             listener();             // 这里的listener是一个监听函数,如果按下的按键有监听事件的处理,就去处理这个监听事件
575         }
576 
577     },
578 
579 
580 
581     // 高分榜的维护管理模块----------------------------------------------------
582     /**
583      * 从本地存储中获取存储的数据(返回的是一个本地存储的高分列表)
584      * @returns {any}
585      */
586     getHighScores : function () {
587         // 把key的值存储起来
588         var key = this.gameName + this.HIGH_SCORES_SUFFIX,
589             highScoresString = localStorage[key];
590 
591 
592         // 如果为空的话,返回一个空的Json数据
593         if (highScoresString == undefined){
594             localStorage[key] = JSON.stringify([]);
595         }
596 
597         // 使用JSON解析字符串内容(返回的是一个JSon与key相对应的数值内容)
598         return JSON.parse(localStorage[key]);
599     },
600     /**
601      * 存储内容到本地存储
602      * @param highScore
603      */
604     setHighScore : function (highScore) {
605         // unshift() 方法不创建新的创建,而是直接修改原有的数组【会在数组的头部插入新的元素】
606         var key = this.gameName + this.HIGH_SCORES_SUFFIX,
607             highScoresString = localStorage[key];
608 
609 
610 
611         // 主要目的是把每一次最高分放在数组的第一位置,方便查看和管理
612         // 这里的highScores数组,是一个用户初始化的数组(全局变量)【数组的第一个元素始终是最高分】
613         //this.highScores.unshift(highScore);(每次都在原理的基础上添加数据)
614         if (this.highScores.length === 0){
615             this.highScores = this.getHighScores();
616         }
617         this.highScores.unshift(highScore);
618 
619 
620         // 游戏的key始终是惟一的,每一次都将会修改为最新的状态
621         localStorage[key] = JSON.stringify(this.highScores);
622     },
623 
624     /**
625      * 清空高分榜(清空浏览器的本地存储)
626      */
627     clearHighScores : function () {
628         // 直接把相应的键对应的值设置为空即可
629         localStorage[this.name + this.HIGH_SCORES_SUFFIX] = JSON.stringify([]);
630     }
631 
632 }

下面的是一个测试案例:

js html5 游戏引擎 js游戏引擎开发_sed

 

功能实现的比较简单,但是也就是对上面的这个引擎的基本使用吧。

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Game 1.0</title>
    <!--引入需要的css文件, 注意这里的引入使用的是link方式来引入css文件-->
    <link rel='stylesheet' type='text/css' href='css/Main.css'/>
</head>
<body>
<!--canvas画布-->
<canvas id="gameCanvas" width="580" height="600">
    Canvas not supported in your browser!
</canvas>


<!--canvas上方的悬浮div-->
<div id="loadingToast">
    <span id="gameTitle">Engine Development</span>
    <span id="loadingContents">
        <p class="mainFunctions">
            主要功能:
        </p>


        <ul class="Contents">
            <li>1.资源加载的画面</li>
            <li>2.游戏资源的管理</li>
            <li>3.声音的播放</li>
            <li>4.具有视差动画的滚动背景</li>
            <li>5.生命数量的显示</li>
            <li>6.高分榜的维护</li>
            <li>7.按键的监听与处理</li>
            <li>8.暂停功能与自动暂停机制实现</li>
            <li>9.游戏结束的流程处理</li>
        </ul>

    </span>

    <span id="loadButtonSpan">
            <button autofocus="true" id="loadButton">载入引擎...</button><br/>
            <span id="loadingMessage">Loading……</span>
        </span>
    <!--用于显示进度条-->
    <div id="progressDiv">
    </div>

</div>


<!--左上角计时器-->
<div id="gameTimerDiv">
    <span>000</span>
</div>
<!--右上角生命值-->
<div id="leftLives">
    <span id="lifeValue">生命值:100</span>
</div>

<!--中部的暂停-->
<div id="pausedToast">
    <span>暂停</span><br/>
    <span>点击此区域任意位置继续</span>
</div>



<!--中下位置的点击按钮-->
<div id="loseLifePanel">
    <button id="loseLifeButton" onmouseover="this.style.backgroundColor='#00aced'"
            onmouseleave="this.style.backgroundColor = ''">开杀
    </button>
</div>



<!--游戏结束及高分榜-->
<div id="gameoverPan">
    <div id="topPan">
        <span id="scoreText">140</span>
    </div>
    <div id="middlePan">
        <span>High Score!</span><br/>
        <span class="Name">What's your name?</span><br/>

        <input id='playerName' type='text' autofocus='true'><button id="addMyScoreButton">添加</button><button id="newGameButton">新游戏</button>
    </div>
    <div id="downPan">
        <span>Previous High Scores</span>
        <ul id="highScoreList">
        </ul>
    </div>
</div>


<!--游戏播放音乐需要使用的音频文件; preload 属性规定是否在页面加载后载入音频。-->
<audio id="pop" preload="auto">
    <source src="sounds/pop.ogg" type="audio/ogg">
    <source src="sounds/pop.mp3" type="audio/mp3">
</audio>
<audio id="whoosh" preload="auto">
    <source src="sounds/whoosh.ogg" type="audio/ogg">
    <source src="sounds/whoosh.mp3" type="audio/mp3">
</audio>



<!--引入需要的js文件-->
<script type="text/javascript" src="Engine/progressbar.js"></script>
<script type="text/javascript" src="Engine/requestNextAnimationFrame.js"></script>
<script type="text/javascript" src="Engine/GameEngine1.0.js"></script>
<script type="text/javascript" src="js/Main.js"></script>
</body>
</html>

  

实现的功能基本介绍:(目前是开发期间的9个版本及功能介绍)

/** V1.0: 引擎实现的基本模块思路* 1.创建一个游戏引擎对象及精灵对象* 2.将精灵对象添加到引擎中去,并实现播放动画效果以及需要用到的回调方法* 3.启动引擎* *//** V2.0: 实现游戏循环模块* 1.如果游戏暂停了,就跳过以下各步骤,并在100毫秒后再次执行游戏循环* 2.更新帧速率* 3.设置游戏时间* 4.清除屏幕内容* 5.在播放动画前,调用startAnimate的方法(可以进行碰撞检测)* 6.绘制精灵背后的内容(绘制背景)* 7.更新精灵* 8.绘制精灵* 9.绘制精灵前方的内容* 10.动画播放完毕之后,调用endAnimate方法* 11.请求浏览器播放下一帧动画** *//** * V3.0: 实现在暂停状态与运行状态之间的切换togglePaused *//** * V4.0:实现基于时间的运动效果 :pixelPerFrame * 计算公式:(pixels / second) * (second / frame) = pixeld / second【单位:每一秒移动的像素数】 *//** * V5.0: 实现加载图像的功能: * queueImage(imageUrl): 将图像放入到加载队列中去 * loadImages(): 开发者需要持续调用该方法,知道返回100位置(方法的返回值表示图像加载完成的百分比) * getImage(imageUrl):返回图像对象, 只有咋loadImages()返回100之后,才可以调用该方法 *//** * V6.0:实现同时播放多个声音的功能 * canPlay(): 用于查询浏览器是否能够播放某种特定格式的声音文件 * playSound():用于播放声音 *//** * V7.0: 键盘事件的处理 * addKeyListener(): 用于向游戏注册按键监听器 *//** * V8.0: 高分榜的维护:游戏的高分榜数组以json格式存档在本地 *//** * V9.0: 实现了一个比较完整的游戏引擎,开始使用这个简单的游戏引擎去制作一个小游戏 * 需求分析: * 1.资源加载的画面 * 2.游戏资源的管理 * 3.声音的播放 * 4.具有视差动画的滚动背景 * 5.生命数量的显示 * 6.高分榜的维护 * 7.按键的监听与处理 * 8.暂停功能与自动暂停机制实现 * 9.游戏结束的流程处理 */

其他的文件已经全部上传到Github,感兴趣的朋友可以下载查看学习交流,如果觉得不错,欢迎给个star支持一下:

 

https://github.com/xiugangzhang/GameEngine

 

其他的几个小游戏也已经分享:

中国象棋:https://github.com/xiugangzhang/ChineseChess

超级马里奥小游戏:https://github.com/xiugangzhang/SuperMarioGame

苏拉卡尔塔小游戏:https://github.com/xiugangzhang/SularaGame