我一直在尝试改善自上而下的透视射击游戏中老板之一的行为,而我未能完全正确实现的一件事是绘制一条拦截轨迹在老板的“钩子”弹丸和玩家之间根据玩家的移动情况。
我尝试使用此处描述的二次方程式来实现它:https://stackoverflow.com/a/2249237/1205340
但是我得到的结果几乎与我想出的算法相同,该算法通常将目标对准玩家的预期位置,但是除非玩家退缩离开老板,否则几乎总是会错过。
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);
}
此方法应该返回上司掷出钩子的theta(角度),以便根据钩子投掷时玩家的运动矢量来拦截玩家。
作为参考,Calculate.Hypotenuse方法仅使用atan2来计算两点之间的角度。 Calculate.Distance获取两个位置之间的距离(以像素为单位)。
有人对如何改进此算法有任何建议吗?还是更好的方法呢?
答案 0 :(得分:0)
您的问题令人困惑(因为您也谈论二次方程式)。如果您的游戏是2D平台游戏,其中老板以给定的速度与地板成一定角度扔了一个钩子,那么我可以提出您的解决方案:
通过运动学方程,您会发现
unique_id id first_name
----------- ----------- ------------
100 12114 John
100 123123 John
90 2591 Mary
10 2 Davy
10 12 Davy
10 43 Davy
其中d是玩家与老板之间的距离,g是重力常数,v是弯钩的初始速度。
答案 1 :(得分:0)
钩子一直丢失的原因是,在集成播放器和钩子的动作时,始终使用固定的1个单位的时间步长。这意味着两个对象的轨迹都是一系列直线“跳跃”。 1个单元的步长太大,无法获得准确的结果-如果速度足够高,则无法保证甚至会遇到循环条件while(Calculate.Distance(position, hPos) < Calculate.Distance(position, 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);
}