Created
November 25, 2024 22:12
-
-
Save lotus128/7d5172f0e6859c4659cfcd64d5e1ac33 to your computer and use it in GitHub Desktop.
pole-based character controller in Bevy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use bevy::{ | |
| log::debug, | |
| math::{Dir3, Vec3}, | |
| }; | |
| fn map_range(from_range: (f32, f32), to_range: (f32, f32), s: f32) -> f32 { | |
| to_range.0 | |
| + (s - from_range.0) * (to_range.1 - to_range.0) | |
| / (from_range.1 - from_range.0) | |
| } | |
| /// get the input about an axis that a character is on. | |
| /// | |
| /// Conceptually, the character is on a cylinder defined by an axis. | |
| /// The character's forward is defined by the cross of the axis and the character's up. | |
| /// | |
| /// # Parameters | |
| /// * `player_axis` - the player axis on which input is determined. | |
| /// * `character_up` - the player axis on which input is determined. | |
| /// * `input` - value of the input. | |
| /// | |
| /// # Returns | |
| /// Right-handed co-ordinate system. | |
| pub fn get_player_character_axis_movement( | |
| player_axis: Dir3, | |
| character_up: Dir3, | |
| input: f32, | |
| ) -> Vec3 { | |
| let character_up_on_player_axis = | |
| bevy_lib_extensions::vec3::project_onto_plane_with_length( | |
| *character_up, | |
| player_axis, | |
| ); | |
| let character_forward = | |
| Vec3::cross(*player_axis, character_up_on_player_axis); | |
| return character_forward * input; | |
| } | |
| pub fn get_pos_z_horizontal_movement( | |
| player_local_input: Vec3, | |
| player_up: Dir3, | |
| player_forward: Dir3, | |
| character_up: Dir3, | |
| ) -> Vec3 { | |
| let player_local_input_x = if Vec3::dot(-*player_forward, *character_up) > 0.0 | |
| { | |
| // character up towards camera | |
| player_local_input.x | |
| } else { | |
| // character up away camera | |
| -player_local_input.x | |
| }; | |
| return get_player_character_axis_movement( | |
| player_up, | |
| character_up, | |
| player_local_input_x, | |
| ); | |
| } | |
| // 45 degrees | |
| static RANGE: f32 = 0.0; | |
| /// Get the global input or player movement. | |
| /// # Design | |
| /// x and y input for each section of a sphere. Each section has "weight". | |
| /// The player has 6 poles (x+, y+, z+, x-, y-, z-). | |
| /// Each pole has movement for x/ y. | |
| /// The dot product of the character's up determines the weight of each pole. | |
| /// ie, the input is blended based on your proximity to poles. | |
| pub fn get_player_movement_input( | |
| player_local_input: Vec3, | |
| player_global_right: Dir3, | |
| player_global_up: Dir3, | |
| player_global_back: Dir3, | |
| character_global_up: Dir3, | |
| ) -> Vec3 { | |
| let pos_x = Vec3::dot(*character_global_up, *player_global_right); | |
| let pos_y = Vec3::dot(*character_global_up, *player_global_up); | |
| let pos_z = Vec3::dot(*character_global_up, *player_global_back); | |
| let neg_x = Vec3::dot(*character_global_up, *-player_global_right); | |
| let neg_y = Vec3::dot(*character_global_up, *-player_global_up); | |
| let neg_z = Vec3::dot(*character_global_up, *-player_global_back); | |
| let pos_x_weight = if pos_x < RANGE { | |
| 0.0 | |
| } else { | |
| map_range((RANGE, 1.0), (0.0, 1.0), pos_x) | |
| }; | |
| let neg_x_weight = if neg_x < RANGE { | |
| 0.0 | |
| } else { | |
| map_range((RANGE, 1.0), (0.0, 1.0), neg_x) | |
| }; | |
| let pos_y_weight = if pos_y < RANGE { | |
| 0.0 | |
| } else { | |
| map_range((RANGE, 1.0), (0.0, 1.0), pos_y) | |
| }; | |
| let neg_y_weight = if neg_y < RANGE { | |
| 0.0 | |
| } else { | |
| map_range((RANGE, 1.0), (0.0, 1.0), neg_y) | |
| }; | |
| let pos_z_weight = if pos_z < RANGE { | |
| 0.0 | |
| } else { | |
| map_range((RANGE, 1.0), (0.0, 1.0), pos_z) | |
| }; | |
| let neg_z_weight = if neg_z < RANGE { | |
| 0.0 | |
| } else { | |
| map_range((RANGE, 1.0), (0.0, 1.0), neg_z) | |
| }; | |
| debug!( | |
| "pos_x={} neg_x={} pos_y={} neg_y={} pos_z={} neg_z={}", | |
| pos_x, neg_x, pos_y, neg_y, pos_z, neg_z, | |
| ); | |
| debug!( | |
| "pos_x_weight={} neg_x_weight={} pos_y_weight={} neg_y_weight={} pos_z_weight={} neg_z_weight={}", | |
| pos_x_weight, neg_x_weight, pos_y_weight, neg_y_weight, pos_z_weight, neg_z_weight, | |
| ); | |
| // x input | |
| // when I am on the -x side of a surface, I want +y/-y input to be west/east | |
| let neg_x_x = get_player_character_axis_movement( | |
| player_global_back, | |
| character_global_up, | |
| -player_local_input.x, | |
| ) * neg_x_weight; | |
| // when I am on the +x side of a surface, I want +y/-y input to be east/ west | |
| let pos_x_x = get_player_character_axis_movement( | |
| player_global_back, | |
| character_global_up, | |
| -player_local_input.x, | |
| ) * pos_x_weight; | |
| // when I am on the +y side of a surface, I want +x/-x input to be west/east | |
| let pos_y_x = get_player_character_axis_movement( | |
| player_global_back, | |
| character_global_up, | |
| -player_local_input.x, | |
| ) * pos_y_weight; | |
| // when I am on the -y side of a surface, I want +x/-x input to be east/west | |
| let neg_y_x = get_player_character_axis_movement( | |
| player_global_back, | |
| character_global_up, | |
| player_local_input.x, | |
| ) * neg_y_weight; | |
| // when I am on the +z side of a surface, I want +x/-x input to be west/east | |
| let pos_z_x = get_player_character_axis_movement( | |
| player_global_up, | |
| character_global_up, | |
| player_local_input.x, | |
| ) * pos_z_weight; | |
| // when I am on the -z side of a surface, I want +x/-x input to be east/west | |
| let neg_z_x = get_player_character_axis_movement( | |
| player_global_up, | |
| character_global_up, | |
| -player_local_input.x, | |
| ) * neg_z_weight; | |
| // y input | |
| // when I am on the -x side of a surface, I want +y/-y input to be west/east | |
| let neg_x_y = get_player_character_axis_movement( | |
| player_global_up, | |
| character_global_up, | |
| -player_local_input.y, | |
| ) * neg_x_weight; | |
| // when I am on the +x side of a surface, I want +y/-y input to be east/ west | |
| let pos_x_y = get_player_character_axis_movement( | |
| player_global_up, | |
| character_global_up, | |
| player_local_input.y, | |
| ) * pos_x_weight; | |
| // when I am on the +y side of a surface, I want +y/-y input to be south/ north | |
| let pos_y_y = get_player_character_axis_movement( | |
| player_global_right, | |
| character_global_up, | |
| -player_local_input.y, | |
| ) * pos_y_weight; | |
| // when I am on the -y side of a surface, I want +y/-y input to be south/ north | |
| let neg_y_y = get_player_character_axis_movement( | |
| player_global_right, | |
| character_global_up, | |
| -player_local_input.y, | |
| ) * neg_y_weight; | |
| // when I am on the +z side of a surface, I want +y/-y input to be south/ north | |
| let pos_z_y = get_player_character_axis_movement( | |
| player_global_right, | |
| character_global_up, | |
| -player_local_input.y, | |
| ) * pos_z_weight; | |
| // when I am on the -z side of a surface, I want +y/-y input to be south/ north | |
| let neg_z_y = get_player_character_axis_movement( | |
| player_global_right, | |
| character_global_up, | |
| -player_local_input.y, | |
| ) * neg_z_weight; | |
| //return pos_z_x + neg_z_x; | |
| return pos_x_x | |
| + neg_x_x | |
| + pos_y_x | |
| + neg_y_x | |
| + neg_z_x | |
| + pos_z_x | |
| + pos_x_y | |
| + neg_x_y | |
| + pos_y_y | |
| + neg_y_y | |
| + pos_z_y | |
| + neg_z_y; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment