基于物理的2D运动:随时间改变物体方向

4

我目前正在使用Java创建一款2D太空游戏,你可以控制飞船在太空中移动。该游戏不使用任何外部库。

飞船应该朝着光标移动。然而,当移动光标时,旧的力量并没有神奇地消失;随着时间的推移,飞船会改变方向,最终朝着期望的方向移动。

然而,我遇到了有关飞船移动的问题。

基本上,我想要实现的粗略示意图如下:

enter image description here

该图显示了飞船在一个游戏刻度内应该如何移动。

  • 圆圈表示飞船的最大速度。

  • 目标角度是当前光标所在的位置。

  • 当前角度是飞船当前行进的方向。

  • 当前角度应该越来越接近目标角度,直到这两个角度相同为止。

  • 飞船应该向最短路线转向目标角度;它可以向左和向右转弯,而不仅仅是向左或向右。

现在我已经解释了我想要实现的内容,接下来我将描述我到目前为止已经实现了什么以及它是如何工作的。

基本上,“飞船”是一张图片,位于屏幕中心。当你“移动”飞船时,飞船保持不动;移动的是游戏区域的其余部分。

相对于表示游戏区域的坐标系,飞船的当前“位置”是整数xPos和yPos。

以下是一些示例代码,展示了系统的工作方式:

 int xPos;
 int yPos;

 public void updateMovement() {
     xPos += xSpeed;
     yPos += ySpeed; 
 }

 public void moveForward() {
     double yTempSpeed = ySpeed;
     double xTempSpeed = xSpeed;
     yTempSpeed += 0.01 * Math.sin(Math.toRadians(targetAngle));
     xTempSpeed += 0.01 * Math.cos(Math.toRadians(targetAngle));
     double resultVector = Math.sqrt(xTempSpeed * xTempSpeed + yTempSpeed * yTempSpeed);
     if (resultVector < 2) {
         ySpeed += 0.01 * Math.sin(Math.toRadians(targetAngle));
         xSpeed += 0.01 * Math.cos(Math.toRadians(targetAngle));
 }

这段代码成功地将船的最大速度设置为所需值,然而,在结果“矢量”大于2时(即速度已经达到最大值且目标角度与船当前行驶角度非常接近(+- Pi / 2)),该代码无法正常工作(船只的航向不会改变)。

基于此实现,我应该如何改变当前角度呢?


所以,如果您删除 resultVector < 2 条件,除了最大速度之外,一切都按预期工作,对吗? - libik
只要速度不超过2,现在一切都按预期工作了,对吧?船改变方向了吗? - misko321
尝试结果向量为Math.sqrt(xTempSpeed*xTempSpeed+yTempSpeed+yTempSpeed) 你在对角线方向上的最大速度比水平或垂直高sqrt(2)! 另一个想法是将您的速度向量规范化为所需的长度。这样做的方法有几种,取决于您所需的“物理学”。例如,如果您的宇宙飞船一旦具有速度和旋转,在真实空间中它不会停止旋转。 - meister_reineke
我删除了我的旧评论(由于某些原因,我无法编辑它)。然而,经过进一步的检查,如果限速被解除,一切都按预期工作。 @meister_reineke 啊,是的。我发布了代码的旧版本。当前实现按照您发布的方式计算resultVector。我会更新主帖。 - Xarkox
我在iOS上使用Swift和SpriteKit做过非常类似的事情。即使SpriteKit为您处理物理问题,它仍然很难,因为实际上飞船的轨迹遵循二阶或更高阶的二维微分方程的解决方案。 - Alnitak
3个回答

1
  public void moveForward() {
     ySpeed += 0.01 * Math.sin(Math.toRadians(targetAngle));
     xSpeed += 0.01 * Math.cos(Math.toRadians(targetAngle));
     double currentSpeed = Math.sqrt(xTempSpeed * xTempSpeed + yTempSpeed * yTempSpeed);
     if (currentSpeed > maxSpeed) {
         //the resulting speed is allways <= maxspeed (normed to that)
         ySpeed *= maxSpeed/currentSpeed;
         xSpeed *= maxSpeed/currentSpeed;
 }

希望这是你需要的...虽然宇宙飞船有最大速度似乎不太现实,但从"可玩性"的角度考虑,我会做同样的事情。


如果(currentSpeed > maxSpeed),那么maxSpeed/currentSpeed < 1,所以ySpeed /= maxSpeed/currentSpeed实际上会使ySpeed(以及xSpeed)变得更大。应该改为:ySpeed /= currentSpeed/maxSpeed; - misko321
啊,这正是我需要的!谢谢!另外一点,你的回答仍然没有反映出misko321指出的更改,而那才是对我有用的。 - Xarkox

0

对于规范船只速度,以免实际超过您的限速(=2),您有何想法:

//it's good to put all constants out of a function in one place
//to make it easier if you ever wanted to change it
private final int MAX_SPEED = 2;
private final double ACCEL_FACTOR = 0.01;

public void moveForward() {
 ySpeed += ACCEL_FACTOR * Math.sin(Math.toRadians(targetAngle));
 xSpeed += ACCEL_FACTOR * Math.cos(Math.toRadians(targetAngle));

 //normalize ship speed, i.e. preserve ratio of xSpeed/ySpeed
 //but make sure that xSpeed^2 + ySpeed^2 <= MAX_SPEED^2

 //your code goes here
 //...

}

了解一下向量归一化。这样,船速的变化将被正常应用(此时速度可以是>= MAX_SPEED),但在归一化后,它永远不会超过MAX_SPEED,因此您的if指令甚至都不需要。


0
您可能会发现以下(Swift)代码有用,尽管您需要自己处理船舶线性和角速度的每帧积分:
func moveShipTowards(location: CGPoint) {
    if let ship = shipNode? {

        let distanceVector = CGVector(origin: ship.position, point: location)
        let targetAngle = distanceVector.angle
        let shipAngle = ship.zRotation
        var dø = targetAngle - shipAngle

        // convert to shortest arc
        if dø > π {
            dø -= 2.0 * π
        } else if dø < -π {
            dø += 2.0 * π
        }

        // resulting angular velocity
        ship.physicsBody.angularVelocity = 12 * dø.clampedTo(π)

        var velocityUnitVector = CGVector(cosf(ship.zRotation), sinf(ship.zRotation))
        var magnitude = distanceVector.length.clampedTo(400)

        ship.physicsBody.velocity = velocityUnitVector * magnitude
    }
}

它处理了当船靠近目标点时减速的情况。但是刚才看了一下,似乎没有正确处理加速度。


如果假设是正确的,比如船只应该自动减速,那么可能是一个好的可行解决方案。 - meister_reineke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接