Files
blub-blub/scenes/aquarium/backdrop.gd
T
ttipo 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>
2026-09-02 12:41:36 +09:00

134 lines
4.7 KiB
GDScript

extends Node2D
class_name Backdrop
## 격자 위쪽의 먼 바다.
##
## 3/4 부감이라 화면 위쪽은 "더 먼 곳"이다. 거기까지 모래를 깔면
## 바다가 아니라 사막처럼 보인다. 그래서 위쪽 띠를 물로 비우고,
## 먼 물 → 흐릿한 능선 → 격자 순으로 이어 깊이를 만든다.
##
## 색은 Seabed와 같은 구역 램프를 쓰므로 좌우로 갈수록 자연스럽게 깊어진다.
var rect_size := Vector2(1280, 220)
## 이 높이까지가 먼 바다. 그 아래부터 격자다.
var grid_top := 46.0
## 능선이 격자선 위로 얼마나 솟는지.
const RIDGE_HEIGHT := 9.0
## 능선에 세우는 먼 실루엣 수(구역당).
const SILHOUETTES_PER_ZONE := 4
## 늘 같은 무늬가 나오도록 하는 해시. 다시 그릴 때마다 배경이 흔들리면 안 된다.
func _hash(i: int, salt: int) -> float:
var h := (i * 73856093) ^ (salt * 83492791)
return float(absi(h) % 1000) / 1000.0
## 스트립 가로 위치 f에서 먼 물의 색. 위쪽 끝(카메라에서 가장 먼 곳)이다.
func _far_color(f: float) -> Color:
return Catalog.ramp_color("tint", f).darkened(0.30)
## 능선 언저리의 색. 모래가 물빛에 절반쯤 먹힌 상태.
func _near_color(f: float) -> Color:
var sand := Catalog.ramp_color("sand", f)
var tint := Catalog.ramp_color("tint", f)
# 깊을수록 모래가 더 많이 먹힌다.
var haze := clampf(0.42 + Catalog.ramp_float("murk", f) * 0.55, 0.0, 0.95)
return sand.lerp(tint, haze)
func _draw() -> void:
var cols := GameState.columns()
if rect_size.x < 4.0 or cols <= 0 or grid_top < 4.0:
return
_draw_open_water(cols)
_draw_ridge(cols)
_draw_silhouettes()
## 먼 물. 열마다 사다리꼴을 그리고 정점 색을 먹여 가로·세로로 동시에 번지게 한다.
func _draw_open_water(cols: int) -> void:
var tw := rect_size.x / cols
for col in cols:
var f0 := float(col) / cols
var f1 := float(col + 1) / cols
var x0 := col * tw
var x1 := x0 + tw + 1.0
draw_polygon(
PackedVector2Array([
Vector2(x0, 0.0), Vector2(x1, 0.0),
Vector2(x1, grid_top), Vector2(x0, grid_top),
]),
PackedColorArray([
_far_color(f0), _far_color(f1),
_near_color(f1), _near_color(f0),
]))
## 격자 바로 위에 얹히는 먼 바닥. 물결치는 윗선이 수평선 노릇을 한다.
func _draw_ridge(cols: int) -> void:
var base_y := grid_top - RIDGE_HEIGHT
var steps := maxi(int(rect_size.x / 26.0), 12)
var points := PackedVector2Array()
var colors := PackedColorArray()
for i in steps + 1:
var f := float(i) / steps
var x := rect_size.x * f
var y := base_y - sin(x / 96.0) * 3.4 - cos(x / 41.0) * 1.6
points.append(Vector2(x, y))
# 능선 마루는 조금 밝게. 빛을 받는 면처럼 보인다.
colors.append(_near_color(f).lightened(0.06))
for i in range(steps, -1, -1):
var f := float(i) / steps
points.append(Vector2(rect_size.x * f, grid_top + 1.0))
colors.append(_near_color(f))
draw_polygon(points, colors)
## 능선 위에 서 있는 먼 것들. 무엇인지 알아볼 필요는 없고,
## 시선이 닿는 곳에 뭔가 더 있다는 신호만 주면 된다.
func _draw_silhouettes() -> void:
var zones := Catalog.zone_count()
var zone_w := rect_size.x / zones
var base_y := grid_top - RIDGE_HEIGHT + 1.0
for z in zones:
var tint := Catalog.ramp_color("tint", (z + 0.5) / float(zones))
for i in SILHOUETTES_PER_ZONE:
var seed_id := z * 31 + i
var x := zone_w * (z + 0.12 + _hash(seed_id, 1) * 0.76)
# 낮고 넓게. 가늘고 높으면 비석처럼 서 버려서 먼 풍경으로 안 읽힌다.
# 띠가 아무리 높아도 실루엣은 능선 언저리에 낮게 깔린다.
# 띠 높이에 그대로 비례시키면 감상 모드에서 산맥이 되어버린다.
var band := minf(grid_top, 96.0)
var h := band * (0.16 + _hash(seed_id, 2) * 0.26)
var w := 5.0 + _hash(seed_id, 3) * 11.0
var shade := tint.darkened(0.16)
shade.a = 0.42
if _hash(seed_id, 4) < 0.5:
# 먼 수초. 가늘게 위로 뻗는다.
for blade in 5:
var bx := x + (blade - 2.0) * w * 0.34
var bh := h * (0.55 + _hash(seed_id, 10 + blade) * 0.7)
draw_polygon(
PackedVector2Array([
Vector2(bx - w * 0.16, base_y),
Vector2(bx + w * 0.16, base_y),
Vector2(bx + _hash(seed_id, 20 + blade) * 4.0 - 2.0, base_y - bh),
]),
PackedColorArray([shade, shade, Color(shade, 0.0)]))
else:
# 먼 바위. 낮고 둥근 덩어리.
var pts := PackedVector2Array()
var sides := 7
for s in sides + 1:
var a := PI + PI * (float(s) / sides)
pts.append(Vector2(x + cos(a) * w * 1.6, base_y + sin(a) * h * 0.62))
pts.append(Vector2(x + w * 1.6, base_y))
pts.append(Vector2(x - w * 1.6, base_y))
draw_colored_polygon(pts, shade)