canvas开发游戏像素碰撞解决不规则图形问题附代码下载

来源:程序思维浏览:2209次
一般来说在使用像素碰撞检测之前会使用AABB矩形包围盒先检测两个精灵是否有碰撞,如果AABB包围盒检测没有碰撞,那一定是没有碰撞到,反之,则不一定,需要进一步进行像素检测。如下图所示,很明显,虽然两个精灵的包围盒发生了碰撞,但两个精灵本身没有发生碰撞,所以在这种精灵的形状极为不规则的情况下,除非使用多边形包围盒,并且需要多边形和精灵的形状极为接近,才能够获取好的效果,而且,别忘了,多边形只适合凸多边形的情况。

使用像素碰撞检测法算是最精确的算法了,当然,带来的代价也是比较明显的,那就是效率上的低下。除非是在极为特殊的情况下,要求使用非常精确的碰撞,否则,一般情况下在游戏中是不建议使用这种算法,特别是在运行效率不太高的HTML5游戏中。

这样,我们就只能采用像素检测算法达到精确检测的目的。接下来,先来看看像素碰撞的原理,首先,我们知道所有的精灵都是由像素点组成,而在canvas的像素数据中每个点都是由RGBA四个数据组成。如果某个点的A(alpha值,透明度)为0则表示该点是透明的,两个图片的空白部分的点的A值为0。如果两个图片发生碰撞,则表示两个图片有像素点发生了重叠,即两个图片的像素点坐标相同。
canvas开发游戏像素碰撞

根据这个原理,基本上我们可以采取以下步骤来进行检测。

(1)选择需要检测的两个图片。

(2)先检测两个图片是否发生包围盒碰撞,如果没有则退出,否则获取两个矩形的相交区域,并继续。

(3)把一个图片绘制到和游戏屏幕等大的空白的后台缓冲区中,获取缓冲区中在相交区域的像素数据。

(4)清除后台缓冲区。

(5)对另一个图片进行步骤3的操作。

(6)得到两个图片在同一个相交矩形的像素数据后,循环比较每一个像素点,如果两个图片在同一位置的透明度不都是0,则表示两个图片有相交,退出循环,返回真。

需要注意的一点是,在第3步获取相交区域的像素数据中,需要在后台另外的一个和游戏屏幕等大的空白区域中绘制,而不能直接获取游戏画面中的相交区域,因为两个图片在相交区域中的像素已经发生了重叠【包围盒】。

由以上的算法可以看出,在进行像素检测的时候,需要另外一个缓冲区,把需要检测的图片绘制一次,需要清空缓冲区,最后还要使用一个for循环检测像素。如果相交区域为100×100个像素点,则最坏的情况需要循环10000 次,由此看来,这真不是一个省时的工作。

代码如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
    <meta name="format-detection" content="telephone=no,email=no,date=no,address=no"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
    <title>像素碰撞</title>
    <style>
        .stage{border:#0000FF 1px solid;}
    </style>
</head>
<body>
<h1>包围盒是否碰撞:<span class="hitTestBox">否</span></h1>
<h1>像素检测是否碰撞:<span class="hitTestPixel">否</span></h1>
<canvas id="stage" class="stage"></canvas>
<img src="./images/1.png" alt="penguinImg" id="penguinImg" style="display:none" />
<img src="./images/2.png" alt="giraffeImg" id="giraffeImg" style="display:none" />
<script>
    window.onload = function () {
        var stage = document.querySelector('#stage'),
            ctx = stage.getContext('2d');
        stage.width = 600;
        stage.height = 600;
        //创建canvas
        var bC = document.createElement("canvas");
        bC.width = stage.width;
        bC.height = stage.height;
        var backBuf = bC.getContext("2d");
        var penguin = document.querySelector('#penguinImg'),giraffe = document.querySelector('#giraffeImg');
        var penguinImg = {
            x:stage.width/2,
            y:stage.height/2,
            r:128,
            data:null
        },giraffeImg = {
            x:100,
            y:100,
            r:128,
            data:null
        };
        function drawImageBox(img,x,y,width,height){
            ctx.beginPath();
            ctx.rect(x,y,width,height);
            ctx.stroke();
            ctx.drawImage(img,x,y,width,height);
        }
        //缓存画布绘制方法
        function drawImageBC(img,x,y,width,height){
            backBuf.clearRect(0,0,stage.width,stage.height);
            backBuf.save();
            backBuf.drawImage(img,x,y,width,height);
            backBuf.restore();
        }
        //获取两个矩形相交区域
        function getInRect(x1,y1,x2,y2,x3,y3,x4,y4) {
            return [Math.max(x1,x3),Math.max(y1,y3),Math.min(x2,x4),Math.min(y2,y4)];
        }
        document.onkeydown = function (event) {
            var e = event || window.event || arguments.callee.caller.arguments[0];
            //根据地图数组碰撞将测
            switch (e.keyCode) {
                case 37:
                    console.log("Left");
                    if (penguinImg.x > 0) {
                        penguinImg.x -= 2;
                    }
                    break;
                case 38:
                    console.log("Top");
                    if (penguinImg.y > 0) {
                        penguinImg.y -= 2;
                    }
                    break;
                case 39:
                    console.log("Right");
                    if (penguinImg.x < stage.width) {
                        penguinImg.x += 2;
                    }
                    break;
                case 40:
                    console.log("Bottom");
                    if (penguinImg.y < stage.height) {
                        penguinImg.y += 2;
                    }
                    break;
                default:
                    return false;
            }
        };
        stage.addEventListener('click', function (event) {
            var x = event.clientX - stage.getBoundingClientRect().left;
            var y = event.clientY - stage.getBoundingClientRect().top;
            penguinImg.x = x;
            penguinImg.y = y;
        });
        function update() {
            ctx.clearRect(0, 0, 600, 600);
            drawImageBox(giraffe,giraffeImg.x,giraffeImg.y,giraffeImg.r,giraffeImg.r);
            drawImageBox(penguin,penguinImg.x,penguinImg.y,penguinImg.r,penguinImg.r);
            document.querySelector('.hitTestBox').innerHTML = "否";
            document.querySelector('.hitTestPixel').innerHTML = "否";
            var rect = getInRect(giraffeImg.x,giraffeImg.y,giraffeImg.x+giraffeImg.r,giraffeImg.y+giraffeImg.r,penguinImg.x,penguinImg.y,penguinImg.x+penguinImg.r,penguinImg.y+penguinImg.r)
            //如果没有相交则退出
            if(rect[0]>=rect[2]||rect[1]>=rect[3]) {
            } else{
                document.querySelector('.hitTestBox').innerHTML = "是";
                giraffeImg.data = null;
                penguinImg.data = null;
                //获取坦克在相交矩形像素数据
                drawImageBC(giraffe,giraffeImg.x,giraffeImg.y,giraffeImg.r,giraffeImg.r);
                giraffeImg.data = backBuf.getImageData(rect[0],rect[1],rect[2],rect[3]).data;
                drawImageBC(penguin,penguinImg.x,penguinImg.y,penguinImg.r,penguinImg.r);
                penguinImg.data = backBuf.getImageData(rect[0],rect[1],rect[2],rect[3]).data;
                for(var i=3;i<giraffeImg.data.length;i+=4)
                {
                    if(giraffeImg.data[i]>0&&penguinImg.data[i]>0){
                        document.querySelector('.hitTestPixel').innerHTML = "是";
                    }
                }
            }
            requestAnimationFrame(update);
        }
        update();
    };
</script>
</body>
</html>

canvas像素碰撞源码下载链接:https://pan.baidu.com/s/1yfvnivsNwstoIjhy5_w4pw  密码:joku

备注:不要直接打开文件测试,需要放到服务器上测试比如:apache、nginx等等。
精品好课
React实战视频教程仿京东移动端电商
React是前端最火的框架之一,就业薪资很高,本课程教您如何快速学会React并应用到实战,对正在工作当中或打算学习React高薪就业的你来说,那么这门课程便是你手中的葵花宝典。
最新完整React视频教程从入门到精通纯干货纯实战
React是目前最火的前端框架,就业薪资很高,本课程教您如何快速学会React并应用到实战,教你如何解决内存泄漏,常用UI库的使用,自己封装组件,正式上线白屏问题,性能优化等。对正在工作当中或打算学习React高薪就...
jQuery视频教程从入门到精通
jquery视频教程从入门到精通,课程主要包含:jquery选择器、jquery事件、jquery文档操作、动画、Ajax、jquery插件的制作、jquery下拉无限加载插件的制作等等......
VUE2+VUE3视频教程从入门到精通(全网最全的Vue课程)
VUE是目前最火的前端框架之一,就业薪资很高,本课程教您如何快速学会VUE+ES6并应用到实战,教你如何解决内存泄漏,常用UI库的使用,自己封装组件,正式上线白屏问题,性能优化等。对正在工作当中或打算学习VUE高薪就...
HTML5基础入门视频教程易学必会
HTML5基础入门视频教程,教学思路清晰,简单易学必会。适合人群:创业者,只要会打字,对互联网编程感兴趣都可以学。课程概述:该课程主要讲解HTML(学习HTML5的必备基础语言)、CSS3、Javascript(学习...
最新完整React+VUE视频教程从入门到精,企业级实战项目
React和VUE是目前最火的前端框架,就业薪资很高,本课程教您如何快速学会React和VUE并应用到实战,教你如何解决内存泄漏,常用库的使用,自己封装组件,正式上线白屏问题,性能优化等。对正在工作当中或打算学习Re...
HTML5视频播放器video开发教程
适用人群1、有html基础2、有css基础3、有javascript基础课程概述手把手教你如何开发属于自己的HTML5视频播放器,利用mp4转成m3u8格式的视频,并在移动端和PC端进行播放支持m3u8直播格式,兼容...
Vue2+Vue3+ES6+TS+Uni-app开发微信小程序从入门到实战视频教程
2021年最新Vue2+Vue3+ES6+TypeScript和uni-app开发微信小程序从入门到实战视频教程,本课程教你如何快速学会VUE和uni-app并应用到实战,教你如何解决内存泄漏,常用UI库的使用,自己...
收藏
扫一扫关注我们