@tool class_name Ground2D extends Node2D @export var scene_name := "" # 用于在 debug 时态下,指定进入的 portal @export_enum("left", "right", "1", "2", "3", "4", "5", "6", "7", "8", "9") var default_portal := "left" @export_group("Player", "player_") @export var player_y_fixed := true: set(val): player_y_fixed = val if is_node_ready(): _reset_player_y() @export var player_y := 70: set(val): player_y = val if is_node_ready(): _reset_player_y() @export var replace_player_to_portal := false: set(val): replace_player_to_portal = false if is_node_ready(): move_player_to_portal(default_portal) _reset_player_y() @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 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: # 仅在编辑器中调试时,通过 main 场景启动 if GlobalConfig.DEBUG and (not Engine.is_editor_hint()) and (not get_parent() is GroundLoader): call_deferred("_restart_from_main") return # 检查 scene_name 是否合法 scene_name = scene_name.strip_edges() if get_parent().name.begins_with("S") and (not scene_name or scene_name.length() != 7): printerr("scene_name is not valid") if Engine.is_editor_hint(): return foreground.layer = GlobalConfig.CANVAS_LAYER_FG _set_camera_and_player_boundary() _load_footstep_audio() # marker 默认就在 foucs player 状态 # camera_focus_marker.focus_node(player) # %ColorRectTop.visible = true # %ColorRectBottom.visible = true # 如果 debug 模式下不通过 GroundLoader 启动,则插入到 main 以下 func _restart_from_main(): var main = load("res://scene/main.tscn").instantiate() # if not main.is_node_ready(): # await main.ready var ground_loader = main.get_node("./GroundLoader") as GroundLoader # ground_loader.ignore_archive = true ground_loader.ignore_archive = false ground_loader.force_archive_scene = scene_name ground_loader.force_archive_portal = default_portal get_node("/root").add_child(main) # ground_loader.transition_to_scene(scene_name, default_portal, true) get_parent().queue_free() func _enter_tree() -> void: if camera_focus_marker: camera_focus_marker.enabled = true func _reset_player_y(): # 从屏幕下边缘算起 if player_y_fixed: 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() func move_player_to_portal(portal_name: String) -> void: var portal_node = get_node_or_null("DeployLayer/portal_" + portal_name) as Node2D if portal_node: player.global_position.x = portal_node.global_position.x if GlobalConfig.DEBUG: print("move player to portal:", portal_name, portal_node.global_position) else: printerr(scene_name + " portal not found: " + portal_name) # 传送后,重置 camera 位置 camera_focus_marker.reset_position_immediately()