Merge pull request #29 from Ewpratten/ewpratten/colliders_api
Bring in a colissions API
This commit is contained in:
commit
614822d464
@ -6,7 +6,19 @@
|
|||||||
"top_texture": {
|
"top_texture": {
|
||||||
"file_path": "assets/env/env_testObject/env_testObjectTop.png"
|
"file_path": "assets/env/env_testObject/env_testObjectTop.png"
|
||||||
},
|
},
|
||||||
"footprint_radius": 128.0,
|
"footprint":[
|
||||||
|
{
|
||||||
|
"position": [
|
||||||
|
-128,
|
||||||
|
-128
|
||||||
|
],
|
||||||
|
"size": [
|
||||||
|
256,
|
||||||
|
256
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"visualization_radius": 256.0,
|
||||||
"physics_colliders": [
|
"physics_colliders": [
|
||||||
{
|
{
|
||||||
"position": [
|
"position": [
|
||||||
|
9
game/dist/map_gameMap.objects.json
vendored
9
game/dist/map_gameMap.objects.json
vendored
@ -1,8 +1,11 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type":"env",
|
"type": "env",
|
||||||
"name":"env_testObject",
|
"name": "env_testObject",
|
||||||
"position": [0,0],
|
"position": [
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
"rotation_radians": 0.5
|
"rotation_radians": 0.5
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -23,10 +23,12 @@ pub struct ObjectCollider {
|
|||||||
pub position: na::Vector2<f32>,
|
pub position: na::Vector2<f32>,
|
||||||
/// Possible sizing
|
/// Possible sizing
|
||||||
pub size: Option<na::Vector2<f32>>,
|
pub size: Option<na::Vector2<f32>>,
|
||||||
/// Possible radius
|
|
||||||
pub radius: Option<f32>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handy aliases
|
||||||
|
pub type ObjectSpaceObjectCollider = ObjectCollider;
|
||||||
|
pub type WorldSpaceObjectCollider = ObjectCollider;
|
||||||
|
|
||||||
/// Definition of an object. Only one of these should exist *per object*, and they will be GPU instanced.
|
/// Definition of an object. Only one of these should exist *per object*, and they will be GPU instanced.
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct WorldObject {
|
pub struct WorldObject {
|
||||||
@ -37,10 +39,11 @@ pub struct WorldObject {
|
|||||||
/// The object's top texture
|
/// The object's top texture
|
||||||
pub top_texture: Option<PossiblyAnimatedTexture>,
|
pub top_texture: Option<PossiblyAnimatedTexture>,
|
||||||
/// colliders describing the object's footprint
|
/// colliders describing the object's footprint
|
||||||
// pub footprint: Vec<ObjectCollider>,
|
pub footprint: Vec<ObjectSpaceObjectCollider>,
|
||||||
pub footprint_radius: Option<f32>,
|
/// A "sphere of influence" for the object. This is used for showing under the roof
|
||||||
|
pub visualization_radius: Option<f32>,
|
||||||
/// Colliders for physics
|
/// Colliders for physics
|
||||||
pub physics_colliders: Vec<ObjectCollider>,
|
pub physics_colliders: Vec<ObjectSpaceObjectCollider>,
|
||||||
/// Temperature
|
/// Temperature
|
||||||
pub temperature: Option<f32>,
|
pub temperature: Option<f32>,
|
||||||
/// Friction
|
/// Friction
|
||||||
|
@ -7,7 +7,7 @@ use crate::{
|
|||||||
rendering::utilities::anim_texture::AnimatedTexture,
|
rendering::utilities::anim_texture::AnimatedTexture,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::world_object::{WorldObject, WorldObjectRef};
|
use super::world_object::{WorldObject, WorldObjectRef, WorldSpaceObjectCollider};
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum WorldObjectPackageLoadError {
|
pub enum WorldObjectPackageLoadError {
|
||||||
@ -32,6 +32,8 @@ pub struct WorldObjectPackage {
|
|||||||
pub bottom_animated_textures: HashMap<String, AnimatedTexture>,
|
pub bottom_animated_textures: HashMap<String, AnimatedTexture>,
|
||||||
/// Top animated textures
|
/// Top animated textures
|
||||||
pub top_animated_textures: HashMap<String, AnimatedTexture>,
|
pub top_animated_textures: HashMap<String, AnimatedTexture>,
|
||||||
|
/// A list of colliders in the world. We pre-solve these to make comput happy :)
|
||||||
|
pub world_space_colliders: Vec<WorldSpaceObjectCollider>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorldObjectPackage {
|
impl WorldObjectPackage {
|
||||||
@ -49,6 +51,7 @@ impl WorldObjectPackage {
|
|||||||
let mut top_static_textures = HashMap::new();
|
let mut top_static_textures = HashMap::new();
|
||||||
let mut bottom_animated_textures = HashMap::new();
|
let mut bottom_animated_textures = HashMap::new();
|
||||||
let mut top_animated_textures = HashMap::new();
|
let mut top_animated_textures = HashMap::new();
|
||||||
|
let mut world_space_colliders: Vec<WorldSpaceObjectCollider> = Vec::new();
|
||||||
for reference in &object_references {
|
for reference in &object_references {
|
||||||
// If this is a new object, load it.
|
// If this is a new object, load it.
|
||||||
let object_key = format!("{}:{}", reference.kind, reference.name);
|
let object_key = format!("{}:{}", reference.kind, reference.name);
|
||||||
@ -94,6 +97,21 @@ impl WorldObjectPackage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep track of all the colliders in the world
|
||||||
|
for collider in &object_definition.physics_colliders {
|
||||||
|
// Get the object's position
|
||||||
|
let object_position = reference.position;
|
||||||
|
|
||||||
|
// Convert the collider's position to world space
|
||||||
|
let world_space_collider = WorldSpaceObjectCollider {
|
||||||
|
position: object_position + collider.position,
|
||||||
|
size: collider.size,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add the collider to the list
|
||||||
|
world_space_colliders.push(world_space_collider);
|
||||||
|
}
|
||||||
|
|
||||||
// Store the object definition
|
// Store the object definition
|
||||||
object_definitions.insert(object_key.to_string(), object_definition);
|
object_definitions.insert(object_key.to_string(), object_definition);
|
||||||
}
|
}
|
||||||
@ -106,6 +124,7 @@ impl WorldObjectPackage {
|
|||||||
top_static_textures,
|
top_static_textures,
|
||||||
bottom_animated_textures,
|
bottom_animated_textures,
|
||||||
top_animated_textures,
|
top_animated_textures,
|
||||||
|
world_space_colliders,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use std::{collections::HashMap, path::PathBuf, sync::Arc};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
asset_manager::{load_texture_from_internal_data, InternalData},
|
asset_manager::{load_texture_from_internal_data, InternalData},
|
||||||
model::world_object_package::WorldObjectPackage,
|
model::{world_object_package::WorldObjectPackage, world_object::WorldSpaceObjectCollider},
|
||||||
};
|
};
|
||||||
use nalgebra as na;
|
use nalgebra as na;
|
||||||
use raylib::{
|
use raylib::{
|
||||||
@ -131,6 +131,14 @@ impl MapRenderer {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the map size
|
||||||
|
pub fn get_map_size(&self) -> na::Vector2<f32> {
|
||||||
|
na::Vector2::new(
|
||||||
|
self.map.width as f32 * 128.0,
|
||||||
|
self.map.height as f32 * 128.0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sample_friction_at(&self, world_position: na::Vector2<f32>) -> Option<f32> {
|
pub fn sample_friction_at(&self, world_position: na::Vector2<f32>) -> Option<f32> {
|
||||||
// Convert to a tile position
|
// Convert to a tile position
|
||||||
let tile_position = na::Vector2::new(
|
let tile_position = na::Vector2::new(
|
||||||
@ -376,7 +384,7 @@ impl MapRenderer {
|
|||||||
// We need to detect if the player is in the footprint of the object
|
// We need to detect if the player is in the footprint of the object
|
||||||
let mut tint = Color::WHITE;
|
let mut tint = Color::WHITE;
|
||||||
if let Some(footprint_radius) =
|
if let Some(footprint_radius) =
|
||||||
obj_def.footprint_radius
|
obj_def.visualization_radius
|
||||||
{
|
{
|
||||||
let player_dist_to_object =
|
let player_dist_to_object =
|
||||||
(obj_ref.position - player_position).norm();
|
(obj_ref.position - player_position).norm();
|
||||||
@ -460,4 +468,81 @@ impl MapRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the list of world colliders
|
||||||
|
pub fn get_world_colliders(&self) -> Vec<WorldSpaceObjectCollider> {
|
||||||
|
self.world_objects.world_space_colliders.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
// /// Used to modify the player's velocity based on the effects of the world
|
||||||
|
// pub fn effect_velocity_with_collisions(
|
||||||
|
// &self,
|
||||||
|
// player_position: na::Vector2<f32>,
|
||||||
|
// player_velocity: na::Vector2<f32>,
|
||||||
|
// ) -> na::Vector2<f32> {
|
||||||
|
// // If the player is not moving, we don't need to do anything
|
||||||
|
// if player_velocity.norm() == 0.0 {
|
||||||
|
// return player_velocity;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Get the velocity unit vector
|
||||||
|
// let player_velocity_unit_vector = player_velocity.normalize();
|
||||||
|
|
||||||
|
// // Find the position 1 pixel infront of the player
|
||||||
|
// let player_position_1_pixel_infront = player_position + player_velocity_unit_vector;
|
||||||
|
// let next_player_position = player_position + player_velocity;
|
||||||
|
|
||||||
|
// // Check if this is in the collision zone of any objects
|
||||||
|
// for obj_ref in &self.world_objects.object_references {
|
||||||
|
// // Filter out anything more than 1000 pixels away
|
||||||
|
// if (obj_ref.position - player_position).norm() > 1000.0 {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Get the object definition
|
||||||
|
// let object_key = format!("{}:{}", obj_ref.kind, obj_ref.name);
|
||||||
|
// let obj_def = self
|
||||||
|
// .world_objects
|
||||||
|
// .object_definitions
|
||||||
|
// .get(&object_key)
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
|
// // Check if the player is about to be in a collision zone
|
||||||
|
// for collider in &obj_def.physics_colliders {
|
||||||
|
// // Handle a radius collider vs a size collider
|
||||||
|
// if let Some(radius) = collider.radius {
|
||||||
|
// // Check if we are about to collide with the circle
|
||||||
|
// if (next_player_position - obj_ref.position).norm() < radius {
|
||||||
|
// // Get the angle from the player to the center of the collider
|
||||||
|
// let angle_to_center =
|
||||||
|
// (obj_ref.position - player_position).angle(&na::Vector2::new(0.0, 0.0));
|
||||||
|
// if angle_to_center.abs() <= std::f32::consts::FRAC_PI_2 {
|
||||||
|
// // Apply the inverse of the velocity to the player
|
||||||
|
// return na::Vector2::zeros();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else if let Some(size) = collider.size {
|
||||||
|
// // TODO: make this work for regular and rotated objects
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Check if the player is about to leave the map
|
||||||
|
// let mut player_velocity = player_velocity;
|
||||||
|
// if next_player_position.x < 0.0 {
|
||||||
|
// player_velocity.x = 0.0;
|
||||||
|
// } else if next_player_position.x > self.map.width as f32 * 128.0 {
|
||||||
|
// player_velocity.x = 0.0;
|
||||||
|
// }
|
||||||
|
// if next_player_position.y > 0.0 {
|
||||||
|
// player_velocity.y = 0.0;
|
||||||
|
// } else if next_player_position.y < self.map.height as f32 * -128.0 {
|
||||||
|
// player_velocity.y = 0.0;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // If we got here, the player is not in a collision zone
|
||||||
|
// player_velocity
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use crate::{
|
|||||||
asset_manager::{load_music_from_internal_data, load_sound_from_internal_data},
|
asset_manager::{load_music_from_internal_data, load_sound_from_internal_data},
|
||||||
discord::{DiscordChannel, DiscordRpcSignal},
|
discord::{DiscordChannel, DiscordRpcSignal},
|
||||||
global_resource_package::GlobalResources,
|
global_resource_package::GlobalResources,
|
||||||
model::player::Player,
|
model::{player::Player, world_object::WorldSpaceObjectCollider},
|
||||||
project_constants::ProjectConstants,
|
project_constants::ProjectConstants,
|
||||||
rendering::utilities::{anim_texture::AnimatedTexture, map_render::MapRenderer},
|
rendering::utilities::{anim_texture::AnimatedTexture, map_render::MapRenderer},
|
||||||
};
|
};
|
||||||
@ -21,6 +21,7 @@ pub struct PlayableScene {
|
|||||||
camera: raylib::camera::Camera2D,
|
camera: raylib::camera::Camera2D,
|
||||||
last_update: SystemTime,
|
last_update: SystemTime,
|
||||||
game_soundtrack: Music,
|
game_soundtrack: Music,
|
||||||
|
world_colliders: Vec<WorldSpaceObjectCollider>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlayableScene {
|
impl PlayableScene {
|
||||||
@ -37,6 +38,7 @@ impl PlayableScene {
|
|||||||
thread,
|
thread,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let world_colliders = map_renderer.get_world_colliders();
|
||||||
|
|
||||||
// Load the game music
|
// Load the game music
|
||||||
let game_soundtrack =
|
let game_soundtrack =
|
||||||
@ -44,22 +46,20 @@ impl PlayableScene {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
has_updated_discord_rpc: false,
|
has_updated_discord_rpc: false,
|
||||||
player: Player::new(na::Vector2::new(10.0 * constants.tile_size as f32, -10.0 * constants.tile_size as f32)),
|
player: Player::new(na::Vector2::new(
|
||||||
|
10.0 * constants.tile_size as f32,
|
||||||
|
-10.0 * constants.tile_size as f32,
|
||||||
|
)),
|
||||||
world_map: map_renderer,
|
world_map: map_renderer,
|
||||||
camera: raylib::camera::Camera2D {
|
camera: raylib::camera::Camera2D {
|
||||||
target: raylib::math::Vector2 {
|
target: raylib::math::Vector2 { x: 0.0, y: 0.0 },
|
||||||
x: 0.0,
|
offset: raylib::math::Vector2 { x: 0.0, y: 0.0 },
|
||||||
y: 0.0,
|
|
||||||
},
|
|
||||||
offset: raylib::math::Vector2 {
|
|
||||||
x: 0.0,
|
|
||||||
y: 0.0
|
|
||||||
},
|
|
||||||
rotation: 0.0,
|
rotation: 0.0,
|
||||||
zoom: 1.0,
|
zoom: 1.0,
|
||||||
},
|
},
|
||||||
last_update: SystemTime::UNIX_EPOCH,
|
last_update: SystemTime::UNIX_EPOCH,
|
||||||
game_soundtrack,
|
game_soundtrack,
|
||||||
|
world_colliders,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,10 +116,6 @@ impl PlayableScene {
|
|||||||
self.world_map
|
self.world_map
|
||||||
.render_map(&mut ctx2d, &self.camera, true, self.player.position);
|
.render_map(&mut ctx2d, &self.camera, true, self.player.position);
|
||||||
|
|
||||||
// NOTE: This is how to check friction and temperature
|
|
||||||
let current_friction = self.world_map.sample_friction_at(self.player.position);
|
|
||||||
let current_temperature = self.world_map.sample_temperature_at(self.player.position);
|
|
||||||
|
|
||||||
let player_size =
|
let player_size =
|
||||||
(constants.tile_size as f32 * constants.player.start_size * self.player.size) as i32;
|
(constants.tile_size as f32 * constants.player.start_size * self.player.size) as i32;
|
||||||
|
|
||||||
@ -159,6 +155,12 @@ impl PlayableScene {
|
|||||||
|
|
||||||
let player = &mut self.player;
|
let player = &mut self.player;
|
||||||
|
|
||||||
|
// NOTE: This is how to check friction and temperature
|
||||||
|
let current_friction = self.world_map.sample_friction_at(player.position);
|
||||||
|
let current_temperature = self.world_map.sample_temperature_at(player.position);
|
||||||
|
let map_size = self.world_map.get_map_size();
|
||||||
|
// TODO: You can access the colission list with: self.world_colliders
|
||||||
|
|
||||||
// Get input direction components
|
// Get input direction components
|
||||||
let h_axis = raylib.is_key_down(KeyboardKey::KEY_D) as i8
|
let h_axis = raylib.is_key_down(KeyboardKey::KEY_D) as i8
|
||||||
- raylib.is_key_down(KeyboardKey::KEY_A) as i8;
|
- raylib.is_key_down(KeyboardKey::KEY_A) as i8;
|
||||||
@ -191,38 +193,42 @@ impl PlayableScene {
|
|||||||
.set_magnitude((constants.player.max_velocity * constants.tile_size) as f32);
|
.set_magnitude((constants.player.max_velocity * constants.tile_size) as f32);
|
||||||
}
|
}
|
||||||
|
|
||||||
player.position += &player.velocity * delta_time;
|
let velocity_modifier = &player.velocity * delta_time;
|
||||||
|
|
||||||
|
player.position += velocity_modifier;
|
||||||
|
|
||||||
self.update_camera(raylib);
|
self.update_camera(raylib);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the camera
|
// Update the camera
|
||||||
pub fn update_camera(
|
pub fn update_camera(&mut self, raylib: &raylib::RaylibHandle) {
|
||||||
&mut self,
|
|
||||||
raylib: & raylib::RaylibHandle,
|
|
||||||
) {
|
|
||||||
|
|
||||||
// Bounding box
|
// Bounding box
|
||||||
let bbox = na::Vector2::new(0.2, 0.2);
|
let bbox = na::Vector2::new(0.2, 0.2);
|
||||||
|
|
||||||
// Get bounding box dimensions on the screen
|
// Get bounding box dimensions on the screen
|
||||||
let bbox_screen_min: raylib::math::Vector2 = (((na::Vector2::new(1.0, 1.0) - bbox) * 0.5).component_mul(
|
let bbox_screen_min: raylib::math::Vector2 = (((na::Vector2::new(1.0, 1.0) - bbox) * 0.5)
|
||||||
&na::Vector2::new(raylib.get_screen_width() as f32, raylib.get_screen_height() as f32)
|
.component_mul(&na::Vector2::new(
|
||||||
)).into();
|
raylib.get_screen_width() as f32,
|
||||||
let bbox_screen_max: raylib::math::Vector2 = (((na::Vector2::new(1.0, 1.0) + bbox) * 0.5).component_mul(
|
raylib.get_screen_height() as f32,
|
||||||
&na::Vector2::new(raylib.get_screen_width() as f32, raylib.get_screen_height() as f32)
|
)))
|
||||||
)).into();
|
.into();
|
||||||
|
let bbox_screen_max: raylib::math::Vector2 = (((na::Vector2::new(1.0, 1.0) + bbox) * 0.5)
|
||||||
|
.component_mul(&na::Vector2::new(
|
||||||
|
raylib.get_screen_width() as f32,
|
||||||
|
raylib.get_screen_height() as f32,
|
||||||
|
)))
|
||||||
|
.into();
|
||||||
|
|
||||||
// Get bounding box in world space
|
// Get bounding box in world space
|
||||||
let mut bbox_world_min = raylib.get_screen_to_world2D(bbox_screen_min, self.camera);
|
let mut bbox_world_min = raylib.get_screen_to_world2D(bbox_screen_min, self.camera);
|
||||||
let mut bbox_world_max = raylib.get_screen_to_world2D(bbox_screen_max, self.camera);
|
let mut bbox_world_max = raylib.get_screen_to_world2D(bbox_screen_max, self.camera);
|
||||||
|
|
||||||
// Invert y
|
// Invert y
|
||||||
bbox_world_min.y *= -1.0;
|
bbox_world_min.y *= -1.0;
|
||||||
bbox_world_max.y *= -1.0;
|
bbox_world_max.y *= -1.0;
|
||||||
|
|
||||||
self.camera.offset = bbox_screen_min;
|
self.camera.offset = bbox_screen_min;
|
||||||
|
|
||||||
if self.player.position.x < bbox_world_min.x {
|
if self.player.position.x < bbox_world_min.x {
|
||||||
self.camera.target.x = self.player.position.x;
|
self.camera.target.x = self.player.position.x;
|
||||||
}
|
}
|
||||||
@ -234,7 +240,7 @@ impl PlayableScene {
|
|||||||
if self.player.position.x > bbox_world_max.x {
|
if self.player.position.x > bbox_world_max.x {
|
||||||
self.camera.target.x = bbox_world_min.x + (self.player.position.x - bbox_world_max.x);
|
self.camera.target.x = bbox_world_min.x + (self.player.position.x - bbox_world_max.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.player.position.y < bbox_world_max.y {
|
if self.player.position.y < bbox_world_max.y {
|
||||||
self.camera.target.y = bbox_world_max.y - (self.player.position.y + bbox_world_min.y);
|
self.camera.target.y = bbox_world_max.y - (self.player.position.y + bbox_world_min.y);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user