Files
blub-blub/scenes/ui/hud.gd
T
ttipo 446d8d177c 바탕화면 바닷속 방치형 위젯 첫 커밋
화면 아래 가장자리에 가로로 깔리는 Godot 4.7 위젯.
거품을 모아 바다를 꾸미고, 꾸밀수록 거품이 더 빨리 모인다.

- 도킹 창: 투명·테두리 없음, 화면 폭 전체, 위/아래 가장자리 스냅,
  높이 프리셋, 클릭 통과(모서리로 복귀), DPI 배율 보정
- 3/4 부감 격자: 구역당 9x6, 전체 45x6 = 270칸.
  바닥은 위에서, 물체는 서 있는 모습으로 그리고 Y 정렬로 앞뒤를 가린다
- 구역 5개(얕은 바다 -> 열수구): 앞 구역을 꾸며야 다음이 열린다
- 칸이 곧 한계인 경제: 배치/이동/치우기(절반 환불), 여러 칸짜리 장식
- 저장: 30초 자동 저장, 백업 한 세대, 8시간 상한 오프라인 보상,
  격자 이전 형식 저장본 자동 이전
- 스모크 테스트 60여 개 (test.ps1)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 17:59:50 +09:00

108 lines
3.9 KiB
GDScript

extends Control
class_name HUDLayer
## 스트립 왼쪽 위에 붙는 한 줄짜리 정보 바와 짧은 알림.
## 스트립은 화면에 도킹되므로 창을 끌어 옮기는 기능은 없다. 위치는 메뉴에서 바꾼다.
signal shop_pressed
signal menu_pressed
@onready var control_bar: PanelContainer = $ControlBar
@onready var bubbles_label: Label = %BubblesLabel
@onready var bps_label: Label = %BpsLabel
@onready var beauty_label: Label = %BeautyLabel
@onready var zone_label: Label = %ZoneLabel
@onready var shop_button: Button = %ShopButton
@onready var menu_button: Button = %MenuButton
@onready var toast: Label = %Toast
@onready var trash: PanelContainer = %Trash
@onready var trash_label: Label = %TrashLabel
var _toast_tween: Tween
func _ready() -> void:
control_bar.add_theme_stylebox_override("panel", UIStyle.panel(UIStyle.PANEL_BG, 12))
UIStyle.label(bubbles_label, 24, UIStyle.INK)
UIStyle.label(bps_label, 13, UIStyle.INK_DIM)
UIStyle.label(beauty_label, 13, UIStyle.GOLD)
UIStyle.label(zone_label, 13, UIStyle.ACCENT)
UIStyle.label(toast, 14, UIStyle.INK)
toast.modulate.a = 0.0
trash.add_theme_stylebox_override("panel", UIStyle.panel(Color(0.35, 0.06, 0.10, 0.88), 12))
UIStyle.label(trash_label, 13, UIStyle.INK)
trash.hide()
UIStyle.style_button(shop_button)
UIStyle.style_button(menu_button)
shop_button.pressed.connect(func(): shop_pressed.emit())
menu_button.pressed.connect(func(): menu_pressed.emit())
GameState.zone_unlocked.connect(func(_i: int): _refresh_zone_label())
GameState.state_changed.connect(_refresh_zone_label)
GameState.reloaded.connect(_refresh_zone_label)
SaveManager.offline_earned.connect(_on_offline_earned)
_refresh_zone_label()
func _process(_delta: float) -> void:
bubbles_label.text = NumberFormat.short(GameState.bubbles)
bps_label.text = "초당 %s" % NumberFormat.short(GameState.bps)
beauty_label.text = "아름다움 %s · x%.2f" % [
NumberFormat.short(GameState.beauty), GameState.beauty_mult
]
## 몇 개 구역을 열었고 다음은 무엇인지. 해금 조건 자체는 잠긴 구역 위에 직접 표시된다.
func _refresh_zone_label() -> void:
var opened := GameState.unlocked_zones
var total := Catalog.zone_count()
if not GameState.has_next_zone():
zone_label.text = "구역 %d/%d · 모두 열었어요" % [opened, total]
return
var next_zone := Catalog.zone(GameState.next_zone_index())
zone_label.text = "구역 %d/%d · 다음 %s %s" % [
opened, total, next_zone.name, NumberFormat.short(GameState.next_zone_cost())
]
func _on_offline_earned(amount: float, seconds: int) -> void:
show_toast("%s 동안 거품 %s를 모았어요" % [
NumberFormat.duration(seconds), NumberFormat.short(amount)
], 4.5)
func show_toast(text: String, duration := 2.0) -> void:
toast.text = text
if _toast_tween and _toast_tween.is_valid():
_toast_tween.kill()
_toast_tween = create_tween()
_toast_tween.tween_property(toast, "modulate:a", 1.0, 0.2)
_toast_tween.tween_interval(duration)
_toast_tween.tween_property(toast, "modulate:a", 0.0, 0.5)
## 클릭했을 때 얻은 양이 위로 떠오르는 숫자.
func float_number(at: Vector2, amount: float) -> void:
var l := Label.new()
add_child(l)
l.text = "+%s" % NumberFormat.short(amount)
UIStyle.label(l, 15, UIStyle.ACCENT)
l.position = at - Vector2(14, 10)
l.mouse_filter = Control.MOUSE_FILTER_IGNORE
var t := create_tween().set_parallel(true)
t.tween_property(l, "position:y", l.position.y - 42.0, 0.9) \
.set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
t.tween_property(l, "modulate:a", 0.0, 0.9)
t.chain().tween_callback(l.queue_free)
## 무언가를 집어 든 동안만 휴지통을 보여준다. 평소에는 자리를 차지하지 않는다.
func set_trash_visible(on: bool) -> void:
trash.visible = on
## 휴지통의 사각형. HUD와 바다가 같은 좌표계(Main의 전체 영역)를 쓰므로 그대로 넘길 수 있다.
func trash_area() -> Rect2:
return Rect2(trash.position, trash.size)