Files
godot-demo-projects/3d/voxel/player/player.gd

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())