0

在使用 Tululoo Game Maker API 编写游戏时,我在准确计算碰撞时遇到问题,因为 x 和 y 是根据弧度计算的。

下面对 x 和 y 的计算产生了大量的小数点,我发现很难检查是否存在碰撞。

image_angle 是子弹最初使用的手枪的最终角度,以度为单位。

在设置中:

x = instance_list(pistol)[0].x;
y = instance_list(pistol)[0].y;
startAng = instance_list(pistol)[0].image_angle;

this.travelX = cos(degtorad(instance_list(pistol)[0].image_angle)) * 5;
this.travelY = sin(degtorad(instance_list(pistol)[0].image_angle)) * 5;

在更新框架中:

x+=this.travelX;
y+=this.travelY;

计算球从墙上反弹的角度,我知道很乱:

for(var i = 0; i < instance_number(bounce_barrier);i++){

    if(place_meeting(x,y,instance_list(bounce_barrier)[i])){

            if(Math.round(x*10)/10 > instance_list(bounce_barrier)[i].x ){
                newAng = 180 - startAng;
                this.travelX = +cos(degtorad(newAng)) * 15;
                this.travelY = +sin(degtorad(newAng)) * 15;
                startAng = newAng;
            }

            else if(y > instance_list(bounce_barrier)[i].y - 5 && y <= instance_list(bounce_barrier)[i].y + 10){

                newAng = 180 - startAng;
                this.travelX = -cos(degtorad(newAng)) * 15;
                this.travelY = -sin(degtorad(newAng)) * 15;
                startAng = newAng;
            }

            else if(y <= instance_list(bounce_barrier)[i].y - 5 + bounce_spr.height && y >= instance_list(bounce_barrier)[i].y + bounce_spr.height - 10){

                newAng = 180 - startAng;
                this.travelX = -cos(degtorad(newAng)) * 15;
                this.travelY = -sin(degtorad(newAng)) * 15;
                startAng = newAng;
            }

            else if(x < instance_list(bounce_barrier)[i].x){

                newAng = 180 - startAng;
                this.travelX = +cos(degtorad(newAng)) * 15;
                this.travelY = +sin(degtorad(newAng)) * 15;
                startAng = newAng;
            }
                    else{}

    }

}

place_meeting 是 Tululoo 游戏 API 的一部分:

 function __place_meeting__(nx, ny, what, many) {
        this.other = null;
        var i, l,
            // sprite, scale:
            ts = this.sprite_index,
            tsx, tsy, tfx, tfy, tst,
            // circle:
            tcx, tcy, tcr,
            // bbox:
            tbl, tbr, tbt, tbb,
            // instances, multiple, output, types:
            tz, tm, ct, ch, ra,
            // other:
            o, ox, oy, os, ost, osx, osy, ofx, ofy, ofr;
        if (ts == null) return false;
        tfx = ts.xoffset;
        tfy = ts.yoffset;
        tsx = this.image_xscale;
        tsy = this.image_yscale;
        tst = ts.collision_shape;
        // bbox:
        if (tst == 2) {
            tbl = nx + tsx * (ts.collision_left - tfx);
            tbr = nx + tsx * (ts.collision_right - tfx);
            tbt = ny + tsy * (ts.collision_top - tfy);
            tbb = ny + tsy * (ts.collision_bottom - tfy);
        }
        // circle:
        if (tst == 3) {
            tcr = ts.collision_radius * (tsx > tsy ? tsx : tsy);
            tcx = nx + tsx * (ts.width / 2 - tfx);
            tcy = ny + tsy * (ts.height / 2 - tfy);
        }
        //
        tz = (what.__instance ? [what] : instance_list(what));
        tm = many ? true : false;
        if (tm) ra = [];
        l = tz.length;
        for (i = 0; i < l; i++) {
            o = tz[i];
            if (!o.collision_checking) continue;
            os = o.sprite_index;
            if (os == null) continue;
            ox = o.x; osx = o.image_xscale;
            oy = o.y; osy = o.image_yscale;
            ost = os.collision_shape;
            ct = (tst << 4) | ost;
            ch = false;
            switch(ct) {
            case 0x22:
                if (osx == 1 && osy == 1) {
                    ofx = os.xoffset; ofy = os.yoffset;
                    if (!collide_bbox_bbox(tbl, tbt, tbr, tbb,
                    ox + os.collision_left - ofx, oy + os.collision_top - ofy,
                    ox + os.collision_right - ofx, oy + os.collision_bottom - ofy)) break;
                } else if (!collide_bbox_sbox(tbl, tbt, tbr, tbb, ox, oy, osx, osy, os)) break;
                ch = true;
                break;
            case 0x23:
                ofr = os.collision_radius * (osx > osy ? osx : osy);
                ofx = ox + osx * (os.width / 2 - os.xoffset);
                ofy = oy + osy * (os.height / 2 - os.yoffset);
                if (!collide_bbox_circle(tbl, tbt, tbr, tbb, ofx, ofy, ofr)) break;
                ch = true;
                break;
            case 0x32:
                if (osx == 1 && osy == 1) {
                    ofx = os.xoffset; ofy = os.yoffset;
                    if (!collide_bbox_circle(
                    ox + os.collision_left - ofx, oy + os.collision_top - ofy,
                    ox + os.collision_right - ofx, oy + os.collision_bottom - ofy,
                    tcx, tcy, tcr)) break;
                } else if (!collide_sbox_circle(ox, oy, osx, osy, os, tcx, tcy, tcr)) break;
                ch = true;
                break;
            case 0x33:
                ofr = os.collision_radius * (osx > osy ? osx : osy);
                ofx = ox + osx * (os.width / 2 - os.xoffset);
                ofy = oy + osy * (os.height / 2 - os.yoffset);
                if (!collide_circle_circle(tcx, tcy, tcr, ofx, ofy, ofr)) break;
                ch = true;
                break;
            } if (!ch) continue;
            this.other = o;
            o.other = this;
            if (!tm) return (o);
            ra.push(o);
        } return ra;
    }

我已经设法让子弹以与其发射位置成比例的角度从墙上反弹,但碰撞检测非常糟糕,因为它会反弹一些子弹但不是全部,有些只是直接穿过墙壁。

刚刚添加了线碰撞检测,我制作了子弹线,所以我可以看到

this.oldTravelX,this.oldTravelY 到 this.travelX,this.travelY。它肯定有所改善,但一些子弹仍在通过。

有些子弹似乎粘在弹跳块内或从弹跳块的左侧滑下,然后决定是向左还是向右。

添加了线路碰撞检测

更新代码:

pntdis = point_distance(this.oldTravelX, this.oldTravelY, this.travelX, this.travelY);
noPoints = pntdis/0.01;

for(var i=0; i < pntdis; i+=noPoints)
{
    pointsArrX[i] = this.travelX - (this.oldTravelX * i);
    pointsArrY[i] = this.travelY -  (this.oldTravelY * i);
}

for(var i=0;i<pointsArrX.length;i++){

    if(place_meeting(Math.round(pointsArrX[i]),Math.round(pointsArrY[i]),bounce_barrier) || place_meeting(x,y,bounce_barrier))
    {
            newAng = 180 - startAng;
            this.travelX = +cos(degtorad(newAng)) * 15;
            this.travelY = +sin(degtorad(newAng)) * 15;
            startAng = newAng;          
    }
}

提前致谢。

4

1 回答 1

1

看起来该place_meeting函数应该确定子弹和障碍物之间是否发生碰撞。但是,此函数仅采用子弹的新位置。鉴于子弹每次更新移动 5 个单位,子弹可能会在一帧内从屏障的一侧移动到另一侧。例如:

     |
   . |
    \|  barrier
     \
     |\_______
       \
        .

place_meeting应该同时获取子弹的旧位置和新位置,以便它可以确定是否有任何中间位置与障碍物相撞(例如,通过对子弹的路径和障碍物的边缘进行线-线相交测试;它可能还应该返回碰撞点)。


编辑

您更新的代码看起来像是用于检查子弹路径上的多个点的碰撞。有更快的方法可以做到这一点,但您的方法可能足够快,并且易于理解。但是,您如何计算要检查的点似乎存在错误。

在纸上手动单步执行代码:

pntdis = 5  // assume point_distance returns 5
noPoints = 5 / 0.01 = 500
i = 0
pointsArrX[0] = this.travelX
pointsArrY[0] = this.travelY
i = i + noPoints = 500
500 < 5: false, for loop ends

其他需要考虑的事项:

  • 在检测到碰撞后,您可能需要一个break声明,这样您就不会继续测试更多的点。
  • 您可以将两个for循环合二为一,因为您没有重用数组中的值。只需计算您的测试点,然后立即进行测试。
  • 尝试console.log在程序运行时输出调试信息,以便更好地了解正在发生的事情。
  • var在变量(例如pntdis和)之前使用noPoints以避免意外创建全局变量。
于 2013-11-04T01:16:42.257 回答