Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/442.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript画布:旋转玩家时与敌人的碰撞不完全有效_Javascript_Canvas_Collision - Fatal编程技术网

Javascript画布:旋转玩家时与敌人的碰撞不完全有效

Javascript画布:旋转玩家时与敌人的碰撞不完全有效,javascript,canvas,collision,Javascript,Canvas,Collision,注意:*可以在我的帖子底部找到完整的JSFIDLE* 问题:我试图摧毁所有接触画布中心蓝线的敌人。然而,事实并非如此,我的实现只是“半途而废”。当一方工作时,另一方不工作。我如何解决这个问题 我尝试的内容:设置基本绘图功能后,我计算了碰撞对象的x和y之间的差值。使用毕达哥拉斯距离计算两点之间的距离。最后检查距离是否小于或等于两个对象的组合半径。使用arctangent,我计算了对象运动的旋转 我想到的另一种解决方案:使用循环沿蓝线创建各种不可见的圆或点,作为碰撞接收器。问题是:它消耗了更多的

注意:*可以在我的帖子底部找到完整的JSFIDLE*

问题:我试图摧毁所有接触画布中心蓝线的敌人。然而,事实并非如此,我的实现只是“半途而废”。当一方工作时,另一方不工作。我如何解决这个问题


我尝试的内容:设置基本绘图功能后,我计算了碰撞对象的x和y之间的差值。使用毕达哥拉斯距离计算两点之间的距离。最后检查距离是否小于或等于两个对象的组合半径。使用arctangent,我计算了对象运动的旋转


我想到的另一种解决方案:使用循环沿蓝线创建各种不可见的圆或点,作为碰撞接收器。问题是:它消耗了更多的资源,一点也不优雅


您最感兴趣的Javascript函数是:

function (player, spawn) {
    return (this.distance(player, spawn) <= player.radius + spawn.radius) && (this.tangent(player, spawn) <= angle - Math.PI * 1);
}
适用于--、-+、++,但不适用于+-(右下角)

因此,最终我完全搞不懂为什么我的逻辑不起作用,但我渴望了解如何实现这一点:


在JSFiddle下面:

我很乐意得到回复,因为我喜欢用Javascript学习新东西:)


编辑(2015年11月3日):如果可能,只发布纯数学解决方案,但也可以发布其他解决方案。为了学习新技术,欢迎提供每一条信息。

代码的问题似乎在于比较角度的方式。不要忘记2Pi与0完全相同。看看这个例子: 你有两个角度,a和b

a=0.1*Pi

b=1.9*Pi

a略高于x轴,而b略低于x轴

看这两种情况时,a似乎都在b之前,所以你会认为a>b是真的。但是等等!看看数字,b比a大得多! 当您想要检查某个角度是否在某个间隔之间时,必须确保间隔是连续的,在本例中,如果角度为0,则该间隔为false

这是我的解决办法。我尽可能地测试了它,但你永远不会知道你是否错过了什么

// Gets the equivalent angle between 0 and MAX
var normalize_angle = function( angle )
{
    var MAX = Math.PI * 2;  // Value for a full rotation. Should be 360 in degrees
    angle %= MAX;
    return angle < 0 ? angle + MAX : angle;
};

var is_angle_between = function( alpha, min, max )
{
    // Convert all the angles to be on the same rotation, between 0 and MAX
    alpha = normalize_angle( alpha );
    min = normalize_angle( min );
    max = normalize_angle( max );

    if( max > min )
    {   // Check if the equal case fits your needs. It's a bit pointless for floats
        return max >= alpha && min <= alpha;    // Traditional method works
    } else {    // This happens when max goes beyond MAX, so it starts from 0 again
        return max >= alpha || min <= alpha;    // alpha has to be between max and 0 or
                                                //                 between min and MAX
    }
};
//获取0和MAX之间的等效角度
var normalize_angle=函数(角度)
{
var MAX=Math.PI*2;//完整旋转的值。应为360度
角度%=最大值;
返回角度<0?角度+最大:角度;
};
var是函数之间的角度(α,最小,最大)
{
//将所有角度转换为相同的旋转角度,介于0和最大值之间
alpha=标准化_角(alpha);
最小值=标准化角度(最小值);
最大值=标准化角度(最大值);
如果(最大>最小)
{//检查大小写是否符合您的需要。这对于浮动来说有点毫无意义

return max>=alpha&&min=alpha | | min简化了磁盘和圆弧之间的碰撞检测问题(编辑:带有@markE建议)(用于调试:)

一些用于比较角度的函数:

function mod(x, value){ // Euclidean modulo http://jsfiddle.net/cLvmrs6m/4/
    return x>=0 ? x%value : value+ x%value;
}

function angularize(x){
    return mod(x+pi, 2*pi)-pi;
}
碰撞检测:

var d_enemy_player = dist(enemy.pos, player.pos)
if (d_enemy_player>player.shieldradius-enemy.radius && d_enemy_player<player.shieldradius+enemy.radius){ 
    //only worth checking when we are approaching the shield distance
    var angle_enemy = atan2(enemy.pos.y-player.pos.y, enemy.pos.x-player.pos.x)

    var delta_with_leftofshield = angularize(angle_enemy-player.angle-player.shieldwidth)
    var delta_with_rightofshield = angularize(angle_enemy-player.angle+player.shieldwidth)
    var delta_with_shield = angularize(angle_enemy-player.angle)

    if (delta_with_leftofshield<0 && delta_with_rightofshield>0){
        console.log('boom')
        enemy.destroyed = true;
    } else if(delta_with_shield>=0 ){
        // check distance with right extremety of shield's arc
        console.log('right')
        var d_rightofshield_enemy = dist(enemy.pos, {x:player.pos.x+player.shieldradius*cos(player.angle+player.shieldwidth), y:player.pos.y+player.shieldradius*sin(player.angle+player.shieldwidth)});
        if (d_rightofshield_enemy<enemy.radius){
            console.log('right boom')
            enemy.destroyed = true;
        }
    } else {
        console.log('left')
        var d_leftofshield_enemy = dist(enemy.pos, {x:player.pos.x+player.shieldradius*cos(player.angle-player.shieldwidth), y:player.pos.y+player.shieldradius*sin(player.angle-player.shieldwidth)});
        if (d_leftofshield_enemy<enemy.radius){
            console.log('left boom')
            enemy.destroyed = true;
        }
    }
}
var d_敌方玩家=dist(敌方位置,玩家位置)
如果(敌方玩家>玩家.shieldradius-敌方.radius&&d敌方玩家=0){
//检查与屏蔽弧右端的距离
console.log('right')
var d_rightofshield_敌军=dist(敌军位置,{x:player.pos.x+player.shieldradius*cos(player.angle+player.shieldwidth),y:player.pos.y+player.shieldradius*sin(player.angle+player.shieldwidth)});

如果Html5画布有一个非常好的命中测试方法:
context.isPointInPath

你可以用这个方法来测试一个圆是否与你的护盾发生碰撞。它可以在护盾的所有角度工作

在您的情况下,路径将是半径为
player.shield.radius敌军.radius
的内弧和半径为
player.shield.radius+敌军.radius
的外弧

mousemove
内部,只需绘制(不划)护盾路径的2个圆弧,并使用
context.isPointInside(敌方.centerX,敌方.centerY)测试每个敌方的中心点

为了获得更高的精度,将护盾路径扫描范围扩大到敌人两端的半径

下面是示例代码和演示:

函数日志(){
console.log.apply(控制台,参数);
}
var canvas=document.getElementById(“canvas”);
var ctx=canvas.getContext(“2d”);
var cw=画布宽度;
var ch=画布高度;
函数reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
var offsetX,offsetY;
reOffset();
window.onscroll=函数(e){
reOffset();
}
window.onresize=函数(e){
reOffset();
}
var isDown=假;
var startX,startY;
var cx=cw/2;
var-cy=ch/2;
var半径=100;
var startAngle=Math.PI/6;
var-enemyRadius=15;
var shieldStrokeWidth=8;
var endRadians=enemyRadius/(2*Math.PI*radius)*(Math.PI*2);
定义hieldhitpath(cx、cy、radius、enemyRadius、startAngle);
牵引护板(cx、cy、半径、星形缠绕、护板行程宽度);
$(“#画布”).mousemove(函数(e){
手推车(e);
});
函数定义hieldHitPath(cx、cy、r、enemyRadius、startAngle){
ctx.beginPath();
ctx.弧(cx,cy,r-半径-屏蔽StrokeWidth/2,星形-端弧度,星形+数学π+端弧度);
ctx.arc(cx,cy,r+enemyRadius+shieldStrokeWidth/2,startAngle+Math.PI+endRadians,startAngle-endRadians,true);
ctx.closePath();
ctx.lineWidth=1;
ctx.strokeStyle=‘黑色’;
//只是为了演示。
//如果您所做的只是“isPointInPath”,则不必笔划()
ctx.stroke();
}
功能吸屏(cx、cy、r、startAngle、strokeWidth){
ctx.beginPath();
ctx.arc(cx、cy、r、startAngle、startAngle+Ma
shield: 
    function (player, spawn) {
        return (this.distance(player, spawn) <= player.radius + spawn.radius) && 
            is_angle_between(this.tangent(player, spawn), angle , angle - Math.PI );
    }
}
function mod(x, value){ // Euclidean modulo http://jsfiddle.net/cLvmrs6m/4/
    return x>=0 ? x%value : value+ x%value;
}

function angularize(x){
    return mod(x+pi, 2*pi)-pi;
}
var d_enemy_player = dist(enemy.pos, player.pos)
if (d_enemy_player>player.shieldradius-enemy.radius && d_enemy_player<player.shieldradius+enemy.radius){ 
    //only worth checking when we are approaching the shield distance
    var angle_enemy = atan2(enemy.pos.y-player.pos.y, enemy.pos.x-player.pos.x)

    var delta_with_leftofshield = angularize(angle_enemy-player.angle-player.shieldwidth)
    var delta_with_rightofshield = angularize(angle_enemy-player.angle+player.shieldwidth)
    var delta_with_shield = angularize(angle_enemy-player.angle)

    if (delta_with_leftofshield<0 && delta_with_rightofshield>0){
        console.log('boom')
        enemy.destroyed = true;
    } else if(delta_with_shield>=0 ){
        // check distance with right extremety of shield's arc
        console.log('right')
        var d_rightofshield_enemy = dist(enemy.pos, {x:player.pos.x+player.shieldradius*cos(player.angle+player.shieldwidth), y:player.pos.y+player.shieldradius*sin(player.angle+player.shieldwidth)});
        if (d_rightofshield_enemy<enemy.radius){
            console.log('right boom')
            enemy.destroyed = true;
        }
    } else {
        console.log('left')
        var d_leftofshield_enemy = dist(enemy.pos, {x:player.pos.x+player.shieldradius*cos(player.angle-player.shieldwidth), y:player.pos.y+player.shieldradius*sin(player.angle-player.shieldwidth)});
        if (d_leftofshield_enemy<enemy.radius){
            console.log('left boom')
            enemy.destroyed = true;
        }
    }
}