zoukankan      html  css  js  c++  java
  • 自己动手打造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/

  • 相关阅读:
    VUE调用函数,以及event的讲解
    VUE双向绑定,以及表达式自增
    使用VUE的准备工作,以及显示文本
    MVC中VUE的学习
    Redis
    C# form表单提交enctype="multipart/form-data" 与 enctype="application/x-www-form-urlencoded" 两者之间的区别
    C# 导入导出
    文件压缩,解压缩
    [vue-router] Duplicate named routes definition: { name: "Front", path: "/home/front" }
    * @/components/widget/AddBands in ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./src/components/pages/Brands.vue
  • 原文地址:https://www.cnblogs.com/xzhang/p/3955352.html
Copyright © 2011-2022 走看看