먼 바다 배경·하루의 빛·층 구조·감상 모드
네 가지 작업이 같은 함수들을 겹쳐 고쳐서 한 커밋으로 묶는다. 쪼개려면 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>
This commit is contained in:
+68
-8
@@ -8,6 +8,7 @@ enum MenuId {
|
||||
SAVE_NOW = 2,
|
||||
RESET = 3,
|
||||
QUIT = 4,
|
||||
FULLSCREEN = 5,
|
||||
}
|
||||
|
||||
## 높이 프리셋. 메뉴에서 고른다.
|
||||
@@ -34,12 +35,17 @@ var _click_through := false
|
||||
var _edge_bottom := true
|
||||
var _strip_height := 220
|
||||
var _screen := 0
|
||||
## 감상 모드. 화면 전체를 덮고 바다를 크게 보여준다.
|
||||
var _fullscreen := false
|
||||
## 화면 배율(125%면 1.25). 창 크기는 물리 픽셀, 게임 좌표는 이 값으로 나눈 논리 픽셀.
|
||||
var _ui_scale := 1.0
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
var state: Dictionary = SaveManager.window_state
|
||||
# state는 SaveManager의 딕셔너리를 그대로 가리킨다. 아래 _apply_dock()이
|
||||
# _remember_window()로 같은 딕셔너리를 덮어쓰므로, 나중에 쓸 값은 미리 빼둔다.
|
||||
var saved_layer := int(state.get("layer", 0))
|
||||
_always_on_top = bool(state.get("always_on_top", true))
|
||||
_click_through = bool(state.get("click_through", false))
|
||||
_edge_bottom = bool(state.get("edge_bottom", true))
|
||||
@@ -60,6 +66,7 @@ func _ready() -> void:
|
||||
# 격자는 정보 바 아래에서 시작해야 한다. 바 높이는 HUD가 재어서 알려준다.
|
||||
hud.reserved_top_changed.connect(func(px: float): aquarium.grid_top = px)
|
||||
aquarium.grid_top = hud.reserved_top()
|
||||
aquarium.current_zone = saved_layer
|
||||
|
||||
hud.shop_pressed.connect(_toggle_shop)
|
||||
hud.menu_pressed.connect(_open_menu)
|
||||
@@ -70,6 +77,11 @@ func _ready() -> void:
|
||||
aquarium.build_mode_changed.connect(_on_build_mode_changed)
|
||||
aquarium.drag_changed.connect(_on_drag_changed)
|
||||
|
||||
# 층 선택기와 바다를 서로 물린다. 어느 쪽으로 층을 바꿔도 표시가 맞는다.
|
||||
hud.layer_bar.layer_picked.connect(func(i: int): aquarium.current_zone = i)
|
||||
aquarium.layer_changed.connect(hud.layer_bar.set_current)
|
||||
hud.layer_bar.set_current(aquarium.current_zone)
|
||||
|
||||
_build_menu()
|
||||
get_window().close_requested.connect(_quit)
|
||||
|
||||
@@ -90,12 +102,23 @@ func _apply_dock() -> void:
|
||||
h = mini(h, maxi(usable.size.y - 60, 120))
|
||||
|
||||
var win := get_window()
|
||||
# 창을 화면과 픽셀까지 똑같이 맞추면 Windows가 독점 전체화면으로 승격시켜 버리고,
|
||||
# 그 뒤로는 size를 넣어도 먹지 않아 감상 모드에서 빠져나올 수 없다.
|
||||
# 그래서 감상 모드도 작업표시줄을 뺀 영역까지만 쓴다.
|
||||
if win.mode != Window.MODE_WINDOWED:
|
||||
win.mode = Window.MODE_WINDOWED
|
||||
win.content_scale_factor = _ui_scale
|
||||
win.size = Vector2i(usable.size.x, h)
|
||||
win.position = Vector2i(
|
||||
usable.position.x,
|
||||
usable.end.y - h if _edge_bottom else usable.position.y
|
||||
)
|
||||
if _fullscreen:
|
||||
win.size = usable.size
|
||||
win.position = usable.position
|
||||
else:
|
||||
win.size = Vector2i(usable.size.x, h)
|
||||
win.position = Vector2i(
|
||||
usable.position.x,
|
||||
usable.end.y - h if _edge_bottom else usable.position.y
|
||||
)
|
||||
win.always_on_top = _always_on_top or _fullscreen
|
||||
aquarium.showcase = _fullscreen
|
||||
|
||||
_apply_passthrough()
|
||||
_remember_window()
|
||||
@@ -104,6 +127,7 @@ func _apply_dock() -> void:
|
||||
func _remember_window() -> void:
|
||||
SaveManager.window_state["always_on_top"] = _always_on_top
|
||||
SaveManager.window_state["click_through"] = _click_through
|
||||
SaveManager.window_state["layer"] = aquarium.current_zone
|
||||
SaveManager.window_state["edge_bottom"] = _edge_bottom
|
||||
SaveManager.window_state["height"] = _strip_height
|
||||
SaveManager.window_state["screen"] = _screen
|
||||
@@ -112,7 +136,8 @@ func _remember_window() -> void:
|
||||
## 클릭 통과를 켜면 안쪽 모서리 하나만 입력을 받는 영역으로 남긴다.
|
||||
## 빈 배열을 넘기면 통과가 꺼지고 창 전체가 다시 입력을 받는다.
|
||||
func _apply_passthrough() -> void:
|
||||
if not _click_through:
|
||||
# 감상 중에는 통과시키지 않는다. 화면을 다 덮은 채 클릭이 새 나가면 곤란하다.
|
||||
if _fullscreen or not _click_through:
|
||||
DisplayServer.window_set_mouse_passthrough(PackedVector2Array())
|
||||
return
|
||||
# 통과 영역은 창의 물리 픽셀 좌표로 준다.
|
||||
@@ -131,9 +156,40 @@ func _apply_passthrough() -> void:
|
||||
|
||||
# --- 게임 입력 ---
|
||||
|
||||
## 감상 모드로 들어가고 나온다. 창을 화면 전체로 키우고,
|
||||
## 바다는 아래에 깔고 그 위를 물기둥으로 채운다.
|
||||
func set_fullscreen(on: bool) -> void:
|
||||
if _fullscreen == on:
|
||||
return
|
||||
_fullscreen = on
|
||||
aquarium.cancel_build()
|
||||
if on:
|
||||
shop.hide()
|
||||
_apply_dock()
|
||||
hud.show_toast("감상 모드 · Esc나 F11로 돌아갑니다" if on else "위젯으로 돌아왔어요", 3.0)
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event.is_action_pressed("ui_cancel") and aquarium.is_building():
|
||||
aquarium.cancel_build()
|
||||
if event.is_action_pressed("ui_cancel"):
|
||||
if aquarium.is_building():
|
||||
aquarium.cancel_build()
|
||||
elif _fullscreen:
|
||||
set_fullscreen(false)
|
||||
else:
|
||||
return
|
||||
get_viewport().set_input_as_handled()
|
||||
return
|
||||
|
||||
if event is InputEventKey and event.pressed and not event.echo:
|
||||
match event.keycode:
|
||||
KEY_F11:
|
||||
set_fullscreen(not _fullscreen)
|
||||
KEY_UP, KEY_PAGEUP:
|
||||
aquarium.current_zone -= 1
|
||||
KEY_DOWN, KEY_PAGEDOWN:
|
||||
aquarium.current_zone += 1
|
||||
_:
|
||||
return
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
@@ -199,6 +255,7 @@ func _build_menu() -> void:
|
||||
menu.clear()
|
||||
menu.add_check_item("항상 위에 표시", MenuId.ALWAYS_ON_TOP)
|
||||
menu.add_check_item("클릭 통과 (모서리로 복귀)", MenuId.CLICK_THROUGH)
|
||||
menu.add_check_item("전체화면으로 감상 (F11)", MenuId.FULLSCREEN)
|
||||
menu.add_separator()
|
||||
for h in HEIGHTS:
|
||||
menu.add_radio_check_item(h.label, h.id)
|
||||
@@ -221,6 +278,7 @@ func _build_menu() -> void:
|
||||
func _sync_menu() -> void:
|
||||
_check(MenuId.ALWAYS_ON_TOP, _always_on_top)
|
||||
_check(MenuId.CLICK_THROUGH, _click_through)
|
||||
_check(MenuId.FULLSCREEN, _fullscreen)
|
||||
for h in HEIGHTS:
|
||||
_check(h.id, _strip_height == int(h.px))
|
||||
_check(EDGE_BOTTOM, _edge_bottom)
|
||||
@@ -270,6 +328,8 @@ func _on_menu_id(id: int) -> void:
|
||||
var corner := "오른쪽 위" if _edge_bottom else "오른쪽 아래"
|
||||
hud.show_toast(
|
||||
"클릭이 바탕화면으로 지나갑니다. %s 모서리를 눌러 돌아오세요." % corner, 5.0)
|
||||
MenuId.FULLSCREEN:
|
||||
set_fullscreen(not _fullscreen)
|
||||
MenuId.SAVE_NOW:
|
||||
_remember_window()
|
||||
SaveManager.save_game()
|
||||
|
||||
Reference in New Issue
Block a user