我正在制作这个SO问题的球物理应用程序的简化版本.我通过阅读该问题提供的代码和链接获得了很多,但是在我的程序中我不使用向量,我只是更新我的球的坐标(每个都有一个随机的角度和速度)和三角函数撞到了墙上.
有关如何在没有三角测量的情况下处理球与球的碰撞的信息,但是我没有找到解释如何用三角函数完成的信息.
--EDIT 9/13/2010--
成功......有点......我完成了我想做的事,但我无法按照自己的意愿去做.如果有一种方法可以在不使用矢量的情况下计算球与球之间的碰撞,那么我就无法实现.即便如此,矢量确实是处理所有类型碰撞的一种更简单的方法......我只是希望我知道当我开始我的程序时...会节省我两三天的工作:)所有的我(完整?)程序的代码如下.我添加了一些简洁的功能,如阴影和减小的球半径,这可以让你看到当一个大球击中一个小球时两个球的质量差异.总共有五个类文件,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); } }
Ball.java
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; } }
BallBuilder.java
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. } }
MouseEventHandler.java
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; } }
Dr. belisari.. 5
当球反弹时,速度角度会发生变化,每次弹跳后都应该改变它.
此外,正如您稍后将看到的,当两个球碰撞时,速度VALUE(称为"模数")也会发生变化.
编辑:::
在我看来,你正在加速球
该
int x = (int) (ball.getBallTempX() + (ball.getBallPathLength() * Math.cos(ball.getBallAngle())));
似乎对应
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*Math.cos(角度)
BallCenterY = BallCenterY + BallSpeed*Math.sin(角度)
忘记任何"临时"和"旧"价值观.它们会削弱你的球碰撞功能.
当球反弹时,速度角度会发生变化,每次弹跳后都应该改变它.
此外,正如您稍后将看到的,当两个球碰撞时,速度VALUE(称为"模数")也会发生变化.
编辑:::
在我看来,你正在加速球
该
int x = (int) (ball.getBallTempX() + (ball.getBallPathLength() * Math.cos(ball.getBallAngle())));
似乎对应
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*Math.cos(角度)
BallCenterY = BallCenterY + BallSpeed*Math.sin(角度)
忘记任何"临时"和"旧"价值观.它们会削弱你的球碰撞功能.