519e4980de
네 가지 작업이 같은 함수들을 겹쳐 고쳐서 한 커밋으로 묶는다. 쪼개려면 hunk 단위로 갈라야 하는데 서로 얽혀 있어 오히려 위험하다. 먼 바다 배경 (backdrop.gd) - 3/4 부감에서 화면 위쪽은 더 먼 곳이다. 거기까지 모래를 깔면 바다가 아니라 사막처럼 보여서, 위쪽 띠를 물로 비웠다. - 먼 물 그라디언트 → 능선 → 먼 실루엣 순으로 깊이를 만든다. - Seabed가 뒷줄일수록 색을 물빛 쪽으로 당겨 공기원근을 준다. - 빛줄기를 알파를 더하는 방향으로 바꿨다. 걷어내는 방향이면 바닥만 밝아지고 물기둥은 텅 빈 채로 남는다. 하루의 빛 (day_cycle.gd) - 실제 시각에 맞춰 새벽/아침/낮/저녁/밤. 이름은 구간으로 끊고 색과 밝기는 계속 보간한다. - 밤에는 물결과 빛줄기가 잦아들고, 스스로 빛나는 것은 어둠을 되밀어 오히려 도드라진다. - godot --path . -- --hour=21 로 특정 시각을 눈으로 확인할 수 있다. 한 화면에 한 층 - 가로 연속 스크롤을 버리고 층 전환으로 바꿨다. 위층(얕은 바다)에서 아래층(열수구)으로 내려간다. 층 선택기·휠·↑↓ 키. - 층은 내부적으로 여전히 옆으로 늘어서 있고 화면이 한 구간만 비춘다. 갈아탈 때만 세로로 미끄러뜨려 깊이가 위아래로 읽히게 했다. - 칸 크기를 가로에서 정하도록 뒤집고 ZONE_COLUMNS를 36으로 올렸다. 층당 칸이 96 -> 216이라 정원과 해금 곡선은 다시 봐야 한다. - 한 화면에 한 층만 보이므로 층 색은 이웃과 섞지 않는다. 감상 모드 (F11) - 창을 화면 전체로. 세로로 남는 공간은 격자가 아니라 물기둥에 준다. 격자를 키우면 타일만 커지고 보이는 열은 오히려 줄어든다. - 창을 화면과 픽셀까지 똑같이 맞추면 Windows가 독점 전체화면으로 승격시켜 size 지정이 먹지 않는다. usable_rect까지만 쓴다. 그 밖에 - 저장 window_state: scroll_x -> layer - 알림이 정보 바와 겹치던 것을 바 아래로 옮겼다 - 난파선 선체 폴리곤이 제 몸을 가로질러 삼각분할이 실패하던 것 수정 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
127 lines
4.8 KiB
GDScript
127 lines
4.8 KiB
GDScript
extends Control
|
|
class_name HUDLayer
|
|
## 스트립 왼쪽 위에 붙는 한 줄짜리 정보 바와 짧은 알림.
|
|
## 스트립은 화면에 도킹되므로 창을 끌어 옮기는 기능은 없다. 위치는 메뉴에서 바꾼다.
|
|
|
|
signal shop_pressed
|
|
signal menu_pressed
|
|
## 정보 바가 위에서 자리를 얼마나 먹는지. 바 높이는 글꼴과 내용에 따라 달라지므로
|
|
## 재어서 알려주고, main이 받아 수조 격자를 그만큼 아래로 내린다.
|
|
signal reserved_top_changed(px: float)
|
|
|
|
## 바 아래로 이만큼은 띄워야 첫 줄 칸이 바에 닿지 않는다.
|
|
const BAR_CLEARANCE := 22.0
|
|
|
|
@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 layer_bar: LayerBar = %LayerBar
|
|
@onready var trash: PanelContainer = %Trash
|
|
@onready var trash_label: Label = %TrashLabel
|
|
|
|
var _toast_tween: Tween
|
|
|
|
|
|
func _ready() -> void:
|
|
# 바를 납작하게 눌러 둔다. 이 높이만큼 격자가 밀리고,
|
|
# 남는 자리에 먼 바다 배경이 드러난다.
|
|
var bar_style := UIStyle.panel(UIStyle.PANEL_BG, 11)
|
|
bar_style.content_margin_top = 6
|
|
bar_style.content_margin_bottom = 6
|
|
control_bar.add_theme_stylebox_override("panel", bar_style)
|
|
UIStyle.label(bubbles_label, 20, 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())
|
|
|
|
control_bar.resized.connect(func():
|
|
_place_toast()
|
|
reserved_top_changed.emit(reserved_top()))
|
|
_place_toast()
|
|
|
|
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 · 생물 %d/%d" % [
|
|
NumberFormat.short(GameState.bps), GameState.fish_total, GameState.capacity_total()
|
|
]
|
|
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 _place_toast() -> void:
|
|
# 층 선택기가 왼쪽 아래를 쓰므로 알림은 그 폭만큼 비켜 앉힌다.
|
|
toast.position = Vector2(
|
|
control_bar.position.x + layer_bar.size.x + 14.0,
|
|
control_bar.position.y + control_bar.size.y + 6.0)
|
|
|
|
|
|
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 set_trash_visible(on: bool) -> void:
|
|
trash.visible = on
|
|
|
|
|
|
## 휴지통의 사각형. HUD와 바다가 같은 좌표계(Main의 전체 영역)를 쓰므로 그대로 넘길 수 있다.
|
|
func trash_area() -> Rect2:
|
|
return Rect2(trash.position, trash.size)
|
|
|
|
|
|
## 정보 바가 먹는 세로 자리. 격자는 이 아래에서 시작해야 한다.
|
|
func reserved_top() -> float:
|
|
return control_bar.position.y + control_bar.size.y + BAR_CLEARANCE
|