Rust 2D碰撞检测库parry2d-f64的使用,高性能几何计算与物理引擎集成

Rust 2D碰撞检测库parry2d-f64的使用,高性能几何计算与物理引擎集成

Parry Logo

Parry 是一套2D和3D几何与碰撞检测库,包括 parry2d, parry3d, parry2d-f64 和 parry3d-f64。这些库由 Dimforge 组织使用Rust编程语言开发,完全免费且开源!

安装

在您的项目目录中运行以下Cargo命令:

cargo add parry2d-f64

或者在您的Cargo.toml中添加以下行:

parry2d-f64 = "0.22.0"

示例代码

下面是一个使用parry2d-f64进行2D碰撞检测的完整示例:

use parry2d_f64::{
    shape::{Ball, Cuboid},
    math::{Isometry, Vector},
    query::{self, Contact},
};

fn main() {
    // 创建一个球体和一个立方体
    let ball = Ball::new(1.0);
    let cube = Cuboid::new(Vector::new(1.0, 1.0));
    
    // 设置它们的位置
    let ball_pos = Isometry::translation(2.0, 0.0);
    let cube_pos = Isometry::identity();
    
    // 检测碰撞
    if let Some(contact) = query::contact(&ball_pos, &ball, &cube_pos, &cube, 0.0) {
        println!("碰撞检测结果:");
        println!("碰撞点: {:?}", contact.point);
        println!("碰撞法线: {:?}", contact.normal);
        println!("穿透深度: {:?}", contact.depth);
    } else {
        println!("没有检测到碰撞");
    }
    
    // 检测距离
    let distance = query::distance(&ball_pos, &ball, &cube_pos, &cube);
    println!("两者之间的距离: {}", distance);
}

物理引擎集成示例

下面是一个更完整的示例,展示如何将parry2d-f64与物理引擎集成:

use parry2d_f64::{
    shape::{Ball, Cuboid},
    math::{Isometry, Vector},
    query::{self, Contact},
    na::Point2,
};

struct PhysicsBody {
    shape: Box<dyn parry2d_f64::shape::Shape>,
    position: Isometry<f64>,
    velocity: Vector<f64>,
    mass: f64,
}

fn main() {
    // 创建物理世界中的两个物体
    let mut bodies = vec![
        PhysicsBody {
            shape: Box::new(Ball::new(1.0)),
            position: Isometry::translation(0.0, 0.0),
            velocity: Vector::new(1.0, 0.0),
            mass: 1.0,
        },
        PhysicsBody {
            shape: Box::new(Cuboid::new(Vector::new(1.0, 1.0))),
            position: Isometry::translation(5.0, 0.0),
            velocity: Vector::new(0.0, 0.0),
            mass: 2.0,
        },
    ];

    // 模拟物理世界
    for _ in 0..100 {
        // 更新位置
        for body in &mut bodies {
            body.position.translation.vector += body.velocity * 0.016; // 16ms时间步长
        }

        // 检测碰撞
        for i in 0..bodies.len() {
            for j in i+1..bodies.len() {
                if let Some(contact) = query::contact(
                    &bodies[i].position, 
                    bodies[i].shape.as_ref(),
                    &bodies[j].position,
                    bodies[j].shape.as_ref(),
                    0.0
                ) {
                    println!("物体 {} 和 {} 发生碰撞", i, j);
                    handle_collision(&mut bodies[i], &mut bodies[j], contact);
                }
            }
        }
    }
}

fn handle_collision(body1: &mut PhysicsBody, body2: &mut PhysicsBody, contact: Contact) {
    // 简化的碰撞响应 - 交换速度
    let temp_vel = body1.velocity;
    body1.velocity = body2.velocity;
    body2.velocity = temp_vel;
}

这个示例展示了如何:

  1. 创建基本的物理物体
  2. 更新它们的运动
  3. 检测碰撞
  4. 处理碰撞响应

您可以根据需要扩展这个基础框架,添加更复杂的物理特性如摩擦力、旋转、力矩等。

完整示例DEMO

下面是一个更完整的2D物理引擎示例,包含了碰撞检测、物理模拟和简单的渲染:

use parry2d_f64::{
    math::{Isometry, Vector},
    na::Point2,
    query::{self, Contact},
    shape::{Ball, Cuboid, Shape},
};
use std::f64::consts::PI;

// 物理物体结构体
struct PhysicsBody {
    shape: Box<dyn Shape>,
    position: Isometry<f64>,
    velocity: Vector<f64>,
    angular_velocity: f64,
    mass: f64,
    restitution: f64, // 弹性系数
}

// 物理世界结构体
struct PhysicsWorld {
    bodies: Vec<PhysicsBody>,
    gravity: Vector<f64>,
}

impl PhysicsWorld {
    fn new() -> Self {
        PhysicsWorld {
            bodies: Vec::new(),
            gravity: Vector::new(0.0, -9.81),
        }
    }

    fn add_body(&mut self, body: PhysicsBody) {
        self.bodies.push(body);
    }

    fn step(&mut self, dt: f64) {
        // 应用重力并更新位置
        for body in &mut self.bodies {
            body.velocity += self.gravity * dt;
            body.position.translation.vector += body.velocity * dt;
            body.position.rotation = body.position.rotation
                .rotate(body.angular_velocity * dt);
        }

        // 碰撞检测和响应
        for i in 0..self.bodies.len() {
            for j in i + 1..self.bodies.len() {
                if let Some(contact) = query::contact(
                    &self.bodies[i].position,
                    self.bodies[i].shape.as_ref(),
                    &self.bodies[j].position,
                    self.bodies[j].shape.as_ref(),
                    0.0,
                ) {
                    self.resolve_collision(i, j, contact);
                }
            }
        }
    }

    fn resolve_collision(&mut self, i: usize, j: usize, contact: Contact) {
        // 更真实的碰撞响应
        let body1 = &mut self.bodies[i];
        let body2 = &mut self.bodies[j];

        let relative_velocity = body2.velocity - body1.velocity;
        let velocity_along_normal = relative_velocity.dot(&contact.normal);

        // 如果物体正在分离,不需要处理碰撞
        if velocity_along_normal > 0.0 {
            return;
        }

        // 计算冲量
        let restitution = body1.restitution.min(body2.restitution);
        let mut impulse_magnitude = -(1.0 + restitution) * velocity_along_normal;
        impulse_magnitude /= 1.0 / body1.mass + 1.0 / body2.mass;

        let impulse = contact.normal * impulse_magnitude;

        // 应用冲量
        body1.velocity -= impulse / body1.mass;
        body2.velocity += impulse / body2.mass;

        // 简单的位置修正以避免物体重叠
        let correction = contact.normal * contact.depth * 0.2;
        body1.position.translation.vector -= correction / body1.mass;
        body2.position.translation.vector += correction / body2.mass;
    }
}

fn main() {
    let mut world = PhysicsWorld::new();

    // 添加一个静态地面
    world.add_body(PhysicsBody {
        shape: Box::new(Cuboid::new(Vector::new(10.0, 0.5))),
        position: Isometry::translation(0.0, -5.0),
        velocity: Vector::zeros(),
        angular_velocity: 0.0,
        mass: f64::INFINITY, // 无限质量表示静态物体
        restitution: 0.5,
    });

    // 添加一些动态物体
    for i in 0..5 {
        world.add_body(PhysicsBody {
            shape: if i % 2 == 0 {
                Box::new(Ball::new(0.5))
            } else {
                Box::new(Cuboid::new(Vector::new(0.5, 0.5)))
            },
            position: Isometry::translation(i as f64 * 1.5 - 3.0, 5.0),
            velocity: Vector::new(0.0, 0.0),
            angular_velocity: if i % 2 == 0 { 0.0 } else { PI / 4.0 },
            mass: 1.0,
            restitution: 0.7,
        });
    }

    // 模拟1000步
    for step in 0..1000 {
        world.step(0.016); // 16ms时间步长

        // 每100步打印一次状态
        if step % 100 == 0 {
            println!("Step {}", step);
            for (i, body) in world.bodies.iter().enumerate() {
                println!(
                    "Body {}: position = {:?}, velocity = {:?}",
                    i, body.position.translation.vector, body.velocity
                );
            }
            println!();
        }
    }
}

这个完整示例展示了:

  1. 创建物理世界和物体
  2. 处理重力、速度和位置更新
  3. 更真实的碰撞响应(包含弹性和冲量计算)
  4. 支持圆形和矩形两种形状
  5. 静态物体和动态物体的区分
  6. 简单的模拟状态输出

您可以通过添加更多物理特性来扩展这个示例,如摩擦力、空气阻力、关节约束等。


1 回复

Rust 2D碰撞检测库parry2d-f64使用指南

介绍

parry2d-f64是一个高性能的2D碰撞检测库,专注于几何计算和物理引擎集成。它是parry物理引擎生态系统的一部分,专门针对2D场景进行了优化,使用f64浮点精度进行计算。

主要特性

  • 支持多种基本几何形状的碰撞检测(圆形、矩形、多边形等)
  • 精确的接触点计算
  • 距离查询
  • 时间碰撞检测(CCD)
  • 高性能的broad-phase和narrow-phase算法
  • 与Rust物理引擎(如Rapier)兼容

完整示例代码

use parry2d::shape::{Ball, Cuboid};
use parry2d::query::{self, ShapeQuery};
use parry2d::math::{Isometry, Vector};
use parry2d::partitioning::{DBVT, DBVTLeaf};

fn main() {
    // 1. 创建几何形状
    println!("=== 创建几何形状 ===");
    let ball = Ball::new(5.0); // 半径5.0的圆形
    let cuboid = Cuboid::new(Vector::new(2.0, 3.0)); // 半边长x=2.0, y=3.0的矩形
    
    // 2. 碰撞检测
    println!("\n=== 碰撞检测 ===");
    let pos1 = Isometry::new(Vector::new(1.0, 2.0), 0.0); // 位置和旋转
    let pos2 = Isometry::new(Vector::new(3.0, 2.0), 0.0);
    
    // 检测碰撞
    let intersects = query::intersection_test(&pos1, &ball, &pos2, &cuboid).unwrap();
    println!("是否碰撞: {}", intersects);
    
    // 计算接触点
    let contact = query::contact(&pos1, &ball, &pos2, &cuboid, 0.0).unwrap();
    if let Some(contact) = contact {
        println!("接触点: {:?}", contact.point);
        println!("接触法线: {:?}", contact.normal);
        println!("穿透深度: {}", contact.dist);
    }
    
    // 3. 距离查询
    println!("\n=== 距离查询 ===");
    let distance = query::distance(&pos1, &ball, &pos2, &cuboid).unwrap();
    println!("两个形状之间的距离: {}", distance);
    
    // 计算最近点
    let closest_points = query::closest_points(&pos1, &ball, &pos2, &cuboid, 10.0).unwrap();
    match closest_points {
        query::ClosestPoints::Intersecting => println!("形状相交"),
        query::ClosestPoints::WithinMargin(p1, p2) => {
            println!("最近点1: {:?}", p1);
            println!("最近点2: {:?}", p2);
        }
        query::ClosestPoints::Disjoint => println!("形状不相交且距离超过阈值"),
    }
    
    // 4. 时间碰撞检测(CCD)
    println!("\n=== 时间碰撞检测 ===");
    let vel1 = Vector::new(1.0, 0.0);
    let vel2 = Vector::new(-1.0, 0.0);
    
    let toi = query::time_of_impact(
        &pos1,
        &vel1,
        &ball,
        &pos2,
        &vel2,
        &cuboid,
        std::f64::MAX,
        true,
    ).unwrap();
    
    if let Some(toi) = toi {
        println!("碰撞将在 {} 秒后发生", toi.toi);
        println!("碰撞时的位置: {:?}", toi.witness1);
    } else {
        println!("不会发生碰撞");
    }
    
    // 5. 性能优化 - BroadPhase
    println!("\n=== 性能优化 ===");
    let mut dbvt = DBVT::new();
    let aabb = ball.compute_aabb(&pos1);
    dbvt.insert(DBVTLeaf::new(aabb, 0)); // 0是物体ID
    println!("创建DBVT用于粗检测");
    
    // 6. 重用查询对象
    let mut contact_manifold = query::ContactManifold::new();
    query::contact_manifolds(
        &pos1,
        &ball,
        &pos2,
        &cuboid,
        &mut contact_manifold,
        false,
    );
    println!("重用ContactManifold减少内存分配");
}

示例说明

这个完整示例展示了parry2d-f64库的主要功能:

  1. 创建几何形状:创建圆形和矩形两种基本形状
  2. 碰撞检测:检测两个形状是否相交,并计算接触点信息
  3. 距离查询:计算两个形状之间的距离和最近点
  4. 时间碰撞检测:预测未来可能发生的碰撞
  5. 性能优化:使用DBVT进行粗检测和重用查询对象

运行此代码需要先在Cargo.toml中添加依赖:

[dependencies]
parry2d = { version = "0.13", features = ["f64"] }
nalgebra = "0.32"  # 用于向量和矩阵运算

总结

parry2d-f64提供了强大的2D碰撞检测功能,这个完整示例展示了其主要API的使用方法。通过合理使用这些功能,可以构建出高效可靠的碰撞检测系统。

回到顶部