use bonked2d::{
make_shared,
object::{
kinematic_body::KinematicBody, static_body::StaticBody, trigger_area::TriggerArea, Object,
},
world::World,
Mask,
};
use macroquad::{miniquad::window, prelude::*};
use parry2d::{
math::{Isometry, Point, Real, Vector},
shape::{Ball, Capsule, Cuboid, Shape},
};
use std::sync::Arc;
#[macroquad::main("2D")]
async fn main() {
const ZOOM: f32 = 0.1;
let mut world = build_world();
loop {
let delta = get_frame_time();
let input = Inputs::read();
clear_background(LIGHTGRAY);
let size = window::screen_size();
let ratio = size.0 / size.1;
set_camera(&Camera2D {
target: Vec2::new(0.0, 3.0),
zoom: Vec2::new(ZOOM, ZOOM * -ratio),
..Default::default()
});
macro_rules! draw {
($body:ident, $color:ident) => {
let body = $body.read();
let shape = AShape::new(body.shape());
let pos = to_glam(body.isometry().translation.vector);
shape.draw(pos, $color);
};
}
for body in world.statics().iter() {
draw!(body, BLUE);
}
for body in world.kinematics().iter() {
draw!(body, RED);
}
for body in world.triggers().iter() {
draw!(body, GREEN);
}
world.update(delta);
if is_quit_requested() {
break;
}
next_frame().await
}
}
enum AShape<'s> {
None,
Box(&'s Cuboid),
Ball(&'s Ball),
Capsule(&'s Capsule),
}
impl<'s> AShape<'s> {
fn new(shape: &'s dyn Shape) -> Self {
if let Some(shape) = shape.as_cuboid() {
Self::Box(shape)
} else if let Some(shape) = shape.as_ball() {
Self::Ball(shape)
} else if let Some(shape) = shape.as_capsule() {
Self::Capsule(shape)
} else {
Self::None
}
}
fn draw(&'s self, pos: Vec2, color: Color) {
const THICKNESS: f32 = 0.05;
match self {
Self::Box(shape) => {
let half = to_glam(shape.half_extents);
let start = pos - half;
let size = half * 2.0;
draw_rectangle_lines(start.x, start.y, size.x, size.y, THICKNESS, color);
}
Self::Ball(shape) => {
draw_circle_lines(pos.x, pos.y, shape.radius, THICKNESS, color);
}
Self::Capsule(shape) => {
let a = pos + to_glam(shape.segment.a.coords);
let b = pos + to_glam(shape.segment.b.coords);
draw_circle_lines(a.x, a.y, shape.radius, THICKNESS, color);
draw_circle_lines(b.x, b.y, shape.radius, THICKNESS, color);
}
_ => {}
}
}
}
fn build_world() -> World<bool> {
const EPSILON: Real = 0.0001;
let mut world = World::with_capacity(EPSILON, 2, 1, 1);
world.add_static({
let (shape, isometry) = new_box([0.0, -0.5], [20.0, 1.0]);
make_shared(StaticBody::new(shape, isometry, (), Mask::MAX))
});
world.add_kinematic({
let (shape, isometry) = new_capsule([0.0, 10.0], 1.0, 2.0);
let mut body = KinematicBody::new(shape, isometry, (), Mask::MAX, Mask::MAX, 1.0, false);
body.velocity.y = -1.0;
make_shared(body)
});
world.add_kinematic({
let (shape, isometry) = new_capsule([0.5, 15.0], 1.0, 2.0);
let mut body = KinematicBody::new(shape, isometry, (), Mask::MAX, Mask::MAX, 1.0, false);
body.velocity.y = -1.5;
make_shared(body)
});
world.add_trigger({
let (shape, isometry) = new_box([5.0, 2.5], [5.0, 5.0]);
make_shared(TriggerArea::new(
shape,
isometry,
false,
Mask::MAX,
|trigger: &mut TriggerArea<bool>, _| {
let flag = trigger.payload_mut();
if !*flag {
*flag = true;
println!("Object entered area");
}
},
))
});
world
}
struct Inputs {
motion: IVec2,
jump: bool,
}
impl Inputs {
fn read() -> Self {
let mut motion = IVec2::ZERO;
if is_key_down(KeyCode::Left) {
motion.x -= 1;
}
if is_key_down(KeyCode::Right) {
motion.x += 1;
}
if is_key_down(KeyCode::Up) {
motion.y -= 1;
}
if is_key_down(KeyCode::Down) {
motion.y += 1;
}
Inputs {
motion,
jump: is_key_pressed(KeyCode::Space),
}
}
}
fn new_box(pos: V2, size: V2) -> (Arc<dyn Shape>, Isometry<Real>) {
let shape = Arc::new(Cuboid::new(to_nalgebra(size) * 0.5));
let pos = Isometry::new(to_nalgebra(pos), 0.0);
(shape, pos)
}
fn new_ball(pos: V2, diameter: f32) -> (Arc<dyn Shape>, Isometry<Real>) {
let shape = Arc::new(Ball::new(diameter * 0.5));
let pos = Isometry::new(to_nalgebra(pos), 0.0);
(shape, pos)
}
fn new_capsule(pos: V2, diameter: f32, height: f32) -> (Arc<dyn Shape>, Isometry<Real>) {
let radius = diameter * 0.5;
let half = (height * 0.5 - radius).max(0.0);
let a = Point::new(0.0, half);
let b = Point::new(0.0, -half * 0.5);
let shape = Arc::new(Capsule::new(a, b, radius));
let pos = Isometry::new(to_nalgebra(pos), 0.0);
(shape, pos)
}
type V2 = [Real; 2];
#[inline]
fn to_glam(v: Vector<Real>) -> Vec2 {
Vec2::new(v.x as f32, v.y as f32)
}
#[inline]
fn to_nalgebra(v: V2) -> Vector<Real> {
Vector::new(v[0], v[1])
}