docs: 캐릭터 디자인 가이드, 색상 시스템, UI 에셋 가이드 및 스크립트 추가

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-04-01 22:43:39 +09:00
parent b817885445
commit 24c67f0c4c
4 changed files with 993 additions and 0 deletions

207
scripts/generate_icon.py Normal file
View File

@@ -0,0 +1,207 @@
"""
Archetype: First Spark - App Icon Generator
1024x1024 PNG 앱 아이콘 생성 스크립트
주 색상: #FF6B35 (불꽃-주황)
"""
from PIL import Image, ImageDraw, ImageFilter
import math
def hex_to_rgb(hex_color):
hex_color = hex_color.lstrip('#')
return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
def create_app_icon(size=1024, output_path="app-icon.png"):
img = Image.new('RGBA', (size, size), (0, 0, 0, 0))
draw = ImageDraw.Draw(img)
# === 배경: 깊은 다크 네이비 → 다크 퍼플 그라디언트 ===
bg = Image.new('RGBA', (size, size))
bg_draw = ImageDraw.Draw(bg)
for y in range(size):
t = y / size
# #0D0D1A (top) → #1A0A2E (bottom)
r = int(13 + (26 - 13) * t)
g = int(13 + (10 - 13) * t)
b = int(26 + (46 - 26) * t)
bg_draw.line([(0, y), (size, y)], fill=(r, g, b, 255))
img = Image.alpha_composite(img, bg)
draw = ImageDraw.Draw(img)
cx, cy = size // 2, size // 2
# === 외곽 글로우 링 (주황빛 오라) ===
glow_layer = Image.new('RGBA', (size, size), (0, 0, 0, 0))
glow_draw = ImageDraw.Draw(glow_layer)
for i in range(12, 0, -1):
alpha = int(15 * (i / 12))
radius = int(size * 0.42) + i * 8
glow_draw.ellipse(
[cx - radius, cy - radius, cx + radius, cy + radius],
fill=(255, 107, 53, alpha)
)
img = Image.alpha_composite(img, glow_layer)
draw = ImageDraw.Draw(img)
# === 메인 원형 배경 (진한 그라디언트 구체) ===
circle_layer = Image.new('RGBA', (size, size), (0, 0, 0, 0))
circle_draw = ImageDraw.Draw(circle_layer)
base_r = int(size * 0.42)
for i in range(base_r, 0, -1):
t = 1 - (i / base_r)
# 어두운 중심부에서 불꽃 주황으로
r = int(20 + (120 - 20) * (1 - t**2))
g = int(8 + (40 - 8) * (1 - t**2))
b = int(35 + (15 - 35) * (1 - t**2))
circle_draw.ellipse(
[cx - i, cy - i, cx + i, cy + i],
fill=(r, g, b, 255)
)
img = Image.alpha_composite(img, circle_layer)
draw = ImageDraw.Draw(img)
# === 불꽃 파티클 (배경 장식) ===
spark_layer = Image.new('RGBA', (size, size), (0, 0, 0, 0))
spark_draw = ImageDraw.Draw(spark_layer)
sparks = [
(cx - 180, cy - 200, 6, 180),
(cx + 200, cy - 160, 5, 160),
(cx - 220, cy + 100, 4, 140),
(cx + 180, cy + 180, 5, 150),
(cx - 60, cy - 280, 7, 200),
(cx + 80, cy - 260, 6, 180),
(cx - 260, cy - 40, 4, 130),
(cx + 260, cy - 60, 5, 150),
(cx - 100, cy + 270, 6, 160),
(cx + 120, cy + 260, 4, 140),
]
for sx, sy, sr, alpha in sparks:
for gi in range(sr, 0, -1):
ga = int(alpha * (gi / sr) ** 2)
spark_draw.ellipse(
[sx - gi, sy - gi, sx + gi, sy + gi],
fill=(255, 180, 80, ga)
)
img = Image.alpha_composite(img, spark_layer)
draw = ImageDraw.Draw(img)
# === 중앙 불꽃 형상 (메인 심볼) ===
flame_layer = Image.new('RGBA', (size, size), (0, 0, 0, 0))
flame_draw = ImageDraw.Draw(flame_layer)
# 불꽃 외형 (여러 레이어로 깊이 표현)
# 레이어 1: 큰 불꽃 (오렌지-레드)
flame1 = [
(cx, cy - 270), # 꼭대기
(cx + 120, cy - 140), # 오른쪽 상단
(cx + 160, cy + 30), # 오른쪽 중간
(cx + 90, cy + 200), # 오른쪽 하단
(cx, cy + 240), # 바닥 중앙
(cx - 90, cy + 200), # 왼쪽 하단
(cx - 160, cy + 30), # 왼쪽 중간
(cx - 120, cy - 140), # 왼쪽 상단
]
flame_draw.polygon(flame1, fill=(255, 90, 20, 230))
# 레이어 2: 중간 불꽃 (밝은 오렌지)
flame2 = [
(cx, cy - 220),
(cx + 90, cy - 100),
(cx + 120, cy + 40),
(cx + 60, cy + 180),
(cx, cy + 200),
(cx - 60, cy + 180),
(cx - 120, cy + 40),
(cx - 90, cy - 100),
]
flame_draw.polygon(flame2, fill=(255, 140, 40, 220))
# 레이어 3: 내부 불꽃 (밝은 노란 오렌지)
flame3 = [
(cx, cy - 160),
(cx + 60, cy - 60),
(cx + 80, cy + 60),
(cx + 35, cy + 150),
(cx, cy + 160),
(cx - 35, cy + 150),
(cx - 80, cy + 60),
(cx - 60, cy - 60),
]
flame_draw.polygon(flame3, fill=(255, 200, 80, 210))
# 레이어 4: 핵심 (흰색-노란색 빛)
flame4 = [
(cx, cy - 80),
(cx + 30, cy - 20),
(cx + 40, cy + 50),
(cx, cy + 80),
(cx - 40, cy + 50),
(cx - 30, cy - 20),
]
flame_draw.polygon(flame4, fill=(255, 240, 180, 200))
# 중심 빛 (흰색 글로우)
for i in range(30, 0, -1):
alpha = int(180 * (i / 30) ** 2)
flame_draw.ellipse(
[cx - i, cy + 10 - i, cx + i, cy + 10 + i],
fill=(255, 255, 240, alpha)
)
# 불꽃 블러 (부드럽게)
flame_blurred = flame_layer.filter(ImageFilter.GaussianBlur(radius=3))
img = Image.alpha_composite(img, flame_blurred)
draw = ImageDraw.Draw(img)
# === 원소 심볼 (아크 형태의 빛나는 링) ===
arc_layer = Image.new('RGBA', (size, size), (0, 0, 0, 0))
arc_draw = ImageDraw.Draw(arc_layer)
# 4개의 원소 호 (불, 물, 바람, 땅 의미)
ring_r = int(size * 0.35)
element_colors = [
(255, 80, 20, 180), # 불
(60, 160, 255, 160), # 물
(160, 240, 120, 140), # 바람
(200, 160, 60, 150), # 땅
]
for i, color in enumerate(element_colors):
start_angle = i * 90 - 30
end_angle = start_angle + 60
for w in range(6, 0, -1):
arc_draw.arc(
[cx - ring_r - w, cy - ring_r - w, cx + ring_r + w, cy + ring_r + w],
start=start_angle, end=end_angle,
fill=(*color[:3], int(color[3] * w / 6)),
width=w * 2
)
arc_blurred = arc_layer.filter(ImageFilter.GaussianBlur(radius=2))
img = Image.alpha_composite(img, arc_blurred)
draw = ImageDraw.Draw(img)
# === 상단 광택 효과 (입체감) ===
gloss_layer = Image.new('RGBA', (size, size), (0, 0, 0, 0))
gloss_draw = ImageDraw.Draw(gloss_layer)
gloss_r = int(size * 0.38)
gloss_draw.ellipse(
[cx - gloss_r, cy - gloss_r, cx + gloss_r, cy - gloss_r // 3],
fill=(255, 255, 255, 25)
)
gloss_blurred = gloss_layer.filter(ImageFilter.GaussianBlur(radius=20))
img = Image.alpha_composite(img, gloss_blurred)
# === 저장 ===
final = img.convert('RGBA')
final.save(output_path, 'PNG', optimize=True)
print(f"[OK] 아이콘 저장: {output_path} ({size}x{size}px)")
return final
# 1024x1024 메인 아이콘
create_app_icon(1024, "C:/Users/jaeoh/Desktop/workspace/Archetype-FirstSpark/app-icon.png")
# 512x512 앱스토어용
icon_1024 = Image.open("C:/Users/jaeoh/Desktop/workspace/Archetype-FirstSpark/app-icon.png")
icon_512 = icon_1024.resize((512, 512), Image.LANCZOS)
icon_512.save("C:/Users/jaeoh/Desktop/workspace/Archetype-FirstSpark/app-icon-512.png", 'PNG', optimize=True)
print("[OK] 512x512 앱스토어용 저장 완료")