zoukankan      html  css  js  c++  java
  • js canvas游戏初级demo-躲避障碍物

    在线演示地址 http://139.196.97.20:3100/html/game_demo.html

    继上次js canvas游戏初级demo-上下左右移动(https://www.cnblogs.com/lzs-888/p/7427440.html),之后,对其新加了矩形下落和碰撞检测功能

    1.头像移动
    设置两个按键监听事件(keydown - 按下,keyup - 松开),每当按下时就将按下的键的state改为true,松开则相反,
    然后,设置了一个定时器,每一定时间会读取这些按键state,根据state和速度进行头像位置坐标的调整,并且擦除头像,重新画上

    2.矩形下落
    与头像移动类似,只是擦除和绘制有多个(本代码中用设置多个定时器来维护这些矩形的绘制),并且横坐标和下落速度在一定范围内随机

    3.碰撞检测
    这个直接看代码,本代码写的有点啰嗦

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>按键盘的上下左右躲开这些黑色障碍物</title>
    </head>
    <style>
        #canvas_dom{border: 1px solid #000;margin: 0 auto;display: block;background: #fff;}
        .title{text-align: center;}
        #game_time{text-align: center;}
    </style>
    <body>
        <h1 class="title">按键盘的上下左右躲开这些黑色障碍物</h1>
        <h2 id="game_time">你坚持的时间:<span>0</span>S</h2>
        <canvas id="canvas_dom"></canvas>
        <script>
            /*
                ddmm
                2018/07/13
    
                1.头像移动
                设置两个按键监听事件(keydown - 按下,keyup - 松开),每当按下时就将按下的键的state改为true,松开则相反,
                然后,设置了一个定时器,每一定时间会读取这些按键state,根据state和速度进行头像位置坐标的调整,并且擦除头像,重新画上
    
                2.矩形下落
                与头像移动类似,只是擦除和绘制有多个(本代码中用设置多个定时器来维护这些矩形的绘制),并且横坐标和下落速度在一定范围内随机
    
                3.碰撞检测
                这个直接看代码,本代码写的有点啰嗦
             */
            //全局变量
            var g = {
    
                ctx: null,//画布会话上下文
                   itv_ids: [],//用于记录定时器id,便于一起clear掉,减少内存消耗
                   game_over: false,//游戏是否结束
                   refreshNumForSec: 60,//每秒画面刷新次数
                   game_time: 0, //游戏坚持的时间
                canvasWidth: 1000,//画布宽度
                canvasHeight: 700,//画布高度
                faceDataUrl: 'http://www.200ok.fun:3100/images/wudier.png',//头像地址
                img: null,//头像图片引用
                faceWidth: 80,
                faceHeight: 82.5,
                faceX: 0,//头像位置x坐标(其初始值会在首次加载被initFacePos方法计算得出,故设置是无效的)
                faceY: 0,//头像位置y坐标(其初始值会在首次加载被initFacePos方法计算得出,故设置是无效的)
                faceLastX: 0,//头像上一次位置x坐标,用于擦除(其初始值会在首次加载被initFacePos方法计算得出,故设置是无效的)
                faceLastY: 0,//头像上一次位置y坐标,用于擦除(其初始值会在首次加载被initFacePos方法计算得出,故设置是无效的)
                keyRight: false,//是否按了→
                keyLeft: false,//是否按了←
                keyUp: false,//是否按了↑
                keyDown: false,//是否按了↓
                faceSpeed: 15,//头像每次刷新画布移动的像素
                   rectW: 200,//矩形的宽度
                rectH: 20,//矩形的高度
                   rectSpeedMin: 3,//方块速度最小值
                   rectSpeedMax: 15,//方块速度最大值
                   rectFlowNumForSec: 1,//每秒生成矩形个数
            };
     
            _main();//主函数
     
            //获取画布上下文
            function getCtx(){
                if(!g.ctx){
                    //获取dom
                    var canvas = document.querySelector('#canvas_dom');
                    
                    //设置宽高
                    canvas.width = g.canvasWidth;
                    canvas.height = g.canvasHeight;
                    
                    //获取会话上下文
                    g.ctx = canvas.getContext('2d');
                }
                
                return g.ctx;
            }
     
            function _main(){
                getCtx();//获取画布上下文
    
                //获取头像图片
                g.img = new Image();
                g.img.src = g.faceDataUrl;
                g.img.width = g.faceWidth;
                g.img.height = g.faceHeight;
                g.img.onload = function(){//图片加载完成
                    g.img.style.border = "1px solid #000";
                    initFacePos();
                }
                 
                 //监听keydown事件
                window.addEventListener('keydown',function(e){
                    var k = e.key;
                    stateJudge(k,true);//修改按键state
                });
             
                 //监听keyup事件
                window.addEventListener('keyup',function(e){
                    var k = e.key;
                    stateJudge(k,false);//修改按键state
                });
                 
                 //设置定时器,1秒重绘 g.refreshNumForSec 次头像
                g.itv_ids.push(
                    setInterval(function(){
                        if(!g.game_over){
                            moveJudge();
                            //擦除上一次画的
                            g.ctx.clearRect(g.faceLastX,g.faceLastY,g.img.width,g.img.height);
                            //绘制之前保存一下位置,用于下次擦除
                            g.faceLastX = g.faceX;
                            g.faceLastY = g.faceY;
                            //绘制头像
                            g.ctx.drawImage(g.img,g.faceX,g.faceY,g.img.width,g.img.height);
                        }
                    },1000/g.refreshNumForSec)
                );
    
                //每1秒生成一个随机位置,随机速度的矩形往下掉
                g.itv_ids.push(
                    setInterval(function(){
                        var max = g.canvasWidth - g.rectW;//画布宽度 - 矩形宽度
                        var x = get_random_num(0,max);//获取x坐标随机值
                        var speed = get_random_num(g.rectSpeedMin,g.rectSpeedMax);//获取速度随机值
                           drawTheRect(x,0,g.rectW,g.rectH,speed);
                    },1000/g.rectFlowNumForSec)
                );
    
                //数秒,改dom
                g.itv_ids.push(
                    setInterval(function(){
                        g.game_time++;
                        document.querySelector('#game_time span').innerText = g.game_time;
                    },1000)
                );
            }
    
            //根据画布和头像宽高,初始化头像位置,将其画于底部中间位置
            function initFacePos(){
                g.faceX = g.canvasWidth/2 - g.img.width/2;
                g.faceY = g.canvasHeight - g.img.height;
                g.faceLastX = g.faceX;
                g.faceLastY = g.faceY;
                g.ctx.drawImage(g.img,g.faceX,g.faceY,g.img.width,g.img.height);//绘制头像
            }
    
            /**
            * [checkRectImpact 碰撞检测,如果所提供的坐标点碰撞则返回true,否则返回false]
            * - - 没看过别人怎么写的,写的有点啰嗦
            * x1 x2 y1 y2 分别为第一个矩形(下称矩形A)的左边横坐标,右边横坐标,上边纵坐标,下边纵坐标
            * a1 a2 b1 b2 分别为第二个矩形(下称矩形B)的左边横坐标,右边横坐标,上边纵坐标,下边纵坐标
            * 坐标点如下图所示
              x1      x2 
                ————————   y1
                |       |
                |       |
                |        |
                ————————   y2
    
                a1      a2 
                ————————   b1
                |       |
                |       |
                |        |
                ————————   b2
            */
            function checkRectImpact(x1,x2,y1,y2,a1,a2,b1,b2){
                //分别为 左上角碰到 || 右上角碰到 || 右下角碰到 || 左下角碰到 || 矩形B大于矩形A且矩形B从下至上包含矩形A穿过 || 矩形B大于矩形A且矩形B从上至下包含矩形A穿过 || 矩形A大于矩形B且矩形A从下至上包含矩形B穿过 || 矩形A大于矩形B且矩形A从上至下包含矩形B穿过
                return (x1 <= a1 && x2 >= a1 && y1 <= b1 && y2 >= b1) || (x1 <= a2 && x2 >= a2 && y1 <= b1 && y2 >= b1) || (x1 <= a2 && x2 >= a2 && y1 <= b2 && y2 >= b2) || (x1 <= a1 && x2 >= a1 && y1 <= b2 && y2 >= b2) || (x1 >= a1 && x2 <= a2 && y2 >= b1 && y1 <= b1) || (x1 >= a1 && x2 <= a2 && y2 >= b2 && y1 <= b2) || (a1 >= x1 && a2 <= x2 && b2 >= y1 && b1 <= y1) || (a1 >= x1 && a2 <= x2 && b2 >= y2 && b1 <= y2);
            }
    
            //获取某个某个区间内的随机整数 ,获取到的值域为[min,max)
            function get_random_num(min,max){
                if(/^-?d+$/.test(min) && /^-?d+$/.test(max) && max>min){
                    return parseInt(Math.random()*(max - min) + min);
                }else{
                    return false;
                }
            }
    
            /**
             * [drawTheRect 画矩形]
             * @param  {[type]} x     [矩形距离左边框的距离]
             * @param  {[type]} y     [矩形距离上边框的距离]
             * @param  {[type]} w     [矩形的宽度]
             * @param  {[type]} h     [矩形的高度]
             * @param  {[type]} speed [每一次重绘画面,矩形下坠的距离,即下坠速度]
             * @return {[type]}       [description]
             */
            function drawTheRect(x,y,w,h,speed){
                var itv_id = setInterval(function(){
                    if(!g.game_over){
                        //绘制之前保存一下位置,用于下次擦除
                        var lastX = x;
                        var lastY = y;
                        // var speed = get_random_num(3,120); //经G民同学提示,可以考虑每次重绘方块时给其变速
                        y += speed;
                        g.ctx.clearRect(lastX,lastY,w,h); //擦除
                        g.ctx.fillRect(x,y,w,h); //
                        if(checkRectImpact(g.faceX,g.faceX + g.img.width,g.faceY,g.faceY + g.img.height,x,x+w,y,y+h)){//碰撞检测
                            game_over();
                            return;
                        }
                        if(y > g.canvasHeight){//如果已经超过了画布的高度那么移除这个定时器,减少内存消耗
                            clearInterval(itv_id);
                        }
                    }
                },1000/g.refreshNumForSec);
                g.itv_ids.push(itv_id);//存到全局变量
            }
    
            //游戏结束
            function game_over(){
                clearAllInterval();
                g.game_over = true;
                setTimeout(function(){//如果不延迟可能会导致上一次绘画未完成就被阻塞了
                    if(confirm("游戏结束,你坚持了" + g.game_time + "S" + ",继续努力!
    重新开始游戏请点'是'")){
                        window.location.reload();
                    }
                },50);
            }
    
            //clear所有定时器
            function clearAllInterval(){
                for(var i in g.itv_ids){
                    r = g.itv_ids[i];
                    clearInterval(r);
                }
            }
             
             //根据按键状态修改头像的坐标
            function moveJudge(){
                if(g.keyRight === true){
                    g.faceX += g.faceSpeed;
                }else if(g.keyLeft === true){
                    g.faceX -= g.faceSpeed;
                }else if(g.keyUp === true){
                    g.faceY -= g.faceSpeed;
                }else if(g.keyDown === true){
                    g.faceY += g.faceSpeed;
                }
    
                //边界情况处理,不要让头像超出边界
                if(g.faceX > g.canvasWidth - g.img.width){
                    g.faceX = g.canvasWidth - g.img.width;
                }else if(g.faceX < 0){
                    g.faceX = 0;
                }
                if(g.faceY > g.canvasHeight - g.img.height){
                    g.faceY = g.canvasHeight - g.img.height;
                }else if(g.faceY < 0){
                    g.faceY = 0;
                }
            }
     
            //根据按键修改状态
            function stateJudge(k,v){
                if(k == 'ArrowRight'){
                    g.keyRight = v;
                }else if(k == 'ArrowLeft'){
                    g.keyLeft = v;
                }else if(k == "ArrowUp"){
                    g.keyUp = v;
                }else if(k == "ArrowDown"){
                    g.keyDown = v;
                }
            }
        </script>
    </body>
    </html>
  • 相关阅读:
    linux之iptable案例
    nginx常用命令参数
    laravel中的多对多关系详解
    MySql计算时间差函数
    总结下Mysql分表分库的策略及应用
    swoole扩展实现真正的数据库连接池
    linux常用命令整理
    innodb mvcc实现机制
    mysqlslap 压力测试使用总结
    mysql索引总结
  • 原文地址:https://www.cnblogs.com/lzs-888/p/9317858.html
Copyright © 2011-2022 走看看