## 营地场景布局表 schema(camp_scene_layout.tres):10 层贴图矩形/命中矩形/门洞缺口/围墙带包络/顶栏禁区。 ## 坐标数值唯一事实源=数据行本表——视图、探针、单测一律读表取值,禁另写坐标字面量。 class_name CampSceneLayoutTable extends Resource @export var canvas := Vector2(1280, 800) # 终稿画布(工程档同幅面) @export var hud_band_h := 48 # 顶栏禁区高度(命中不侵) @export var gate_gap := Vector2i(520, 760) # 门洞缺口 x 范围(透出背景层小路,禁填死) @export var wall_band := Rect2(0, 240, 1280, 70) # 围墙带包络(门柱顶 240—桩底 310;围墙贴图须落在其内) @export var layers: Array[CampSceneLayerSpec] = [] ## 加载期 fail-fast 校验(ConfigDB 装载即查,问题非空=数据致命)。 ## 校验面按件拆分:层身份与贴图矩形/命中区边界/命中区互斥/围墙带包络。 func validate() -> Array[String]: var problems: Array[String] = [] var clickables: Array[CampSceneLayerSpec] = _collect_clickables(problems) _check_hotspot_bounds(clickables, problems) _check_hotspot_disjoint(clickables, problems) _check_wall_band(problems) return problems func _collect_clickables(problems: Array[String]) -> Array[CampSceneLayerSpec]: var seen: Dictionary[StringName, bool] = {} var clickables: Array[CampSceneLayerSpec] = [] for layer in layers: if layer.key == &"": problems.append("层键为空") continue if seen.has(layer.key): problems.append("层键重复: %s" % layer.key) seen[layer.key] = true if _out_of_canvas(layer.texture_rect): problems.append("贴图矩形越画面: %s %s" % [layer.key, layer.texture_rect]) if layer.is_clickable(): if layer.hotspot_key == &"": problems.append("可点层缺命中键: %s" % layer.key) clickables.append(layer) elif layer.hotspot_key != &"": problems.append("不可点层带命中键: %s" % layer.key) return clickables func _check_hotspot_bounds(clickables: Array[CampSceneLayerSpec], problems: Array[String]) -> void: for h in clickables: if h.hotspot.position.x < 0.0 or h.hotspot.position.y < hud_band_h \ or _out_of_canvas(h.hotspot): problems.append("命中区越画面或侵顶栏禁区: %s %s" % [h.key, h.hotspot]) func _check_hotspot_disjoint(clickables: Array[CampSceneLayerSpec], problems: Array[String]) -> void: for i in clickables.size(): for j in range(i + 1, clickables.size()): if clickables[i].hotspot.intersects(clickables[j].hotspot): problems.append("命中区相交: %s × %s" % [clickables[i].key, clickables[j].key]) func _check_wall_band(problems: Array[String]) -> void: for layer in layers: if layer.key == &"wall" and not wall_band.encloses(layer.texture_rect): problems.append("围墙贴图越围墙带包络: %s" % layer.texture_rect) func _out_of_canvas(rect: Rect2) -> bool: return rect.position.x < 0.0 or rect.position.y < 0.0 \ or rect.end.x > canvas.x or rect.end.y > canvas.y