Graphics 如何用三角函数代替矢量处理球对球的碰撞?

Graphics 如何用三角函数代替矢量处理球对球的碰撞?,graphics,collision-detection,physics,trigonometry,Graphics,Collision Detection,Physics,Trigonometry,我正在制作一个简化版的ball physics应用程序,该应用程序位于。通过阅读该问题提供的代码和链接,我收获了很多,但在我的程序中,我不使用向量,我只是在球撞击墙壁时用三角函数更新球的坐标(每个球都有一个随机的角度和速度) 关于如何在不使用三角法的情况下处理球与球之间的碰撞,到处都有信息,但我没有发现任何信息可以解释如何使用三角法来处理球与球之间的碰撞 --编辑2010年9月13日-- 成功。。。有点我做了我想做的事,但我无法按照我的意愿去做。如果有一种不用矢量计算球对球碰撞的方法,那我就找不

我正在制作一个简化版的ball physics应用程序,该应用程序位于。通过阅读该问题提供的代码和链接,我收获了很多,但在我的程序中,我不使用向量,我只是在球撞击墙壁时用三角函数更新球的坐标(每个球都有一个随机的角度和速度)

关于如何在不使用三角法的情况下处理球与球之间的碰撞,到处都有信息,但我没有发现任何信息可以解释如何使用三角法来处理球与球之间的碰撞

--编辑2010年9月13日--

成功。。。有点我做了我想做的事,但我无法按照我的意愿去做。如果有一种不用矢量计算球对球碰撞的方法,那我就找不到了。即使如此,向量似乎是处理所有类型碰撞的更简单的方法。。。我只是希望我在开始我的计划时就知道。。。可以节省我两三天的工作:)下面是我(完整的?)程序的所有代码。我添加了一些简洁的特性,比如阴影和球半径的减小,这让你可以看到当一个大球撞击一个小球时,两个球的质量的差异。总共有五个类文件,
AddLogic.java
Ball.java
BallBuilder.java
MouseEventHandler.java
,和
Vector2D.java

AddLogic.java

import java.awt.*;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import java.util.ArrayList;

public class AddLogic implements Runnable {//Make AddLogic a runnable task.

    private BallBuilder ballBuilder;
    private BufferStrategy strategy;
    private static ArrayList objectsToDraw = new ArrayList();
    private int floorHeight = 33;

    public AddLogic(BallBuilder ballBuilder, BufferStrategy strategy) {
        this.ballBuilder = ballBuilder;
        this.strategy = strategy;
    }

    private void logic(BallBuilder ballBuilder, BufferStrategy strategy) {
        this.ballBuilder = ballBuilder;
        this.strategy = strategy;

        while (true) {//Main loop. Draws all objects on screen and calls update methods.
            Graphics2D g = (Graphics2D) strategy.getDrawGraphics();//Creates the Graphics2D object g and uses it with the double buffer.
            g.setColor(Color.gray);
            g.fillRect(0, 0, ballBuilder.getWidth(), ballBuilder.getHeight());//Draw the wall.
            g.setColor(Color.lightGray);
            g.fillRect(0, ballBuilder.getHeight() - floorHeight, ballBuilder.getWidth(), floorHeight);//Draw the floor.
            g.setColor(Color.black);
            g.drawLine(0, ballBuilder.getHeight() - floorHeight, ballBuilder.getWidth(), ballBuilder.getHeight() - floorHeight);//Draw the line between the wall and floor.

            if (objectsToDrawIsEmpty() == true) {//If no balls have been made display message telling users how to make new ball.
                g.setColor(Color.red);
                g.drawString("Click Mouse For New Ball", (ballBuilder.getWidth() / 2) - 70, ballBuilder.getHeight() / 2);
            }

            for (int i = 0; i < objectsToDraw.size(); i++) {//Draw shadows for all balls.
                Ball ball = (Ball) objectsToDraw.get(i);
                g.setColor(Color.darkGray);
                g.fillOval(
                        (int) ball.ballPosition.getX() - (int) ((ball.ballPosition.getY() / (350 / ball.getBallRadius())) / 2),
                        ballBuilder.getHeight() - (floorHeight / 2) - (int) ((ball.ballPosition.getY() / (1250 / ball.getBallRadius())) / 2),
                        (int) ball.ballPosition.getY() / (350 / ball.getBallRadius()),
                        (int) ball.ballPosition.getY() / (1250 / ball.getBallRadius()));
            }

            for (int i = 0; i < objectsToDraw.size(); i++) {//Draw all balls by looping through them and checking for any vector or collision updates that need to be made.
                Ball ball = (Ball) objectsToDraw.get(i);
                g.setColor(ball.getBallColor());
                g.fillOval(
                        (int) ball.ballPosition.getX() - ball.getBallRadius(),
                        (int) ball.ballPosition.getY() - ball.getBallRadius(),
                        ball.getBallRadius() * 2,
                        ball.getBallRadius() * 2);

                vectorUpdate(ball);//Update ball vector coordinates.

                collisionCheck(ball);//Check ball to ball and ball to wall collisions.

            }

            if (MouseEventHandler.mouseEventCheck() == true) {// Creates a new ball when mouse is clicked.
                Ball ball = new Ball(ballBuilder);
                objectsToDraw.add(ball); //Adds the new ball to the array list.
                MouseEventHandler.mouseEventUpdate(); //Resets the mouse click event to false.

            }

            g.dispose();//To aid Java in garbage collection.
            strategy.show();//Show all graphics drawn on the buffer.

            try {//Try to make thread sleep for 5ms.  Results in a frame rate of 200FPS.
                Thread.sleep(5);
            }

            catch (Exception e) {//Catch any exceptions if try fails.

            }
        }
    }

    private void vectorUpdate(Ball ball) {//Update the ball vector based upon the ball's current position and its velocity.

        ball.ballPosition.setX(ball.ballPosition.getX() + ball.ballVelocity.getX());
        ball.ballPosition.setY(ball.ballPosition.getY() + ball.ballVelocity.getY());

    }

    private void collisionCheck(Ball ball) {//Check for ball to wall collisions. Call check for ball to ball collisions at end of method.

        if (ball.ballPosition.getX() - ball.getBallRadius() < 0) {//Check for ball to left wall collision.
            ball.ballPosition.setX(ball.getBallRadius());
            ball.ballVelocity.setX(-(ball.ballVelocity.getX()));
            ball.decreaseBallRadius(ball);//Decrease ball radius by one pixel. Called on left, top, and right walls, but not bottom because it looks weird watching shadow get smaller during bottom bounce.

        }

        else if (ball.ballPosition.getX() + ball.getBallRadius() > ballBuilder.getWidth()) {//Check for ball to right wall collision.
            ball.ballPosition.setX(ballBuilder.getWidth() - ball.getBallRadius());
            ball.ballVelocity.setX(-(ball.ballVelocity.getX()));
            ball.decreaseBallRadius(ball);//Decrease ball radius by one pixel. Called on left, top, and right walls, but not bottom because it looks weird watching shadow get smaller during bottom bounce.
        }

        if (ball.ballPosition.getY() - ball.getBallRadius() < 0) {//Check for ball to top wall collision.
            ball.ballPosition.setY(ball.getBallRadius());
            ball.ballVelocity.setY(-(ball.ballVelocity.getY()));
            ball.decreaseBallRadius(ball);//Decrease ball radius by one pixel. Called on left, top, and right walls, but not bottom because it looks weird watching shadow get smaller during bottom bounce.
        }

        else if (ball.ballPosition.getY() + ball.getBallRadius() + (floorHeight / 2) > ballBuilder.getHeight()) {//Check for ball to bottom wall collision.  Floor height is accounted for to give the appearance that ball is bouncing in the center of the floor strip.
            ball.ballPosition.setY(ballBuilder.getHeight() - ball.getBallRadius() - (floorHeight / 2));
            ball.ballVelocity.setY(-(ball.ballVelocity.getY()));
        }

        for (int i = 0; i < objectsToDraw.size(); i++) {//Check to see if a ball is touching any other balls by looping through all balls and checking their positions.
            Ball otherBall = (Ball) objectsToDraw.get(i);

            if (ball != otherBall && Math.sqrt(Math.pow(ball.ballPosition.getX() - otherBall.ballPosition.getX(), 2.0) + Math.pow(ball.ballPosition.getY() - otherBall.ballPosition.getY(), 2.0)) < ball.getBallRadius() + otherBall.getBallRadius()) {
                resolveBallToBallCollision(ball, otherBall);//If the ball is hitting another ball calculate the new vectors based on the variables of the balls involved.
            }

        }

    }

    private void resolveBallToBallCollision(Ball ball, Ball otherBall) {//Calculate the new vectors after ball to ball collisions.
        Vector2D delta = (ball.ballPosition.subtract(otherBall.ballPosition));//Difference between the position of the two balls involved in the collision.
        float deltaLength = delta.getLength();//The (x, y) of the delta squared.

        Vector2D minimumTranslationDistance = delta.multiply(((ball.getBallRadius() + otherBall.getBallRadius()) - deltaLength) / deltaLength);//The minimum distance the balls should move apart once they.

        float ballInverseMass = 1 / ball.getBallMass();//half the ball mass.
        float otherBallInverseMass = 1 / otherBall.getBallMass();//half the other ball mass.

        ball.ballPosition = ball.ballPosition.add(minimumTranslationDistance.multiply(ballInverseMass / (ballInverseMass + otherBallInverseMass)));//Calculate the new position of the ball.
        otherBall.ballPosition = otherBall.ballPosition.subtract(minimumTranslationDistance.multiply(otherBallInverseMass / (ballInverseMass + otherBallInverseMass)));//Calculate the new position of the other ball.

        Vector2D impactVelocity = (ball.ballVelocity.subtract(otherBall.ballVelocity));//Find the veloicity of the impact based upon the velocities of the two balls involved.
        float normalizedImpactVelocity = impactVelocity.dot(minimumTranslationDistance.normalize());//

        if (normalizedImpactVelocity > 0.0f) {//Returns control to calling object if ball and other ball are intersecting, but moving away from each other.
            return;
        }

        float restitution = 2.0f;//The constraint representing friction. A value of 2.0 is 0 friction, a value smaller than 2.0 is more friction, and a value over 2.0 is negative friction.

        float i = (-(restitution) * normalizedImpactVelocity) / (ballInverseMass + otherBallInverseMass);
        Vector2D impulse = minimumTranslationDistance.multiply(i);

        ball.ballVelocity = ball.ballVelocity.add(impulse.multiply(ballInverseMass));//Change the velocity of the ball based upon its mass.
        otherBall.ballVelocity = otherBall.ballVelocity.subtract(impulse.multiply(otherBallInverseMass));//Change the velocity of the other ball based upon its mass.
    }

    public static boolean objectsToDrawIsEmpty() {//Checks to see if there are any balls to draw.
        boolean empty = false;
        if (objectsToDraw.isEmpty()) {
            empty = true;
        }

        return empty;

    }

    public void run() {//Runs the AddLogic instance logic in a new thread.
        logic(ballBuilder, strategy);
    }
}
import java.awt.*;

public class Ball {

    private int ballRadius;
    private float ballMass;
    public Vector2D ballPosition = new Vector2D();
    public Vector2D ballVelocity = new Vector2D();
    private Color ballColor;

    public Ball(BallBuilder ballBuilder) {//Construct a new ball.
        this.ballRadius = 75;//When ball is created make its radius 75 pixels.
        this.ballMass = ((float)(4 / 3 * Math.PI * Math.pow(ballRadius, 3.0)));//When ball is created make its mass that the volume of a sphere the same size.
        this.ballPosition.set(ballRadius, ballBuilder.getHeight() - ballRadius);//When ball is created make its starting coordinates the bottom left hand corner of the screen.
        this.ballVelocity.set(randomVelocity(), randomVelocity());//When ball is created make its (x, y) velocity a random value between 0 and 2.

        if (AddLogic.objectsToDrawIsEmpty() == true) {//If the ball being created is the first ball, make it blue, otherwise pick a random color.
            this.ballColor = Color.blue;
        } else {
            this.ballColor = randomColor();
        }

    }

    public void decreaseBallRadius(Ball ball){//Decrease the ball radius.
        if(ball.getBallRadius() <= 15){//If the ball radius is less than or equal to 15 return control to calling object, else continue.
            return;
        }

        ball.setBallRadius(ball.getBallRadius() - 1);//Decrease the ball radius by 1 pixel.
        ball.setBallMass((float)(4 / 3 * Math.PI * Math.pow(ballRadius, 3.0)));//Recalcualte the mass based on the new radius.

    }

    public int getBallRadius() {
        return ballRadius;
    }

    public float getBallMass(){
        return ballMass;
    }

    public Color getBallColor() {
        return ballColor;
    }

    private void setBallRadius(int newBallRadius) {
        this.ballRadius = newBallRadius;
    }

    private void setBallMass(float newBallMass){
        this.ballMass = newBallMass;
    }

    private float randomVelocity() {//Generate a random number between 0 and 2 for the (x, y) velocity of a ball.
        float speed = (float)(Math.random() * 2);
        return speed;
    }

    private Color randomColor() {//Generate a random color for a new ball based on the generation of a random red, green, and blue value.
        int red = (int) (Math.random() * 255);
        int green = (int) (Math.random() * 255);
        int blue = (int) (Math.random() * 255);
        ballColor = new Color(red, green, blue);
        return ballColor;
    }

}
import java.awt.*;
import java.awt.image.*;
import java.util.concurrent.*;
import javax.swing.*;

public class BallBuilder extends Canvas{
    private int frameHeight = 600;
    private int frameWidth = 800;

    private static BufferStrategy strategy;//Create a buffer strategy named strategy.

    public BallBuilder(){
        setIgnoreRepaint(true);//Tell OS that we will handle any repainting manually.
        setBounds(0,0,frameWidth,frameHeight);

        JFrame frame = new JFrame("Bouncing Balls");
        JPanel panel = new JPanel();

        panel.setPreferredSize(new Dimension(frameWidth, frameHeight));
        panel.add(this);

        frame.setContentPane(panel);
        frame.pack();
        frame.setResizable(false);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        addMouseListener(new MouseEventHandler());

        createBufferStrategy(2);//Create a double buffer for smooth graphics.
    strategy = getBufferStrategy();//Apply the double buffer to the buffer strategy named strategy.

    }

    public static void main(String[] args) {
        BallBuilder ballBuilder = new BallBuilder(); // Creates a new ball builder.
        ExecutorService executor = Executors.newSingleThreadExecutor();//Creates a thread executor that uses a single thread.
        executor.execute(new AddLogic(ballBuilder, strategy));//Executes the runnable task AddLogic on the previously created thread.

    }

}
import java.awt.event.*;

public class MouseEventHandler extends MouseAdapter{

    private static boolean mouseClicked = false;

    public void mousePressed(MouseEvent e){//If either of the mouse buttons is pressed the mouse clicked variable is set to true.
        mouseClicked = true;
    }

    public static void mouseEventUpdate(){//When called, sets the mouse clicked variable back to false.
        mouseClicked = false;
    }

    public static boolean mouseEventCheck(){//Returns the state of the mouse clicked variable.
        if(mouseClicked){
            return true;
        }

        else{
            return false;
        }

    }

}
MouseEventHandler.java

import java.awt.*;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import java.util.ArrayList;

public class AddLogic implements Runnable {//Make AddLogic a runnable task.

    private BallBuilder ballBuilder;
    private BufferStrategy strategy;
    private static ArrayList objectsToDraw = new ArrayList();
    private int floorHeight = 33;

    public AddLogic(BallBuilder ballBuilder, BufferStrategy strategy) {
        this.ballBuilder = ballBuilder;
        this.strategy = strategy;
    }

    private void logic(BallBuilder ballBuilder, BufferStrategy strategy) {
        this.ballBuilder = ballBuilder;
        this.strategy = strategy;

        while (true) {//Main loop. Draws all objects on screen and calls update methods.
            Graphics2D g = (Graphics2D) strategy.getDrawGraphics();//Creates the Graphics2D object g and uses it with the double buffer.
            g.setColor(Color.gray);
            g.fillRect(0, 0, ballBuilder.getWidth(), ballBuilder.getHeight());//Draw the wall.
            g.setColor(Color.lightGray);
            g.fillRect(0, ballBuilder.getHeight() - floorHeight, ballBuilder.getWidth(), floorHeight);//Draw the floor.
            g.setColor(Color.black);
            g.drawLine(0, ballBuilder.getHeight() - floorHeight, ballBuilder.getWidth(), ballBuilder.getHeight() - floorHeight);//Draw the line between the wall and floor.

            if (objectsToDrawIsEmpty() == true) {//If no balls have been made display message telling users how to make new ball.
                g.setColor(Color.red);
                g.drawString("Click Mouse For New Ball", (ballBuilder.getWidth() / 2) - 70, ballBuilder.getHeight() / 2);
            }

            for (int i = 0; i < objectsToDraw.size(); i++) {//Draw shadows for all balls.
                Ball ball = (Ball) objectsToDraw.get(i);
                g.setColor(Color.darkGray);
                g.fillOval(
                        (int) ball.ballPosition.getX() - (int) ((ball.ballPosition.getY() / (350 / ball.getBallRadius())) / 2),
                        ballBuilder.getHeight() - (floorHeight / 2) - (int) ((ball.ballPosition.getY() / (1250 / ball.getBallRadius())) / 2),
                        (int) ball.ballPosition.getY() / (350 / ball.getBallRadius()),
                        (int) ball.ballPosition.getY() / (1250 / ball.getBallRadius()));
            }

            for (int i = 0; i < objectsToDraw.size(); i++) {//Draw all balls by looping through them and checking for any vector or collision updates that need to be made.
                Ball ball = (Ball) objectsToDraw.get(i);
                g.setColor(ball.getBallColor());
                g.fillOval(
                        (int) ball.ballPosition.getX() - ball.getBallRadius(),
                        (int) ball.ballPosition.getY() - ball.getBallRadius(),
                        ball.getBallRadius() * 2,
                        ball.getBallRadius() * 2);

                vectorUpdate(ball);//Update ball vector coordinates.

                collisionCheck(ball);//Check ball to ball and ball to wall collisions.

            }

            if (MouseEventHandler.mouseEventCheck() == true) {// Creates a new ball when mouse is clicked.
                Ball ball = new Ball(ballBuilder);
                objectsToDraw.add(ball); //Adds the new ball to the array list.
                MouseEventHandler.mouseEventUpdate(); //Resets the mouse click event to false.

            }

            g.dispose();//To aid Java in garbage collection.
            strategy.show();//Show all graphics drawn on the buffer.

            try {//Try to make thread sleep for 5ms.  Results in a frame rate of 200FPS.
                Thread.sleep(5);
            }

            catch (Exception e) {//Catch any exceptions if try fails.

            }
        }
    }

    private void vectorUpdate(Ball ball) {//Update the ball vector based upon the ball's current position and its velocity.

        ball.ballPosition.setX(ball.ballPosition.getX() + ball.ballVelocity.getX());
        ball.ballPosition.setY(ball.ballPosition.getY() + ball.ballVelocity.getY());

    }

    private void collisionCheck(Ball ball) {//Check for ball to wall collisions. Call check for ball to ball collisions at end of method.

        if (ball.ballPosition.getX() - ball.getBallRadius() < 0) {//Check for ball to left wall collision.
            ball.ballPosition.setX(ball.getBallRadius());
            ball.ballVelocity.setX(-(ball.ballVelocity.getX()));
            ball.decreaseBallRadius(ball);//Decrease ball radius by one pixel. Called on left, top, and right walls, but not bottom because it looks weird watching shadow get smaller during bottom bounce.

        }

        else if (ball.ballPosition.getX() + ball.getBallRadius() > ballBuilder.getWidth()) {//Check for ball to right wall collision.
            ball.ballPosition.setX(ballBuilder.getWidth() - ball.getBallRadius());
            ball.ballVelocity.setX(-(ball.ballVelocity.getX()));
            ball.decreaseBallRadius(ball);//Decrease ball radius by one pixel. Called on left, top, and right walls, but not bottom because it looks weird watching shadow get smaller during bottom bounce.
        }

        if (ball.ballPosition.getY() - ball.getBallRadius() < 0) {//Check for ball to top wall collision.
            ball.ballPosition.setY(ball.getBallRadius());
            ball.ballVelocity.setY(-(ball.ballVelocity.getY()));
            ball.decreaseBallRadius(ball);//Decrease ball radius by one pixel. Called on left, top, and right walls, but not bottom because it looks weird watching shadow get smaller during bottom bounce.
        }

        else if (ball.ballPosition.getY() + ball.getBallRadius() + (floorHeight / 2) > ballBuilder.getHeight()) {//Check for ball to bottom wall collision.  Floor height is accounted for to give the appearance that ball is bouncing in the center of the floor strip.
            ball.ballPosition.setY(ballBuilder.getHeight() - ball.getBallRadius() - (floorHeight / 2));
            ball.ballVelocity.setY(-(ball.ballVelocity.getY()));
        }

        for (int i = 0; i < objectsToDraw.size(); i++) {//Check to see if a ball is touching any other balls by looping through all balls and checking their positions.
            Ball otherBall = (Ball) objectsToDraw.get(i);

            if (ball != otherBall && Math.sqrt(Math.pow(ball.ballPosition.getX() - otherBall.ballPosition.getX(), 2.0) + Math.pow(ball.ballPosition.getY() - otherBall.ballPosition.getY(), 2.0)) < ball.getBallRadius() + otherBall.getBallRadius()) {
                resolveBallToBallCollision(ball, otherBall);//If the ball is hitting another ball calculate the new vectors based on the variables of the balls involved.
            }

        }

    }

    private void resolveBallToBallCollision(Ball ball, Ball otherBall) {//Calculate the new vectors after ball to ball collisions.
        Vector2D delta = (ball.ballPosition.subtract(otherBall.ballPosition));//Difference between the position of the two balls involved in the collision.
        float deltaLength = delta.getLength();//The (x, y) of the delta squared.

        Vector2D minimumTranslationDistance = delta.multiply(((ball.getBallRadius() + otherBall.getBallRadius()) - deltaLength) / deltaLength);//The minimum distance the balls should move apart once they.

        float ballInverseMass = 1 / ball.getBallMass();//half the ball mass.
        float otherBallInverseMass = 1 / otherBall.getBallMass();//half the other ball mass.

        ball.ballPosition = ball.ballPosition.add(minimumTranslationDistance.multiply(ballInverseMass / (ballInverseMass + otherBallInverseMass)));//Calculate the new position of the ball.
        otherBall.ballPosition = otherBall.ballPosition.subtract(minimumTranslationDistance.multiply(otherBallInverseMass / (ballInverseMass + otherBallInverseMass)));//Calculate the new position of the other ball.

        Vector2D impactVelocity = (ball.ballVelocity.subtract(otherBall.ballVelocity));//Find the veloicity of the impact based upon the velocities of the two balls involved.
        float normalizedImpactVelocity = impactVelocity.dot(minimumTranslationDistance.normalize());//

        if (normalizedImpactVelocity > 0.0f) {//Returns control to calling object if ball and other ball are intersecting, but moving away from each other.
            return;
        }

        float restitution = 2.0f;//The constraint representing friction. A value of 2.0 is 0 friction, a value smaller than 2.0 is more friction, and a value over 2.0 is negative friction.

        float i = (-(restitution) * normalizedImpactVelocity) / (ballInverseMass + otherBallInverseMass);
        Vector2D impulse = minimumTranslationDistance.multiply(i);

        ball.ballVelocity = ball.ballVelocity.add(impulse.multiply(ballInverseMass));//Change the velocity of the ball based upon its mass.
        otherBall.ballVelocity = otherBall.ballVelocity.subtract(impulse.multiply(otherBallInverseMass));//Change the velocity of the other ball based upon its mass.
    }

    public static boolean objectsToDrawIsEmpty() {//Checks to see if there are any balls to draw.
        boolean empty = false;
        if (objectsToDraw.isEmpty()) {
            empty = true;
        }

        return empty;

    }

    public void run() {//Runs the AddLogic instance logic in a new thread.
        logic(ballBuilder, strategy);
    }
}
import java.awt.*;

public class Ball {

    private int ballRadius;
    private float ballMass;
    public Vector2D ballPosition = new Vector2D();
    public Vector2D ballVelocity = new Vector2D();
    private Color ballColor;

    public Ball(BallBuilder ballBuilder) {//Construct a new ball.
        this.ballRadius = 75;//When ball is created make its radius 75 pixels.
        this.ballMass = ((float)(4 / 3 * Math.PI * Math.pow(ballRadius, 3.0)));//When ball is created make its mass that the volume of a sphere the same size.
        this.ballPosition.set(ballRadius, ballBuilder.getHeight() - ballRadius);//When ball is created make its starting coordinates the bottom left hand corner of the screen.
        this.ballVelocity.set(randomVelocity(), randomVelocity());//When ball is created make its (x, y) velocity a random value between 0 and 2.

        if (AddLogic.objectsToDrawIsEmpty() == true) {//If the ball being created is the first ball, make it blue, otherwise pick a random color.
            this.ballColor = Color.blue;
        } else {
            this.ballColor = randomColor();
        }

    }

    public void decreaseBallRadius(Ball ball){//Decrease the ball radius.
        if(ball.getBallRadius() <= 15){//If the ball radius is less than or equal to 15 return control to calling object, else continue.
            return;
        }

        ball.setBallRadius(ball.getBallRadius() - 1);//Decrease the ball radius by 1 pixel.
        ball.setBallMass((float)(4 / 3 * Math.PI * Math.pow(ballRadius, 3.0)));//Recalcualte the mass based on the new radius.

    }

    public int getBallRadius() {
        return ballRadius;
    }

    public float getBallMass(){
        return ballMass;
    }

    public Color getBallColor() {
        return ballColor;
    }

    private void setBallRadius(int newBallRadius) {
        this.ballRadius = newBallRadius;
    }

    private void setBallMass(float newBallMass){
        this.ballMass = newBallMass;
    }

    private float randomVelocity() {//Generate a random number between 0 and 2 for the (x, y) velocity of a ball.
        float speed = (float)(Math.random() * 2);
        return speed;
    }

    private Color randomColor() {//Generate a random color for a new ball based on the generation of a random red, green, and blue value.
        int red = (int) (Math.random() * 255);
        int green = (int) (Math.random() * 255);
        int blue = (int) (Math.random() * 255);
        ballColor = new Color(red, green, blue);
        return ballColor;
    }

}
import java.awt.*;
import java.awt.image.*;
import java.util.concurrent.*;
import javax.swing.*;

public class BallBuilder extends Canvas{
    private int frameHeight = 600;
    private int frameWidth = 800;

    private static BufferStrategy strategy;//Create a buffer strategy named strategy.

    public BallBuilder(){
        setIgnoreRepaint(true);//Tell OS that we will handle any repainting manually.
        setBounds(0,0,frameWidth,frameHeight);

        JFrame frame = new JFrame("Bouncing Balls");
        JPanel panel = new JPanel();

        panel.setPreferredSize(new Dimension(frameWidth, frameHeight));
        panel.add(this);

        frame.setContentPane(panel);
        frame.pack();
        frame.setResizable(false);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        addMouseListener(new MouseEventHandler());

        createBufferStrategy(2);//Create a double buffer for smooth graphics.
    strategy = getBufferStrategy();//Apply the double buffer to the buffer strategy named strategy.

    }

    public static void main(String[] args) {
        BallBuilder ballBuilder = new BallBuilder(); // Creates a new ball builder.
        ExecutorService executor = Executors.newSingleThreadExecutor();//Creates a thread executor that uses a single thread.
        executor.execute(new AddLogic(ballBuilder, strategy));//Executes the runnable task AddLogic on the previously created thread.

    }

}
import java.awt.event.*;

public class MouseEventHandler extends MouseAdapter{

    private static boolean mouseClicked = false;

    public void mousePressed(MouseEvent e){//If either of the mouse buttons is pressed the mouse clicked variable is set to true.
        mouseClicked = true;
    }

    public static void mouseEventUpdate(){//When called, sets the mouse clicked variable back to false.
        mouseClicked = false;
    }

    public static boolean mouseEventCheck(){//Returns the state of the mouse clicked variable.
        if(mouseClicked){
            return true;
        }

        else{
            return false;
        }

    }

}
Vector2D

public class Vector2D {//A class that takes care of ball position and speed vectors.

    private float x;
    private float y;

    public Vector2D() {
        this.setX(0);
        this.setY(0);
    }

    public Vector2D(float x, float y) {
        this.setX(x);
        this.setY(y);
    }

    public void set(float x, float y) {
        this.setX(x);
        this.setY(y);
    }

    public void setX(float x) {
        this.x = x;
    }

    public void setY(float y) {
        this.y = y;
    }

    public float getX() {
        return x;
    }

    public float getY() {
        return y;
    }

    public float dot(Vector2D v2) {//Speciality method used during calculations of ball to ball collisions.
        float result = 0.0f;
        result = this.getX() * v2.getX() + this.getY() * v2.getY();
        return result;
    }

    public float getLength() {
        return (float) Math.sqrt(getX() * getX() + getY() * getY());
    }

    public Vector2D add(Vector2D v2) {
        Vector2D result = new Vector2D();
        result.setX(getX() + v2.getX());
        result.setY(getY() + v2.getY());
        return result;
    }

    public Vector2D subtract(Vector2D v2) {
        Vector2D result = new Vector2D();
        result.setX(this.getX() - v2.getX());
        result.setY(this.getY() - v2.getY());
        return result;
    }

    public Vector2D multiply(float scaleFactor) {
        Vector2D result = new Vector2D();
        result.setX(this.getX() * scaleFactor);
        result.setY(this.getY() * scaleFactor);
        return result;
    }

    public Vector2D normalize() {//Speciality method used during calculations of ball to ball collisions.
        float length = getLength();
        if (length != 0.0f) {
            this.setX(this.getX() / length);
            this.setY(this.getY() / length);
        } else {
            this.setX(0.0f);
            this.setY(0.0f);
        }

        return this;
    }

}

我将开始这个答案,并一步一步地编辑它,直到我们完成。我将尝试引导您使用“半矢量化”版本的程序,尽量减少工作量。
请不断更新代码,并对我的建议进行评论

首先,有几件事:

您使用的是弧度,但在yCoordinateUpdate和yCoordinateUpdate中,您似乎使用的是相同的角度(因为您使用的是90)

对于所有涉及的数学来说,使用弧度更容易

此外,由于速度角将处理该问题,因此不需要vars GoingUp等

您可以在间隔(0…2π)内设置初始随机角度

你应该在每次边界碰撞后改变(事实上反映)速度角(之后,当我们完成时,在每次国际米兰的球碰撞之后)

考虑到这一点,法律是:

Upper or lower wall:    New Angle =  - OldAngle 

For Right or left wall: New Angle = Pi - OldAngle  
稍后我们将保留顶点上的反射…

当球反弹时,速度角会发生变化,您应该在每次反弹后更改它

此外,正如您稍后将看到的,当两个球碰撞时,速度值(称为“模数”)也会发生变化

编辑:::

在我看来,你在加速球

似乎符合

int x = (int) (ball.getBallTempX() + 
              (ball.getBallSpeed() * Math.cos(ball.getBallAngle())));  
“y”也是一样

我说得对吗

编辑2::

事实上,您也不需要TempX、TempY、PreviousCenterX和PreviousCenterY

试试这些方法

private int xCoordinateUpdate(Ball ball) {

   int x = (int) (ball.getBallCenterX()+ (ball.Speed() 
                                      * Math.cos(ball.getBallAngle())));

return x;
(与Y相同)

编辑3::

还有一个…:)

您需要编写的是以下公式(当您尝试碰撞球时,任何其他公式都将失败):

伪代码>

BallCenterX=BallCenterX+BallSpeed*数学cos(角度)

球中心Y=球中心Y+球速*数学sin(角度)


忘记任何“临时”和“旧”值。它们会削弱你的击球能力。

矢量是伪装的三角野兽。这是一种缩短方程式的方法,一次为所有维度编写一个等式。我现在看到,我的程序几乎完成了,我用基本的三角法进行了所有的更新,如果我介绍我最近学到的向量,那么到目前为止我编写的所有碰撞检测都将是徒劳的。在这一点上,我似乎要尝试重叠这两个,使用我已经使用的碰撞检测,但只使用球到球碰撞的向量。看起来像是一种浪费(如果我当时知道我现在知道的,我会在所有事情上使用向量),但除非有人知道如何排除向量,否则我就不知所措了。@typoknig我想重用代码应该很容易。请发布一个已经运行的方法或函数,我们可以尝试为您提供一些提示。@typoknig感谢您发布代码。我认为有很多地方需要改进,例如,UpdateX应该只有一两行代码(本质上是newX=oldX+speed*Math.Cos(angle))。但是在课程中有一些你没有包括的变量,我不清楚,所以我不想冒着错误答案的风险,这会消耗我们两人很多时间。请尝试发布完整的代码。Tnx。。。(并用文字“@belisarius”发表评论,这样我会收到so消息警报)@belisarius,问题已经更新。还感谢您让我知道
@
标志。我把它放进去通常只是因为我看到人们在做什么,但我没有意识到这是提醒用户的。还有其他这样的约定吗?@belisarius很好,更新坐标时我忘了转换成弧度。我已经编辑了代码来解决这个问题。至于我的
GoingUp
GoingRight
变量,你说“速度角”是什么意思?我想指出的是,球的速度是
1-5
中的一个随机值,该值是斜边(
ballPathLength
)每帧增加的量。@typoknig我正在发布一张图below@belisarius虽然我没有完全理解,但我实现了你所说的,而且效果相当好。清理了我的代码很多,虽然球在一个轻微的角度仍然坚持顶部的一些原因。我正在更新问题中的
AddLogic.java
文件。@belisarius当球的角度小于等于15度时,球似乎会粘在屏幕顶部。@typoknig也。。。setPathLength的用途是什么?@belis