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>
134 lines
4.7 KiB
GDScript
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)
|