Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何正确检查两个移动椭圆(圆)之间的碰撞并更新其位置+;速度_Java_Math_Collision Detection_Collision_Game Physics - Fatal编程技术网

Java 如何正确检查两个移动椭圆(圆)之间的碰撞并更新其位置+;速度

Java 如何正确检查两个移动椭圆(圆)之间的碰撞并更新其位置+;速度,java,math,collision-detection,collision,game-physics,Java,Math,Collision Detection,Collision,Game Physics,好的,所以我试着写一个简单的碰撞检测两个移动的椭圆(圆)和更新他们的位置和速度使用我找到的指南。 然而,我得到的结果不是很完美,对象正在碰撞和更新,但并不总是正确的,它们有时会纠结在一起,我很沮丧,因为我不明白为什么会发生这种情况。 注意:请忽略对象与边界的碰撞,它们有时会卡住(我需要更改,这不是我遇到的问题) 以下是我使用的两个类的代码: package letifer.com; import java.awt.Color; import java.awt.Container; import

好的,所以我试着写一个简单的碰撞检测两个移动的椭圆(圆)和更新他们的位置和速度使用我找到的指南。 然而,我得到的结果不是很完美,对象正在碰撞和更新,但并不总是正确的,它们有时会纠结在一起,我很沮丧,因为我不明白为什么会发生这种情况。
注意:请忽略对象与边界的碰撞,它们有时会卡住(我需要更改,这不是我遇到的问题)

以下是我使用的两个类的代码:

package letifer.com;

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Point2D;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class MaxwellsDemon extends JPanel{
    public static final long serialVersionUID = 1L;

    private final Dimension DIMS = new Dimension(800, 500);
    private final int UPDATE_INTERVAL = 20, BALLS_COUNT = 20;
    private final JFrame FRAME = new JFrame("MaxwellsDemon");
    private int leftCountR, leftCountB, rightCountR, rightCountB;
    private Ball balls[];
    private Random random;
    private Timer timer;

    public MaxwellsDemon(){
        super();
        initThis();
        initFrame();
        initBalls();
        registerTimer();
        timer.start();
    }

    private void initThis(){
        setSize(DIMS);
        setBackground(Color.white);
        random = new Random();
    }

    private void initFrame(){
        FRAME.setContentPane(new Container(){
            public void paint(Graphics g){
                super.paint(g);
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
                for(int i=0; i<2; i++)
                    g2d.drawLine(0, DIMS.height+i, DIMS.width-1, DIMS.height+i);
                g2d.setColor(Color.blue);
                g2d.fillOval(25, DIMS.height+20, Ball.radius*2, Ball.radius*2);
                g2d.fillOval(DIMS.width-90, DIMS.height+20, Ball.radius*2, Ball.radius*2);
                g2d.setColor(Color.red);
                g2d.fillOval(25, DIMS.height+60, Ball.radius*2, Ball.radius*2);
                g2d.fillOval(DIMS.width-90, DIMS.height+60, Ball.radius*2, Ball.radius*2);
                g2d.setFont(new Font("Serif", Font.BOLD, 18));
                g2d.setColor(Color.black);
                g2d.drawString(String.format("= %d", leftCountB), 45, DIMS.height+32);
                g2d.drawString(String.format("= %d", leftCountR), 45, DIMS.height+72);
                g2d.drawString(String.format("= %d", rightCountB), DIMS.width-70, DIMS.height+32);
                g2d.drawString(String.format("= %d", rightCountR), DIMS.width-70, DIMS.height+72);
            }
        });
        FRAME.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        FRAME.getContentPane().setPreferredSize(new Dimension(DIMS.width, DIMS.height+100));
        FRAME.getContentPane().add(this);
        FRAME.setResizable(false);
        FRAME.pack();
        FRAME.setLocationRelativeTo(null);
        FRAME.setVisible(true);
    }

    private void initBalls(){
        balls = new Ball[BALLS_COUNT];
        leftCountR = 0; leftCountB = 0; rightCountR = 0; rightCountB = 0;
        for(int i=0, f=1; i<BALLS_COUNT; i++, f=-f){
            balls[i] = new Ball(new Point2D.Double(random.nextInt(DIMS.width-Ball.radius*2-1)+Ball.radius+1,
                                random.nextInt(DIMS.height-Ball.radius*2-1)+Ball.radius+1),
                                0.15,
                                random.nextDouble()*(Math.PI*2),
                                (f>0?Color.blue:Color.red));
            if(balls[i].c == Color.blue){
                if(balls[i].locOnScreen.x < DIMS.width/2){
                    leftCountB++;
                }else{
                    rightCountB++;
                }
            }else{
                if(balls[i].locOnScreen.x < DIMS.width/2){
                    leftCountR++;
                }else{
                    rightCountR++;
                }
            }
        }
    }

    private void update(){
        checkForCollisions();
        updateBalls();      
    }

    private void checkForCollisions(){
        for(int i=0; i<BALLS_COUNT-1; i++){
            for(int j=i+1; j<BALLS_COUNT; j++){
                double dx = balls[i].getX()-balls[j].getX();
                double dy = balls[i].getY()-balls[j].getY();
                Point2D.Double difference = new Point2D.Double(dx, dy);
                double distanceAtFrameEnd = Math.sqrt(dx*dx+dy*dy);
                double collisionDistance = Ball.radius*2;
                if(distanceAtFrameEnd < collisionDistance){

                    //go back in time :)
                    double millisecondsAfterCollision = moveBackToCollisionPoint(balls[i], balls[j], distanceAtFrameEnd, collisionDistance);

                    //calculate the new distance vector.
                    dx = balls[i].getX()-balls[j].getX();
                    dy = balls[i].getY()-balls[j].getY();
                    difference.setLocation(dx, dy);
                    Point2D.Double normalPlane = new Point2D.Double(difference.x, difference.y);
                    //normalize it
                    double distance = Math.sqrt(dx*dx+dy*dy);
                    normalPlane.x /= distance;
                    normalPlane.y /= distance;

                    //calculate the collision vector(rotate by 90deg PI/2).
                    Point2D.Double collisionPlane = new Point2D.Double(-normalPlane.y, normalPlane.x);

                    //calculate prior velocities relative the the collision plane and normal.
                    double velIx = balls[i].getVX(), velIy = balls[i].getVY();
                    double velJx = balls[j].getVX(), velJy = balls[j].getVY();
                    double n_vel1 = (velIx * normalPlane.x) + (velIy * normalPlane.y);
                    double c_vel1 = (velIx * collisionPlane.x) + (velIy * collisionPlane.y);
                    double n_vel2 = (velJx * normalPlane.x) + (velJy * normalPlane.y);
                    double c_vel2 = (velJx * collisionPlane.x) + (velJy * collisionPlane.y);

                    //calculate the scaler velocities of each object after the collision.
                    double n_vel1_after = ((n_vel1 * (1/*ballI mass*/ - 1/*ballJ mass*/)) + (2 * 1/*ballJ mass*/ * n_vel2)) / (1/*ballJ mass*/ + 1/*ballJ mass*/);
                    double n_vel2_after = ((n_vel2 * (1/*ballJ mass*/ - 1/*ballI mass*/)) + (2 * 1/*ballJ mass*/ * n_vel1)) / (1/*ballJ mass*/ + 1/*ballJ mass*/);
                    //double velObject2Tangent_After = c_vel2;
                    //double velObject1Tangent_After = c_vel1;

                    //convert the scalers to vectors by multiplying by the normalized plane vectors.
                    Point2D.Double vec_n_vel2_after = new Point2D.Double(n_vel2_after * normalPlane.x, n_vel2_after * normalPlane.y);
                    Point2D.Double vec_c_vel2 = new Point2D.Double(c_vel2 * collisionPlane.x, c_vel2 * collisionPlane.y);
                    Point2D.Double vec_n_vel1_after = new Point2D.Double(n_vel1_after * normalPlane.x, n_vel1_after * normalPlane.y);
                    Point2D.Double vec_c_vel1 = new Point2D.Double(c_vel1 * collisionPlane.x, c_vel1 * collisionPlane.y);

                    //combine the vectors back into a single vector in world space.
                    Point2D.Double vel1_after = new Point2D.Double(vec_n_vel1_after.x + vec_c_vel1.x, vec_n_vel1_after.y + vec_c_vel1.y);
                    Point2D.Double vel2_after = new Point2D.Double(vec_n_vel2_after.x + vec_c_vel2.x, vec_n_vel2_after.y + vec_c_vel2.y);

                    //reapply the move-back from before the collision (using the post collision velocity)
                    Point2D.Double object1AdjustedPositionAfterCollision = new Point2D.Double(balls[i].getX() + vel1_after.x * millisecondsAfterCollision, balls[i].getY() + vel1_after.y * millisecondsAfterCollision);
                    Point2D.Double object2AdjustedPositionAfterCollision = new Point2D.Double(balls[j].getX() + vel2_after.x * millisecondsAfterCollision, balls[j].getY() + vel2_after.y * millisecondsAfterCollision);

                    //set the objects new positions and velocities.
                    balls[i].setX(object1AdjustedPositionAfterCollision.x);
                    balls[i].setY(object1AdjustedPositionAfterCollision.y);
                    balls[j].setX(object2AdjustedPositionAfterCollision.x);
                    balls[j].setY(object2AdjustedPositionAfterCollision.y);

                    balls[i].setVX(vel1_after.x);
                    balls[i].setVY(vel1_after.y);
                    balls[j].setVX(vel2_after.x);
                    balls[j].setVY(vel2_after.y);                   
                }
            }
        }
    }

    private double moveBackToCollisionPoint(Ball object1, Ball object2, double distanceAtFrameEnd, double collisionDistance){
        //calc the position at the start of the frame.
        double object1PosAtFrameStart_X = (object1.getX() - object1.getVX() * (double)UPDATE_INTERVAL);
        double object1PosAtFrameStart_Y = (double)(object1.getY() - object1.getVY() * (double)UPDATE_INTERVAL);
        Point2D.Double object1PosAtFrameStart = new Point2D.Double(object1PosAtFrameStart_X, object1PosAtFrameStart_Y);

        double object2PosAtFrameStart_X = (object2.getX() - object2.getVX() * (double)UPDATE_INTERVAL);
        double object2PosAtFrameStart_Y = (object2.getY() - object2.getVY() * (double)UPDATE_INTERVAL);
        Point2D.Double object2PosAtFrameStart = new Point2D.Double(object2PosAtFrameStart_X, object2PosAtFrameStart_Y);

        //calc the distance between the objects at the start of the frame.
        Point2D.Double differenceAtFrameStart = new Point2D.Double(object2PosAtFrameStart.x - object1PosAtFrameStart.x, object2PosAtFrameStart.y - object1PosAtFrameStart.y);
        double distanceAtFrameStart = Math.sqrt(differenceAtFrameStart.x*differenceAtFrameStart.x + differenceAtFrameStart.y*differenceAtFrameStart.y);

        //calculate the total distance change during the frame and the required change to reach the collision.
        double distanceTotalDelta = distanceAtFrameEnd - distanceAtFrameStart;
        double distanceDeltaToCollision = collisionDistance - distanceAtFrameStart;

        // Calculate the percentage change to the collision and after the collision.
        double percentageDeltaToCollision = distanceDeltaToCollision / distanceTotalDelta;
        double percentageDeltaAfterCollision = 1 - percentageDeltaToCollision;

        // Calculate the time before and after the collision in the frame.
        double millisecondsToCollision = (double)UPDATE_INTERVAL * percentageDeltaToCollision;
        double millisecondsAfterCollision = (double)UPDATE_INTERVAL * percentageDeltaAfterCollision;

        // Calculate and move the objects to their positions at the point of collision.
        double object1PosAtCollision_X = (object1PosAtFrameStart_X + object1.getVX() * millisecondsToCollision);
        double object1PosAtCollision_Y = (object1PosAtFrameStart_Y + object1.getVY() * millisecondsToCollision);
        Point2D.Double object1PosAtCollision = new Point2D.Double(object1PosAtCollision_X, object1PosAtCollision_Y);
        object1.setX(object1PosAtCollision.x);
        object1.setY(object1PosAtCollision.y);

        double object2PosAtCollision_X = (object2PosAtFrameStart_X + object2.getVX() * millisecondsToCollision);
        double object2PosAtCollision_Y = (object2PosAtFrameStart_Y + object2.getVY() * millisecondsToCollision);
        Point2D.Double object2PosAtCollision = new Point2D.Double(object2PosAtCollision_X, object2PosAtCollision_Y);
        object2.setX(object2PosAtCollision.x);
        object2.setY(object2PosAtCollision.y);

        return millisecondsAfterCollision;
    }

    private void updateBalls(){
        leftCountR = 0; leftCountB = 0; rightCountR = 0; rightCountB = 0;
        for(int i=0; i<BALLS_COUNT; i++){
            balls[i].update(UPDATE_INTERVAL, DIMS);
            if(balls[i].c == Color.blue){
                if(balls[i].locOnScreen.x < DIMS.width/2){
                    leftCountB++;
                }else{
                    rightCountB++;
                }
            }else{
                if(balls[i].locOnScreen.x < DIMS.width/2){
                    leftCountR++;
                }else{
                    rightCountR++;
                }
            }
        }
    }

    private void registerTimer(){
        timer = new Timer(UPDATE_INTERVAL, new ActionListener() {           
            @Override
            public void actionPerformed(ActionEvent arg0) {
                update();
                FRAME.repaint();
            }
        });
        timer.setRepeats(true);
        timer.setDelay(UPDATE_INTERVAL);
    }

    public void paintComponent(Graphics g){
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));

        for(int i=0; i<BALLS_COUNT; i++){
            g2d.setColor(balls[i].c);
            g2d.fillOval(balls[i].locOnScreen.x-Ball.radius, balls[i].locOnScreen.y-Ball.radius, Ball.radius*2, Ball.radius*2);
        }
    }

    public static void main(String[] args){
        new MaxwellsDemon();
    }

}
package letifer.com;
导入java.awt.Color;
导入java.awt.Container;
导入java.awt.Dimension;
导入java.awt.Font;
导入java.awt.Graphics;
导入java.awt.Graphics2D;
导入java.awt.RenderingHints;
导入java.awt.event.ActionEvent;
导入java.awt.event.ActionListener;
导入java.awt.geom.Point2D;
导入java.util.Random;
导入javax.swing.JFrame;
导入javax.swing.JPanel;
导入javax.swing.Timer;
公共类MaxwellsDemon扩展了JPanel{
公共静态最终长serialVersionUID=1L;
专用最终尺寸标注DIMS=新尺寸(800500);
私人最终整数更新间隔=20,球数=20;
专用最终JFrame=新JFrame(“MaxwellsDemon”);
private int leftCountR、leftCountB、righCountr、righCountb;
私人舞会[];
私有随机;
私人定时器;
公共MaxwellsDemon(){
超级();
initThis();
initFrame();
initBalls();
注册表项();
timer.start();
}
private void initThis(){
设置大小(DIMS);
挫折地面(颜色:白色);
随机=新随机();
}
私有void initFrame(){
FRAME.setContentPane(新容器(){
公共空间涂料(图g){
超级油漆(g);
Graphics2D g2d=(Graphics2D)g.create();
g2d.SetRenderingHists(新的RenderingHists(RenderingHists.KEY_ANTIALIASING,RenderingHists.VALUE_ANTIALIAS_ON));
对于(int i=0;i假设i=1,2没有碰撞,则ui和vi分别为当前时间步之前和之后的圆i的位置。对于i=1,2,ri为圆i的半径。R=r1+r2

假设圆在时间步长内以恒定速度移动。也就是说,球i的路径由pi(t)=(1-t)ui+tvi,t在[0,1]中描述,假设没有碰撞

我们可以通过求解方程| | p2(t)-p1(t)| |=r1+r2=R来检测碰撞。让我们简化这个方程

||p2(t)-p1(t)| |=R

||(1-t)(u2-u1)+t(v2-v1)| 2=R2

||(1-t)U+tV | | 2=R2,其中U=u2-u1和V=v2-v1

=R2

(1-t)+t=R2

(1-t)2+(1-t)t+t(1-t)+t2=R2

(1-t)2+2(1-t)t+t2=R2

在这一点上,应该很清楚:这只是t!中的一个二次方程,求t的可能值。如果区间[0,1]中没有实解,则不存在碰撞。如果区间[0,1]中有任何实解,则区间[0,1]中的最早(最小)解描述了碰撞时间


设T为区间[0,1]中描述的最早实解,然后为p1(T)和p2(T)是碰撞时的位置。这些位置之间的向量是碰撞的法线。

你是指圆还是椭圆?@CodesInChaos我指的是圆。所以基本上你是想说我对碰撞时间的计算是错误的?这就是为什么物体会纠结在一起?@dimaligin好吧,看看你的代码和fi弄清楚里面发生了什么并不容易。这是如何确定圆碰撞的数学理论。我认为你所采取的方法更多的是,在每一个时间步,检查彼此之间是否有圆(相交)然后将它们分开。这种方法会有稳定性问题。这种方法从一开始就不会让圆彼此相交。它假设在每个时间步的持续时间内呈线性,然后解决精确的碰撞时刻。我想你建议基本上模拟下一个时间点的时间路径(我真正想做的事)在绘制int之前,但在这种情况下,它会对性能产生相当大的影响,这是我希望避免的。@DimaAligin这种方法可以比在相交时将事物分开的方法表现得更好,实际上……如果不太麻烦的话,你能分享一个实现模拟的示例的链接吗?我想不出一个没有性能损失的实现方法。
package letifer.com;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.geom.Point2D;

public class Ball{

    public static int radius = 7;

    private Point2D.Double loc;
    private double speed;
    private double dir;
    Point locOnScreen;  
    Color c;

    public Ball(Point2D.Double loc, double speed, double dir, Color c){
        this.loc = loc;
        this.speed = speed;
        this.dir = dir;
        this.c = c;
        this.locOnScreen = new Point((int)(loc.x+0.5f), (int)(loc.y+0.5f));
    }

    public void update(int timePassed, Dimension dims){     
        loc.x = loc.x + speed * timePassed * Math.cos(dir);
        loc.y = loc.y + speed * timePassed * Math.sin(dir);
        if(loc.x <= 0 + radius){
            dir = (-dir+Math.PI+2*Math.PI)%(2*Math.PI);
        }
        if(loc.x >= dims.width - radius - 1){
            dir = (-dir+Math.PI+2*Math.PI)%(2*Math.PI);
        }
        if(loc.y <= 0 + radius){
            dir = (-dir+2*Math.PI)%(2*Math.PI);
        }
        if(loc.y >= dims.height - radius - 1){
            dir = (-dir+2*Math.PI)%(2*Math.PI);
        }   
        locOnScreen.setLocation((int)(loc.x+0.5f), (int)(loc.y+0.5f));
    }

    public double getX(){
        return loc.x;
    }

    public double getY(){
        return loc.y;
    }

    public void setX(double x){
        loc.x = x;
    }

    public void setY(double y){
        loc.y = y;
    }

    public double getVX(){
        return speed * Math.cos(dir);
    }

    public double getVY(){
        return speed * Math.sin(dir);
    }

    public void setVX(double vx){
        double vy = getVY();
        speed = Math.sqrt(vy*vy+vx*vx);
        dir = Math.atan2(vx, vy);
    }

    public void setVY(double vy){
        double vx = getVX();
        speed = Math.sqrt(vy*vy+vx*vx);
        dir = Math.atan2(vx, vy);
    }
}