3

I wanted to implement ball physics and as i m newbie, i adapt the code in tutorial http://adam21.web.officelive.com/Documents/JavaPhysicsTutorial.pdf .

i try to follow that as i much as i can, but i m not able to apply all physical phenomenon in code, can somebody please tell me, where i m mistaken or i m still doing some silly programming mistake.

The balls are moving when i m not calling bounce method and i m unable to avail the bounce method and ball are moving towards left side instead of falling/ending on floor**,

Can some body recommend me some better way or similar easy compact way to accomplish this task of applying physics on two ball or more balls with interactivity.

here is code ;

    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;
    }

}

using 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);
            }
        });
    }

}   

*EDIT:*ok splitting the code using new approach for gravity from this forum: this code also not working the ball is not coming on floor:

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;

    }
}

thanks a lot for any correction and help.

jibby

static void main
  • 728
  • 2
  • 9
  • 34
  • 4
    That's quite a lot of code you posted. Can you please reduce your demo code to the simplest possible example that demonstrates the problem? – Steve McLeod Oct 03 '10 at 21:31
  • i m not sure from where the problems coming,so i shown the whole code for better picture. – static void main Oct 03 '10 at 22:29
  • 2
    @jibbylala: The point is that you must know something about what parts of your code are responsible for updating positions and velocities, right? If your dynamics are wrong you are doing something wrong in *those* parts of the code. The exercise of finding *where* the error lies may help you to find the error. – dmckee --- ex-moderator kitten Oct 03 '10 at 23:02
  • In your bounce method, do any of the conditions ever get met, or does it always fall through to the "both balls are moving" part? The reason I ask is because it's not usually a good idea to compare an `integer` (0) to a `double` since a `double` is a lot more precise and therefore less likely to ever equal exactly 0. – keyboardP Oct 03 '10 at 23:42
  • @KeyBoardP:yes your assumption is it always come in that part when i was debugging. – static void main Oct 03 '10 at 23:54
  • @jibbylala - Have a read here http://stackoverflow.com/questions/1650091/c-whats-the-best-way-to-compare-double-and-int to see how you should compare your double and ints. – keyboardP Oct 03 '10 at 23:56
  • @dmckee:i thought the problem is with deltaX and deltaY value they have to be get and set from somewhere with decrement in those. – static void main Oct 03 '10 at 23:59
  • You may do yourself a favor using as an example a vector-based implementation – Dr. belisarius Oct 04 '10 at 05:58

2 Answers2

5

At the moment I cannot run java, so I cannot test your code, but I notice a couple of things:

  1. You have two independent sets of variables for (almost) the same thing: {dx, dy} and {m_velocityX, m_velocityY}. As a result, your getSpeed and setSpeed are independent, and so are getDirection and Setdirection. I cannot believe that this will work the way you expect.
  2. Your coordinates seem a little confused (which is probably why the ball "falls" to the left). You seem to be measuring angle clockwise from the X direction, which is not standard. EDIT: Plínio Pantaleão has identified the reason for falling to the left.
  3. Your code seems to involve simple linear motion, bouncing, gravity and friction. You should implement and test these one at a time. I suggest the motion first, then add bouncing, then friction and gravity separately (in either order) and finally combine them.

EDIT:
This code for gravity is wrong:

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

First you are using 400 as a "magic number"; I can't tell what it represents. Then you are restricting the effect to a region near the top of the screen, for reasons that are not clear. At the end of the function you change m_y in a way that makes no sense. And (maybe) worst of all, you have gravity reversing and multiplying vertical velocity, which is not how gravity works.

Try this instead:

// 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;
Beta
  • 96,650
  • 16
  • 149
  • 150
  • thanks a lot for your feedback but non of the tries giving the desired effects, every try has negative consequences, i was trying to do whatever people advised and discussed on forums. – static void main Oct 05 '10 at 22:25
  • @jibbylala: Sei nicht niedergeschlagen! These things take time. What works now? Motion? Bouncing? Let us get motion working, then bouncing, then friction or gravity. And when a step goes wrong, tell us what you see. – Beta Oct 06 '10 at 02:47
  • @Beta:)vielen danke, i checked the gravity code in some other example its working, not the above one but the one given in tutorial,the main concern is ball to ball intersection/collision now. – static void main Oct 06 '10 at 04:01
  • @jibbylala: Good, show us your code for ball-ball collision. The physics is a little trickier than ball-wall collision. – Beta Oct 07 '10 at 13:38
2

Ok, not solving the problem yet. But a few things that may help:

1 - Math.sin/cos take angle in radians. (that may help a litle). - AdobeBall

2 - You have to calculate gravDir for each invoke of move() since m_velocityX/Y may have change direction- AdobeBall

3 - Your main loop in runball() are taking the same ball two times and comparing to itself... Use j=i+1 in second loop

4 - You call runball() in your thread loop. So I would expect the balls to move anyway.... Not just if you call bounce(). I'm correct?

You may correct your code and try again. And post the news! :)

Plínio Pantaleão
  • 1,229
  • 8
  • 13
  • as far as my understanding for point 1: double gravDir = 90/57.2960285258; and for point 3 the changes not let the ball moves: for (int j=i+1; j < currentNumBalls; j++) { – static void main Oct 04 '10 at 14:43
  • you could use just double gravDir=Math.PI/2. Its more readable. And about point 3 - I think this code is supposed to test one ball with other (what is the point in find the distance between one ball and itself?). If you let the code this way, in some cases i==j and you will run this test. But I dont get the ideia of your test, so if it make sense to you, keep that way :) – Plínio Pantaleão Oct 04 '10 at 16:43
  • thanks, i actually don't care about the code i mean i just want two ball moving and demonstrate the physical phenomenon involved (motion, bouncing, gravity and friction,collision), that's why i adapt that code because it supposed to do that but if i could have someother code doing that i have no problem. – static void main Oct 04 '10 at 17:52