Skip to content

Instantly share code, notes, and snippets.

@lotus128
Created November 25, 2024 22:12
Show Gist options
  • Select an option

  • Save lotus128/7d5172f0e6859c4659cfcd64d5e1ac33 to your computer and use it in GitHub Desktop.

Select an option

Save lotus128/7d5172f0e6859c4659cfcd64d5e1ac33 to your computer and use it in GitHub Desktop.
pole-based character controller in Bevy
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