zoukankan      html  css  js  c++  java
  • HTML5游戏开发系列教程10(译)

    原文地址:http://www.script-tutorials.com/html5-game-development-lesson-10/

     最后我们将继续使用canvas来进行HTML5游戏开发系列的文章。今天我准备了一个新游戏--SkyWalker。这游戏基本上算是一个射击模拟类型的游戏(有飞机和敌人)。我们的目标是到达终点线。该游戏有几个关键的特征:使用了精灵来处理飞机和爆炸,可以按下多个按键(比如你可以移动飞机的同时发射子弹),a certain level length,增强的碰撞检测(敌人可以撞击飞机),飞机的生命值和获得的分数。

    之前的翻译文章可以点击这里:http://www.cnblogs.com/pigzhu/p/3281537.html

    第一步:HTML

     和往常一样,我们有个基本的html文件:

     1 <!DOCTYPE html>
     2 <html lang="en" >
     3     <head>
     4         <meta charset="utf-8" />
     5         <title>HTML5 Game Development - Lesson 10 (SkyWalker) | Script Tutorials</title>
     6         <link href="css/main.css" rel="stylesheet" type="text/css" />
     7 
     8         <script src="js/jquery-2.0.0.min.js"></script>
     9         <script src="js/script.js"></script>
    10     </head>
    11     <body>
    12         <header tabindex="0">
    13             <h2>HTML5 Game Development - Lesson 10</h2>
    14             <a  href="http://www.script-tutorials.com/html5-game-development-lesson-10/" class="stuts">Back to original tutorial on <span>Script Tutorials</span></a>
    15         </header>
    16 
    17         <div class="container">
    18             <canvas id="scene" width="700" height="700" tabindex="1"></canvas>
    19         </div>
    20     </body>
    21 </html>
    View Code

    第二步:JS

    现在,请在文件夹js中创建一个空文件:“script.js”,并把下面的这些代码粘贴到里面去。我将解释主要的功能。

      1 //内部变量
      2 var canvas, ctx;
      3 
      4 //各种图片
      5 var backgroundImage;
      6 var oRocketImage;
      7 var oExplosionImage;
      8 var introImage;
      9 var oEnemyImage;
     10 
     11 var iBgShiftY = 9300;
     12 var bPause = true;   //游戏是否暂停
     13 var plane = null;    //飞机
     14 var rockets = [];    //子弹对象的数组
     15 var enemies = [];   //敌人对象的数组
     16 var explosions = [];   //爆炸对象的数组
     17 var planeW = 200;  //飞机的宽度
     18 var planeH = 110;  //飞机的高度
     19 var iSprPos = 2;   //飞机初始帧的位置
     20 var iMoveDir = 0;   //移动的方向
     21 var iEnemyW = 128;  //敌人的宽度
     22 var iEnemyH = 128;   //敌人的高度
     23 var iRocketSpeed =10;   //子弹的速度
     24 var iEnemySpeed = 5;   //敌人的速度
     25 var pressedKeys = [];  //按键的序列
     26 var iScore = 0;       //总得分
     27 var iLife = 100;      //生命值
     28 var iDamage = 10;     //每次敌人碰到飞机的损耗值
     29 var enTimer = null;   //产生敌人的定时器
     30 
     31 function Plane(x, y, w, h, image) {
     32     this.x = x;
     33     this.y = y;
     34     this.w = w;
     35     this.h = h;
     36     this.image = image;
     37     this.bDrag = false;
     38 }
     39 
     40 function Rocket(x, y, w, h, speed, image) {
     41     this.x = x;
     42     this.y = y;
     43     this.w = w;
     44     this.h = h;
     45     this.speed = speed;
     46     this.image = image;
     47 }
     48 
     49 function Enemy(x, y, w, h, speed, image) {
     50     this.x = x;
     51     this.y = y;
     52     this.w = w;
     53     this.h = h;
     54     this.speed = speed;
     55     this.image = image;
     56 }
     57 
     58 function Explosion(x, y, w, h, sprite, image) {
     59     this.x = x;
     60     this.y = y;
     61     this.w = w;
     62     this.h = h;
     63     this.sprite = sprite;
     64     this.image = image;
     65 }
     66 
     67 //得到位于x和y之间的随机数
     68 function getRand(x, y) {
     69     return Math.floor(Math.random() * y) + x;
     70 }
     71 
     72 //显示游戏介绍界面
     73 function displayIntro() {
     74     ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
     75     ctx.drawImage(introImage, 0, 0, 700, 700);
     76 }
     77 
     78 //绘制主要的场景
     79 function drawScene() {
     80     if (! bPause) {
     81         iBgShiftY -= 2;  //主背景向下移动
     82         if (iBgShiftY < 5) {
     83             bPause = true;
     84 
     85             //绘制分数
     86             ctx.font = '40px Verdana';
     87             ctx.fillStyle = '#fff';
     88             ctx.fillText('Finish, your score: ' + iSore * 10 + ' points', 50, 200);
     89             return;
     90         }
     91 
     92         //处理按下的建
     93         processPressedKeys();
     94 
     95         //清除整个画布
     96         ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
     97 
     98         //通过剪切图片绘制背景
     99         ctx.drawImage(backgroundImage, 0, 0 + iBgShiftY, 700, 700, 0, 0, 700, 700);
    100 
    101         //绘制飞机
    102         ctx.drawImage(plane.image, iSprPos * plane.w, 0, plane.w, plane.h, 
    103             plane.x - plane.w / 2, plane.y - plane.h / 2, plane.w, plane.h);
    104 
    105         //绘制子弹
    106         if (rockets.length > 0) {
    107             for (var key in rockets) {
    108                 if (rockets[key] != undefined) {
    109                     ctx.drawImage(rockets[key].image, rockets[key].x, rockets[key].y);
    110                     rockets[key].y -= rockets[key].speed;
    111 
    112                     if (rockets[key].y < 0) {  //如果子弹飞出界面,删除掉
    113                         delete rockets[key];
    114                     }
    115                 }
    116             }
    117         }
    118 
    119         //绘制爆炸
    120         if (explosions.length > 0) {
    121             for (var key in explosions) {
    122                 ctx.drawImage(explosions[key].image, explosions[key].sprite * explosions[key].w, 0, explosions[key].w, explosions[key].h, 
    123                     explosions[key].x - explosions[key].w / 2, explosions[key].y - explosions[key].h / 2, 
    124                     explosions[key].w, explosions[key].h);
    125                 explosions[key].sprite++;
    126 
    127                 if (explosions[key].sprite > 10) {  //如果爆炸播放完,则删除掉
    128                     delete explosions[key];
    129                 }
    130             }
    131         }
    132 
    133         //绘制敌人
    134         if (enemies.length > 0) {
    135             for (var ekey in enemies) {
    136                 if (enemies[ekey] != undefined) {
    137                     ctx.drawImage(enemies[ekey].image, enemies[ekey].x, enemies[ekey].y);
    138                     enemies[ekey].y -= enemies[ekey].speed;
    139                 }
    140 
    141                 if (enemies[ekey].y > canvas.height) { //如果敌人飞出界面,则删除掉
    142                     delete enemies;
    143                 }
    144             }
    145         }
    146 
    147         //处理各种碰撞
    148         if (enemies.length > 0) {
    149             for (var ekey in enemies) {
    150                 if (enemies[ekey] != undefined) {
    151 
    152                     //子弹打中敌人
    153                     if (rockets.length > 0) {
    154                         for (var key in rockets) {
    155                             if (rockets[key] != undefined && enemies[ekey] != undefined) {
    156                                 if (rockets[key].y < enemies[ekey].y + enemies[ekey].h / 2 
    157                                     && rockets[key].x > enemies[ekey].x 
    158                                     && rockets[key].x + rockets[key].w < enemies[ekey].x + enemies[ekey].w) {
    159                                     explosions.push(new Explosion(enemies[ekey].x + enemies[ekey].w / 2, enemies[ekey].y + enemies[ekey].h / 2,
    160                                         120, 120, 0, oExplosionImage));
    161 
    162                                 delete enemies[ekey];
    163                                 delete rockets[key];
    164                                 iScore++;
    165                                 }
    166                             }
    167                         }
    168                     }
    169 
    170                     //敌人打中飞机
    171                     if (enemies[ekey] != undefined) {
    172                         if (plane.y - plane.h / 2 < enemies[ekey].y + enemies[ekey].h / 2 
    173                             && plane.x - plane.w / 2 < enemies[ekey].x + enemies[ekey].w
    174                             && plane.x + plane.w / 2 > enemies[ekey].x) {
    175                             explosions.push(new Explosion(enemies[ekey].x + enemies[ekey].w / 2, enemies[ekey].y + enemies[ekey].h / 2, 
    176                                 120, 120, 0, oExplosionImage));
    177 
    178                             delete enemies[ekey];
    179                             iLife -= iDamage;
    180 
    181                             if (iLife <= 0) {  //游戏结束
    182                                 bPause = true;
    183 
    184                                 ctx.font = '38px Verdana';
    185                                 ctx.fillStyle = '#fff';
    186                                 ctx.fillText('Game voer, your socre: ' + iScore * 10 + ' points', 25, 200);
    187                                 return;
    188                             }
    189                         }
    190                     }
    191 
    192                 }
    193             }
    194         }
    195 
    196         //即时显示分数和生命值
    197         ctx.font = '14px Verdana';
    198         ctx.fillStyle = '#fff';
    199         ctx.fillText('life: ' + iLife + ' / 100', 50, 660);
    200         ctx.fillText('Score: ' + iScore * 10, 50, 680);
    201     }
    202 }
    203 
    204 //处理各种按键事件
    205 function processPressedKeys() {
    206     if (pressedKeys[37] != undefined) {
    207         if (iSprPos > 0) {
    208             iSprPos--;
    209             iMoveDir = -7;
    210         }
    211         if (plane.x - plane.w / 2 > 10) {
    212             plane.x += iMoveDir;
    213         }
    214     } else if (pressedKeys[39] != undefined) {
    215         if (iSprPos < 4) {
    216             iSprPos++;
    217             iMoveDir = 7;
    218         }
    219         if (plane.x + plane.w / 2 < canvas.width - 10) {
    220             plane.x += iMoveDir;
    221         }
    222     }
    223 }
    224 
    225 //不定时间的增加敌人
    226 function addEnemy() {
    227     clearInterval(enTimer);
    228     var randX = getRand(0, canvas.height - iEnemyH);
    229     enemies.push(new Enemy(randX, 0, iEnemyW, iEnemyH, -iEnemySpeed, oEnemyImage));
    230 
    231     var interval = getRand(1000, 4000);
    232     enTimer = setInterval(addEnemy, interval);
    233 }
    234 
    235 // 主初始化
    236 $(function() {
    237     canvas = document.getElementById('scene');
    238     ctx = canvas.getContext('2d');
    239 
    240     //加载背景图片
    241     backgroundImage = new Image();
    242     backgroundImage.src = 'images/levelmap.jpg';
    243     backgroundImage.onload = function() {}
    244     backgroundImage.onerror = function() {
    245         console.log('Error loading the background image.');
    246     }
    247 
    248     //初始化介绍图片
    249     introImage = new Image();
    250     introImage.src = 'images/intro.jpg';
    251 
    252     oRocketImage = new Image();
    253     oRocketImage.src = 'images/rocket.png';
    254     oRocketImage.onload = function(){}
    255 
    256     oExplosionImage = new Image();
    257     oExplosionImage.src = 'images/explosion.png';
    258     oExplosionImage.onload = function() {}
    259 
    260     oEnemyImage = new Image();
    261     oEnemyImage.src = 'images/enemy.png';
    262     oEnemyImage.onload = function() {}
    263 
    264     var oPlaneImage = new Image();
    265     oPlaneImage.src = 'images/plane.png';
    266     oPlaneImage.onload = function() {
    267         plane = new Plane(canvas.width / 2, canvas.height - 100, planeW, planeH, oPlaneImage);
    268     }    
    269 
    270     $(window).keydown(function(evt) {  //按下事件处理
    271         var pk = pressedKeys[evt.keyCode];
    272         if (!pk) {
    273             pressedKeys[evt.keyCode] = 1;
    274         }
    275 
    276         if (bPause && evt.keyCode == 13) {  //enter键 开始游戏
    277             bPause = false;
    278             setInterval(drawScene , 30);
    279             addEnemy();
    280         }
    281     });
    282 
    283     $(window).keyup(function(evt) {  //键释放事件处理
    284         var pk = pressedKeys[evt.keyCode];
    285         if (pk) {
    286             delete pressedKeys[evt.keyCode];
    287         }
    288         if (evt.keyCode == 65) {   //'A'键发射子弹
    289            rockets.push(new Rocket(plane.x - 16, plane.y - plane.h, 32, 32, iRocketSpeed, oRocketImage));
    290         }
    291         if (evt.keyCode ==37 || evt.keyCode == 39) {  
    292             //左右移动   37 左  39 右  但移动完之后,飞机恢复正常状态
    293             if (iSprPos > 2) {
    294                 for (var i = iSprPos; i >= 2; i--) {
    295                     iSprPos = i;
    296                     iMoveDir = 0;
    297                 } 
    298             } else {
    299                 for (var i = iSprPos; i <= 2; i++) {
    300                     iSprPos = i;
    301                     iMoveDir = 0;
    302                 }
    303             }
    304         }
    305     });
    306 
    307     introImage.onload = function() {
    308         displayIntro();
    309     }
    310 });

    在主初始化代码中,加载了所有必需的图片资源(level map, 介绍图片,子弹,爆炸,敌人和飞机)。然后,我们使用了数组来处理多个按键事件(在我们渲染主场景的时候,将会使用这个数组来操作我们的飞机),最后,介绍页被加载--显示界面图片。下面是一个重要的代码片段--怎样处理多个按键,请看下面代码:

     1 var pressedKeys = []; //按键的序列
     2 
     3 $(window).keydown(function (evt){ //鼠标按下事件处理
     4     var pk = pressedKeys[evt.keyCode];
     5     if (! pk) {
     6         pressedKeys[evt.keyCode] = 1; // 把按下的键添加的按键序列中
     7     }
     8 });
     9 
    10 $(window).keyup(function (evt) { // 鼠标释放事件处理
    11     var pk = pressedKeys[evt.keyCode];
    12     if (pk) {
    13         delete pressedKeys[evt.keyCode]; // 从按键序列中删除
    14     }
    15 });

    这个技术允许我们操作多个按键,在渲染我们主场景时,我们绘制的对象有:背景,飞机,子弹,敌人和爆炸。一旦我们击中敌人,将会在敌人的位置处绘制出爆炸。最后我们的对手是有攻击力的,当他们碰到飞机时,他们会爆炸,同时对飞机造成损伤。如果我们飞机的生命值小于0,则游戏结束。为了实现碰撞和爆炸,我使用了下面的代码:

     1 if (plane.y - plane.h/2 < enemies[ekey].y + enemies[ekey].h/2 && plane.x - plane.w/2 < enemies[ekey].x + enemies[ekey].w && plane.x + plane.w/2 > enemies[ekey].x) {
     2     explosions.push(new Explosion(enemies[ekey].x + enemies[ekey].w / 2, enemies[ekey].y + enemies[ekey].h / 2, 120, 120, 0, oExplosionImage));
     3 
     4     // 删除敌人,并计算损伤
     5     delete enemies[ekey];
     6     iLife -= iDamage;
     7 
     8     if (iLife <= 0) { // 游戏结束
     9         bPause = true;
    10 
    11         // 绘制分数
    12         ctx.font = '38px Verdana';
    13         ctx.fillStyle = '#fff';
    14         ctx.fillText('Game over, your score: ' + iScore * 10 + ' points', 25, 200);
    15         return;
    16     }
    17 }

    第三步:Custom graphics

    enemy.png, explosion.png, intro.jpg, levelmap.jpg, plane.png, rocket.png

    上面所有的图片都在源码包里。

    结论

    你喜欢我们的新SkyWalker游戏吗? 我非常乐意看见你的谢意和评论。好运!

  • 相关阅读:
    Master公式计算递归时间复杂度
    对数器的使用
    HTML翻转菜单练习
    剑指offer题目解答合集(C++版)
    HTML---仿网易新闻登录页
    两个有序数组中的中位数以及求第k个最小数的值
    算法之重建二叉树
    AFNetWorking 上传功能使用及源码分析
    图片Alpha预乘的作用[转]
    C#/.NET 学习之路——从入门到放弃
  • 原文地址:https://www.cnblogs.com/pigzhu/p/3310841.html
Copyright © 2011-2022 走看看