HTML5游戏开发系列教程7(译)

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

 今天我们将完成我们第一个完整的游戏--打砖块。这次教程中,将展示怎样进行基本的碰撞检测和使用HTML5的本地存储。你可以使用鼠标和键盘来操作挡板,上一次游戏的持续时间和分数将会保存。

前一篇的的介绍在HTML5游戏开发系列教程6(译)。

第一步:HTML

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

第二步:CSS

下面是css样式文件

css/main.css

 1 /* page layout styles */
 2 *{
 3     margin:0;
 4     padding:0;
 5 }
 6 body {
 7     background-color:#eee;
 8     color:#fff;
 9     font:14px/1.3 Arial,sans-serif;
10 }
11 header {
12     background-color:#212121;
13     box-shadow: 0 -1px 2px #111111;
14     display:block;
15     height:70px;
16     position:relative;
17     width:100%;
18     z-index:100;
19 }
20 header h2{
21     font-size:22px;
22     font-weight:normal;
23     left:50%;
24     margin-left:-400px;
25     padding:22px 0;
26     position:absolute;
27     width:540px;
28 }
29 header a.stuts,a.stuts:visited{
30     border:none;
31     text-decoration:none;
32     color:#fcfcfc;
33     font-size:14px;
34     left:50%;
35     line-height:31px;
36     margin:23px 0 0 110px;
37     position:absolute;
38     top:0;
39 }
40 header .stuts span {
41     font-size:22px;
42     font-weight:bold;
43     margin-left:5px;
44 }
45 .container {
46     margin: 20px auto;
47     overflow: hidden;
48     position: relative;
49     width: 800px;
50 }

第三步:JS

js/jquery-2.0.0.min.js  (原文使用的是jquery-1.5.2.min.js)

js/script.js

  1 //内部变量
  2 var canvas, ctx;
  3 
  4 var iStart = 0;
  5 var bRightBut = false;
  6 var bLeftBut = false;
  7 var oBall, oPadd, oBricks;
  8 var aSounds = [];
  9 var iPoints = 0;
 10 var iGameTimer;
 11 var iElapsed = iMin = iSec = 0;
 12 var sLastTime, sLastPoints;
 13 
 14 /**
 15 * @brief    球体对象
 16 *
 17 * @param    x   横坐标
 18 * @param    y   纵坐标
 19 * @param    dx  横坐标将要移动的距离 
 20 * @param    dy  纵坐标将要移动的距离
 21 * @param    r   半径
 22 *
 23 * @return   
 24 */
 25 function Ball(x, y, dx, dy, r) {
 26     this.x = x;
 27     this.y = y;
 28     this.dx = dx;
 29     this.dy = dy;
 30     this.r = r;
 31 }
 32 
 33 /**
 34 * @brief   挡板对象 
 35 *
 36 * @param    x   横坐标--纵坐标固定的
 37 * @param    w   宽端
 38 * @param    h   高度
 39 * @param    img 图片
 40 *
 41 * @return   
 42 */
 43 function Padd(x, w, h, img) {
 44     this.x = x;
 45     this.w = w;
 46     this.h = h;
 47     this.img = img;
 48 }
 49 
 50 /**
 51 * @brief    砖块对象
 52 *
 53 * @param    w   宽度
 54 * @param    h   高度
 55 * @param    r   row 第几排 
 56 * @param    c   column  第几列
 57 * @param    p   砖块之间的间隙
 58 *
 59 * @return   
 60 */
 61 function Bricks(w, h, r, c, p) {
 62     this.w = w;
 63     this.h = h;
 64     this.r = r;
 65     this.c = c;
 66     this.p = p;
 67     this.objs;
 68     this.colors = ['#9d9d9d', '#f80207', '#feff01', '#0072ff', '#fc01fc', '#03fe03'];
 69 }
 70 
 71 function clear() {
 72     ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
 73 
 74     ctx.fillStyle = '#111';
 75     ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
 76 }
 77 
 78 function drawScene() {
 79     clear(); 
 80 
 81     //绘制球
 82     ctx.fillStyle = '#f66';
 83     ctx.beginPath();
 84     ctx.arc(oBall.x, oBall.y, oBall.r, 0, Math.PI * 2, true);
 85     ctx.closePath();
 86     ctx.fill();
 87 
 88     //padd左右移动
 89     if (bRightBut) {
 90         oPadd.x += 5;
 91     } else if (bLeftBut) {
 92         oPadd.x -= 5;
 93     }
 94 
 95     ctx.drawImage(oPadd.img, oPadd.x, ctx.canvas.height - oPadd.h);
 96 
 97     //绘制砖块
 98     for (i = 0; i < oBricks.r; i++) {
 99         ctx.fillStyle = oBricks.colors[i];
100         for (j = 0; j < oBricks.c; j++) {
101             if (oBricks.objs[i][j] == 1) {
102                 ctx.beginPath();
103                 ctx.rect((j * (oBricks.w + oBricks.p)) + oBricks.p, (i * (oBricks.h + oBricks.p)) + oBricks.p, oBricks.w, oBricks.h);
104                 ctx.closePath();
105                 ctx.fill();
106             }
107         }
108     }
109 
110     //处理碰撞检测
111     iRowH = oBricks.h + oBricks.p;
112     iRow = Math.floor(oBall.y / iRowH);
113     iCol = Math.floor(oBall.x / (oBricks.w + oBricks.p));
114 
115     if (oBall.y < oBricks.r * iRowH && iRow >= 0 && iCol >= 0 && oBricks.objs[iRow][iCol] == 1) {  //处理球碰到砖块
116         oBricks.objs[iRow][iCol] = 0;
117         oBall.dy = -oBall.dy;
118         iPoints++;
119 
120         aSounds[0].play();
121     }
122 
123     if (oBall.x + oBall.dx + oBall.r > ctx.canvas.width || oBall.x + oBall.dx - oBall.r < 0) { //处理左右边界
124         oBall.dx = -oBall.dx;
125     }
126 
127     if (oBall.y + oBall.dy - oBall.r < 0) {   //处理上边界
128         oBall.dy = -oBall.dy;
129     } else if (oBall.y + oBall.dy + oBall.r > ctx.canvas.height - oPadd.h) {   //处理下边界
130         if (oBall.x > oPadd.x && oBall.x < oPadd.x + oPadd.w) {  //球碰到挡板反弹
131             oBall.dx = 10 * ((oBall.x - (oPadd.x + oPadd.w / 2)) / oPadd.w);
132             oBall.dy = -oBall.dy;
133 
134             aSounds[2].play();
135         } else if (oBall.y + oBall.dy + oBall.r > ctx.canvas.height) { //失败
136             clearInterval(iStart);
137             clearInterval(iGameTimer);
138 
139             //在Local Storage中存持续时间和分数
140             localStorage.setItem('last-time', iMin + ':' + iSec);
141             localStorage.setItem('last-points', iPoints);
142 
143             aSounds[1].play();
144         }
145     }
146 
147     oBall.x += oBall.dx;
148     oBall.y += oBall.dy;
149 
150     //显示分数和时间
151     ctx.font = '16px Verdana';
152     ctx.fillStyle = '#fff';
153     iMin = Math.floor(iElapsed / 60);
154     iSec = iElapsed % 60;
155     if (iMin < 10) {
156         iMin = "0" + iMin;
157     }
158     if (iSec < 10) {
159         iSec = "0" + iSec;
160     }
161     ctx.fillText('Time: ' + iMin + ':' + iSec, 600, 520);
162     ctx.fillText('Points: ' + iPoints, 600, 550);
163 
164     if (sLastTime != null && sLastPoints != null) {
165         ctx.fillText('Last Time: ' + sLastTime, 600, 400);
166         ctx.fillText('Last Points: ' + sLastPoints, 600, 490);
167     }
168 }
169 
170 //初始化
171 $(function() {
172     canvas = document.getElementById('scene');
173     ctx = canvas.getContext('2d');
174 
175     var width = canvas.width;
176     var height = canvas.height;
177 
178     var padImg = new Image();
179     padImg.src = 'images/padd.png';
180     padImg.onload = function() {};
181 
182     oBall = new Ball(width / 2, 550, 0.5, -5, 10);
183     oPadd = new Padd(width / 2, 120, 20, padImg);
184     oBricks = new Bricks((width / 8) - 1, 20, 6, 8, 2); 
185 
186     oBricks.objs = new Array(oBricks.r);   //oBricks.objs  是个二维数组
187     for (i = 0; i <oBricks.r; i++) {
188         oBricks.objs[i] = new Array(oBricks.c);
189         for (j = 0; j < oBricks.c; j++) {
190             oBricks.objs[i][j] = 1;
191         }
192     }
193 
194     //声音
195     aSounds[0] = new Audio('media/snd1.wav');
196     aSounds[0].volume = 0.9;
197     aSounds[1] = new Audio('media/snd2.wav');
198     aSounds[1].volume = 0.9;
199     aSounds[2] = new Audio('media/snd3.wav');
200     aSounds[2].volume = 0.9;
201 
202     iStart = setInterval(drawScene, 10);  //重绘
203     iGameTimer = setInterval(countTimer, 1000);  //计数器
204 
205     sLastTime = localStorage.getItem('last-time');
206     sLastPoints = localStorage.getItem('last-points');
207 
208     $(window).keydown(function(event) {
209         switch (event.keyCode) {
210             case 37:
211                 bLeftBut = true;
212                 break;
213             case 39:
214                 bRightBut = true;
215                 break;
216         }
217     });
218     $(window).keyup(function(event) {
219         switch (event.keyCode) {
220             case 37:
221                 bLeftBut = false;
222                 break;
223             case 39:
224                 bRightBut = false;
225                 break;
226         }
227     });
228 
229     //处理挡板跟随鼠标移动
230     var iCanvX1 = $(canvas).offset().left;
231     var iCanvX2 = iCanvX1 + width;
232     $('#scene').mousemove(function(e) {
233         if (e.pageX > iCanvX1 && e.pageX < iCanvX2) {
234             oPadd.x = Math.max(e.pageX - iCanvX1 - (oPadd.w / 2), 0);
235             oPadd.x = Math.min(ctx.canvas.width - oPadd.w, oPadd.x);
236         }
237     });
238 });
239 
240 function countTimer() {
241     iElapsed++;
242 }

我在很多地方添加了注释,希望这些代码很容易理解。注意localStorage对象,并理解它在HTML5本地存储中是如果使用的(使用setItem方法来存储数据,使用getItem来取出数据)。同样,理解怎样处理球和砖块之间的碰撞检测将会很有趣。

结论:

这次我们编写了我们的第一个打砖块游戏。最重要的功能已经呈现了,并且学习了碰撞检测和HTML5的本地存储。我非常乐意看见你的谢意和评论。好运!

原文地址:https://www.cnblogs.com/pigzhu/p/3214077.html