Files
SoundGame/web/simon/gen/gen_cover.py
T
SoundGame Dev cc0f8c008d HTML5 重写:6 款游戏 + 用户系统 + 全局设置 + 5 轮迭代重构
- 新增 web/ 纯音频 HTML5 平台:共享引擎(手势/WebAudio 三层/设置/震动)、
  Node 零依赖服务器(静态 + 注册登录 + 进度存档 API)
- 游戏:剑与声(RPG 新作)、声命线、暗室(重设计)、音序回响(重设计)、
  节拍行者(跟随领奏重构)、盲航(同向闪避重构);声东击西删除
- 全局功能:男声/女声双套 TTS 资产、三档语速、BGM 音量三档、
  震动反馈语言(10 种震型)、服务器进度同步
- 资产:899 ogg + 899 m4a(Qwen3-TTS 双性别语音、Stable Audio 3 音效、
  Mureka BGM 4+3 首),终检零缺失
- 文档:docs/design/ 13 篇设计审查文档、docs/audio/ 资产管线、
  web/server/README.md API 文档
2026-08-29 21:55:44 +08:00

77 lines
2.6 KiB
Python

"""web/simon 封面:1080x1920,深色底 + 游戏名 + 玩法提示 + 手势图例。"""
import math
import os
import random
from PIL import Image, ImageDraw, ImageFilter, ImageFont
W, H = 1080, 1920
FONT = "/home/ubuntu/SoundGame/app/src/reflex/gen/NotoSansSC-Bold.ttf"
OUT = os.path.join(os.path.dirname(__file__), "..", "cover.png")
img = Image.new("RGB", (W, H), (10, 12, 26))
d = ImageDraw.Draw(img)
# 背景:竖向微渐变 + 随机声波弧线
for y in range(H):
t = y / H
d.line([(0, y), (W, y)], fill=(int(10 + 14 * t), int(12 + 10 * t), int(26 + 30 * t)))
wave = Image.new("RGBA", (W, H), (0, 0, 0, 0))
wd = ImageDraw.Draw(wave)
rng = random.Random(42)
colors = [(94, 234, 212), (129, 140, 248), (244, 114, 182), (250, 204, 21)]
for i in range(28):
cx, cy = rng.randint(0, W), rng.randint(0, H)
r = rng.randint(60, 320)
c = colors[i % len(colors)]
a = rng.randint(10, 36)
wd.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c + (a,), width=rng.randint(2, 5))
wave = wave.filter(ImageFilter.GaussianBlur(1.5))
img.paste(wave, (0, 0), wave)
d = ImageDraw.Draw(img)
# 中央:六边形"声波脉冲"标志
cx, cy = W // 2, 640
pulse_cols = [(94, 234, 212), (129, 140, 248), (244, 114, 182)]
for i, r in enumerate([120, 200, 280]):
col = pulse_cols[i]
alpha = 200 - i * 55
ring = Image.new("RGBA", (W, H), (0, 0, 0, 0))
rd = ImageDraw.Draw(ring)
rd.ellipse([cx - r, cy - r, cx + r, cy + r], outline=col + (alpha,), width=10 - i * 2)
img.paste(ring, (0, 0), ring)
d = ImageDraw.Draw(img)
d.ellipse([cx - 52, cy - 52, cx + 52, cy + 52], fill=(94, 234, 212))
# 标题
f_title = ImageFont.truetype(FONT, 190)
f_sub = ImageFont.truetype(FONT, 56)
f_small = ImageFont.truetype(FONT, 40)
def ctext(y, s, font, fill, spacing=0):
bbox = d.textbbox((0, 0), s, font=font)
w = bbox[2] - bbox[0]
x = (W - w) / 2
for ch in s:
d.text((x, y), ch, font=font, fill=fill)
bb = d.textbbox((0, 0), ch, font=font)
x += (bb[2] - bb[0]) + spacing
ctext(980, "音序回响", f_title, (240, 244, 255), spacing=12)
ctext(1230, "听音辨序 · 复现手势", f_sub, (148, 163, 184))
# 手势图例(两行三列)
hints = ["↑ 上滑 上行琶音", "↓ 下滑 下行低音", "← 左滑 左侧声道",
"→ 右滑 右侧声道", "· 单击 短促鼓点", "·· 双击 两声连响"]
hx, hy = [60, 370, 680], 1400
for i, s in enumerate(hints):
col = i % 3
row = i // 3
d.text((hx[col], hy + row * 84), s, font=f_small, fill=(110, 130, 160))
ctext(1700, "从 2 个音开始,每轮 +1,看你能坚持几轮", f_small, (90, 105, 135))
img.save(OUT)
print(OUT, img.size)