qq_43478653 2021-09-07 11:48 采纳率: 25%
浏览 49
已结题

关于使用动量定理及能量守恒定理求解C语言版反弹球中小球重叠的问题

问题

  1. 小球重叠。当三球或多球碰撞时。
  2. 速度骤升。

代码

注:关键代码见115-116、125-141行。

#include <graphics.h>
#include <stdlib.h>
#include <conio.h>
#include <vector>
#include <utility>
#include <time.h>

using namespace std;

#define Width 640
#define Height 480
#define COMMON_RADIUS 10
#define BALL_VELOCITY 1
#define MAX_BALL_COUNT 40

class mycircle {
public:
    // 球半径
    int radius;
    // 球位置
    int circle_x;// x, 竖
    int circle_y;// y, 横
    // 球速度
    int velocity_x;// x速度
    int velocity_y;// y速度
    float absolute_move_distance;// 移动一次的绝对距离

    mycircle(int x = Width / 2, int y = Height / 2, int r = COMMON_RADIUS, int v_x = BALL_VELOCITY, int v_y = BALL_VELOCITY) {
        circle_x = x;
        circle_y = y;
        radius = r;
        velocity_x = (rand() % 2 == 0) ? v_x : -v_x;
        velocity_y = (rand() % 2 == 0) ? v_y : -v_y;
        absolute_move_distance = (float)sqrt(pow(velocity_x, 2) + pow(velocity_y, 2));
    }

    void move() {
        // 拉回到可行区域
        if (circle_x < radius) circle_x = radius;
        if (circle_x > (Width - radius)) circle_x = Width - radius;
        if (circle_y < radius) circle_y = radius;
        if (circle_y > (Height - radius)) circle_y = Height - radius;

        /*方法一
        int new_circle_x = min(max(0 + radius, circle_x + velocity_x), Width - radius);
        int new_circle_y = min(max(0 + radius, circle_y + velocity_y), Height - radius);
        // 判断反向情况
        if ((velocity_x < 0 && new_circle_x >= circle_x) || // 向左撞墙
            (velocity_x > 0 && new_circle_x <= circle_x)) {// 向右撞墙
            velocity_x = -velocity_x;
        }
        if (velocity_y < 0 && new_circle_y >= circle_y || // 向上撞墙
            velocity_y > 0 && new_circle_y <= circle_y) {// 向下撞墙
            velocity_y = -velocity_y;
        }
        circle_x = new_circle_x;
        circle_y = new_circle_y;
        */
        /*方法二*/
        if ((circle_x + velocity_x < radius) || (Width - radius < circle_x + velocity_x)) {
            velocity_x = -velocity_x;
        }
        if ((circle_y + velocity_y < radius) || (Height - radius < circle_y + velocity_y)) {
            velocity_y = -velocity_y;
        }
        circle_x += velocity_x;
        circle_y += velocity_y;
    }
};

int main() {
    //mycircle b[5] = { mycircle(10, 10, 5), mycircle(50, 50, 10), mycircle(100, 100, 15), mycircle(150, 150, 20), mycircle(200, 200, 25) };
    //vector<mycircle> balls(b, b + 5);
    srand((unsigned)time(NULL));
    vector<mycircle> balls;
    for (int i = 0; i < MAX_BALL_COUNT; i++) {
        mycircle c(rand() % Width, rand() % Height);
        bool is_fit = true;
        for (int j = 0; j < balls.size(); j++) {
            double dis = pow(c.circle_x - balls[j].circle_x, 2) + pow(c.circle_y - balls[j].circle_y, 2);
            if (dis <= 4 * COMMON_RADIUS * COMMON_RADIUS) {
                i--;
                is_fit = false;
                break;
            }
        }
        if (is_fit)
            balls.push_back(c);
    }

    initgraph(Width, Height);
    setbkcolor(WHITE);
    cleardevice();

    BeginBatchDraw();// 开始批量绘图,暂不输出到屏幕
    while (true) {
        // 清除当前位置
        setcolor(WHITE);
        setfillcolor(WHITE);
        for (int i = 0; i < balls.size(); i++) {
            fillcircle(balls[i].circle_x, balls[i].circle_y, balls[i].radius);
        }

        // 所有小球移动
        for (int i = 0; i < balls.size(); i++) {
            balls[i].move();
        }
        // 判断碰撞
        // 1. 初始化二维数组
        vector<vector<pair<double, int>>> min_dis(balls.size(), vector<pair<double, int>>(0));// 最小距离二维数组,[0]代表距当前元素的最短距离,[1]
        // 2. 计算距离每个球最近的球的距离和下标
        for (int i = 0; i < balls.size(); i++) {
            for (int j = 0; j < balls.size(); j++) {
                if (i != j) {
                    double dis = pow(balls[i].circle_x - balls[j].circle_x, 2) + pow(balls[i].circle_y - balls[j].circle_y, 2);
                    double collision_dis = pow(2 * COMMON_RADIUS + balls[i].absolute_move_distance + balls[j].absolute_move_distance, 2);// 公式=(2R+两球的absolute_move_distance)^2。注:加上absolute_move_distance是为了防止两球下一次移动发生重合
                    if (dis < collision_dis) {// 等于或小于碰撞阈值则发生碰撞
                        min_dis[i].push_back(pair<double, int>(dis, j));// 记录距i的距离超过阈值的球的距离dis和下标j
                    }
                }
            }
        }
        // 3. 计算新速度
        vector<mycircle> tmp_balls(balls);
        for (int i = 0; i < min_dis.size(); i++) {
            // 3.1 判断i球是否需要重置速度
            if (min_dis[i].size() == 0)
                continue;
            // 3.2 计算关于i球的所有碰撞球的合并速度,即将所有待碰撞小球合并为一个球
            // 3.2.1 根据动量(dl)定理求出速度方向,根据动能(dn)定理求出速度大小
            double new_vx_dl = 0, new_vy_dl = 0, new_vx_dn = 0, new_vy_dn = 0;
            for (const pair<double, int>& e : min_dis[i]) {// 多球的完全弹性碰撞
                new_vx_dl += tmp_balls[e.second].velocity_x;
                new_vy_dl += tmp_balls[e.second].velocity_y;
                new_vx_dn += pow(tmp_balls[e.second].velocity_x, 2);
                new_vy_dn += pow(tmp_balls[e.second].velocity_y, 2);
            }
            // 3.2.2 交换速度大小和方向
            balls[i].velocity_x = ((new_vx_dl > 0) ? 1 : -1) * sqrt(new_vx_dn);// 置球的速度为新球速度
            balls[i].velocity_y = ((new_vy_dl > 0) ? 1 : -1) * sqrt(new_vy_dn);
        }
        // 显示当前位置
        setcolor(BLACK);
        for (int i = 0; i < balls.size(); i++) {
            switch (i % 3) {
            case 0: setfillcolor(RGB(255, 0, 0)); break;
            case 1: setfillcolor(RGB(0, 255, 0)); break;
            case 2: setfillcolor(RGB(0, 0, 255)); break;
            }
            fillcircle(balls[i].circle_x, balls[i].circle_y, balls[i].radius);
        }
        FlushBatchDraw();// 立即绘图
        // 暂停
        Sleep(2);
    }
    EndBatchDraw();// 结束批量绘图
    _getch();
    closegraph();
    return 0;
}

说明

  • 代码采用物理的解法。
  • 碰撞阈值=两球半径之和 + 两球移动距离之和。
  • 写回答

0条回答 默认 最新

    报告相同问题?

    问题事件

    • 系统已结题 9月15日
    • 修改了问题 9月7日
    • 创建了问题 9月7日

    悬赏问题

    • ¥15 请提供一个符合要求的网页链接。
    • ¥20 用HslCommunication 连接欧姆龙 plc有时会连接失败。报异常为“未知错误”
    • ¥15 网络设备配置与管理这个该怎么弄
    • ¥20 机器学习能否像多层线性模型一样处理嵌套数据
    • ¥20 西门子S7-Graph,S7-300,梯形图
    • ¥50 用易语言http 访问不了网页
    • ¥50 safari浏览器fetch提交数据后数据丢失问题
    • ¥15 matlab不知道怎么改,求解答!!
    • ¥15 永磁直线电机的电流环pi调不出来
    • ¥15 用stata实现聚类的代码