using Godot; public partial class Player : CharacterBody3D { [Export] public double Speed = 5; [Export] public double SprintMultiplier = 2; [Export] public double JumpForce = 5; [Export] public double MouseSensitivity = 0.2; public Control GameMenu { get; set; } public Character PlayerData { get; set; } private Vector3 gravityVelocity = Vector3.Zero; private Vector3 movementVelocity = Vector3.Zero; private double cameraPitch = 0; private Camera3D camera; private GravityReceiver gravityReceiver; public override void _Ready() { camera = GetNode("Camera"); gravityReceiver = GetNode("GravityReceiver"); PlayerData.UpdateNodePosition(); } public override void _Process(double delta) { PlayerData.SetCoordinatesFromLocal(GlobalPosition); PlayerData.Simulate(delta); } public override void _PhysicsProcess(double delta) { Vector3 newGravityVelocity = gravityVelocity; Vector3 newMovementVelocity = movementVelocity; if (IsInGravity()) { ProcessGravity(delta, ref newGravityVelocity); if (!GameMenu.Visible) { ProcessControlsGravity(ref newGravityVelocity, ref newMovementVelocity); } } else { newGravityVelocity = Vector3.Zero; ProcessCamera(delta); if (!GameMenu.Visible) { ProcessControls(ref newMovementVelocity); } } gravityVelocity = newGravityVelocity; movementVelocity = newMovementVelocity; Velocity = newGravityVelocity + newMovementVelocity; MoveAndSlide(); } public override void _Input(InputEvent @event) { if (GameMenu.Visible) { return; } if (@event is InputEventMouseMotion motion) { double yawDelta = -motion.Relative.X * MouseSensitivity; double yawDeltaRad = Mathf.DegToRad(yawDelta); double pitchDelta = -motion.Relative.Y * MouseSensitivity; double pitchDeltaRad = Mathf.DegToRad(pitchDelta); RotateObjectLocal(Vector3.Up, yawDeltaRad); if (IsInGravity()) { cameraPitch = Mathf.Clamp(cameraPitch + pitchDelta, -90, 90); } else { RotateObjectLocal(Vector3.Right, pitchDeltaRad); } camera.RotationDegrees = new Vector3(cameraPitch, 0, 0); } } public bool IsInGravity() { return gravityReceiver.InGravityZone && gravityReceiver.GetGravityStrength() > 0; } public bool IsOnGravityFloor() { for (int i = 0; i < GetSlideCollisionCount(); i++) { KinematicCollision3D collision = GetSlideCollision(i); Vector3 collisionNormal = collision.GetNormal(); double alignment = collisionNormal.Dot(gravityReceiver.GetGravityDirection()); if (alignment > 0.7) { return true; } } return false; } private void ProcessGravity(double delta, ref Vector3 newGravityVelocity) { if (!IsOnGravityFloor()) { newGravityVelocity -= gravityReceiver.GetGravityDirection() * gravityReceiver.GetGravityStrength() * (float)delta; } else { newGravityVelocity = -gravityReceiver.GetGravityDirection(); } Vector3 currentUp = GlobalTransform.Basis.Y; Vector3 targetUp = gravityReceiver.GetGravityDirection(); Vector3 axis = currentUp.Cross(targetUp); if (axis.Length() < 0.00001) { return; } double angle = currentUp.AngleTo(targetUp); GlobalRotate(axis.Normalized(), angle * delta * 10); } private void ProcessCamera(double delta) { if (cameraPitch > 0.01) { cameraPitch -= 90 * delta; } else if (cameraPitch < -0.001) { cameraPitch += 90 * delta; } if (Mathf.Abs(cameraPitch) < 1) { cameraPitch = 0; } camera.RotationDegrees = new Vector3(cameraPitch, 0, 0); } private void ProcessControlsGravity(ref Vector3 newGravityVelocity, ref Vector3 newMovementVelocity) { float inputX = Input.GetAxis("move_left", "move_right"); float inputZ = Input.GetAxis("move_forward", "move_back"); bool sprint = Input.IsActionPressed("sprint"); Vector3 direction = GlobalTransform.Basis.X * inputX + GlobalTransform.Basis.Z * inputZ; if (direction != Vector3.Zero) { newMovementVelocity.X = direction.X * Speed * (sprint ? SprintMultiplier : 1); newMovementVelocity.Y = direction.Y * Speed * (sprint ? SprintMultiplier : 1); newMovementVelocity.Z = direction.Z * Speed * (sprint ? SprintMultiplier : 1); } else { newMovementVelocity.X = 0; newMovementVelocity.Y = 0; newMovementVelocity.Z = 0; } if (Input.IsActionJustPressed("jump") && IsOnGravityFloor()) { newGravityVelocity = gravityReceiver.GetGravityDirection() * JumpForce; } } private void ProcessControls(ref Vector3 newMovementVelocity) { float inputX = Input.GetAxis("move_left", "move_right"); float inputY = -Input.GetAxis("move_up", "move_down"); float inputZ = Input.GetAxis("move_forward", "move_back"); bool sprint = Input.IsActionPressed("sprint"); Vector3 direction = Transform.Basis.X * inputX + Transform.Basis.Y * inputY + Transform.Basis.Z * inputZ; if (direction != Vector3.Zero) { newMovementVelocity.X = direction.X * Speed * (sprint ? SprintMultiplier : 1); newMovementVelocity.Y = direction.Y * Speed * (sprint ? SprintMultiplier : 1); newMovementVelocity.Z = direction.Z * Speed * (sprint ? SprintMultiplier : 1); } else { newMovementVelocity.X = 0; newMovementVelocity.Y = 0; newMovementVelocity.Z = 0; } double inputRotateZ = Input.GetAxis("rotate_left", "rotate_right"); double rotateAmountZ = Mathf.DegToRad(inputRotateZ); RotateObjectLocal(Vector3.Forward, rotateAmountZ); } }