먼 바다 배경·하루의 빛·층 구조·감상 모드
네 가지 작업이 같은 함수들을 겹쳐 고쳐서 한 커밋으로 묶는다. 쪼개려면 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:
+87
-21
@@ -25,13 +25,14 @@ func _ready() -> void:
|
||||
_test_zone_gating()
|
||||
_test_zone_unlock()
|
||||
_test_zone_beauty_is_per_zone()
|
||||
_test_ramp_continuity()
|
||||
_test_zone_colors()
|
||||
_test_serialization_round_trip()
|
||||
_test_legacy_save_migration()
|
||||
_test_number_format()
|
||||
_test_korean_particles()
|
||||
_test_shape_families()
|
||||
_test_upgrades_are_all_production()
|
||||
_test_day_cycle()
|
||||
|
||||
if _failures == 0:
|
||||
print("모든 검사 통과")
|
||||
@@ -324,6 +325,9 @@ func _test_release_fish() -> void:
|
||||
func _test_space_is_the_limit() -> void:
|
||||
print("공간이 곧 한계")
|
||||
_rich_and_open()
|
||||
# 한 구역을 끝까지 채우려면 값이 1.15배씩 오르는 집을 200채 넘게 지어야 한다.
|
||||
# 거품이 먼저 떨어지면 "칸이 차서" 멈춘 것인지 알 수 없다.
|
||||
GameState.bubbles = 1e30
|
||||
var cells := Catalog.GRID_ROWS * Catalog.ZONE_COLUMNS
|
||||
check("구역 하나는 %d칸" % cells, GameState.free_cells_in_zone(0) == cells,
|
||||
str(GameState.free_cells_in_zone(0)))
|
||||
@@ -412,30 +416,38 @@ func _test_zone_beauty_is_per_zone() -> void:
|
||||
check("건드리지 않은 구역은 0", is_equal_approx(GameState.beauty_in_zone(4), 0.0))
|
||||
|
||||
|
||||
func _test_ramp_continuity() -> void:
|
||||
print("색 램프")
|
||||
## 한 화면에 한 층만 보이므로 층 색은 이웃과 섞지 않고 제 색을 그대로 쓴다.
|
||||
func _test_zone_colors() -> void:
|
||||
print("층 색")
|
||||
var n := Catalog.zone_count()
|
||||
check("왼쪽 끝은 첫 구역 색",
|
||||
check("왼쪽 끝은 첫 층 색",
|
||||
Catalog.ramp_color("tint", 0.0).is_equal_approx(Catalog.ZONES[0].tint))
|
||||
check("오른쪽 끝은 마지막 구역 색",
|
||||
check("오른쪽 끝은 마지막 층 색",
|
||||
Catalog.ramp_color("tint", 1.0).is_equal_approx(Catalog.ZONES[n - 1].tint))
|
||||
# 램프가 끊기지 않아야 구역 경계에 이음매가 보이지 않는다.
|
||||
var max_jump := 0.0
|
||||
var prev := Catalog.ramp_color("tint", 0.0)
|
||||
for i in range(1, 201):
|
||||
var c := Catalog.ramp_color("tint", float(i) / 200.0)
|
||||
max_jump = maxf(max_jump, absf(c.r - prev.r) + absf(c.g - prev.g) + absf(c.b - prev.b))
|
||||
prev = c
|
||||
check("램프에 색이 튀는 곳이 없다", max_jump < 0.05, "최대 변화 %f" % max_jump)
|
||||
# 구역 중심에서는 그 구역 값 그대로여야 한다.
|
||||
var center_ok := true
|
||||
|
||||
# 층 안 어디를 찍어도 그 층의 색이어야 한다. 안 그러면 한 화면 안에서
|
||||
# 좌우로 색이 흐르는데, 이제 깊이는 가로가 아니라 층으로 표현한다.
|
||||
var flat := true
|
||||
for i in n:
|
||||
var f := (float(i) + 0.5) / n
|
||||
if not Catalog.ramp_color("sand", f).is_equal_approx(Catalog.ZONES[i].sand):
|
||||
center_ok = false
|
||||
if not is_equal_approx(Catalog.ramp_float("murk", f), float(Catalog.ZONES[i].murk)):
|
||||
center_ok = false
|
||||
check("구역 중심은 그 구역 값 그대로", center_ok)
|
||||
for k in 9:
|
||||
var f := (float(i) + 0.05 + 0.9 * k / 8.0) / n
|
||||
if not Catalog.ramp_color("sand", f).is_equal_approx(Catalog.ZONES[i].sand):
|
||||
flat = false
|
||||
if not is_equal_approx(Catalog.ramp_float("murk", f), float(Catalog.ZONES[i].murk)):
|
||||
flat = false
|
||||
check("층 안에서는 색이 평평하다", flat)
|
||||
|
||||
var deepens := true
|
||||
for i in range(1, n):
|
||||
if Catalog.ZONES[i].murk <= Catalog.ZONES[i - 1].murk:
|
||||
deepens = false
|
||||
check("아래층일수록 흐리다", deepens)
|
||||
|
||||
var picks := true
|
||||
for i in n:
|
||||
if Catalog.zone_at((float(i) + 0.5) / n) != i:
|
||||
picks = false
|
||||
check("가로 위치로 층을 바르게 찾는다", picks)
|
||||
|
||||
|
||||
func _test_serialization_round_trip() -> void:
|
||||
@@ -607,3 +619,57 @@ func _test_upgrades_are_all_production() -> void:
|
||||
check("같은 것을 두 번은 못 산다", not GameState.buy_upgrade(id))
|
||||
check("업그레이드는 칸도 자리도 안 먹는다",
|
||||
GameState.free_cells_in_zone(0) > 0 and GameState.capacity_in_zone(0) > 0)
|
||||
|
||||
|
||||
func _test_day_cycle() -> void:
|
||||
print("하루의 빛")
|
||||
var before := DayCycle.forced_hour
|
||||
|
||||
DayCycle.forced_hour = 5.0
|
||||
check("새벽 5시는 새벽", DayCycle.phase == "새벽", DayCycle.phase)
|
||||
DayCycle.forced_hour = 8.0
|
||||
check("아침 8시는 아침", DayCycle.phase == "아침", DayCycle.phase)
|
||||
DayCycle.forced_hour = 13.0
|
||||
check("오후 1시는 낮", DayCycle.phase == "낮", DayCycle.phase)
|
||||
DayCycle.forced_hour = 18.0
|
||||
check("저녁 6시는 저녁", DayCycle.phase == "저녁", DayCycle.phase)
|
||||
DayCycle.forced_hour = 22.0
|
||||
check("밤 10시는 밤", DayCycle.phase == "밤", DayCycle.phase)
|
||||
# 자정을 넘긴 시각은 전날 밤이 이어져야 한다. 여기서 구간이 비면 이름이 빈다.
|
||||
DayCycle.forced_hour = 1.0
|
||||
check("새벽 1시도 밤", DayCycle.phase == "밤", DayCycle.phase)
|
||||
|
||||
# 가장 밝은 시각이 한낮 언저리에 있어야 한다. 정점 시각 자체는 표에서 조절한다.
|
||||
var peak_hour := 0.0
|
||||
var peak_light := -1.0
|
||||
for i in 480:
|
||||
var h := float(i) / 20.0
|
||||
var l := DayCycle.light_at(h)
|
||||
if l > peak_light:
|
||||
peak_light = l
|
||||
peak_hour = h
|
||||
check("가장 밝은 때가 한낮 언저리", peak_hour >= 10.0 and peak_hour <= 14.0,
|
||||
"%.1f시" % peak_hour)
|
||||
check("한낮 밝기는 1.0", is_equal_approx(peak_light, 1.0), str(peak_light))
|
||||
DayCycle.forced_hour = 0.0
|
||||
var midnight := DayCycle.light
|
||||
DayCycle.forced_hour = 24.0
|
||||
check("자정과 24시가 이어진다", is_equal_approx(DayCycle.light, midnight),
|
||||
"%f vs %f" % [DayCycle.light, midnight])
|
||||
|
||||
# 빛이 튀면 해가 순간이동하는 것처럼 보인다. 하루를 훑으며 급변이 없는지 본다.
|
||||
var max_jump := 0.0
|
||||
DayCycle.forced_hour = 0.0
|
||||
var prev := DayCycle.light
|
||||
for i in range(1, 481):
|
||||
DayCycle.forced_hour = float(i) / 20.0
|
||||
max_jump = maxf(max_jump, absf(DayCycle.light - prev))
|
||||
prev = DayCycle.light
|
||||
check("빛이 튀는 곳이 없다", max_jump < 0.05, "최대 변화 %f" % max_jump)
|
||||
|
||||
var night := DayCycle.tint_at(2.0)
|
||||
var noon := DayCycle.tint_at(12.0)
|
||||
check("밤은 낮보다 어둡다", night.r + night.g + night.b < noon.r + noon.g + noon.b)
|
||||
check("저녁은 붉은 쪽으로 기운다", DayCycle.tint_at(18.5).r > DayCycle.tint_at(18.5).b)
|
||||
|
||||
DayCycle.forced_hour = before
|
||||
|
||||
Reference in New Issue
Block a user