自己动手打造html5星际迷航!
学习html5的canvas第三天,觉得还没过瘾,转眼就忘,于是趁着有空,准备弄个小游戏来玩!游戏应该需要注意性能,还有一些逻辑需要斟酌,我想还需要用户可修改性,也就是用户配置。好,开始我们简单但有趣的旅程吧——
想先看效果的,先跳转试玩一下吧!
第一步,当然需要一张画布
1 <canvas id="canvas" width="300" height="400">你的浏览器不支持Canvas</canvas>
JavaScript的总体结构如下:
var定义一些变量
planeMoveTimer飞机移动监听/计时器
attackEnemyTimer发射炮弹计时器
onkeydown监听
onkeyup监听
drawPlane画飞机
drawEnemy画敌人
首先预定义一些变量
1 var _keyA = _keyD = _keyW = _keyS = 0, // 存储按键状态 2 3 step = 8, // 飞机移动速率 4 w = 34, h = 44, // 飞机实际大小 5 planeX = 133, planeY = 356, // 飞机目前位置 6 planeSrc = "feiji.png", // 飞机素材位置 7 imgW = 177, imgH = 220, // 飞机源图尺寸 8 9 cw = 300, ch = 400, // 画布大小 10 canvas = document.getElementById("canvas"), 11 ctx = canvas.getContext("2d");
本游戏只用到一个外部资源,就是那张图片,获取地址请访问本文底部给出的项目GitHub位置链接。跳转
先看画图的两个方法
1 function drawPlane(x, y) { 2 var img = new Image(); 3 img.src = planeSrc; // 飞机源图地址 4 img.onload = function() { 5 ctx.drawImage(img, 0, 0, imgW, imgH, planeX, planeY, w, h); 6 } 7 } 8 function drawEnemy(){ 9 for(var a=0;a<cw;a+=10) { 10 for(var b=0;b<ch-300;b+=10) { 11 ctx.beginPath(); 12 ctx.fillStyle = "orange"; 13 ctx.strokeStyle = "black"; 14 ctx.strokeRect(a, b, 10 ,10); 15 ctx.fillRect(a, b, 10, 10); 16 ctx.closePath(); 17 } 18 } 19 }
飞机的图片一定要在onload()方法里才能把飞机画出来,目前的敌人还是一堆橙色的不会动的砖头,通过遍历在画布的顶端把它们画出来。
接着,看两个键盘事件:
1 window.document.onkeydown = function(evt){ 2 evt = (evt) ? evt : window.event; 3 switch(evt.keyCode) { 4 case 65: // A 5 _keyA = 1; 6 break; 7 case 68: // D 8 _keyD = 1; 9 break; 10 case 87: // W 11 _keyW = 1; 12 break; 13 case 83: // S 14 _keyS = 1; 15 break; 16 } 17 } 18 window.document.onkeyup = function(evt){ 19 evt = (evt) ? evt : window.event; 20 switch(evt.keyCode) { 21 case 65: // A 22 _keyA = 0; 23 break; 24 case 68: // D 25 _keyD = 0; 26 break; 27 case 87: // W 28 _keyW = 0; 29 break; 30 case 83: // S 31 _keyS = 0; 32 break; 33 } 34 }
至于为什么要在两个事件里设置变量_keyA、_keyD、_keyW、_keyS,而不是直接触发画图事件,原因是——同时长按两个键时,无法同时触发事件,先按者只触发一次,只有后按者在按着键的时候才能一直触发事件,简单点来说,如果我想要向左上角移动,同时按下A和W,假设A比W慢了一点点,即时很微小,那么飞机的移动路径是先上移一步然后一直向左移动,这显然不是我想要的,我用4个变量来存储按键的状态,按下键的时候,设置其状态为1,在按键起来的时候,设置其状态为0,然后我们用计时器来不断地读这些键的状态并及时更新飞机的状态。
飞机移动计时器如下:
1 var planeMoveTimer = window.setInterval(function(){ 2 if(_keyA||_keyD||_keyW||_keyS){ 3 ctx.clearRect(planeX, planeY, w, h); 4 _keyA && (planeX-=step); 5 _keyD && (planeX+=step); 6 _keyW && (planeY-=step); 7 _keyS && (planeY+=step); 8 planeX>=0 || (planeX=0); 9 planeX<=(cw-w) || (planeX=cw-w); 10 planeY>=0 || (planeY=0); 11 planeY<=(ch-h) || (planeY=ch-h); 12 drawPlane(planeX, planeY); 13 } 14 }, 10);
ctx.clearRect()用来清除原来位置的飞机,为绘制飞机的下一状态做准备,但是有一个很大的问题,它是直接清除整块的,要是游戏有背景,有交叠,那不是要把这些不是飞机的东西也一并清空了?恕我愚昧,暂时不考虑这个问题。
通过判断按键状态,每次移动的步距为预先设置的step,并做好边界处理。
然后是攻击计时器:
1 var attackEnemyTimer = window.setInterval(function(){ 2 var cannonX = planeX+Math.floor(w/2); // 炮口X轴位置 3 var cannonY = planeY; // 炮口Y轴位置 4 var tmpTimer = window.setInterval(function(){ // 每颗炮弹的移动计时器 5 ctx.clearRect(cannonX-1, cannonY-3, 2, 3); 6 cannonY -= 3; // 炮弹步距 7 if(cannonY<0){ 8 // 炮弹的贯透效果 9 ctx.beginPath(); 10 ctx.moveTo(cannonX, 100); // 100为enemy的最低位置 11 ctx.strokeStyle = "white"; 12 ctx.lineWidth = "4"; // 模拟不规则毁坏,暂时尚未没实现 13 ctx.lineTo(cannonX, 0); 14 ctx.stroke(); 15 ctx.closePath(); 16 window.clearInterval(tmpTimer); // 清除该炮弹的计时器 17 } 18 ctx.clearRect(cannonX-1, cannonY-3, 2, 3); 19 ctx.beginPath(); 20 ctx.fillStyle="red"; 21 ctx.fillRect(cannonX-1, cannonY-3, 2, 3); // 炮弹大小:2X3 22 ctx.closePath(); 23 }, 0); 24 }, 500);
每0.5s发射一颗炮弹,每颗炮弹又单独设置一个计时器,以便控制,炮弹的移动我也是采用先擦后画的方式,由于炮弹移动也有步距,所谓炮弹贯透效果就是直接画一条颜色跟背景色一样的直线而已。试着修改炮弹步距可以调节炮弹的移动速度,也可以调节炮弹的尺寸。
最后一步了,还差点什么,一开始就要画好敌人和飞机啦!
1 drawPlane(); 2 drawEnemy();
大功告成!还想继续优化增加可玩性的,但实在是没时间弄了,还有很多其他的东西要学,所以这个游戏就先这样啦!是不是很简单!哈哈,囧了吧,标题太诱惑人,没办法!
其他:
在线试玩:http://xzh-loop.github.io/Manji/lab/html5game/Plane.html
访问这个项目的GitHub位置:https://github.com/xzh-loop/Manji/tree/gh-pages/lab/html5game
访问我的GitHub Pages:http://xzh-loop.github.io/