@tool class_name Ground2D extends Node2D @export var scene_name := "" @export_group("Player", "player_") @export var player_y_fixed := true @export var player_y := 70: set(val): player_y = val if is_node_ready(): _reset_player_positon() @export var reset_player_pos := false: set(val): reset_player_pos = false _reset_player_positon() @export_group("Sound") @export_enum("none", "ghost", "walking", "running", "crawling", "concrete") var footstep_type: String = "concrete": set(val): footstep_type = val if is_node_ready(): _load_footstep_audio() @onready var player = %MainPlayer as MainPlayer @onready var directional_light := %DirectionalLight2D as DirectionalLight2D @onready var bg_sprite = %BGSprite2D as Sprite2D @onready var foreground = %ParallaxForeground as ParallaxBackground @onready var camera_focus_marker = %CameraFocusMarker as CameraFocusMarker @onready var camera = %Camera2D as Camera2D @onready var footstep_audio = %FootstepAudioPlayer as RandomAudioStreamPlayer const FOOTSTEP_AUDIO = { #"wood": preload("res://config/audio/footstep/footstep_wood.tres"), #"carpet": preload("res://config/audio/footstep/footstep_carpet.tres"), "concrete": preload("res://config/audio/footstep/footstep_concrete.tres"), #"grass": preload("res://config/audio/footstep/footstep_grass.tres"), #"snow": preload("res://config/audio/footstep/footstep_snow.tres"), "ghost": preload("res://config/audio/sfx/footstep_ghost.tres"), "walking": preload("res://config/audio/sfx/footstep_walking.tres"), "running": preload("res://config/audio/sfx/footstep_running.tres"), "crawling": preload("res://config/audio/sfx/footstep_crawling.tres"), } func _ready() -> void: foreground.layer = GlobalConfig.CANVAS_LAYER_FG _reset_player_positon() # 检查 scene_name 是否合法 scene_name = scene_name.strip_edges() if not scene_name or scene_name.length() != 7: printerr("scene_name is not valid") return if Engine.is_editor_hint(): return # 如果 debug 模式下不通过 GroundLoader 启动,读取 palyer 位置 if GlobalConfig.DEBUG and not get_parent() is GroundLoader: player.global_position.x = ArchiveManager.archive.player_global_position_x _set_camera_and_player_boundary() _load_footstep_audio() focus_player() # %ColorRectTop.visible = true # %ColorRectBottom.visible = true func _enter_tree() -> void: if camera: camera.enabled = true func _reset_player_positon(): player.position.x = $DeployLayer/portal_left.position.x # 从屏幕下边缘算起 player.set_y_from_ground(158.0 - player_y) func _set_camera_and_player_boundary(): # set current_boarder by bg size if bg_sprite.texture and not Engine.is_editor_hint(): var size = bg_sprite.texture.get_size() * bg_sprite.scale # camera rect var camera_size = Vector2(max(564.0, size.x), max(size.y, 316.0)) var camera_upleft = Vector2(0, -camera_size.y / 2.0) var camera_rect = Rect2(camera_upleft, camera_size) # player rect should be set centered, with 30px x padding var up_left = Vector2(0, -size.y / 2.0) size.x -= 36.0 up_left.x = bg_sprite.position.x + 18.0 var player_rect = Rect2(up_left, size) SceneManager.set_camera_boundary(camera_rect) SceneManager.set_player_boundary(player_rect) if GlobalConfig.DEBUG: print("_set_camera_and_player_boundary:", camera_rect, player_rect) func _load_footstep_audio(): # foot step sound footstep_audio.audio_collections.clear() if footstep_type != "none": var audio = FOOTSTEP_AUDIO[footstep_type] as AudioStreamCollection footstep_audio.audio_collections.append(audio) func play_footstep_sound() -> void: if not footstep_audio.audio_collections.is_empty(): footstep_audio.play_random() var focus_mutex := Mutex.new() func get_player() -> MainPlayer: return player func focus_node(node: CanvasItem) -> void: if node.is_ancestor_of(camera_focus_marker): return focus_mutex.lock() var parent = camera_focus_marker.get_parent() if parent: parent.remove_child(camera_focus_marker) node.add_child(camera_focus_marker) focus_mutex.unlock() func focus_player() -> void: focus_node(player)