1

So I am trying to make a Physics Simulation, which aims to simulate a missile going off a long distance away, (for my example I have used 1.6km) and I have a AA weapon at this 1.6km distance; when the missile is launched, due to maths I have worked out when I need to release the AA weapon to hit the missile. So cool the maths all checks out, and I am 99% certain mathematically this should work; but the error seems to occur in the animation stage, and I feel this is something to do with how I am handling gravity.

The issue is instead of the flight path of the missile following a lovely parabola, it is instead taking a path consisting of many different linear functions (hence the Eulerian integration speculation(Source: http://natureofcode.com/book/chapter-5-physics-libraries/)); and I believe the reason for this is to do with my scale. I am using a scale of 1 pixel = 1 meter. The image updates every 60 seconds, so I am saying every cycle, take the vertical velocity - (9.81 / 60) and then update the position by (Vertical velocity / 60). The problem is, it only wants to round to the nearest pixel, so in the first 2 seconds or so, it only wants to have the vertical position change at 4 pixels per cycle, and then it goes to 5, then 6... This causes the flight path (when the missile was launched at 200m/s at 50 degrees and the AA launched at 70degrees with the same speed) to look like: Simulation Test

If anyone knows how I can fix this issue to turn the inaccurate linear representation into a nice parabolic one that was still accurate to the time, (the time after launch is about 3 seconds for this). If anyone has any suggestions and/or solutions, or if you are able to explain why this is happening and don't mind spending a little time explaining it to me to get a better understanding of the problem, that would be greatly appreciated! If you require any more information to help you help me, then just leave a comment and I will provide you with any info. The relevant pieces of code are:

public void gravity(){
    for (int i = 0; i < rockets.size(); i++){

        if(timeToStart <= milliSecondTimer){
            rockets.get(1).fired = true;
            //Trail of other rocket
            if (milliSecondTimer > 0.1 * count){
                rockets.add(new Ball(rockets.get(1).x - 20 ,rockets.get(1).y - HEIGHT + 100,5,0,0,false));
            }

        }

        if(rockets.get(i).fired){
            rockets.get(i).vSpeed -= 9.81 / 60;
            rockets.get(i).move(0, (int) (rockets.get(i).vSpeed / 60));
            rockets.get(i).move(1, (int) (rockets.get(i).hSpeed / 60));
        } else if (timeToStart==1110){
            //function to work out the time displacment
            derr(1);
        }

        //For the trail
        if (milliSecondTimer > 0.1 * count){
            rockets.add(new Ball(rockets.get(0).x - 20 ,rockets.get(0).y - HEIGHT + 100,5,0,0,false));
            count++;
        }

    }
}   
public static void main(String[] args) throws InterruptedException{
    JFrame frame = new JFrame("App Name");
    Rockets app = new Rockets();
    frame.setSize((int)(WIDTH * SCALER),(int)(HEIGHT * SCALER));
    frame.add(app);
    frame.setVisible(true);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setResizable(false);
    frame.requestFocus();
    long lastLoopTime = System.nanoTime();
    int fps = 0, lastFpsTime = 0, count = 1;
    final int TARGET_FPS = 60;
    final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
    //Game Loop
    while(true){
        long now = System.nanoTime();
        long updateLength = now - lastLoopTime;
        lastLoopTime = now;
        delta = updateLength / ((double)OPTIMAL_TIME);
        lastFpsTime += updateLength;
        fps++;
        if (lastFpsTime > 100000000 * count){
           milliSecondTimer += 0.1;
           count++;
        }
        if (lastFpsTime >= 1000000000){
            System.out.println("(FPS: "+fps+")");
            lastFpsTime = 0;
            fps = 0;
            count = 1;
        }
        loopsGone++;
        app.repaint();
        Thread.sleep( (lastLoopTime-System.nanoTime() + OPTIMAL_TIME)/1000000 );
    }
}

Thank you,

Sam

  • 1
    Most likely yes. One possible workaround is to use smaller time intervals. Alternatively, compute the position etc using the analytical formulae. However if you need to simulate real-world physics of motion under gravity with high fidelity, you need to understand the computational mathematics. – Stephen C Mar 12 '17 at 01:30
  • I have tried experimenting with smaller time intervals, but they seemed to only make the problem worse. But I feel you are right it is my lack of the understanding of the computational mathematics that is weighing me down. I was just wondering therefore, do you know of any good books or webpages on the matter to help further my knowledge? – Sam Brotherton Mar 12 '17 at 01:36

1 Answers1

1

Perhaps you can change your x and y rocket coordinates to a float (I'm assuming they are int right now). And then when you are drawing, that is when you add the (int) cast. For instance,

rockets.get(i).move(0, (int) (rockets.get(i).vSpeed / 60));

Should not have an int cast.

Also, you're going to want to change

(rockets.get(i).vSpeed / 60));

to

(rockets.get(i).vSpeed / 60.0)); 

You want your position to retain precision, which it's not doing currently with the int cast. This is what using a float would achieve.

It doesn't look like scale is the issue. It would be an issue if you are trying to draw a parabola on an image that is, say, 10 x 10 pixels.

Could you post your rocket class? I'd like to run this, looks interesting.

Thanks for accepting my answer. Here is another issue I saw.

For much better precision in position, you will want to update your position function much more frequent, meaning more than once every time you repaint. This is how my game loop looks.

public void run() {
        long lastTime=System.nanoTime();
        final double amountOfTicks=60.0;
        double ns=1000000000/amountOfTicks;
        double delta=0; 

        int updates=0;
        int frames=0;
        long timer=System.currentTimeMillis();

        while (running) {
            long now=System.nanoTime();
            delta +=(now-lastTime) / ns;
            lastTime=now;
            if (delta>1) {      
                update();     <--- this is where your gravity() method belongs
                delta--;
                updates++;
            }
            render();     <---- this will be your repaint method
            frames++;
            if (System.currentTimeMillis()-timer>1000) {
                timer+=1000;
                currUpdates=updates;
                currFrames=frames;
                updates=0;
                frames=0;
            }
        }
    }

What it does is updates spaceship positions once every millisecond, but only renders in 60 fps.

Nick Ziebert
  • 1,258
  • 1
  • 9
  • 17
  • Sorry it took me a while to figure out how to use git, the Java class should be here: https://github.com/Velinguard/Rockets/blob/master/src/Rockets/Rockets.java . Thanks to your edits, it is now working for the given angle and speeds! Thank you! However it does not seem to work as well for other angles, so I will dig further into this, and will update the git register with relevant changes. Also, sorry my code is quite messy, I tend to prefer cleaning it up after I have proved a task is possible. Thanks again, Sam. – Sam Brotherton Mar 12 '17 at 02:54
  • Don't use `float`, use `double` for more precision. – Jim Garrison Mar 12 '17 at 02:54
  • 1
    No problem Sam. The other thing that would make your path much smoother and much more precise would be to separate your repaint method from your gravity method. Try this: Remove gravity() from paint() to here: if (lastFpsTime > 100000000 * count){ milliSecondTimer += 0.1; gravity(); count++; } Now your position will be much more smooth. I'll post what my game loop looks like for something like this. – Nick Ziebert Mar 12 '17 at 03:07
  • 1
    @SamBrotherton Keep in mind that floating point numbers are only an approximation of real numbers. This is important when you think the math all checks out but the code doesn't. I strongly encourage you to learn about the limitations of floating point numbers and floating point arithmetic. – Code-Apprentice Mar 12 '17 at 03:33
  • @JimGarrison If you guys were really concerned about precision, you would have pointed out that his position method is only getting updated once every 60 seconds. The difference between float and double here is negligible. – Nick Ziebert Mar 12 '17 at 03:41
  • @NickZiebert just to let you know I have finished working on the whole maths side of it all, with your help to fix the Eulerian integration issue, the code will now work for most angles (as long as AA angle > launch angle) , the git repository has been suitably updated; I will now work on implementing your ideas on accuracy and implementing a cleaner (i.e: one similar to your own) game loop. Thanks for the help, Sam. – Sam Brotherton Mar 12 '17 at 16:42
  • Great, no problem. Nifty program. – Nick Ziebert Mar 12 '17 at 18:44
  • Are you going to get the grid to move if the collision happens off screen :p Looks like your going to try to rotate the missile .jpeg as well? Good luck with that, I couldn't figure that out. – Nick Ziebert Mar 12 '17 at 19:05
  • I have removed the grid for a more "aesthetic" look for the current build, but the real reason is that I have tried to have the screen move with an object multiple times but I can never seem to master it, and I get loads of graphical glitches. I have after much anguish, managed to get the rotation working lol, my code for this is on GIT, I have also uploaded the .jar file to amazon drive https://www.amazon.co.uk/clouddrive/share/NP7gxnTTGdsrBGfjY8XZaqBRQVvUIajFcwIsIcce7ma?ref_=cd_ph_share_link_copy if you are interested. – Sam Brotherton Mar 12 '17 at 21:44
  • @NickZiebert I know this is an old post but I have been too busy with exams; I am now working on another project and attempting to again use your game loop. So where I have gravity, I have a value called pixels per meter, so what I have at the moment is vSpeed -= 9.81 / 60 * pixelsPerMeter; but I know this is wrong as the implementation you have shown me now updates it much more than 60 times per second, so what value do I multiply 9.81 by to accurately get the gravitational pull? – Sam Brotherton May 27 '17 at 01:13