Skip to main content

physics_simulation/
physics_simulation.rs

1//! This example is a show off about physics.
2//! Making use of the velocity and collision components, two shapes (circles) are moving and colliding around the window.
3//! The collision happens between the two shapes and with the borders of the window.
4
5use lotus_engine::*;
6use rand::{RngExt, rng, rngs::ThreadRng};
7
8#[derive(Clone, Component)]
9struct Object();
10
11#[derive(Clone, Component)]
12struct Border();
13
14your_game!(
15    WindowConfiguration::default(),
16    setup,
17    update
18);
19
20fn setup(context: &mut Context) {
21    let circle: Circle = Circle::new(64, 0.5);
22    let red_object: Shape = Shape::new(Orientation::Horizontal, GeometryType::Circle(circle.clone()), Color::by_option(ColorOption::Red));
23    let blue_object: Shape = Shape::new(Orientation::Horizontal, GeometryType::Circle(circle.clone()), Color::by_option(ColorOption::Blue));
24
25    context.commands.spawn(
26        vec![
27            Box::new(red_object),
28            Box::new(Object()),
29            Box::new(Transform::new(
30                Position::new(Vector2::new(0.0, 0.0), Strategy::Normalized),
31                0.0,
32                Vector2::new(0.30, 0.30)
33            )),
34            Box::new(Velocity::new(Vector2::new(0.45, 0.45))),
35            Box::new(Collision::new(Collider::new_simple(GeometryType::Square)))
36        ]
37    );
38
39    context.commands.spawn(
40        vec![
41            Box::new(blue_object),
42            Box::new(Object()),
43            Box::new(Transform::new(
44                Position::new(Vector2::new(-0.45, 0.0), Strategy::Normalized),
45                0.0,
46                Vector2::new(0.30, 0.30)
47            )),
48            Box::new(Velocity::new(Vector2::new(0.45, 0.45))),
49            Box::new(Collision::new(Collider::new_simple(GeometryType::Square)))
50        ]
51    );
52
53    spawn_border(context, Orientation::Horizontal, Vector2::new(0.0, -1.), Vector2::new(context.window_configuration.width as f32, 0.01));
54    spawn_border(context, Orientation::Horizontal, Vector2::new(0.0, 1.), Vector2::new(context.window_configuration.width as f32, 0.01));
55    spawn_border(context, Orientation::Vertical, Vector2::new(1.35, 0.), Vector2::new(0.01, context.window_configuration.height as f32));
56    spawn_border(context, Orientation::Vertical, Vector2::new(-1.35, 0.), Vector2::new(0.01, context.window_configuration.height as f32));
57}
58
59fn update(context: &mut Context) {
60    let mut query: Query = Query::new(&context.world).with::<Object>();
61    let entities: Vec<Entity> = query.entities_with_components().unwrap();
62
63    check_border_collision(context, &entities);
64    check_object_collision(context, &entities);
65    move_objects(context, &entities);
66}
67
68fn spawn_border(context: &mut Context, orientation: Orientation, position: Vector2<f32>, scale: Vector2<f32>) {
69    let border: Shape = Shape::new(orientation, GeometryType::Rectangle, Color::by_option(ColorOption::White));
70
71    context.commands.spawn(
72        vec![
73            Box::new(border),
74            Box::new(Border()),
75            Box::new(Transform::new(
76                Position::new(position, Strategy::Normalized),
77                0.0,
78                scale
79            )),
80            Box::new(Collision::new(Collider::new_simple(GeometryType::Rectangle)))
81        ]
82    );
83}
84
85fn check_border_collision(context: &mut Context, entities: &Vec<Entity>) {
86    let mut border_query: Query = Query::new(&context.world).with::<Border>();
87    let borders: Vec<Entity> = border_query.entities_with_components().unwrap();
88
89    for entity in entities {
90        let mut velocity: ComponentRefMut<'_, Velocity> = context.world.get_entity_component_mut::<Velocity>(entity).unwrap();
91        let collision: ComponentRef<'_, Collision> = context.world.get_entity_component::<Collision>(entity).unwrap();
92
93        for border in &borders {
94            let border_collision: ComponentRef<'_, Collision> = context.world.get_entity_component::<Collision>(&border).unwrap();
95    
96            if Collision::check(CollisionAlgorithm::Aabb, &collision, &border_collision) {
97                let border_transform: ComponentRef<'_, Transform> = context.world.get_entity_component::<Transform>(&border).unwrap();
98    
99                if border_transform.position.x.abs() < border_transform.position.y.abs() {
100                    velocity.y *= -1.0;
101                } else {
102                    velocity.x *= -1.0;
103                }
104                break;
105            }
106        }
107    }
108}
109
110fn check_object_collision(context: &mut Context, entities: &Vec<Entity>) {
111    for (index, entity) in entities.iter().enumerate() {
112        let mut velocity: ComponentRefMut<'_, Velocity> = context.world.get_entity_component_mut::<Velocity>(entity).unwrap();
113        let collision: ComponentRef<'_, Collision> = context.world.get_entity_component::<Collision>(entity).unwrap();
114
115        if let Some(next_entity) = entities.get(index + 1) {
116            let mut next_entity_velocity: ComponentRefMut<'_, Velocity> = context.world.get_entity_component_mut::<Velocity>(next_entity).unwrap();
117            let next_entity_collision: ComponentRef<'_, Collision> = context.world.get_entity_component::<Collision>(next_entity).unwrap();
118
119            if Collision::check(CollisionAlgorithm::Aabb, &collision, &next_entity_collision) {
120                let mut thread_rng: ThreadRng = rng();
121                let random_angle: f32 = thread_rng.random_range(0.0..std::f32::consts::TAU);
122                let new_direction: Vector2<f32> = Vector2::new(random_angle.cos(), random_angle.sin());
123                let collision_impulse: f32 = 1.5;
124
125                velocity.update_values(new_direction * collision_impulse);
126                next_entity_velocity.update_values(-new_direction * collision_impulse);
127            }
128        }
129    }
130}
131
132fn move_objects(context: &mut Context, entities: &Vec<Entity>) {
133    for entity in entities {
134        let mut transform: ComponentRefMut<'_, Transform> = context.world.get_entity_component_mut::<Transform>(entity).unwrap();
135        let velocity: ComponentRef<'_, Velocity> = context.world.get_entity_component::<Velocity>(entity).unwrap();
136
137        let new_position: Vector2<f32> = transform.position.to_vec() + velocity.to_vec() * context.delta;
138        transform.set_position(&context.render_state, new_position);
139    }
140}