刚体碰撞检测与响应(C++实现)

本文实现一个经典的物理算法:刚体碰撞检测与响应。这个算法用于检测两个刚体(如矩形或圆形)是否发生碰撞,并在碰撞时更新它们的速度和位置。我们将使用C++来实现这个算法,并结合**边界框(Bounding Box)**数据结构来管理刚体的碰撞检测。


1. 问题描述

刚体碰撞检测与响应是物理引擎中的核心问题。我们将实现一个简单的二维刚体碰撞系统,支持矩形和圆形刚体的碰撞检测,并在碰撞时应用基本的物理响应(如动量守恒)。


2. 数据结构设计

  • 刚体(RigidBody):包含位置、速度、质量、形状(矩形或圆形)等属性。
  • 边界框(BoundingBox):用于快速检测刚体是否可能发生碰撞。
  • 系统(CollisionSystem):管理所有刚体,并检测和处理碰撞。

3. 算法实现

#include <iostream>
#include <vector>
#include <cmath>// 定义二维向量
struct Vector2 {double x, y;Vector2(double x = 0, double y = 0) : x(x), y(y) {}Vector2 operator+(const Vector2& other) const {return Vector2(x + other.x, y + other.y);}Vector2 operator-(const Vector2& other) const {return Vector2(x - other.x, y - other.y);}Vector2 operator*(double scalar) const {return Vector2(x * scalar, y * scalar);}double length() const {return std::sqrt(x * x + y * y);}
};// 刚体形状基类
struct Shape {virtual bool collidesWith(const Shape* other) const = 0;virtual ~Shape() = default;
};// 圆形刚体
struct Circle : Shape {double x, y; // 圆形的位置double radius;Circle(double x, double y, double r) : x(x), y(y), radius(r) {}bool collidesWith(const Shape* other) const override {if (const Circle* circle = dynamic_cast<const Circle*>(other)) {double distance = (Vector2(x, y) - Vector2(circle->x, circle->y)).length();return distance <= (radius + circle->radius);}return false;}
};// 矩形刚体
struct Rectangle : Shape {double x, y; // 矩形的位置double width, height;Rectangle(double x, double y, double w, double h) : x(x), y(y), width(w), height(h) {}bool collidesWith(const Shape* other) const override {if (const Rectangle* rect = dynamic_cast<const Rectangle*>(other)) {return !(x + width < rect->x || rect->x + rect->width < x ||y + height < rect->y || rect->y + rect->height < y);}return false;}
};// 刚体类
struct RigidBody {Vector2 position;Vector2 velocity;double mass;Shape* shape;RigidBody(Vector2 pos, Vector2 vel, double m, Shape* s): position(pos), velocity(vel), mass(m), shape(s) {// 更新形状的位置if (Circle* circle = dynamic_cast<Circle*>(shape)) {circle->x = pos.x;circle->y = pos.y;} else if (Rectangle* rect = dynamic_cast<Rectangle*>(shape)) {rect->x = pos.x;rect->y = pos.y;}}void update(double dt) {position = position + velocity * dt;// 更新形状的位置if (Circle* circle = dynamic_cast<Circle*>(shape)) {circle->x = position.x;circle->y = position.y;} else if (Rectangle* rect = dynamic_cast<Rectangle*>(shape)) {rect->x = position.x;rect->y = position.y;}}
};// 碰撞系统类
struct CollisionSystem {std::vector<RigidBody*> bodies;void addBody(RigidBody* body) {bodies.push_back(body);}void checkCollisions() {for (size_t i = 0; i < bodies.size(); ++i) {for (size_t j = i + 1; j < bodies.size(); ++j) {if (bodies[i]->shape->collidesWith(bodies[j]->shape)) {resolveCollision(bodies[i], bodies[j]);}}}}void resolveCollision(RigidBody* body1, RigidBody* body2) {// 简单动量守恒碰撞响应Vector2 relativeVelocity = body2->velocity - body1->velocity;Vector2 normal = (body2->position - body1->position).length() > 0? (body2->position - body1->position) * (1.0 / (body2->position - body1->position).length()): Vector2(1, 0);double velocityAlongNormal = relativeVelocity.x * normal.x + relativeVelocity.y * normal.y;if (velocityAlongNormal > 0) return; // 已经分离,不需要处理double e = 1.0; // 恢复系数(完全弹性碰撞)double j = -(1 + e) * velocityAlongNormal;j /= (1 / body1->mass + 1 / body2->mass);Vector2 impulse = normal * j;body1->velocity = body1->velocity - impulse * (1 / body1->mass);body2->velocity = body2->velocity + impulse * (1 / body2->mass);}void update(double dt) {for (auto body : bodies) {body->update(dt);}checkCollisions();}
};int main() {// 创建碰撞系统CollisionSystem system;// 创建刚体RigidBody* body1 = new RigidBody(Vector2(0, 0), Vector2(1, 0), 1.0, new Circle(0, 0, 1.0));RigidBody* body2 = new RigidBody(Vector2(3, 0), Vector2(-1, 0), 1.0, new Circle(3, 0, 1.0));system.addBody(body1);system.addBody(body2);// 模拟double dt = 0.01;for (int i = 0; i < 100; i++) {system.update(dt);std::cout << "Body1 Position: (" << body1->position.x << ", " << body1->position.y << ")\n";std::cout << "Body2 Position: (" << body2->position.x << ", " << body2->position.y << ")\n";}// 释放内存delete body1->shape;delete body2->shape;delete body1;delete body2;return 0;
}

4. 代码解释

  1. Vector2:表示二维向量,支持加减乘等操作。
  2. Shape:刚体形状基类,支持圆形和矩形的碰撞检测。
  3. CircleRectangle:分别表示圆形和矩形刚体,实现碰撞检测逻辑。
  4. RigidBody:表示刚体,包含位置、速度、质量和形状。
  5. CollisionSystem:管理所有刚体,检测碰撞并应用碰撞响应。
  6. main函数:创建系统、刚体,并进行模拟。

5. 运行结果

程序会输出两个刚体的位置随时间的变化,模拟它们的碰撞和运动。

在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/75053.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

常用的国内镜像源

常见的 pip 镜像源 阿里云镜像&#xff1a;https://mirrors.aliyun.com/pypi/simple/ 清华大学镜像&#xff1a;https://pypi.tuna.tsinghua.edu.cn/simple 中国科学技术大学镜像&#xff1a;https://pypi.mirrors.ustc.edu.cn/simple/ 豆瓣镜像&#xff1a;https://pypi.doub…

鸿蒙小案例-京东登录

效果 代码实现 Entry Component struct Index {build() {Column() {Row() {Image($r(app.media.jd_cancel)).width(20)Text(帮助).fontSize(16).fontColor(#666)}.width(100%).justifyContent(FlexAlign.SpaceBetween)Image($r(app.media.jd_logo)).height(250).width(250)// …

《 Scikit-learn与MySQL的深度协同:构建智能数据生态系统的架构哲学》

在机器学习工程实践中&#xff0c;数据存储与模型训练的割裂始终是制约算法效能的关键瓶颈。Scikit-learn作为经典机器学习库&#xff0c;其与MySQL的深度协同并非简单的数据管道连接&#xff0c;而是构建了一个具备自组织能力的智能数据生态系统。这种集成突破了传统ETL流程的…

华为AI-agent新作:使用自然语言生成工作流

论文标题 WorkTeam: Constructing Workflows from Natural Language with Multi-Agents 论文地址 https://arxiv.org/pdf/2503.22473 作者背景 华为&#xff0c;北京大学 动机 当下AI-agent产品百花齐放&#xff0c;尽管有ReAct、MCP等框架帮助大模型调用工具&#xff0…

关于软件bug描述

软件缺陷&#xff08;Defect&#xff09;&#xff0c;常常又被叫做Bug。 所谓软件缺陷&#xff0c;即为计算机软件或程序中存在的某种破坏正常运行能力的问题、错误&#xff0c;或者隐藏的功能缺陷。缺陷的存在会导致软件产品在某种程度上不能满足用户的需要。IEEE729-1983对缺…

【元表 vs 元方法】

元表 vs 元方法 —— 就像“魔法书”和“咒语”的关系 1. 元表&#xff08;Metatable&#xff09;&#xff1a;魔法书 是什么&#xff1f; 元表是一本**“规则说明书”**&#xff0c;它本身是一个普通的 Lua 表&#xff0c;但可以绑定到其他表上&#xff0c;用来定义这个表应该…

Spring Boot 通过全局配置去除字符串类型参数的前后空格

1、问题 避免前端输入的字符串参数两端包含空格&#xff0c;通过统一处理的方式&#xff0c;trim掉空格 2、实现方式 /*** 去除字符串类型参数的前后空格* author yanlei* since 2022-06-14*/ Configuration AutoConfigureAfter(WebMvcAutoConfiguration.class) public clas…

C语言核心知识点整理:结构体对齐、预处理、文件操作与Makefile

目录 结构体的字节对齐预处理指令详解文件操作基础Makefile自动化构建总结 1. 结构体的字节对齐 字节对齐原理 内存对齐&#xff1a;CPU访问内存时&#xff0c;对齐的地址能提高效率。操作系统要求变量按类型大小对齐。对齐规则&#xff1a; 每个成员的起始地址必须是min(成…

VBA+BOS单据+插件,解决计划任务跟踪的问题之二:导入ERP

第二步&#xff0c;就是要将拆分好的任务导入ERP了 1、将建一个BOS单据叫“任务池”&#xff0c;大概是这样的 然后在拆分工具中进行导数据&#xff0c;点击“数据导出准备”&#xff0c;跳转到“导入ERP”界面&#xff0c;然后点“获取数据”&#xff0c;将拆分好的数据转过来…

使用uglifyjs对静态引入的js文件进行压缩

前言 因为有时候js文件没有npm包&#xff0c;或者需要修改&#xff0c;只能引入静态的js&#xff0c;那么这个时候就可以对js进行压缩了。我其实想通过vite、webpack等插件进行压缩的&#xff0c;可是他都不能定位到public目录下面的文件&#xff0c;所以我只能自己压缩了。编…

蓝桥杯 web 水果拼盘 (css3)

做题步骤&#xff1a; 看结构&#xff1a;html 、css 、f12 分析: f12 查看元素&#xff0c;你会发现水果的高度刚好和拼盘的高度一样&#xff0c;每一种水果的盘子刚好把页面填满了&#xff0c;所以咱们就只要让元素竖着排列&#xff0c;加上是竖着&#xff0c;排不下的换行…

差分音频转单端音频单电源方案

TI LMV321介绍 TI的LMV321是单通道的低压轨到轨输出运算放大器&#xff0c;适用于需要低工作压、节省空间和低成本的应用。 其中&#xff0c;芯片设计中的轨到轨输出&#xff08;Rail-to-Rail Output&#xff09; 是指通过特定的电路设计&#xff0c;使芯片&#xff08;如运算…

Pandas 库

Pandas 是一个开源的数据分析和数据处理库&#xff0c;它是基于 Python 编程语言的。 Pandas 提供了易于使用的数据结构和数据分析工具&#xff0c;特别适用于处理结构化数据&#xff0c;如表格型数据 Pandas 是数据科学和分析领域中常用的工具之一&#xff0c;它使得用户能够…

Vue 3 的<Teleport>功能与用法

Vue 3 的 <Teleport> 功能与用法 1. 基本用法 <Teleport> 是 Vue 3 的一个内置组件&#xff0c;允许将组件的内容渲染到 DOM 中的任意位置&#xff0c;而不改变其逻辑结构。以下是基本用法&#xff1a; 定义目标 DOM 元素&#xff1a;<div id"teleport-…

MySQL随机获取记录之方法(The Method of Randomly Obtaining Records in MySQL)

MySQL中如何随机获取一条记录 随机获取一条记录是在数据库查询中常见的需求&#xff0c;特别在需要展示随机内容或者随机推荐的场景下。在 MySQL 中&#xff0c;有多种方法可以实现随机获取一条记录&#xff0c;每种方法都有其适用的情况和性能特点。在本文中&#xff0c;我们将…

synchronized锁升级详解

synchronized锁升级详解 synchronized是Java中实现线程同步的关键字&#xff0c;它在JVM内部实现了锁的升级机制&#xff0c;从偏向锁到轻量级锁再到重量级锁&#xff0c;这种优化是为了减少锁操作带来的性能开销。 1. 锁的四种状态 Java对象头中的Mark Word会记录锁的状态&…

C++函数如何返回多个参数

在编程中&#xff0c;我们经常会遇到需要函数返回多个值的场景。虽然 C 函数不能直接返回多个参数&#xff0c;但通过一些间接的方法&#xff0c;我们可以轻松实现这一需求。本文将详细介绍几种常见的实现方式&#xff0c;并分析它们的优缺点和适用场景。 1. 引言 在 C 中&…

最新版PhpStorm超详细图文安装教程,带补丁包(2025最新版保姆级教程)

目录 前言 一、PhpStorm最新版下载 二、PhpStorm安装 三、PhpStorm补丁 四、运行PhpStorm 前言 PhpStorm 是 JetBrains 公司推出的 专业 PHP 集成开发环境&#xff08;IDE&#xff09;&#xff0c;专为提升 PHP 开发效率设计。其核心功能包括智能代码补全、实时语法错误检…

【杂谈】Godot4.4导出到Android平台(正式导出)

学博而后可约&#xff0c;事历而后知要。 目录 一、准备二、Gradle构建三、配置Java SDK四、配置Android SDK五、配置密钥 一、准备 本文在前文【杂谈】Godot4.4导出到安卓平台&#xff08;调试导出&#xff09;的基础上&#xff0c;进行正式导出。调试导出并不是真正的编译导…

隔行换色总结

功能效果展示&#xff1a; 第一种思路&#xff1a; 使用数组&#xff0c;将数组的内容渲染到页面上&#xff0c;序号也就是将数组的下标输出到第一个td上&#xff0c;将数组的内容输出到第二个td上&#xff0c;&#xff08;使用拼接字符串&#xff09; 具体操作&#xff1a; …