【问题标题】:Simple Physics Simulation in java not working.java中的简单物理模拟不起作用。
【发布时间】:2011-04-20 13:47:42
【问题描述】:

我想实现球物理,因为我是新手,所以我修改了教程中的代码 http://adam21.web.officelive.com/Documents/JavaPhysicsTutorial.pdf.

我尽可能地遵循这一点, 但是我无法在代码中应用所有物理现象,有人可以告诉我,我在哪里弄错了,或者我还在做一些愚蠢的编程错误。

当我没有调用bounce方法并且我无法使用bounce方法时,球正在移动,并且球正在向左侧移动而不是落在地板上**

有人可以推荐我一些更好的方法或类似的简单紧凑的方法来完成这项将物理应用于两个或更多具有交互性的球的任务。

这里是代码;

    import java.awt.*;
    public class AdobeBall {

    protected int radius = 20;
    protected Color color;

    // ... Constants
    final static int DIAMETER = 40;

    // ... Instance variables
    private int m_x; // x and y coordinates upper left
    private int m_y;

    private double dx = 3.0; // delta x and y 
    private double dy = 6.0;

    private double m_velocityX; // Pixels to move each time move() is called.
    private double m_velocityY;

    private int m_rightBound; // Maximum permissible x, y values.
    private int m_bottomBound;

    public AdobeBall(int x, int y, double velocityX, double velocityY,
            Color color1) {
        super();
        m_x = x;
        m_y = y;
        m_velocityX = velocityX;
        m_velocityY = velocityY;
        color = color1;
    }

    public double getSpeed() {
        return Math.sqrt((m_x + m_velocityX - m_x) * (m_x + m_velocityX - m_x)
                + (m_y + m_velocityY - m_y) * (m_y + m_velocityY - m_y));
    }

    public void setSpeed(double speed) {
        double currentSpeed = Math.sqrt(dx * dx + dy * dy);
        dx = dx * speed / currentSpeed;
        dy = dy * speed / currentSpeed;
    }

    public void setDirection(double direction) {
        m_velocityX = (int) (Math.cos(direction) * getSpeed());
        m_velocityY = (int) (Math.sin(direction) * getSpeed());
    }

    public double getDirection() {
        double h = ((m_x + dx - m_x) * (m_x + dx - m_x))
                + ((m_y + dy - m_y) * (m_y + dy - m_y));
        double a = (m_x + dx - m_x) / h;
        return a;
    }

    // ======================================================== setBounds
    public void setBounds(int width, int height)  {
        m_rightBound = width - DIAMETER;
        m_bottomBound = height - DIAMETER;
    }

    // ============================================================== move
    public void move()  {

        double gravAmount = 0.02;
        double gravDir = 90; // The direction for the gravity to be in.
        // ... Move the ball at the give velocity.
        m_x += m_velocityX;
        m_y += m_velocityY;

//       ... Bounce the ball off the walls if necessary.
        if (m_x < 0) { // If at or beyond left side
            m_x = 0; // Place against edge and
            m_velocityX = -m_velocityX;

        } else if (m_x > m_rightBound) { // If at or beyond right side
            m_x = m_rightBound; // Place against right edge.
            m_velocityX = -m_velocityX;
        }

        if (m_y < 0) { // if we're at top
            m_y = 0;
            m_velocityY = -m_velocityY;

        } else if (m_y > m_bottomBound) { // if we're at bottom
            m_y = m_bottomBound;
            m_velocityY = -m_velocityY;
        }

     //   double speed = Math.sqrt((m_velocityX * m_velocityX)
     //         + (m_velocityY * m_velocityY));

        // ...Friction stuff 
        double fricMax = 0.02; // You can use any number, preferably less than 1
        double friction = getSpeed();
        if (friction > fricMax)
            friction = fricMax;
        if (m_velocityX >= 0) {
            m_velocityX -= friction;
        }
        if (m_velocityX <= 0) {
            m_velocityX += friction;
        }
        if (m_velocityY >= 0) {
            m_velocityY -= friction;
        }
        if (m_velocityY <= 0) {
            m_velocityY += friction;
        }

        // ...Gravity stuff

        m_velocityX += Math.cos(gravDir) * gravAmount;
        m_velocityY += Math.sin(gravDir) * gravAmount;

    }

    public Color getColor() {
        return color;
    }

    public void setColor(Color newColor)  {
        color = newColor;
    }

    // ============================================= getDiameter, getX, getY
    public int getDiameter() {
        return DIAMETER;
    }

  public double getRadius() {
        return radius; // radius should be a local variable in Ball.
    }

    public int getX() {
        return m_x;
    }

    public int getY() {
        return m_y;
    }

}

使用 adobeBall:

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;

    public class AdobeBallImplementation implements Runnable {

    private static final long serialVersionUID = 1L;

    private volatile boolean Play;
    private long mFrameDelay;
    private JFrame frame;
    private MyKeyListener pit;

    /** true means mouse was pressed in ball and still in panel. */
    private boolean _canDrag = false;

    private static final int MAX_BALLS = 50; // max number allowed
    private int currentNumBalls = 2; // number currently active
    private AdobeBall[] ball = new AdobeBall[MAX_BALLS];

    public AdobeBallImplementation(Color ballColor) {

        frame = new JFrame("simple gaming loop in java");
        frame.setSize(400, 400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pit = new MyKeyListener();
        pit.setPreferredSize(new Dimension(400, 400));
        frame.setContentPane(pit);

        ball[0] = new AdobeBall(34, 150, 7, 2, Color.YELLOW);
        ball[1] = new AdobeBall(50, 50, 5, 3, Color.BLUE);
        frame.pack();
        frame.setVisible(true);
        frame.setBackground(Color.white);
        start();
        frame.addMouseListener(pit);
        frame.addMouseMotionListener(pit);

    }

    public void start() {
        Play = true;
        Thread t = new Thread(this);
        t.start();
    }

    public void stop() {
        Play = false;
    }

    public void run() {

        while (Play == true) {
            // bounce(ball[0],ball[1]);
            runball();
            pit.repaint();
            try {
                Thread.sleep(mFrameDelay);
            } catch (InterruptedException ie) {
                stop();
            }
        }
    }

    public void drawworld(Graphics g) {

        for (int i = 0; i < currentNumBalls; i++) {
            g.setColor(ball[i].getColor());
            g.fillOval(ball[i].getX(), ball[i].getY(), 40, 40);
        }
    }

    public double pointDistance (double x1, double y1, double x2, double y2) {
        return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
    }

    public void runball() {

        while (Play == true) {
            try {

                for (int i = 0; i < currentNumBalls; i++) {
                    for (int j = 0; j < currentNumBalls; j++) {

                        if (pointDistance(ball[i].getX(), ball[i].getY(),
                                ball[j].getX(), ball[j].getY()) < ball[i]
                                                                       .getRadius()
                                                                       + ball[j].getRadius() + 2) {
                            // bounce(ball[i],ball[j]);
                            ball[i].setBounds(pit.getWidth(), pit.getHeight());
                            ball[i].move();

                            pit.repaint();

                        }
                    }
                }

                try {
                    Thread.sleep(50);
                } catch (Exception e) {
                    System.exit(0);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static double pointDirection(int x1, int y1, int x2, int y2) {
        double H = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); // The
        // hypotenuse
        double x = x2 - x1; // The opposite
        double y = y2 - y1; // The adjacent
        double angle = Math.acos(x / H);
        angle = angle * 57.2960285258;
        if (y < 0) {
            angle = 360 - angle;
        }
        return angle;
    }

    public static void bounce(AdobeBall b1, AdobeBall b2) {
        if (b2.getSpeed() == 0 && b1.getSpeed() == 0) {
            // Both balls are stopped.
            b1.setDirection(pointDirection(b1.getX(), b1.getY(), b2.getX(), b2
                    .getY()));
            b2.setDirection(pointDirection(b2.getX(), b2.getY(), b1.getX(), b1
                    .getY()));
            b1.setSpeed(1);
            b2.setSpeed(1);
        } else if (b2.getSpeed() == 0 && b1.getSpeed() != 0) {
            // B1 is moving. B2 is stationary.
            double angle = pointDirection(b1.getX(), b1.getY(), b2.getX(), b2
                    .getY());
            b2.setSpeed(b1.getSpeed());
            b2.setDirection(angle);
            b1.setDirection(angle - 90);
        } else if (b1.getSpeed() == 0 && b2.getSpeed() != 0) {
            // B1 is moving. B2 is stationary.
            double angle = pointDirection(b2.getX(), b2.getY(), b1.getX(), b1
                    .getY());
            b1.setSpeed(b2.getSpeed());
            b1.setDirection(angle);
            b2.setDirection(angle - 90);
        } else {
            // Both balls are moving.
            AdobeBall tmp = b1;
            double angle = pointDirection(b2.getX(), b2.getY(), b1.getX(), b1
                    .getY());
            double origangle = b1.getDirection();
            b1.setDirection(angle + origangle);
            angle = pointDirection(tmp.getX(), tmp.getY(), b2.getX(), b2.getY());
            origangle = b2.getDirection();
            b2.setDirection(angle + origangle);
        }
    }

    public static void main(String[] args) {

        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new AdobeBallImplementation(Color.red);
            }
        });
    }

}   

*编辑:*可以使用这个论坛的重力新方法拆分代码:这个代码也不起作用,球不会落地:

public void mymove() {

    m_x += m_velocityX;
    m_y += m_velocityY;

    if (m_y + m_bottomBound > 400) {
        m_velocityY *= -0.981;
    //  setY(400 - m_bottomBound);
        m_y = 400 - m_bottomBound;

    }

    // ... Bounce the ball off the walls if necessary.
    if (m_x < 0) { // If at or beyond left side
        m_x = 0; // Place against edge and
        m_velocityX = -m_velocityX;

    } else if (m_x > m_rightBound) { // If at or beyond right side
        m_x = m_rightBound - 20; // Place against right edge.
        m_velocityX = -m_velocityX;
    }

    if (m_y < 0) { // if we're at top
        m_y = 1;
        m_velocityY = -m_velocityY;

    } else if (m_y > m_bottomBound) { // if we're at bottom
        m_y = m_bottomBound - 20;
        m_velocityY = -m_velocityY;

    }
}

非常感谢您的纠正和帮助。

吉比

【问题讨论】:

  • 您发布的代码相当多。您能否将您的演示代码简化为演示问题的最简单示例?
  • 我不确定问题出在哪里,所以我展示了整个代码以获得更好的图片。
  • @jibylala:关键是您必须了解代码的哪些部分负责更新位置和速度,对吧?如果你的动态是错误的,那么你在代码的那些部分做错了。查找哪里错误所在的练习可以帮助您找到错误。
  • 在您的反弹方法中,是否满足任何条件,或者它是否总是落入“两个球都在移动”部分?我问的原因是因为将integer (0) 与double 进行比较通常不是一个好主意,因为double 更精确,因此不太可能完全等于0。
  • @KeyBoardP:是的,你的假设是它总是在我调试时出现在那个部分。

标签: java implementation simulation physics


【解决方案1】:

目前我无法运行 java,因此无法测试您的代码,但我注意到了几件事:

  1. 对于(几乎)相同的事物,您有两组独立的变量:{dx, dy} 和 {m_velocityX, m_velocityY}。因此,您的 getSpeed 和 setSpeed 是独立的,getDirection 和 Setdirection 也是如此。我不敢相信这会按您期望的方式工作。
  2. 您的坐标似乎有点混乱(这可能是球“下落”到左侧的原因)。您似乎是从 X 方向测量角度顺时针,这不是标准的。 编辑:Plínio Pantaleão 确定了向左摔倒的原因。
  3. 您的代码似乎涉及简单的线性运动、弹跳、重力和摩擦。您应该一次实施和测试这些。我建议先进行运动,然后添加弹跳,然后分别添加摩擦和重力(以任意顺序),最后将它们结合起来。

编辑:
这个重力代码是错误的:

if (m_y + m_bottomBound > 400) {
    m_velocityY *= -0.981;
    //  setY(400 - m_bottomBound);
    m_y = 400 - m_bottomBound;
}

首先,您使用 400 作为“幻数”;我说不清它代表什么。然后您将效果限制在屏幕顶部附近的区域,原因尚不清楚。在函数结束时,您以毫无意义的方式更改 m_y。而且(也许)最糟糕的是,你有重力反转和相乘垂直速度,这不是重力的工作原理。

试试这个:

// You will have to adjust this. Start with a small value and increase it until
// you see an effect.
final static double GRAVITY = 0.1;

// Note that +y is down, so accelerating downward means increasing vY
m_velocityY += GRAVITY;

【讨论】:

  • 非常感谢您的反馈,但没有一次尝试达到预期效果,每次尝试都会产生负面影响,我正在尝试做任何人们在论坛上建议和讨论的事情。
  • @jibylala:Sei nicht niedergeschlagen!这些事情需要时间。现在什么有效?运动?弹跳?让我们开始运动,然后弹跳,然后是摩擦或重力。当某个步骤出错时,请告诉我们您看到了什么。
  • @Beta :)vielen danke,我在其他示例中检查了重力代码,它的工作原理不是上面的,而是教程中给出的,现在主要关注的是球对球的交叉/碰撞。
  • @jibylala:很好,向我们展示你的球-球碰撞代码。物理学比球壁碰撞要复杂一些。
【解决方案2】:

好的,还没有解决问题。但有几件事可能会有所帮助:

1 - Math.sin/cos 以弧度表示角度。 (这可能会有所帮助)。 - 土坯球

2 - 由于 m_velocityX/Y 可能改变方向 - Adob​​eBall

3 - 你在 runball() 中的主循环两次取同一个球并与自身进行比较...在第二个循环中使用 j=i+1

4 - 您在线程循环中调用 runball()。所以我希望球无论如何都会移动......不仅仅是如果你调用bounce()。我说的对吗?

您可以更正您的代码并重试。并发布消息! :)

【讨论】:

  • 据我对第 1 点的理解:double gravDir = 90/57.2960285258;对于第 3 点,更改不允许球移动: for (int j=i+1; j
  • 您可以只使用 double gravDir=Math.PI/2。它更具可读性。关于第 3 点 - 我认为这段代码应该测试一个球和另一个球(找到一个球与自身之间的距离有什么意义?)。如果你让代码这样,在某些情况下 i==j 你会运行这个测试。但我不明白你的测试的想法,所以如果它对你有意义,请保持这种方式:)
  • 谢谢,我实际上并不关心代码,我的意思是我只想让两个球移动并演示所涉及的物理现象(运动、弹跳、重力和摩擦、碰撞),这就是我适应它的原因代码,因为它应该这样做,但如果我可以有其他代码这样做,我没有问题。
猜你喜欢
  • 2011-08-08
  • 1970-01-01
  • 1970-01-01
  • 2014-04-01
  • 2015-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-12
相关资源
最近更新 更多