How to get smoother 2d projectile physics

2019-07-28 18:06发布

问题:

So I want to start working on a 2D platforming game, and I won't be in physics till next year, so I found the equations in an old physics book I had to help be find the x and y positions along with their velocities. The problem is, they seem to accelerate too fast in that the rendering is too slow. It's not the programs fault, its my fault in that I don't know how to slow down the acceleration so that it looks smoother. Here is the code for the ball class that i'm using (i know the code is an absolute mess! i was just messing around trying to get the equations right.... I would NEVER write my code like this if I was working on a serious project so please don't remind me that it looks bad)

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JComponent;


public class Ball extends JComponent{
    public double xpos;
     double oX;
     double oY;
     double ypos;
    double xvel;
     double yvel;
     long time;
     double oxvel;
     double oyvel;
    long angle = 45;
    long startTime = System.currentTimeMillis();

    public Ball(int p1, int p2, long t){
        xpos = p1;
        oxvel = 50;
        oX = p1;
        ypos = p2;
        time = t;
        oyvel = -50;

    }

    public void update(){
        xvel = (oxvel*Math.cos(45));
        yvel = (oyvel*Math.sin(45)) + (9.8*time);
        if(!(xpos + xvel + 10 > getWidth())&&!(xpos + xvel < 0))
            xpos +=xvel;
        if(!(ypos + yvel + 10 > getHeight())&&!(ypos + yvel <= 0))
            ypos+=yvel;
        time++;
    }

    public void paint(Graphics g){
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(Color.RED);
        g2d.fillOval((int)xpos, (int)ypos, 15, 15);
    }
}

回答1:

You have hard coded time, within your update loop you simply multiply your acceleration by a precomputed timestep (of 1 arbitrary units, it would be much better to use units of seconds), which you assume is the same every time, this always leads to jumpy motion. You should instead pass the variable timestep to the method and use that. This means it doesn't matter how fast the rendering is taking place you will get smooth motion. Remember you may ask for 1/60 of a second frames but you won't always get exactly that.

public void update(double time){
    xvel = (oxvel*Math.cos(45));
    yvel = (oyvel*Math.sin(45)) + (9.8*time);
    if(!(xpos + xvel + 10 > getWidth())&&!(xpos + xvel < 0))
        xpos +=xvel*time;
    if(!(ypos + yvel + 10 > getHeight())&&!(ypos + yvel <= 0))
        ypos+=yvel*time;
}

Within the overall loop you would measure the actual timestep and pass that. You can do that measuring using System.currentTimeMillis();

long previousStep=System.currentTimeMillis();
public void gameLoop(){
    long newStep=System.currentTimeMillis();
    double frameTime=(newStep-previousStep)/1000.0;

    ball.update(frameTime);
    previousStep= newStep;
}

time should very likely not be a long but should be a double, real time measured in integer increments is very difficult to get right.



回答2:

The Euler method is rather bad for faithful physics simulation, especially if you take the time step as large as the time between two frames. Either perform a number of 10-20 simulation step per frame or use an higher order integration method.

Even with the Euler method, you need to apply the time step at all parts of the differential system. So it is

velocity += acceleration*timestep
position += velocity*timestep

And if you intend it to be some kind of symplectic integrator, assuming that the acceleration results from a gradient force field and only depends on position, exchange the parts, update the position first,

position += velocity*timestep
velocity += acceleration*timestep