Java 二维目标截获算法

Java 二维目标截获算法,java,2d,game-physics,Java,2d,Game Physics,我一直在努力改进我正在开发的自上而下透视射击游戏中一个老板的行为,但有一件事我没有完全正确地实现,那就是根据玩家的移动在老板的“钩子”投射物和玩家之间绘制拦截轨迹 我试着用这里描述的二次方程来实现它: 但我得到的结果与我提出的这个算法几乎相同,它通常会瞄准球员的预期位置,但几乎总是失误,除非球员从老板身边后退 private float findPlayerIntercept(Pair<Float> playerPos, Pair<Float> playerVel, in

我一直在努力改进我正在开发的自上而下透视射击游戏中一个老板的行为,但有一件事我没有完全正确地实现,那就是根据玩家的移动在老板的“钩子”投射物和玩家之间绘制拦截轨迹

我试着用这里描述的二次方程来实现它:

但我得到的结果与我提出的这个算法几乎相同,它通常会瞄准球员的预期位置,但几乎总是失误,除非球员从老板身边后退

private float findPlayerIntercept(Pair<Float> playerPos, Pair<Float> playerVel, int delta) {
    float hookSpeed = HOOK_THROW_SPEED * delta;
    Pair<Float> hPos = new Pair<Float>(position);
    Pair<Float> pPos = new Pair<Float>(playerPos);

    // While the hook hasn't intercepted the player yet.
    while(Calculate.Distance(position, hPos) < Calculate.Distance(position, pPos)) {
        float toPlayer = Calculate.Hypotenuse(position, pPos);

        // Move the player according to player velocity.
        pPos.x += playerVel.x;
        pPos.y += playerVel.y;

        // Aim the hook at the new player position and move it in that direction.
        hPos.x += ((float)Math.cos(toPlayer) * hookSpeed);
        hPos.y += ((float)Math.sin(toPlayer) * hookSpeed);
    }

    // Calculate the theta value between Stitches and the hook's calculated intercept point.
    return Calculate.Hypotenuse(position, hPos);
}
private float findPlayerIntercept(Pair playerPos,Pair playerVel,int delta){
浮动钩速=钩抛速度*增量;
配对hPos=新配对(位置);
成对PPO=新的成对(playerPos);
//而钩子还没有截住玩家。
while(计算距离(位置,hPos)<计算距离(位置,pPos)){
浮顶层=计算斜边(位置,pPos);
//根据玩家的速度移动玩家。
pPos.x+=playerVel.x;
pPos.y+=playerVel.y;
//将钩子对准新玩家位置,并朝该方向移动。
hPos.x+=((浮点)数学cos(顶层)*挂钩速度);
hPos.y+=((浮点)数学sin(顶层)*挂钩速度);
}
//计算针数和钩子计算的截距点之间的θ值。
返回计算斜边(位置,hPos);
}
这种方法应该返回θ(角度),以便老板根据球员在抛出钩子时的运动矢量来投掷钩子,从而拦截球员

作为参考,计算斜边方法仅使用atan2计算两点之间的角度。计算。距离获取两个位置之间的距离(以像素为单位)


有人对如何改进这个算法有什么建议吗?还是一种更好的方法?

你的问题令人困惑(因为你还谈到了一个二次方程)。如果你的游戏是一个2d平台游戏,在这个游戏中,老板以给定的速度抛出一个与地板成一定角度的钩子,那么我给出了你的解决方案:

通过运用运动学方程,你会发现


θ;
=
阿辛
⁡;
(
D
G
)
v
2.
∗;
1.
2.

钩子一直丢失的原因是,在集成播放器和钩子的运动时,始终使用1个单位的固定时间步长。这意味着两个对象的轨迹都是一系列直线“跳跃”。1个单位的时间步长太大,无法得到准确的结果-如果速度足够高,则无法保证(计算距离(位置,hPos)<计算距离(位置,pPos))时的环路条件
甚至会被击中

你提到的二次方程方法是正确的,但是由于你还没有理解其中的联系,我将在这里尝试推导一个类似的方法

假设球员和钩子的初始位置和速度分别为
p0,u
q0,v
(2D向量)
v
的方向是未知的所需数量。下面是设置的示意图:

应用余弦规则:

应该使用哪个根,它是否始终存在

  • 如果平方根内的项为负,则
    t
    没有实根-没有解(钩子永远不会到达玩家)
  • 如果两个根(或单个根)都是负的,那么也没有有效的解决方案-钩子需要“向后”触发
  • 如果只有一个根是正数,则使用它
  • 如果两个根都为正,则使用较小的根
  • 如果速度相等,即
    v=u
    ,则解决方案为:

    如果是否定的,再次拒绝

一旦知道
t
的值,就可以计算碰撞点和速度方向:


更新:示例Java代码:

private float findPlayerIntercept(Pair<Float> playerPos, Pair<Float> playerVel, int delta) 
{
    // calculate the speeds
    float v = HOOK_THROW_SPEED * delta;
    float u = Math.sqrt(playerVel.x * playerVel.x + 
                        playerVel.y * playerVel.y);

    // calculate square distance
    float c = (position.x - playerPos.x) * (position.x - playerPos.x) +
              (position.y - playerPos.y) * (position.y - playerPos.y);

    // calculate first two quadratic coefficients
    float a = v * v - u * u;
    float b = playerVel.x * (position.x - playerPos.x) + 
              playerVel.y * (position.y - playerPos.y);

    // collision time
    float t = -1.0f; // invalid value

    // if speeds are equal
    if (Math.abs(a)) < EPSILON) // some small number, e.g. 1e-5f
        t = c / (2.0f * b);
    else {
        // discriminant
        b /= a;
        float d = b * b + c / a;

        // real roots exist
        if (d > 0.0f) {
            // if single root
            if (Math.abs(d) < EPSILON)
                t = b / a;
            else {
                // how many positive roots?
                float e = Math.sqrt(d);
                if (Math.abs(b) < e)
                    t = b + e;
                else if (b > 0.0f)
                    t = b - e;
            }
        }
    }

    // check if a valid root has been found
    if (t < 0.0f) {
        // nope.
        // throw an exception here?
        // or otherwise change return value format
    }

    // compute components and return direction angle
    float x = playerVel.x + (playerPos.x - position.x) / t;
    float y = playerVel.y + (playerPos.y - position.y) / t;
    return Math.atan2(y, x);
}
private float findPlayerIntercept(Pair playerPos,Pair playerVel,int delta)
{
//计算速度
浮球v=钩抛速度*增量;
浮点u=Math.sqrt(playerVel.x*playerVel.x+
playerVel.y*playerVel.y);
//计算平方距离
浮点c=(position.x-playerPos.x)*(position.x-playerPos.x)+
(position.y-playerPos.y)*(position.y-playerPos.y);
//计算前两个二次系数
浮点数a=v*v-u*u;
浮动b=playerVel.x*(position.x-playerPos.x)+
playerVel.y*(position.y-playerPos.y);
//碰撞时间
浮点t=-1.0f;//无效值
//如果速度相等
if(Math.abs(a))0.0f){
//如果是单根
if(数学绝对值(d)0.0f)
t=b-e;
}
}
}
//检查是否已找到有效的根目录
如果(t<0.0f){
//没有。
//在这里抛出异常?
//或者更改返回值格式
}
//计算组件和返回方向角
float x=playerVel.x+(playerPos.x-position.x)/t;
浮动y=playerVel.y+(playerPos.y-po