- 共享引擎(app/src/main):GestureEngine 6 手势识别、AudioEngine 三层音频 (ExoPlayer BGM 交叉淡入 / 对白 ducking / SoundPool 低延迟)、Game/GameEngine 状态机接口、MainActivity 全屏封面 + TalkBack - 《声命线》storyline:末世互动广播剧,3 幕 8 抉择点 4 结局 - 《声东击西》reflex:听声辨位反应,12 波战斗 + BGM 三档 BPM 变速 - 《暗室》escape:3x3 网格密室逃脱,钥匙→铁柜→保险丝→电箱→大门谜题链 - 文档:setup(从零搭建)/ architecture(架构)/ engine-api(引擎 API)/ asset-pipeline(资产流水线)/ games/*(三款游戏设计文档) - 资产管线:Qwen3-TTS 对白 + 程序合成音效/BGM + Mureka BGM 的 gen 脚本与台账, 全部 ogg 资产入库可复现 - env.d 环境脚本:android/tts 入库;sfx/mureka 含密钥不入库,仅提交 .example.sh 模板
71 lines
2.4 KiB
Python
71 lines
2.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""《暗室》封面图:1080x1920 深色底 + 标题大字 + 手势提示。"""
|
|
import os
|
|
from PIL import Image, ImageDraw, ImageFont, ImageFilter
|
|
|
|
BASE = os.path.dirname(os.path.abspath(__file__))
|
|
FONT = os.path.join(BASE, "font.otf")
|
|
OUT = os.path.join(BASE, "..", "res", "drawable", "cover.png")
|
|
|
|
W, H = 1080, 1920
|
|
|
|
img = Image.new("RGB", (W, H))
|
|
d = ImageDraw.Draw(img)
|
|
|
|
# 深色径向渐变底(中心略亮的深蓝黑)
|
|
cx, cy = W / 2, H * 0.42
|
|
for y in range(H):
|
|
for_x = None
|
|
img = Image.new("RGB", (W, H))
|
|
px = img.load()
|
|
for y in range(H):
|
|
for x in range(W):
|
|
dist = ((x - cx) ** 2 + (y - cy) ** 2) ** 0.5 / (W * 0.75)
|
|
v = max(0.0, 1.0 - dist)
|
|
px[x, y] = (int(8 + 18 * v), int(10 + 22 * v), int(16 + 34 * v))
|
|
|
|
d = ImageDraw.Draw(img)
|
|
|
|
# 门框剪影(一道微开的门缝透光)
|
|
door_w, door_h = 420, 760
|
|
dx0, dy0 = (W - door_w) // 2, int(H * 0.30)
|
|
d.rectangle([dx0, dy0, dx0 + door_w, dy0 + door_h], outline=(60, 70, 95), width=10)
|
|
slit_x = dx0 + door_w - 46
|
|
d.rectangle([slit_x, dy0 + 12, slit_x + 18, dy0 + door_h - 12], fill=(220, 230, 200))
|
|
glow = Image.new("RGBA", (W, H), (0, 0, 0, 0))
|
|
gd = ImageDraw.Draw(glow)
|
|
gd.rectangle([slit_x - 60, dy0, slit_x + 80, dy0 + door_h], fill=(200, 215, 170, 60))
|
|
glow = glow.filter(ImageFilter.GaussianBlur(40))
|
|
img = Image.alpha_composite(img.convert("RGBA"), glow).convert("RGB")
|
|
d = ImageDraw.Draw(img)
|
|
d.rectangle([slit_x, dy0 + 12, slit_x + 18, dy0 + door_h - 12], fill=(235, 242, 210))
|
|
|
|
# 标题
|
|
f_title = ImageFont.truetype(FONT, 230)
|
|
f_sub = ImageFont.truetype(FONT, 46)
|
|
f_hint = ImageFont.truetype(FONT, 40)
|
|
|
|
title = "暗 室"
|
|
bb = d.textbbox((0, 0), title, font=f_title)
|
|
tw = bb[2] - bb[0]
|
|
d.text(((W - tw) / 2, H * 0.11), title, font=f_title, fill=(225, 228, 235))
|
|
|
|
sub = "纯 音 频 密 室 逃 脱"
|
|
bb = d.textbbox((0, 0), sub, font=f_sub)
|
|
d.text(((W - (bb[2] - bb[0])) / 2, H * 0.275), sub, font=f_sub, fill=(130, 140, 160))
|
|
|
|
# 手势提示
|
|
hints = ["四 向 滑 动 · 移 动", "单 击 · 检 查 双 击 · 使 用"]
|
|
yy = H * 0.78
|
|
for line in hints:
|
|
bb = d.textbbox((0, 0), line, font=f_hint)
|
|
d.text(((W - (bb[2] - bb[0])) / 2, yy), line, font=f_hint, fill=(150, 158, 175))
|
|
yy += 78
|
|
|
|
tip = "请 佩 戴 耳 机 游 玩"
|
|
bb = d.textbbox((0, 0), tip, font=f_sub)
|
|
d.text(((W - (bb[2] - bb[0])) / 2, H * 0.92), tip, font=f_sub, fill=(100, 108, 128))
|
|
|
|
img.save(OUT)
|
|
print("saved", os.path.abspath(OUT), img.size)
|