iter53-卡1:卷轴视口批: 关卡画面视口化(level-map.md 布局决策5落地)——视口承载件/锚点跟随/平滑滚动减动效/拖拽滚轮平移与clamp/指引件/探针26断言/golden两Shot冻结
This commit is contained in:
1 parent
fd9c8ad90e
commit
55f4de3ca4
22 files changed
+983
-152
No files matched your search
@@ -31,6 +31,8 @@ const SLOT_STEP_Y := 58 # 同层槽位纵向步距
|
||||
const HIT_PAD := 6.0 # 图上命中外扩(指宽容差)
|
||||
const CONFIRM_WINDOW_S := 3.0 # 放弃确认态窗口(秒——超时回落)
|
||||
const COL_HP_BAR_BACK := UiKit.HP_BAR_BACK # 迷你血条深底(对暗面板可辨;值单点收 UiKit)
|
||||
const DRAG_THRESHOLD := 8.0 # 拖动距离阈值(布局决策 5 provisional——超阈值进拖拽态,拖拽不触发选路)
|
||||
const WHEEL_STEP := 60.0 # 滚轮横滚单档步进(provisional 实现期定值;方向映射=代拍板:下/右=向右前进向)
|
||||
|
||||
## 内部状态件(u2 子域拆件——契约 9 职责轴):_MapState=选路屏运行态与图绘制态(单一变化原因=
|
||||
## 选路呈现)。layer_decor 域字典承载换层装饰三件(图标贴图/石纹面/tint 层号——装饰域归并形态)。
|
||||
@@ -39,6 +41,7 @@ class _MapState extends RefCounted:
|
||||
var runner = null
|
||||
var title: Label
|
||||
var canvas: Control
|
||||
var viewport: LevelMapViewport # 卷轴视口承载件(布局决策 5——画布宿主/裁切/clamp/跟随/指引)
|
||||
var level_label: Label
|
||||
var gp_value: Label
|
||||
var hp_value: Label
|
||||
@@ -54,6 +57,8 @@ class _MapState extends RefCounted:
|
||||
var _cand_ids: Array[int] = [] # 本停点候选 id 集(图上命中合法集;探针直读冻结——住宿主)
|
||||
var _content_size := Vector2(620, 240) # 图内容包络(open_choices 按图拟合——画布 min 尺寸随它;探针直读冻结)
|
||||
var _hover_id := -1 # 悬停预承诺候选(-1 = 无;探针直读冻结)
|
||||
var _drag_active := false # 拖拽态(超阈值至抬杆——拖拽不触发选路,布局决策 5)
|
||||
var _drag_press_pos := Vector2(-1, -1) # 拖距判定基准(按下点画布局部坐标;-1 = 无按下跟踪)
|
||||
var abandon_btn: Button # 放弃入口(可见性归组合根 set_abandon_available——只在冒险模式开;探针直读冻结)
|
||||
var status_badge: Button # 状态面板入口角标(顶栏 GP 徽右侧;连接归组合根;探针直读冻结)
|
||||
var _layout_cache := {"nodes": [], "canvas_size": Vector2.ZERO, "pos": {}} # 布局缓存(_layout 纯函数于 (nodes, canvas)——键=nodes 内容等价+画布尺寸,图态换代或画布尺寸变即自然失效;探针直读冻结)
|
||||
@@ -74,15 +79,15 @@ func _init() -> void:
|
||||
base.add_child(tex)
|
||||
_map.layer_decor.noise_rect = tex # 世界侧 tint 承载面(换层色板渐变挂点——§2.7 切片档)
|
||||
base.add_child(UiKit.vignette_rect())
|
||||
var center := CenterContainer.new()
|
||||
center.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
base.add_child(center)
|
||||
var box := VBoxContainer.new()
|
||||
box.add_theme_constant_override("separation", 10)
|
||||
center.add_child(box)
|
||||
_build_top_bar(box)
|
||||
_build_canvas(box)
|
||||
_build_status_bar(box)
|
||||
# 三段纵排页(布局决策 5 卷轴视口:顶栏/标题在上、图区视口扩张充盈、底栏在下——
|
||||
# 视口为窗口屏为框,画布随图生长可超屏)
|
||||
var page := VBoxContainer.new()
|
||||
page.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
page.add_theme_constant_override("separation", 10)
|
||||
base.add_child(page)
|
||||
_build_top_bar(page)
|
||||
_build_canvas(page)
|
||||
_build_status_bar(page)
|
||||
visible = false
|
||||
|
||||
|
||||
@@ -122,14 +127,21 @@ func _build_top_bar(box: VBoxContainer) -> void:
|
||||
box.add_child(_map.title)
|
||||
|
||||
|
||||
## 节点图画布域(图上点击 = 唯一提交通道——布局决策 2;mouse_filter STOP 接悬停/点击)
|
||||
## 节点图区(卷轴视口承载——布局决策 5):视口件为窗口(裁切/offset/clamp/跟随/指引),
|
||||
## 画布为其子件;图上点击 = 唯一提交通道(布局决策 2),拖拽/滚轮判定留在画布 _gui_input
|
||||
## (事件流同源——悬停/选路同源,视口件不做父子事件路由)
|
||||
func _build_canvas(box: VBoxContainer) -> void:
|
||||
_map.canvas = Control.new()
|
||||
_map.canvas.custom_minimum_size = Vector2(620, 240)
|
||||
_map.canvas.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
_map.canvas.draw.connect(_draw_graph.bind(_map.canvas))
|
||||
_map.canvas.gui_input.connect(_on_canvas_input)
|
||||
box.add_child(_map.canvas)
|
||||
_map.canvas.mouse_exited.connect(_on_canvas_mouse_exited)
|
||||
_map.viewport = LevelMapViewport.new()
|
||||
_map.viewport.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_map.viewport.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
box.add_child(_map.viewport)
|
||||
_map.viewport.setup(_map.canvas, _viewport_anchor, Vector2(1280, 800)) # 兜底尺寸=设计基座(实况窗口以布局为准)
|
||||
|
||||
|
||||
## 底部状态栏域(合并单条:亮米面板一站读齐=头像+生命+属性+本关加成/流派件——demo ④;
|
||||
@@ -297,6 +309,9 @@ func open_choices(cands: Array, runner: LevelRunner) -> void:
|
||||
_fit_canvas(_map.runner.nodes())
|
||||
_apply_level_tint(int(_map.runner.level_row.get("level_index", 0)))
|
||||
visible = true
|
||||
# 视口锚定(布局决策 5):入口停点(尚无当前节点)=首锚即达;其余停点/兑付回图=平滑跟随
|
||||
# (停点切换与兑付确认回图——减动效开由视口件跳终态)
|
||||
_map.viewport.reanchor(_map.runner.current_node_id() >= 0)
|
||||
if not was_visible:
|
||||
# 独立屏淡入(spec Transitions:约 200ms;alpha 淡入非明度变化——art-bible §2 明度恒定)
|
||||
modulate.a = 0.0
|
||||
@@ -323,6 +338,7 @@ func _fit_canvas(nodes: Array) -> void:
|
||||
SLOT_BASE_Y + maxslot * SLOT_STEP_Y + NODE_HALF.y + 24.0) # +24 = 类型文字标签行
|
||||
_content_size = _content_size.max(Vector2(620, 256)) # 下限(抬高容纳标签行)
|
||||
_map.canvas.custom_minimum_size = _content_size
|
||||
_map.canvas.size = _content_size # 同帧收敛(视口锚点/包络数学与 _layout 同源读画布尺寸——容器延迟收敛不等锚点)
|
||||
|
||||
|
||||
# 换层色板渐变挂点(art-bible §2.7 下探过渡切片档——关卡结算确认→下一关→独立屏淡入链路上的
|
||||
@@ -356,20 +372,70 @@ func apply_status(payload: Dictionary) -> void:
|
||||
|
||||
|
||||
# ---- 图上命中(布局决策 2——唯一提交通道;探针 press_candidate 同路径) ----
|
||||
# 拖拽与点击以拖动距离阈值区分(布局决策 5):按下起跟踪拖距,超阈值进拖拽态逐帧平移视口,
|
||||
# 抬杆不提交;未超阈值的完整点击(按下→抬杆)=提交沿——命中候选即提交,语义零改动。
|
||||
|
||||
func _on_canvas_input(ev: InputEvent) -> void:
|
||||
if _map.runner == null or not visible:
|
||||
return
|
||||
if ev is InputEventMouseMotion:
|
||||
var nid := _candidate_at(ev.position) # gui_input 事件坐标 = 接收控件局部坐标(push_input 同成立)
|
||||
if nid != _hover_id:
|
||||
_hover_id = nid
|
||||
_map.canvas.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND if nid >= 0 else Control.CURSOR_ARROW
|
||||
if ev is InputEventMouseButton and ev.pressed and _wheel_pan_step(ev.button_index) != 0.0:
|
||||
_map.viewport.pan_by(Vector2(_wheel_pan_step(ev.button_index), 0))
|
||||
elif ev is InputEventMouseMotion:
|
||||
_on_canvas_motion(ev)
|
||||
elif ev is InputEventMouseButton and ev.button_index == MOUSE_BUTTON_LEFT:
|
||||
_on_canvas_click(ev)
|
||||
|
||||
|
||||
## 悬停预承诺与拖拽态推进沿(画布局部坐标——gui_input 事件流同源)
|
||||
func _on_canvas_motion(ev: InputEventMouseMotion) -> void:
|
||||
if _drag_active:
|
||||
_map.viewport.pan_by(ev.relative) # 拖拽态逐帧平移(视口件执行 clamp——本屏只判定)
|
||||
return
|
||||
if _drag_press_pos.x >= 0.0 and ev.position.distance_to(_drag_press_pos) >= DRAG_THRESHOLD:
|
||||
_drag_active = true
|
||||
_drag_press_pos = Vector2(-1, -1)
|
||||
if _hover_id != -1: # 进拖拽态清悬停预承诺(拖拽非选路意图)
|
||||
_hover_id = -1
|
||||
_map.canvas.mouse_default_cursor_shape = Control.CURSOR_ARROW
|
||||
_map.canvas.queue_redraw()
|
||||
elif ev is InputEventMouseButton and ev.button_index == MOUSE_BUTTON_LEFT and ev.pressed:
|
||||
var nid2 := _candidate_at(ev.position)
|
||||
if nid2 >= 0:
|
||||
_submit_choice(nid2)
|
||||
_map.viewport.pan_by(ev.relative) # 跨阈值帧位移计入平移(拖距全长=平移全长)
|
||||
return
|
||||
var nid := _candidate_at(ev.position) # gui_input 事件坐标 = 接收控件局部坐标(push_input 同成立)
|
||||
if nid != _hover_id:
|
||||
_hover_id = nid
|
||||
_map.canvas.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND if nid >= 0 else Control.CURSOR_ARROW
|
||||
_map.canvas.queue_redraw()
|
||||
|
||||
|
||||
## 点击沿与拖拽抬杆(未超阈值的完整点击=提交沿——布局决策 2 语义零改动)
|
||||
func _on_canvas_click(ev: InputEventMouseButton) -> void:
|
||||
if ev.pressed:
|
||||
_drag_press_pos = ev.position
|
||||
_drag_active = false
|
||||
return
|
||||
_drag_press_pos = Vector2(-1, -1)
|
||||
if _drag_active:
|
||||
_drag_active = false # 拖拽抬杆=平移结束,不触发选路提交(布局决策 5)
|
||||
return
|
||||
var nid2 := _candidate_at(ev.position)
|
||||
if nid2 >= 0:
|
||||
_submit_choice(nid2)
|
||||
|
||||
|
||||
## 滚轮横滚步进(代拍板:下/右=向右前进向——视线移向深层,画布左移;上/左反向;非滚轮=0)
|
||||
func _wheel_pan_step(button_index: int) -> float:
|
||||
if button_index == MOUSE_BUTTON_WHEEL_DOWN or button_index == MOUSE_BUTTON_WHEEL_RIGHT:
|
||||
return -WHEEL_STEP
|
||||
if button_index == MOUSE_BUTTON_WHEEL_UP or button_index == MOUSE_BUTTON_WHEEL_LEFT:
|
||||
return WHEEL_STEP
|
||||
return 0.0
|
||||
|
||||
|
||||
## 拖距跟踪器生命周期清理(按下即武装——鼠标离画布且未入拖拽态时解除,
|
||||
## 防丢失 release 沿后的残留 armed 态把后续 motion 误判成拖拽激活)
|
||||
func _on_canvas_mouse_exited() -> void:
|
||||
if not _drag_active:
|
||||
_drag_press_pos = Vector2(-1, -1)
|
||||
|
||||
|
||||
# 候选命中:位置 = 布局公式(与 _draw_graph 同源),矩形 = 节点方块 + 外扩容差;非候选格不响应
|
||||
@@ -413,6 +479,42 @@ func press_candidate(index: int) -> void:
|
||||
_submit_choice(nid)
|
||||
|
||||
|
||||
# ---- 视口锚点(布局决策 5:当前节点居中;入口停点尚无当前节点=锚入口组包围盒居中) ----
|
||||
|
||||
## 锚点画布坐标提供方(构造注入视口件——本屏供数,视口件只消费)
|
||||
func _viewport_anchor() -> Vector2:
|
||||
if _map.runner == null:
|
||||
return _content_size / 2.0
|
||||
var nodes: Array[Variant] = _map.runner.nodes()
|
||||
var pos := _layout(nodes, _map.canvas)
|
||||
var cur_id: int = _map.runner.current_node_id()
|
||||
if cur_id < 0:
|
||||
return _entry_group_center(nodes, pos) # 入口停点(尚未走出第一步)
|
||||
var cur := _node_by_id(cur_id, nodes)
|
||||
if cur.is_empty():
|
||||
return _content_size / 2.0
|
||||
return pos[cur]
|
||||
|
||||
|
||||
func _entry_group_center(nodes: Array, pos: Dictionary) -> Vector2:
|
||||
# 入口组 = 全部无入边节点(与 _assign_layers 入口判据同源);多入口取包围盒居中
|
||||
var inbound := {}
|
||||
for node in nodes:
|
||||
for tid in _out_ids(node, nodes):
|
||||
inbound[tid] = true
|
||||
var lo := Vector2(INF, INF)
|
||||
var hi := Vector2(-INF, -INF)
|
||||
for node in nodes:
|
||||
if inbound.has(_id_of(node)):
|
||||
continue
|
||||
var p: Vector2 = pos[node]
|
||||
lo = lo.min(p)
|
||||
hi = hi.max(p)
|
||||
if lo.x > hi.x:
|
||||
return _content_size / 2.0 # 空图兜底(无入口——不达路径,防御位)
|
||||
return (lo + hi) / 2.0
|
||||
|
||||
|
||||
func _id_of(node: Dictionary) -> int:
|
||||
# 编号显示经行进器读面(编号空间解析单一出口——单链 = 下标 / 图 = node_depth)
|
||||
return _map.runner.node_id_of(node)
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
## 卷轴视口承载件(level-map.md 布局决策 5——关卡画面选路屏):画布宿主与裁切、平移状态
|
||||
## 与 clamp、锚点跟随与平滑滚动、指引件(屏缘渐隐带+平移提示 chip)。
|
||||
## 纯呈现态:平移位置零核心写、不进存档(重进本屏按锚点规则重新定位);跟随只随停点切换/
|
||||
## 兑付回图发生(零自动推进)。拖拽/滚轮判定留画布 _gui_input(事件流同源——悬停/选路同源),
|
||||
## 本件只管裁切/offset/clamp/跟随/指引;画布包络=画布实际尺寸(open_choices 拟合后同帧收敛)。
|
||||
class_name LevelMapViewport
|
||||
extends Control
|
||||
|
||||
const SCROLL_DUR := 0.3 # 平滑滚动时长(布局决策 5 provisional 0.3s 单段缓动)
|
||||
const FADE_BAND_W := 70.0 # 屏缘渐隐带宽(布局决策 5/像素级判据②)
|
||||
const CHIP_SIZE := Vector2(240, 30) # 平移提示 chip(像素级判据③——图区底部居中)
|
||||
const CHIP_CORNER := 6 # chip 圆角(布局决策 5)
|
||||
const CHIP_BOTTOM_GAP := 24.0 # chip 距视口底缘(效果图档同族取值)
|
||||
const CHIP_TEXT := "◄ 拖拽画布 / 滚轮平移 ►" # 14px(FS_HINT 档——布局决策 5)
|
||||
const EDGE_EPS := 0.5 # 包络边界判定容差(浮点平移累积)
|
||||
|
||||
var _canvas: Control
|
||||
var _anchor_provider: Callable # () -> Vector2 锚点画布坐标(screen 提供——当前节点/入口组)
|
||||
var _viewport_size := Vector2.ZERO # 窗口尺寸兜底(setup 注入设计基座——实况窗口以控件 size 为准,布局晚于构造)
|
||||
var _offset := Vector2.ZERO: # 画布左上角在视口件坐标系的位置(纯呈现态——直驱画布 position)
|
||||
set(value):
|
||||
_offset = value
|
||||
if _canvas != null:
|
||||
_canvas.position = _offset
|
||||
var _view_tween: Tween
|
||||
var _guided_visible := false # 指引件可见态(画布超窗才暗示——浅图无平移余量即隐藏)
|
||||
|
||||
|
||||
## 一次交齐注入(code-conventions §1.6 拆分交接:画布宿主、锚点来源、窗口兜底尺寸)
|
||||
func setup(canvas: Control, anchor_provider: Callable, viewport_size: Vector2) -> void:
|
||||
_canvas = canvas
|
||||
_anchor_provider = anchor_provider
|
||||
_viewport_size = viewport_size
|
||||
clip_contents = true
|
||||
add_child(canvas)
|
||||
_build_guidance()
|
||||
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
# 窗口实况尺寸落定/变化即重锚(首开布局晚于 open_choices 的锚定——隐藏子树容器排序
|
||||
# 首个可见帧才传播,4.7 行为;实况窗口以本件 size 为准,落定即收敛锚点)
|
||||
if what == NOTIFICATION_RESIZED and _canvas != null and _anchor_provider.is_valid():
|
||||
reanchor(false)
|
||||
|
||||
|
||||
## 锚点跟随(布局决策 5:锚点居中;smooth=停点切换/兑付回图平滑滚动,减动效开=跳终态;
|
||||
## 重入先杀在飞 tween 再建新——快速连续停点切换防叠加)
|
||||
func reanchor(smooth: bool) -> void:
|
||||
if _canvas == null or not _anchor_provider.is_valid():
|
||||
return
|
||||
var target := _clamped_center_offset(_anchor_provider.call())
|
||||
_kill_scroll_tween()
|
||||
if not smooth or ArmedTimer.motion_still():
|
||||
_offset = target
|
||||
_sync_guidance()
|
||||
return
|
||||
_view_tween = create_tween()
|
||||
_view_tween.tween_property(self, "_offset", target, UiKit.motion(SCROLL_DUR)) \
|
||||
.set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_OUT)
|
||||
_sync_guidance()
|
||||
|
||||
|
||||
## 手动平移(画布 _gui_input 判定后调用——拖拽沿逐帧/滚轮步进;纯呈现零核心写)
|
||||
func pan_by(delta: Vector2) -> void:
|
||||
if _canvas == null:
|
||||
return
|
||||
_kill_scroll_tween()
|
||||
var win := _window_size()
|
||||
_offset = Vector2(
|
||||
_axis_pan_offset(_offset.x, delta.x, _canvas.size.x, win.x),
|
||||
_axis_pan_offset(_offset.y, delta.y, _canvas.size.y, win.y))
|
||||
_sync_guidance()
|
||||
|
||||
|
||||
## 视口中线的画布坐标(锚点居中判据投影面——浅图=画布中心,深图=夹紧域内当前注视点)
|
||||
func viewport_center_canvas_pos() -> Vector2:
|
||||
return _window_size() / 2.0 - _offset
|
||||
|
||||
|
||||
## 视口是否钉在画布包络边界(任一超窗轴 offset 顶边——clamp 判据面)
|
||||
func is_clamped_at_edge() -> bool:
|
||||
var win := _window_size()
|
||||
if _canvas == null:
|
||||
return false
|
||||
return _axis_at_edge(_offset.x, _canvas.size.x, win.x) \
|
||||
or _axis_at_edge(_offset.y, _canvas.size.y, win.y)
|
||||
|
||||
|
||||
func _kill_scroll_tween() -> void:
|
||||
if _view_tween != null and _view_tween.is_valid():
|
||||
_view_tween.kill()
|
||||
_view_tween = null
|
||||
|
||||
|
||||
## 单轴锚定 offset:画布不超窗=居中分支(夹紧域退化——布局决策 5);
|
||||
## 超窗=锚点夹紧在画布包络内,锚点落视口中线(offset = 半窗 − 锚点)
|
||||
func _axis_center_offset(anchor: float, canvas_len: float, half_win: float) -> float:
|
||||
if canvas_len <= half_win * 2.0:
|
||||
return half_win - canvas_len / 2.0
|
||||
return half_win - clampf(anchor, half_win, canvas_len - half_win)
|
||||
|
||||
|
||||
func _clamped_center_offset(anchor: Vector2) -> Vector2:
|
||||
var half := _window_size() / 2.0
|
||||
return Vector2(
|
||||
_axis_center_offset(anchor.x, _canvas.size.x, half.x),
|
||||
_axis_center_offset(anchor.y, _canvas.size.y, half.y))
|
||||
|
||||
|
||||
## 单轴平移收敛:居中轴零平移余量不动;超窗轴 offset 收敛在 [半窗−画布, 0](不出露屏底空档)
|
||||
func _axis_pan_offset(cur: float, delta: float, canvas_len: float, win_len: float) -> float:
|
||||
if canvas_len <= win_len:
|
||||
return cur
|
||||
return clampf(cur + delta, win_len - canvas_len, 0.0)
|
||||
|
||||
|
||||
func _axis_at_edge(offset: float, canvas_len: float, win_len: float) -> bool:
|
||||
if canvas_len <= win_len:
|
||||
return false
|
||||
return offset >= -EDGE_EPS or offset <= win_len - canvas_len + EDGE_EPS
|
||||
|
||||
|
||||
## 实况窗口优先(容器布局晚于构造——注入值只兜构造帧数学)
|
||||
func _window_size() -> Vector2:
|
||||
if size.x > 0.0 and size.y > 0.0:
|
||||
return size
|
||||
return _viewport_size
|
||||
|
||||
|
||||
## 指引件(布局决策 5/像素级判据②③):屏缘渐隐带盖在画布之上+图区底部居中提示 chip;
|
||||
## 可见性=画布超窗(_sync_guidance 随锚定/平移刷新)
|
||||
func _build_guidance() -> void:
|
||||
var band_l := TextureRect.new()
|
||||
band_l.name = "FadeL"
|
||||
band_l.texture = _fade_gradient(false)
|
||||
band_l.set_anchors_preset(Control.PRESET_LEFT_WIDE)
|
||||
band_l.offset_right = FADE_BAND_W
|
||||
band_l.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||
band_l.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
add_child(band_l)
|
||||
var band_r := TextureRect.new()
|
||||
band_r.name = "FadeR"
|
||||
band_r.texture = _fade_gradient(true)
|
||||
band_r.set_anchors_preset(Control.PRESET_RIGHT_WIDE)
|
||||
band_r.offset_left = -FADE_BAND_W
|
||||
band_r.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||
band_r.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
add_child(band_r)
|
||||
var chip := PanelContainer.new()
|
||||
chip.name = "PanHint"
|
||||
chip.add_theme_stylebox_override("panel",
|
||||
UiKit.panel_flat(UiKit.HP_BAR_BACK, UiKit.STONE, CHIP_CORNER, 1, Vector4(12, 5, 12, 5)))
|
||||
chip.set_anchors_preset(Control.PRESET_CENTER_BOTTOM)
|
||||
chip.offset_left = -CHIP_SIZE.x / 2.0
|
||||
chip.offset_right = CHIP_SIZE.x / 2.0
|
||||
chip.offset_top = -CHIP_SIZE.y - CHIP_BOTTOM_GAP
|
||||
chip.offset_bottom = -CHIP_BOTTOM_GAP
|
||||
chip.custom_minimum_size = CHIP_SIZE
|
||||
chip.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
var chip_label := UiKit.label(CHIP_TEXT, UiKit.FS_HINT, false, UiKit.WARM)
|
||||
chip_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
chip_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
chip.add_child(chip_label)
|
||||
add_child(chip)
|
||||
_sync_guidance()
|
||||
|
||||
|
||||
## 屏缘渐隐渐变(色取屏底同族 BG_ROOT——代拍板:不透明端 0.9 单段衰减)
|
||||
func _fade_gradient(transparent_at_left: bool) -> GradientTexture2D:
|
||||
var grad := Gradient.new()
|
||||
grad.offsets = PackedFloat32Array([0.0, 1.0])
|
||||
grad.colors = PackedColorArray([Color(UiKit.BG_ROOT, 0.9), Color(UiKit.BG_ROOT, 0.0)])
|
||||
var tex := GradientTexture2D.new()
|
||||
tex.gradient = grad
|
||||
tex.fill_from = Vector2(1, 0.5) if transparent_at_left else Vector2(0, 0.5)
|
||||
tex.fill_to = Vector2(0, 0.5) if transparent_at_left else Vector2(1, 0.5)
|
||||
tex.width = int(FADE_BAND_W)
|
||||
return tex
|
||||
|
||||
|
||||
func _sync_guidance() -> void:
|
||||
var win := _window_size()
|
||||
var over := _canvas != null and (_canvas.size.x > win.x or _canvas.size.y > win.y)
|
||||
if over == _guided_visible:
|
||||
return
|
||||
_guided_visible = over
|
||||
for nm in ["FadeL", "FadeR", "PanHint"]:
|
||||
var node := get_node_or_null(NodePath(nm)) as Control
|
||||
if node != null:
|
||||
node.visible = over
|
||||
@@ -0,0 +1 @@
|
||||
uid://dgin6f6214cbg
|
||||
Reference in new issue
Block a user