feat: player controls commenting pass

This commit is contained in:
Sara 2024-03-18 19:29:40 +01:00
parent 99b844cc76
commit 4f564a4f4c
2 changed files with 37 additions and 8 deletions

View file

@ -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);
}

View file

@ -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<PlayerCharacter>(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;