弹性碰撞后速度方向_Java实现小球间的弹性碰撞(考虑小球质量)

知乎视频​www.zhihu.com

起因

最近在做一个类似坦克大战的局域网联机版Demo,其中炮弹想做成可以弹性碰撞的球体(滑稽)。然后在网上只找到了JS、C#、C实现的代码,所以我参考它们用Java重新实现了一遍(本人数学比较渣,花了一天时间...)。

参考链接 https://www.jianshu.com/p/02ecbbb6afeb

e06497ce-bd23-eb11-8da9-e4434bdf6706.png

小球弹性碰撞原理

将两个小球的速度分别在球心线(连接两球心)和垂直球心线上投影得到它们的分速度(球心线上有:球1分速度、球2分速度;垂直球心线上有:球1分速度、球2分速度)。

垂直球心线上的分速度不会影响碰撞过程, 所以我们只需处理球心线上的两个分速度了(so easy.png)。

如果两个小球质量相等的话,事情将会变得非常简单!只需交换它们在球心线上的分速度就ok了,最后将它们各自的球心线分速度和垂直球心线分速度合并,就是它们碰撞后的速度了。

至于质量不相等,我就贴个公式吧,你们自己去体会体会(我就看一眼)。

// 表示小球a的球心线分速度, 同理; 表示小球a的质量, 同理
=( •( - )+2• • )/( + )
=( •( - )+2• • )/( + )

最后是实现代码

        /**
         * 两小球弹性碰撞
         * <p>
         * 参考链接: https://www.jianshu.com/p/02ecbbb6afeb
         */
        private static void ballCollide(MovableUnit ball1, MovableUnit ball2) {
            // 初始速度向量
            Vector2d ball1SpeedInitial = ball1.getVector();
            Vector2d ball2SpeedInitial = ball2.getVector();

            // 球心方向单位向量
            final double radian = computeRadian(ball1.getCentrePosition(), ball2.getCentrePosition());
            Vector2d horizontalVector = getVector(radian, 1);

            // 垂直球心方向单位向量
            Vector2d perpendicularVector = getVector(radian + Constant.HALF_PI, 1);

            // 速度在球心向量上的分速度投影长度
            double ball1SpeedHorizontalProjectionLength = ball1SpeedInitial.dotProduct(horizontalVector);
            double ball2SpeedHorizontalProjectionLength = ball2SpeedInitial.dotProduct(horizontalVector);

            // 速度在垂直球心向量上的分速度投影长度
            double ball1SpeedPerpendicularProjectionLength = ball1SpeedInitial.dotProduct(perpendicularVector);
            double ball2SpeedPerpendicularProjectionLength = ball2SpeedInitial.dotProduct(perpendicularVector);

            // 碰撞后球心方向上的分速度投影长度
            double ball1SpeedHorizontalProjectionFinalLength;
            double ball2SpeedHorizontalProjectionFinalLength;
            if (ball1.weight == ball2.weight) {
                // 质量相等
                ball1SpeedHorizontalProjectionFinalLength = ball2SpeedHorizontalProjectionLength;
                ball2SpeedHorizontalProjectionFinalLength = ball1SpeedHorizontalProjectionLength;
            } else {
                // 质量不相等
                ball1SpeedHorizontalProjectionFinalLength = (ball1SpeedHorizontalProjectionLength * (ball1.weight - ball2.weight) + 2 * ball2.weight * ball2SpeedHorizontalProjectionLength)
                        / (ball1.weight + ball2.weight);
                ball2SpeedHorizontalProjectionFinalLength = (ball2SpeedHorizontalProjectionLength * (ball2.weight - ball1.weight) + 2 * ball1.weight * ball1SpeedHorizontalProjectionLength)
                        / (ball1.weight + ball2.weight);
            }

            // 碰撞后球心方向上的分速度向量
            Vector2d ball1SpeedHorizontalProjectionFinalVector = horizontalVector.multiply(ball1SpeedHorizontalProjectionFinalLength);
            Vector2d ball2SpeedHorizontalProjectionFinalVector = horizontalVector.multiply(ball2SpeedHorizontalProjectionFinalLength);

            // 碰撞后垂直球心方向上的分速度向量
            Vector2d ball1SpeedPerpendicularProjectionVector = perpendicularVector.multiply(ball1SpeedPerpendicularProjectionLength);
            Vector2d ball2SpeedPerpendicularProjectionVector = perpendicularVector.multiply(ball2SpeedPerpendicularProjectionLength);

            // 两个球最终的速度向量
            Vector2d ball1SpeedFinalVector = ball1SpeedHorizontalProjectionFinalVector.add(ball1SpeedPerpendicularProjectionVector);
            Vector2d ball2SpeedFinalVector = ball2SpeedHorizontalProjectionFinalVector.add(ball2SpeedPerpendicularProjectionVector);

            // 更新速度
            applyVector(ball1SpeedFinalVector, ball1);
            applyVector(ball2SpeedFinalVector, ball2);
        }


/**
 * 平面向量工具类
 * <p>
 * 参考链接: https://www.cnblogs.com/vokie/p/3602063.html
 */
public class Vector2d {
    private double x;
    private double y;

    public Vector2d() {
        x = 0;
        y = 0;
    }

    public Vector2d(double x, double y) {
        this.x = x;
        this.y = y;
    }

    //获取弧度
    public double getRadian() {
        return Math.atan2(y, x);
    }

    //获取角度
    public double getAngle() {
        return getRadian() / Math.PI * 180;
    }

    @Override
    public Vector2d clone() {
        return new Vector2d(x, y);
    }

    /**
     * 获取长度
     */
    public double getLength() {
        return Math.sqrt(getLengthSQ());
    }

    public double getLengthSQ() {
        return x * x + y * y;
    }

    //向量置零
    public Vector2d Zero() {
        x = 0;
        y = 0;
        return this;
    }

    public boolean isZero() {
        return x == 0 && y == 0;
    }

    //向量的长度设置为我们期待的value
    public void setLength(double value) {
        double _angle = getAngle();
        x = Math.cos(_angle) * value;
        y = Math.sin(_angle) * value;
    }

    //向量的标准化(方向不变,长度为1)
    public Vector2d normalize() {
        double length = getLength();
        x = x / length;
        y = y / length;
        return this;
    }

    //是否已经标准化
    public boolean isNormalized() {
        return getLength() == 1.0;
    }

    //向量的方向翻转
    public Vector2d reverse() {
        x = -x;
        y = -y;
        return this;
    }

    //2个向量的数量积(点积)
    public double dotProduct(Vector2d v) {
        return x * v.x + y * v.y;
    }

    //2个向量的向量积(叉积)
    public double crossProduct(Vector2d v) {
        return x * v.y - y * v.x;
    }

    //计算2个向量的夹角弧度
    //参考点积公式:v1 * v2 = cos<v1,v2> * |v1| *|v2|
    public static double radianBetween(Vector2d v1, Vector2d v2) {
        if (!v1.isNormalized()) v1 = v1.clone().normalize(); // |v1| = 1
        if (!v2.isNormalized()) v2 = v2.clone().normalize(); // |v2| = 1
        return Math.acos(v1.dotProduct(v2));
    }

    //弧度 = 角度乘以PI后再除以180、 推理可得弧度换算角度的公式
    //弧度转角度
    public static double radian2Angle(double radian) {
        return radian / Math.PI * 180;
    }

    //向量加
    public Vector2d add(Vector2d v) {
        return new Vector2d(x + v.x, y + v.y);
    }

    //向量减
    public Vector2d subtract(Vector2d v) {
        return new Vector2d(x - v.x, y - v.y);
    }

    //向量乘
    public Vector2d multiply(double value) {
        return new Vector2d(x * value, y * value);
    }

    //向量除
    public Vector2d divide(double value) {
        return new Vector2d(x / value, y / value);
    }
}

如果对这个游戏Demo感兴趣, 或者此分享对你有帮助,请给我的GitHub来个star吧!

Yhaobo/TankWarMagicChangeOnline​github.com
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值