【问题标题】:2d html5 canvas collision, howto2d html5 画布碰撞,操作方法
【发布时间】:2013-07-18 05:41:42
【问题描述】:

正如标题所示,我遇到了对象碰撞问题... 我目前正在使用 JavaScript 开发 2d Html5 画布游戏。我知道如何防止“玩家”对象超出游戏画布的宽度/高度,并且我知道当玩家与对象(例如加电或敌人或其他任何东西)发生碰撞时如何做某事,但我只是不知道如何制作“固体”物体,意思是当玩家撞到固体物体时,玩家只是停下来,无法穿过固体物体。

这就是我现在所拥有的(不是所有的代码都是我觉得相关的,如果它太多/太少了抱歉。:

  var canvasPlayer = document.getElementById('canvasPlayer');
var ctxPlayer = canvasPlayer.getContext('2d');
var canvasWalls = document.getElementById('canvasWalls');
var ctxWalls = canvasWalls.getContext('2d');

function checkKeyDown(e) {
        var keyID = (e.keyCode) || e.which;
        if (keyID === 38 || keyID === 87)   { // up arrow OR W key
        if (!player1.isDownKey && !player1.isLeftKey && !player1.isRightKey) {
        player1.isUpKey = true;
        e.preventDefault();
        } }
        if (keyID === 39 || keyID === 68)   { //right arrow OR D key
        if (!player1.isDownKey && !player1.isLeftKey && !player1.isUpKey) {
        player1.isRightKey = true;
        e.preventDefault();
        } }
        if (keyID === 40 || keyID === 83)   {//down arrow OR S key
        if (!player1.isUpKey && !player1.isLeftKey && !player1.isRightKey) {
        player1.isDownKey = true;
        e.preventDefault();
        } }
        if (keyID === 37 || keyID === 65)   {//left arrow OR A key
        if (!player1.isDownKey && !player1.isUpKey && !player1.isRightKey) {
        player1.isLeftKey = true;
        e.preventDefault();
        }
        }
    }

    Walls.prototype.draw = function (){
        ctxWalls.drawImage(imgSprite,this.srcX,this.srcY,this.width,this.height,this.drawX,this.drawY,this.width,this.height);
        this.checkHitPlayer();
        };
    Walls.prototype.checkHitPlayer = function() {
        if (this.drawX > player1.drawX &&
        this.drawX <= player1.drawX + player1.width &&
        this.drawY >= player1.drawY &&
        this.drawY < player1.drawY + player1.height) {
        player1.isUpKey = false;
        player1.isDownKey = false;
        player1.isRightKey = false;
        player1.isLeftKey = false;
            }
    }; 

这行得通...除了尝试向上或向左移动时,玩家只能移动 2-3 个像素,因此需要 3 个向左或向上箭头才能向左或向上移动。玩家也可以直接穿过墙壁,这不是我想要的。如果我包含太多或没有足够的代码,任何帮助都非常感谢。哦,我也忘了说这个游戏是一个益智游戏,我已经设置了一个玩家一次只能移动一个方向直到撞到墙。

【问题讨论】:

    标签: javascript html canvas html5-canvas collision


    【解决方案1】:

    如果您只是希望播放器在到达墙壁时停止,您可以应用一些数学运算:

    例如:假设你的播放器是一个 10px x 10px 的矩形,右边墙的 X 位置是 200。

    矩形右侧的X位置计算如下:

    var playerRightSide = player.x + player.width;
    

    您可以像这样测试玩家是否已经到达墙壁:

    if( playerRightSide >= 200 )
    

    如果用户试图将他们的播放器推到墙外,您可以使用播放器 X 位置将播放器固定在墙的左侧。

    if( playerRightSide >= 200 ) { player.x = 190; }  
    

    190 是墙的 X 位置 (200) 减去玩家的宽度 (10)。

    如果您有兴趣进行更高级的碰撞测试,请进一步阅读。

    许多基本的游戏碰撞可以分为 3 种类型:

    • 圆与圆碰撞
    • 矩形与矩形碰撞
    • 矩形与圆形碰撞

    下面是如何检测这些常见碰撞的说明。

    假设您这样定义一个圆:

    var circle1={
        x:30,
        y:30,
        r:10
    };
    

    假设你这样定义一个矩形:

    var rect1={
        x:20,
        y:100,
        w:20,
        h:20
    };
    

    您可以像这样检测圆形与圆形碰撞...

    ...使用此 Circle vs Circle 碰撞测试代码:

        // return true if the 2 circles are colliding
        // c1 and c2 are circles as defined above
    
        function CirclesColliding(c1,c2){
            var dx=c2.x-c1.x;
            var dy=c2.y-c1.y;
            var rSum=c1.r+c2.r;
            return(dx*dx+dy*dy<=rSum*rSum);
        }
    

    您可以像这样检测矩形与矩形的碰撞...

    ...使用这个 Rectangle vs Rectangle 碰撞测试代码:

        // return true if the 2 rectangles are colliding
        // r1 and r2 are rectangles as defined above
    
        function RectsColliding(r1,r2){
            return !(r1.x>r2.x+r2.w || r1.x+r1.w<r2.x || r1.y>r2.y+r2.h || r1.y+r1.h<r2.y);
        }
    

    您可以像这样检测矩形与圆形的碰撞...

    ...使用这个 Rectangle vs Circle 碰撞测试代码:

        // return true if the rectangle and circle are colliding
        // rect and circle are a rectangle and a circle as defined above
    
        function RectCircleColliding(rect,circle){
            var dx=Math.abs(circle.x-(rect.x+rect.w/2));
            var dy=Math.abs(circle.y-(rect.y+rect.y/2));
    
            if( dx > circle.r+rect.w2 ){ return(false); }
            if( dy > circle.r+rect.h2 ){ return(false); }
    
            if( dx <= rect.w ){ return(true); }
            if( dy <= rect.h ){ return(true); }
    
            var dx=dx-rect.w;
            var dy=dy-rect.h
            return(dx*dx+dy*dy<=circle.r*circle.r);
        }
    

    例如,您可以使用这些碰撞测试来响应玩家触摸通电立方体:

        // create a circular player object
        // that's located at [30,30] and has a radius of 10px
    
        var player={x:30,y:30,r:10};
    
    
        // create a rectangular power-up at position [200,30]
    
        var powerup={x:200, y:30, w:20, h:20};
    
    
        // Let's say the user keys the player to coordinate [200,35] 
        // (touching the power-up)
    
        player.x = 220;
        player.y = 35;
    
    
        // you can test if the circular player is touching the rectangular power-up
    
        if( RectCircleColliding(powerup,player)  ) {
    
            // the player has collided with the power-up, give bonus power!
    
            player.power += 100;
    
        }
    

    这是代码和小提琴:http://jsfiddle.net/m1erickson/u6t48/

    <!doctype html>
    <html>
    <head>
    <link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
    <script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
    
    <style>
        body{ background-color: ivory; padding:20px; }
        canvas{border:1px solid red;}
    </style>
    
    <script>
        $(function(){
    
            var canvas=document.getElementById("canvas");
            var ctx=canvas.getContext("2d");
    
            window.requestAnimFrame = (function(callback) {
              return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
              function(callback) {
                window.setTimeout(callback, 1000 / 60);
              };
            })();
    
            ctx.fillStyle="lightgray";
            ctx.strokeStyle="skyblue";
    
            // top collision circle vs circle
            var circle1={x:30,y:30,r:10};
            var circle2={x:70,y:40,r:10};
            var circle3={x:100,y:30,r:10};
            var direction1=1;
    
            // middle collision rect vs rect
            var rect1={x:20,y:100,w:20,h:20};
            var rect2={x:50,y:110,w:20,h:20};
            var rect3={x:90,y:100,w:20,h:20};
            var direction2=1;
    
            // bottom collision rect vs circle
            var circle4={x:30,y:200,r:10};
            var rect4={x:50,y:205,w:20,h:20};
            var circle5={x:100,y:200,r:10};
            var direction3=1;
    
    
            function drawAll(){
                ctx.clearRect(0,0,canvas.width,canvas.height);
                drawCircle(circle1);
                drawCircle(circle2);
                drawCircle(circle3);
                drawCircle(circle4);
                drawCircle(circle5);
                drawRect(rect1);
                drawRect(rect2);
                drawRect(rect3);
                drawRect(rect4);
            }
    
            function drawCircle(c){
                ctx.beginPath();
                ctx.arc(c.x,c.y,c.r,0,Math.PI*2,false);
                ctx.closePath();
                ctx.fill();
                ctx.stroke();
            }
    
            function drawRect(r){
                ctx.beginPath();
                ctx.rect(r.x,r.y,r.w,r.h);
                ctx.closePath();
                ctx.fill();
                ctx.stroke();
            }
    
            // return true if the 2 circles are colliding
            function CirclesColliding(c1,c2){
                var dx=c2.x-c1.x;
                var dy=c2.y-c1.y;
                var rSum=c1.r+c2.r;
                return(dx*dx+dy*dy<=rSum*rSum);
            }
    
            // return true if the 2 rectangles are colliding
            function RectsColliding(r1,r2){
                return !(r1.x>r2.x+r2.w || r1.x+r1.w<r2.x || r1.y>r2.y+r2.h || r1.y+r1.h<r2.y);
            }
    
            // return true if the rectangle and circle are colliding
            function RectCircleColliding(rect,circle){
                var dx=Math.abs(circle.x-(rect.x+rect.w/2));
                var dy=Math.abs(circle.y-(rect.y+rect.h/2));
    
                if( dx > circle.r+rect.w/2 ){ return(false); }
                if( dy > circle.r+rect.h/2 ){ return(false); }
    
                if( dx <= rect.w ){ return(true); }
                if( dy <= rect.h ){ return(true); }
    
                var dx=dx-rect.w;
                var dy=dy-rect.h
                return(dx*dx+dy*dy<=circle.r*circle.r);
            }
    
            var fps = 15;
            function animate() {
                setTimeout(function() {
                    requestAnimFrame(animate);
    
                    // circle vs circle
                    circle2.x = circle2.x+direction1;
                    if( CirclesColliding(circle2,circle1) || CirclesColliding(circle2,circle3)  ){ 
                        direction1=-direction1; 
                    }
    
                    // rect vs rect
                    rect2.x = rect2.x+direction2;
                    if( RectsColliding(rect2,rect1) || RectsColliding(rect2,rect3) ){
                        direction2=-direction2;
                    }
    
                    // rect vs circle
                    rect4.x = rect4.x+direction3;
                    if( RectCircleColliding(rect4,circle4) || RectCircleColliding(rect4,circle5) ){
                        direction3=-direction3;
                    }
    
                    drawAll();
    
                }, 1000 / fps);
            }
    
            animate();
    
        }); // end $(function(){});
    </script>
    
    </head>
    
    <body>
        <canvas id="canvas" width=300 height=300></canvas>
    </body>
    </html>
    

    【讨论】:

    • 嘿,我知道这个话题很老了,但我又遇到了麻烦,我设置了块之间的碰撞,如果“玩家”击中“对象”,“玩家”会停在左边物体”。但我希望“玩家”能够从任何一侧(左、上、下、右)击中同一个“物体”,并适当地停止。新手有什么建议吗?哦,我忘了提,谢谢你的回复:)
    • 我发布的碰撞函数将处理来自各个方面的碰撞。看看这个小提琴:jsfiddle.net/m1erickson/EKCRJ
    • 感谢您的快速回复!我正在阅读您的评论链接,我可能是错的,但我不确定这正是我想要做的。如果您不介意看一下,我今天早些时候有一个问题可以更好地解释自己?:stackoverflow.com/questions/17932679/…
    猜你喜欢
    • 2014-08-30
    • 2014-02-15
    • 2021-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-07
    • 2013-04-19
    相关资源
    最近更新 更多