mirror of
https://github.com/godotengine/godot-demo-projects.git
synced 2025-12-16 05:20:06 +01:00
131 lines
4.9 KiB
GDScript
131 lines
4.9 KiB
GDScript
extends CharacterBody3D
|
|
|
|
const EYE_HEIGHT_STAND = 1.6
|
|
const EYE_HEIGHT_CROUCH = 1.4
|
|
|
|
const MOVEMENT_SPEED_GROUND = 70.0
|
|
const MOVEMENT_SPEED_AIR = 13.0
|
|
const MOVEMENT_SPEED_CROUCH_MODIFIER = 0.5
|
|
const MOVEMENT_SPEED_SPRINT_MODIFIER = 1.375
|
|
const MOVEMENT_FRICTION_GROUND = 12.5
|
|
const MOVEMENT_FRICTION_AIR = 2.25
|
|
const MOVEMENT_JUMP_VELOCITY = 9.0
|
|
|
|
var _mouse_motion := Vector2()
|
|
var _selected_block := 6
|
|
|
|
@onready var head: Node3D = $Head
|
|
@onready var camera: Camera3D = $Head/Camera3D
|
|
@onready var raycast: RayCast3D = $Head/RayCast3D
|
|
@onready var camera_attributes: CameraAttributes = $Head/Camera3D.attributes
|
|
@onready var selected_block_texture: TextureRect = $SelectedBlock
|
|
@onready var voxel_world: Node = $"../VoxelWorld"
|
|
@onready var crosshair: CenterContainer = $"../PauseMenu/Crosshair"
|
|
@onready var aim_preview: MeshInstance3D = $AimPreview
|
|
@onready var neutral_fov: float = camera.fov
|
|
|
|
|
|
func _ready() -> void:
|
|
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
|
|
|
|
|
func _process(_delta: float) -> void:
|
|
# Mouse movement.
|
|
_mouse_motion.y = clampf(_mouse_motion.y, -1560, 1560)
|
|
transform.basis = Basis.from_euler(Vector3(0, _mouse_motion.x * -0.001, 0))
|
|
head.transform.basis = Basis.from_euler(Vector3(_mouse_motion.y * -0.001, 0, 0))
|
|
|
|
# Block selection.
|
|
var ray_position := raycast.get_collision_point()
|
|
var ray_normal := raycast.get_collision_normal()
|
|
if Input.is_action_just_pressed(&"pick_block"):
|
|
# Block picking.
|
|
var block_global_position: Vector3 = (ray_position - ray_normal / 2).floor()
|
|
var block_sub_position: Vector3 = block_global_position.posmod(16)
|
|
var chunk_position: Vector3 = (block_global_position - block_sub_position) / 16
|
|
_selected_block = voxel_world.get_block_in_chunk(chunk_position, block_sub_position)
|
|
else:
|
|
# Block prev/next keys.
|
|
if Input.is_action_just_pressed(&"prev_block"):
|
|
_selected_block -= 1
|
|
if Input.is_action_just_pressed(&"next_block"):
|
|
_selected_block += 1
|
|
_selected_block = wrapi(_selected_block, 1, 30)
|
|
# Set the appropriate texture.
|
|
var uv := Chunk.calculate_block_uvs(_selected_block)
|
|
selected_block_texture.texture.region = Rect2(uv[0] * 512, Vector2.ONE * 64)
|
|
|
|
# Block breaking/placing.
|
|
if crosshair.visible and raycast.is_colliding():
|
|
aim_preview.visible = true
|
|
var ray_current_block_position := Vector3i((ray_position - ray_normal / 2).floor())
|
|
aim_preview.global_position = Vector3(ray_current_block_position) + Vector3(0.5, 0.5, 0.5)
|
|
var breaking := Input.is_action_just_pressed(&"break")
|
|
var placing := Input.is_action_just_pressed(&"place")
|
|
# Either both buttons were pressed or neither are, so stop.
|
|
if breaking == placing:
|
|
return
|
|
|
|
if breaking:
|
|
var block_global_position := ray_current_block_position
|
|
voxel_world.set_block_global_position(block_global_position, 0)
|
|
elif placing:
|
|
# Calculate the position of the block to be placed.
|
|
var block_global_position := Vector3i((ray_position + ray_normal / 2).floor())
|
|
voxel_world.set_block_global_position(block_global_position, _selected_block)
|
|
else:
|
|
aim_preview.visible = false
|
|
|
|
|
|
func _physics_process(delta: float) -> void:
|
|
camera_attributes.dof_blur_far_enabled = Settings.fog_enabled
|
|
camera_attributes.dof_blur_far_distance = Settings.fog_distance * 1.5
|
|
camera_attributes.dof_blur_far_transition = Settings.fog_distance * 0.125
|
|
# Crouching.
|
|
var crouching: bool = Input.is_action_pressed(&"crouch")
|
|
var sprinting: bool = Input.is_action_pressed(&"move_sprint")
|
|
head.transform.origin.y = lerpf(head.transform.origin.y, EYE_HEIGHT_CROUCH if crouching else EYE_HEIGHT_STAND, 1.0 - exp(-delta * 16.0))
|
|
|
|
# Keyboard movement.
|
|
var movement_vec2: Vector2 = Input.get_vector("move_left", "move_right", "move_forward", "move_back")
|
|
var movement: Vector3 = transform.basis * (Vector3(movement_vec2.x, 0, movement_vec2.y))
|
|
|
|
if is_on_floor():
|
|
movement *= MOVEMENT_SPEED_GROUND
|
|
else:
|
|
movement *= MOVEMENT_SPEED_AIR
|
|
|
|
if crouching:
|
|
movement *= MOVEMENT_SPEED_CROUCH_MODIFIER
|
|
sprinting = false
|
|
var target_fov: float = neutral_fov
|
|
if sprinting:
|
|
movement *= MOVEMENT_SPEED_SPRINT_MODIFIER
|
|
target_fov = neutral_fov * 1.25
|
|
camera.fov = lerpf(camera.fov, target_fov, 1.0 - exp(-delta * 10.0))
|
|
|
|
# Gravity.
|
|
if not is_on_floor():
|
|
var factor: float = 3.0 - clampf(velocity.y / -MOVEMENT_JUMP_VELOCITY, 0.0, 2.0)
|
|
velocity += get_gravity() * (delta * factor)
|
|
|
|
velocity += Vector3(movement.x, 0, movement.z) * delta
|
|
# Apply horizontal friction.
|
|
var friction_delta := exp(-(MOVEMENT_FRICTION_GROUND if is_on_floor() else MOVEMENT_FRICTION_AIR) * delta)
|
|
velocity = Vector3(velocity.x * friction_delta, velocity.y, velocity.z * friction_delta)
|
|
move_and_slide()
|
|
|
|
# Jumping, applied next frame.
|
|
if is_on_floor() and Input.is_action_pressed(&"jump"):
|
|
velocity.y = MOVEMENT_JUMP_VELOCITY
|
|
|
|
|
|
func _input(event: InputEvent) -> void:
|
|
if event is InputEventMouseMotion:
|
|
if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
|
|
_mouse_motion += event.screen_relative
|
|
|
|
|
|
func chunk_pos() -> Vector3i:
|
|
return Vector3i((transform.origin / Chunk.CHUNK_SIZE).floor())
|