fix(gameplay): escape pause menu + block placement collision + perf optim render_distance/throttling

This commit is contained in:
Floppyrj45
2026-04-19 17:32:59 +02:00
parent 3fa02492a2
commit 9546fcd96b
8 changed files with 164 additions and 21 deletions

View File

@@ -83,7 +83,7 @@ toggle_inventory={
escape={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":16777217,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}

6
scenes/PauseMenu.tscn Normal file
View File

@@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://pause_menu"]
[ext_resource type="Script" path="res://scripts/PauseMenu.gd" id="1_pause"]
[node name="PauseMenu" type="Control"]
script = ExtResource("1_pause")

View File

@@ -7,6 +7,9 @@ extends Node3D
var inventory: Inventory = Inventory.new()
var _inventory_ui: CanvasLayer = null
var _pause_menu: Control = null
var _last_chunk_update_pos: Vector3 = Vector3(99999, 99999, 99999)
func _ready() -> void:
@@ -29,6 +32,12 @@ func _ready() -> void:
else:
push_error("Main: could not load InventoryUI.tscn")
# Load and attach PauseMenu
var pause_scene: PackedScene = preload("res://scenes/PauseMenu.tscn")
_pause_menu = pause_scene.instantiate() as Control
add_child(_pause_menu)
_pause_menu.hide()
AudioManager.play_ambient_loop("underwater_ambient")
AudioManager.play_music("underwater_theme")
AudioManager.play_whale_call_random()
@@ -51,10 +60,30 @@ func _ready() -> void:
func _process(_delta: float) -> void:
world.update_player_position(dolphin.global_position)
# Throttle chunk updates: only when dolphin moved > 4m
if dolphin.global_position.distance_to(_last_chunk_update_pos) > 4.0:
world.update_player_position(dolphin.global_position)
_last_chunk_update_pos = dolphin.global_position
plankton_follower.global_position = dolphin.global_position
func _unhandled_input(event: InputEvent) -> void:
if not event.is_action_pressed("escape"):
return
# Priority: close inventory first if open
if _inventory_ui != null and _inventory_ui.get("_is_open"):
_inventory_ui.call("_toggle_inventory")
get_viewport().set_input_as_handled()
return
# Toggle pause menu
if is_instance_valid(_pause_menu):
if _pause_menu.visible:
_pause_menu.hide_pause()
else:
_pause_menu.show_pause()
get_viewport().set_input_as_handled()
func _on_block_break(hit_position: Vector3, _normal: Vector3) -> void:
var broken_id: int = world.break_block(hit_position)
if broken_id > 0:
@@ -68,7 +97,17 @@ func _on_block_place(hit_position: Vector3, normal: Vector3) -> void:
return
if not ItemDatabase.is_placeable(selected["item_id"]):
return
var place_pos := hit_position + normal * 0.5
var block_coord := Vector3(floor(place_pos.x), floor(place_pos.y), floor(place_pos.z))
var block_center := block_coord + Vector3(0.5, 0.5, 0.5)
# Refuse if bloc overlaps player AABB (CapsuleShape3D r=0.4 h=2.0)
var dolphin_pos: Vector3 = dolphin.global_position
var dist := block_center - dolphin_pos
if abs(dist.x) < 1.0 and abs(dist.y) < 1.8 and abs(dist.z) < 1.0:
return # too close to player, silent refuse
if world.place_block(place_pos, selected["item_id"]):
inventory.remove_item_from_slot(inventory.selected_hotbar, 1)

67
scripts/PauseMenu.gd Normal file
View File

@@ -0,0 +1,67 @@
extends Control
func _ready() -> void:
process_mode = Node.PROCESS_MODE_ALWAYS
set_anchors_preset(Control.PRESET_FULL_RECT)
# Semi-transparent background
var bg := ColorRect.new()
bg.set_anchors_preset(Control.PRESET_FULL_RECT)
bg.color = Color(0, 0, 0, 0.7)
add_child(bg)
# Center VBox
var vbox := VBoxContainer.new()
vbox.set_anchors_preset(Control.PRESET_CENTER)
vbox.add_theme_constant_override("separation", 16)
vbox.set_offset(SIDE_LEFT, -120)
vbox.set_offset(SIDE_RIGHT, 120)
vbox.set_offset(SIDE_TOP, -120)
vbox.set_offset(SIDE_BOTTOM, 120)
add_child(vbox)
var title := Label.new()
title.text = "PAUSE"
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
title.add_theme_font_size_override("font_size", 48)
vbox.add_child(title)
var btn_resume := Button.new()
btn_resume.text = "Reprendre"
btn_resume.pressed.connect(_on_resume)
vbox.add_child(btn_resume)
var btn_menu := Button.new()
btn_menu.text = "Menu principal"
btn_menu.pressed.connect(_on_main_menu)
vbox.add_child(btn_menu)
var btn_quit := Button.new()
btn_quit.text = "Quitter"
btn_quit.pressed.connect(_on_quit)
vbox.add_child(btn_quit)
func show_pause() -> void:
show()
get_tree().paused = true
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
func hide_pause() -> void:
hide()
get_tree().paused = false
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
func _on_resume() -> void:
hide_pause()
func _on_main_menu() -> void:
get_tree().paused = false
get_tree().change_scene_to_file("res://scenes/MainMenu.tscn")
func _on_quit() -> void:
get_tree().quit()

View File

@@ -77,13 +77,6 @@ func _setup_input_actions() -> void:
ev.button_index = mouse_actions[action_name]
InputMap.action_add_event(action_name, ev)
if not InputMap.has_action("escape"):
InputMap.add_action("escape")
var ev := InputEventKey.new()
ev.keycode = KEY_ESCAPE
InputMap.action_add_event("escape", ev)
func _input(event: InputEvent) -> void:
if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
var motion := event as InputEventMouseMotion
@@ -107,11 +100,6 @@ func _input(event: InputEvent) -> void:
_do_raycast_place()
if event.is_action_pressed("echolocate"):
_trigger_echolocation()
if event.is_action_pressed("escape"):
if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
else:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
func _physics_process(delta: float) -> void:
@@ -252,9 +240,9 @@ func _raycast(reach: float) -> Dictionary:
func take_damage(amount: float) -> void:
health = max(0.0, health - amount)
emit_signal("stats_changed", oxygen, health, hunger)
stats_changed.emit(oxygen, health, hunger)
if health <= 0.0:
emit_signal("player_died")
player_died.emit()
func _trigger_echolocation() -> void:

View File

@@ -266,6 +266,9 @@ func _input(event: InputEvent) -> void:
if event.is_action_pressed("toggle_inventory"):
_toggle_inventory()
get_viewport().set_input_as_handled()
elif event.is_action_pressed("escape") and _is_open:
_toggle_inventory()
get_viewport().set_input_as_handled()
func _toggle_inventory() -> void:

View File

@@ -8,6 +8,7 @@ var _fish_meshes: Array[MeshInstance3D] = []
var _fish_velocities: Array[Vector3] = []
var _time: float = 0.0
var _player: CharacterBody3D = null
var _boid_update_timer: float = 0.0
func _ready() -> void:
_find_player()
@@ -62,6 +63,17 @@ func _process(delta: float) -> void:
if _player == null:
_find_player()
# Throttle boid calculations to 10 Hz
_boid_update_timer += delta
if _boid_update_timer >= 0.1:
_update_boids(_boid_update_timer)
_boid_update_timer = 0.0
# Apply velocities every frame for smooth movement
_apply_velocities(delta)
func _update_boids(dt: float) -> void:
for i: int in range(_fish_meshes.size()):
var fish: MeshInstance3D = _fish_meshes[i]
var vel: Vector3 = _fish_velocities[i]
@@ -91,12 +103,12 @@ func _process(delta: float) -> void:
avg_vel /= float(neighbor_count)
var cohesion_force: Vector3 = (avg_pos - fish.position).normalized() * 0.5
var alignment_force: Vector3 = avg_vel.normalized() * 0.3
vel += (cohesion_force + alignment_force) * delta * 2.0
vel += (cohesion_force + alignment_force) * dt * 2.0
# --- Global cohesion: return to school center ---
var center_offset: Vector3 = fish.position # positions are local to school node
var center_offset: Vector3 = fish.position
if center_offset.length() > school_radius:
vel += -center_offset.normalized() * 1.5 * delta * 4.0
vel += -center_offset.normalized() * 1.5 * dt * 4.0
# --- Player avoidance ---
if is_instance_valid(_player):
@@ -104,7 +116,7 @@ func _process(delta: float) -> void:
var dist_to_player: float = world_fish_pos.distance_to(_player.global_position)
if dist_to_player < 3.0:
var flee: Vector3 = (world_fish_pos - _player.global_position).normalized()
vel += flee * 4.0 * delta * (3.0 - dist_to_player)
vel += flee * 4.0 * dt * (3.0 - dist_to_player)
# Clamp speed
if vel.length() > swim_speed * 1.5:
@@ -113,6 +125,12 @@ func _process(delta: float) -> void:
vel = vel.normalized() * 0.3
_fish_velocities[i] = vel
func _apply_velocities(delta: float) -> void:
for i: int in range(_fish_meshes.size()):
var fish: MeshInstance3D = _fish_meshes[i]
var vel: Vector3 = _fish_velocities[i]
fish.position += vel * delta
# Rotate mesh to face velocity direction

View File

@@ -3,7 +3,7 @@ class_name ChunkManager
const CHUNK_SIZE: int = 16
@export var render_distance: int = 4
@export var render_distance: int = 2
@export var world_seed: int = 12345
var chunks: Dictionary = {}
@@ -36,6 +36,26 @@ func update_player_position(pos: Vector3) -> void:
if not chunks.has(coord):
_load_chunk(coord)
func _regen_border_neighbors(chunk_coord: Vector3i, local: Vector3i) -> void:
# Regen adjacent chunk only if block is on the border face
var offsets: Array[Vector3i] = []
if local.x == 0:
offsets.append(Vector3i(-1, 0, 0))
elif local.x == CHUNK_SIZE - 1:
offsets.append(Vector3i(1, 0, 0))
if local.y == 0:
offsets.append(Vector3i(0, -1, 0))
elif local.y == CHUNK_SIZE - 1:
offsets.append(Vector3i(0, 1, 0))
if local.z == 0:
offsets.append(Vector3i(0, 0, -1))
elif local.z == CHUNK_SIZE - 1:
offsets.append(Vector3i(0, 0, 1))
for offset: Vector3i in offsets:
var neighbor: Vector3i = chunk_coord + offset
if chunks.has(neighbor):
chunks[neighbor].generate_mesh()
func break_block(world_pos: Vector3) -> int:
var chunk_coord: Vector3i = world_to_chunk_coord(world_pos)
if not chunks.has(chunk_coord):
@@ -47,6 +67,7 @@ func break_block(world_pos: Vector3) -> int:
return 0
chunk.set_block(local.x, local.y, local.z, BlockDatabase.BlockType.AIR)
chunk.generate_mesh()
_regen_border_neighbors(chunk_coord, local)
return old_id
func place_block(world_pos: Vector3, block_id: int) -> bool:
@@ -60,6 +81,7 @@ func place_block(world_pos: Vector3, block_id: int) -> bool:
return false
chunk.set_block(local.x, local.y, local.z, block_id)
chunk.generate_mesh()
_regen_border_neighbors(chunk_coord, local)
return true
func get_block(world_pos: Vector3) -> int: