|
| 1 | +extends CharacterBody3D |
| 2 | + |
| 3 | +const MOUSE_SENSITIVITY = 2.5 |
| 4 | +const CAMERA_SMOOTH_SPEED = 10.0 |
| 5 | +const MOVE_SPEED = 3.0 |
| 6 | +const FRICTION = 10.0 |
| 7 | +const JUMP_VELOCITY = 8.0 |
| 8 | +const BULLET_SPEED = 9.0 |
| 9 | + |
| 10 | +var _yaw := 0.0 |
| 11 | +var _pitch := 0.0 |
| 12 | +var _dir := Vector3(sin(_yaw), 0, cos(_yaw)) |
| 13 | +var _prox := 3 |
| 14 | + |
| 15 | +var _tps_look_from := Vector3() |
| 16 | + |
| 17 | +enum CameraType { |
| 18 | + CAM_FIXED, |
| 19 | + CAM_FPS, |
| 20 | + CAM_TPS, |
| 21 | +} |
| 22 | + |
| 23 | +var _bullet_scene: PackedScene = load("res://bullet.tscn") |
| 24 | +var _cam_type := CameraType.CAM_FIXED |
| 25 | + |
| 26 | +var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity") |
| 27 | + |
| 28 | + |
| 29 | +func _ready() -> void: |
| 30 | + Input.mouse_mode = Input.MOUSE_MODE_CAPTURED |
| 31 | + $Rig/Camera_TPS.top_level = true |
| 32 | + cycle_camera_type() |
| 33 | + |
| 34 | + |
| 35 | +func _input(event: InputEvent) -> void: |
| 36 | + if event is InputEventMouseMotion: |
| 37 | + _yaw -= event.screen_relative.x * MOUSE_SENSITIVITY * 0.001 |
| 38 | + _pitch += event.screen_relative.y * MOUSE_SENSITIVITY * 0.002 |
| 39 | + _pitch = clamp(_pitch, -PI, PI) |
| 40 | + $Rig.rotation = Vector3(0, _yaw, 0) |
| 41 | + |
| 42 | + |
| 43 | +func _update_camera(delta: float) -> void: |
| 44 | + _dir.x = sin(_yaw) |
| 45 | + _dir.z = cos(_yaw) |
| 46 | + |
| 47 | + $Rig/Head.rotation = Vector3(_pitch * -0.5, 0, 0) |
| 48 | + |
| 49 | + match _cam_type: |
| 50 | + CameraType.CAM_TPS: |
| 51 | + var target: Vector3 = $Rig/Head.get_global_transform_interpolated().origin |
| 52 | + var pos := target |
| 53 | + pos.x += _dir.x * _prox |
| 54 | + pos.z += _dir.z * _prox |
| 55 | + pos.y += 2.0 + (_pitch * (0.2 * _prox)) |
| 56 | + |
| 57 | + var offset: Vector3 = pos - _tps_look_from |
| 58 | + var l: float = offset.length() |
| 59 | + |
| 60 | + var tps_cam_speed: float = CAMERA_SMOOTH_SPEED * delta |
| 61 | + if (l > tps_cam_speed): |
| 62 | + offset *= tps_cam_speed / l |
| 63 | + _tps_look_from += offset |
| 64 | + |
| 65 | + $Rig/Camera_TPS.look_at_from_position(_tps_look_from, target, Vector3(0, 1, 0)) |
| 66 | + |
| 67 | + |
| 68 | +func cycle_camera_type() -> void: |
| 69 | + match _cam_type: |
| 70 | + CameraType.CAM_FIXED: |
| 71 | + _cam_type = CameraType.CAM_FPS |
| 72 | + $Rig/Head/Camera_FPS.make_current() |
| 73 | + CameraType.CAM_FPS: |
| 74 | + _cam_type = CameraType.CAM_TPS |
| 75 | + $Rig/Camera_TPS.make_current() |
| 76 | + CameraType.CAM_TPS: |
| 77 | + _cam_type = CameraType.CAM_FIXED |
| 78 | + get_node("../Camera_Fixed").make_current() |
| 79 | + |
| 80 | + # Hide body in FPS view (but keep shadow casting to improve spatial awareness). |
| 81 | + if _cam_type == CameraType.CAM_FPS: |
| 82 | + $Rig/Mesh_Body.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_SHADOWS_ONLY |
| 83 | + else: |
| 84 | + $Rig/Mesh_Body.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_ON |
| 85 | + |
| 86 | + |
| 87 | +func _process(delta: float) -> void: |
| 88 | + if Input.is_action_just_pressed(&"cycle_camera_type"): |
| 89 | + cycle_camera_type() |
| 90 | + |
| 91 | + if Input.is_action_just_pressed(&"toggle_physics_interpolation"): |
| 92 | + get_tree().physics_interpolation = not get_tree().physics_interpolation |
| 93 | + |
| 94 | + if Input.is_action_just_pressed(&"fire"): |
| 95 | + var bullet: RigidBody3D = _bullet_scene.instantiate() |
| 96 | + var transform_3d: Transform3D = $Rig/Head/Fire_Origin.get_global_transform_interpolated() |
| 97 | + bullet.position = transform_3d.origin |
| 98 | + var bul_dir: Vector3 = transform_3d.basis[2].normalized() |
| 99 | + bullet.linear_velocity = bul_dir * -BULLET_SPEED |
| 100 | + get_parent().add_child(bullet) |
| 101 | + bullet.reset_physics_interpolation() |
| 102 | + |
| 103 | + bullet.position -= bul_dir * (1.0 - Engine.get_physics_interpolation_fraction()) |
| 104 | + |
| 105 | + |
| 106 | + # If we pressed reset, or too far from the origin... move back to origin. |
| 107 | + if Input.is_action_just_pressed(&"reset_position") or position.length() > 10.0: |
| 108 | + position = Vector3(0, 1, 0) |
| 109 | + velocity = Vector3() |
| 110 | + reset_physics_interpolation() |
| 111 | + _yaw = 0.0 |
| 112 | + _pitch = 0.0 |
| 113 | + $Rig.rotation = Vector3(0, _yaw, 0) |
| 114 | + |
| 115 | + if Input.is_action_just_pressed(&"jump") and is_on_floor(): |
| 116 | + velocity.y += JUMP_VELOCITY |
| 117 | + |
| 118 | + _update_camera(delta) |
| 119 | + |
| 120 | + |
| 121 | +func _physics_process(delta: float) -> void: |
| 122 | + var move := Vector3() |
| 123 | + |
| 124 | + var input: Vector2 = Input.get_vector(&"move_left", &"move_right", &"move_forward", &"move_backward") * MOVE_SPEED |
| 125 | + move.x = input.x |
| 126 | + move.z = input.y |
| 127 | + |
| 128 | + # Apply gravity. |
| 129 | + move.y -= gravity * delta |
| 130 | + |
| 131 | + # Apply mouse rotation to the move. |
| 132 | + move = move.rotated(Vector3(0, 1, 0), _yaw) |
| 133 | + |
| 134 | + velocity += move |
| 135 | + |
| 136 | + move_and_slide() |
| 137 | + |
| 138 | + # Apply friction to horizontal motion in a tick rate-independent manner. |
| 139 | + var friction_delta := exp(-FRICTION * delta) |
| 140 | + velocity = Vector3(velocity.x * friction_delta, velocity.y, velocity.z * friction_delta) |
0 commit comments