From 4f564a4f4ca1981fc8c11f9b0892f3e5676ad650 Mon Sep 17 00:00:00 2001 From: Sara Date: Mon, 18 Mar 2024 19:29:40 +0100 Subject: [PATCH] feat: player controls commenting pass --- src/player_character.cpp | 21 +++++++++++++++++---- src/tunnels_player.cpp | 24 ++++++++++++++++++++---- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/player_character.cpp b/src/player_character.cpp index 6a62956..4208864 100644 --- a/src/player_character.cpp +++ b/src/player_character.cpp @@ -23,8 +23,12 @@ void PlayerCharacter::_process(double delta_time) { GDGAMEONLY(); } void PlayerCharacter::_physics_process(double delta_time) { GDGAMEONLY(); + // accelerate towards velocity target Vector3 const new_velocity = this->get_velocity().move_toward(this->velocity_target, delta_time * PlayerCharacter::ACCELERATION); - this->set_velocity(new_velocity); + // only apply velocity if not grounded + Vector3 const gravity{this->is_on_floor() ? Vector3() : Vector3{0.f, this->get_velocity().y - 9.8f, 0.f}}; + this->set_velocity(new_velocity + gravity); + // update position this->move_and_slide(); } @@ -33,9 +37,12 @@ void PlayerCharacter::move(Vector3 world_vector) { } void PlayerCharacter::aim(Vector3 at) { + // calculate the forward vector by normalized difference between this node and the target Vector3 const position{this->get_global_position()}; Vector3 const forward{(Vector3{at.x, 0.f, at.z} - Vector3{position.x, 0.f, position.z}).normalized()}; + // we always want up to be the global unit up Vector3 const up{0.f, 1.f, 0.f}; + // left is the cross product of the two this->target_rotation = Basis{up.cross(forward), up, forward}; } @@ -65,14 +72,20 @@ void PlayerCharacter::process_ai(double delta_time) { } void PlayerCharacter::process_rotation(double delta_time) { + // copy the current transform and basis matrix Transform3D trans{this->get_global_transform()}; Basis basis = trans.get_basis(); + // construct the current rotation .. Quaternion const current_quaternion = basis.get_rotation_quaternion(); + // .. and the target rotation from their respective bases Quaternion const target_quaternion = this->target_rotation.get_rotation_quaternion(); + // calculate the angle that still needs to be traveled float const angle = current_quaternion.angle_to(target_quaternion); - basis.set_quaternion(angle >= delta_time * PlayerCharacter::ROTATION_SPEED - ? current_quaternion.slerp(target_quaternion, delta_time * (this->rotation_speed_curve->sample(angle) * PlayerCharacter::ROTATION_SPEED) / angle) - : target_quaternion); + // calculate the angle amount that can be moved this frame + float const angle_step{float(this->rotation_speed_curve->sample(angle) * PlayerCharacter::ROTATION_SPEED * delta_time)}; + // update this object's global transform with the new rotation + basis.set_quaternion(angle < angle_step ? target_quaternion // to avoid overshooting, check if the max step is smaller than the angle distance + : current_quaternion.slerp(target_quaternion, angle_step / angle)); // convert the angle step to a lerp t value between current and target rotations trans.set_basis(basis); this->set_global_transform(trans); } diff --git a/src/tunnels_player.cpp b/src/tunnels_player.cpp index bbc7682..6381494 100644 --- a/src/tunnels_player.cpp +++ b/src/tunnels_player.cpp @@ -31,20 +31,27 @@ void TunnelsPlayer::_exit_tree() { GDGAMEONLY(); } void TunnelsPlayer::_process(double delta_time) { GDGAMEONLY(); - this->process_mouse_location(delta_time); + this->process_mouse_location(delta_time); // get the current screen location of the cursor + // convert screen location to world location on the same y plane as the player character Vector3 const mouse_world_location = this->get_mouse_world_position(Vector3{0.f, 1.f, 0.f}, this->character->get_global_position().y + 1.f); + // move the reticle this->reticle->set_global_position(mouse_world_location); + // rotate the camera this->process_camera_rotation(delta_time); switch(this->state) { default: case State::ManualControl: + // send the current wasd input to the character this->character->move(this->get_world_move_input().normalized()); + // send the current world cursor position the character this->character->aim(mouse_world_location); + // move the camera along with the character this->set_global_position(this->character->get_global_position()); break; case State::Tactics: break; case State::Overview: + // move camera along with the input this->set_global_position(this->get_global_position() + this->get_world_move_input().normalized()); break; } @@ -53,15 +60,20 @@ void TunnelsPlayer::_process(double delta_time) { GDGAMEONLY(); void TunnelsPlayer::process_mouse_location(double delta_time) { Viewport *view = this->get_viewport(); Vector2 const pixel_location = view->get_mouse_position(); + // convert cursor's global pixel position to normalized screen coordinates this->mouse_location = pixel_location / view->get_visible_rect().get_size(); + // get the direction the mouse is pointing in the world this->mouse_world_ray_normal = this->camera->project_ray_normal(pixel_location); } void TunnelsPlayer::process_camera_rotation(double delta_time) { Vector3 rotation = this->get_global_rotation(); - float const y_multiplier = std::max(0.1f, this->mouse_location.y); + float const y_multiplier = std::max(0.4f, this->mouse_location.y); // the influence of the mouse's y position on the rotation speed + // rotate the camera when the mouse is close to the edge of the screen if(this->mouse_location.x < TunnelsPlayer::ROTATION_MARGIN) { + // normalized measurement of how far into the rotation margin the mouse is float const normalized{1.f - (this->mouse_location.x / TunnelsPlayer::ROTATION_MARGIN)}; + // rotate based on delta time and use a curve to make the rotation zone feel more natural rotation.y += delta_time * TunnelsPlayer::ROTATION_SPEED * camera_rotation_ramp->sample(normalized) * y_multiplier; } if(this->mouse_location.x > 1.f - TunnelsPlayer::ROTATION_MARGIN) { @@ -74,6 +86,7 @@ void TunnelsPlayer::process_camera_rotation(double delta_time) { rotation.y -= 6.283185; while(rotation.y < 0.f) rotation.y += 6.283185; + // apply new rotation this->set_global_rotation(rotation); } @@ -101,11 +114,11 @@ void TunnelsPlayer::initialize_character() { return; if(player_scene->get_state()->get_node_type(0) != StringName("PlayerCharacter")) return; - // instantiate and store as player character + // instantiate and store the player character this->character = Object::cast_to(player_scene->instantiate()); this->get_parent()->add_child(this->character); this->character->set_global_transform(this->get_global_transform()); - // toggle manual mode, meaning the character's navigation agent is disabled and direct input to move(..) is used instead + // disable navmesh navigation and start using player input this->character->set_manual_mode(true); } @@ -124,9 +137,12 @@ Vector3 TunnelsPlayer::get_world_move_input() const { } Vector3 TunnelsPlayer::get_mouse_world_position(Vector3 axis, float depth) const { + // cache camera location Vector3 const cam_origin = this->camera->get_global_position(); + // get the ray and origin depths along the axis float const cam_depth = axis.dot(cam_origin); float const ray_step = axis.dot(this->mouse_world_ray_normal); + // calculate the number of "steps" the ray needs to take to get the target depth from the origin along the axis float const distance = depth - cam_depth; float const steps = distance / ray_step; return cam_origin + this->mouse_world_ray_normal * steps;