Files

11 KiB
Raw Permalink Blame History

CONTRACT.md — 资产 / 地图 / 代码三方契约(唯一真源)

工作目录:/Users/leidianyayi/Documents/GitHub/ModelTest20260714/Kimi-K3 PRD:同目录 PRD.md(所有剧情/数值/台词以 PRD 为准,本文件只定义技术接口)。

工具路径:

  • Aseprite: /Applications/Aseprite.app/Contents/MacOS/aseprite(用法:aseprite -b --script file.lua;绘制用 app.activeImage:drawPixel(x, y, app.pixelColor.rgba(r,g,b,a));导出用 spr:saveAs("out.png")。已验证可用)
  • Tiled 1.12.2: /Applications/Tiled.app/Contents/MacOS/Tiled(用法:Tiled -e script.js 执行脚本,脚本内用 var f = new TextFile(path, TextFile.WriteOnly); f.write(xml); f.commit(); 写 .tmx;再用 Tiled --export-map --embed-tilesets in.tmx out.json 导出 JSON。已验证可用;注意 -e 模式无编辑器,tiled.open/TileMap 部分 API 不可用,一律走 TextFile 写 XML

1. 目录结构

public/assets/
  tileset.png                 # 256×12816×16 瓦片,16 列 × 8 行 = 128 瓦片
  sprites/player.png          # 64×96:帧 16×244 列(行走帧 0-3) × 4 行(方向 0=down 1=up 2=left 3=right),第 0 帧兼待机
  sprites/npc_afu.png         # 48×243 帧横排 = smile / flat / off
  sprites/npc_yuanyuan.png    # 同上
  sprites/npc_dabao.png       # 同上
  sprites/npc_mimi.png        # 同上
  sprites/npc_fuyisheng.png   # 同上
  sprites/npc_guiyeye.png     # 同上
  sprites/npc_bird.png        # 32×242 帧 = 闭嘴 / 张嘴
  sprites/extra_squirrel.png  # 32×242 帧循环
  sprites/extra_hedgehog.png  # 同上
  sprites/extra_fox.png       # 同上
  sprites/extra_pig.png       # 同上
  sprites/extra_chick.png     # 同上
  portraits/player_smile.png  # 32×32
  portraits/player_real.png   # 32×32E3 用:微笑消失、疲惫真实的脸)
  portraits/afu.png yuanyuan.png dabao.png mimi.png fuyisheng.png guiyeye.png bird.png  # 各 32×32smile
  maps/house.json plaza.json bakery.json shop.json clinic.json lake.json field.json well.json fog.json white.json
  maps/*.tmx                  # Tiled 中间产物(保留在仓库)
  maps/tileset.tsx            # 外部瓦片集描述,image source="../tileset.png"
tools/aseprite/*.lua          # 素材生成脚本(保留)
tools/tiled/genmaps.js        # 地图生成脚本(保留)
src/                          # 游戏代码

素材规范(PRD §11):糖果色板(草绿 #7DC95E/#5FB34A、奶油黄 #FFF3D6、蜜糖橙 #F5A623、天空蓝 #8FD3FF、石路 #E8DCC8、湖水 #6EC1E4/#4FA8D8、粉 #F7B7CB、棕 #8B5A2B),白色层独立灰白板(#F5F5F5/#DADADA/#BDBDBD)。无抗锯齿、无渐变。off 档与 smile 档差异控制在 1–2 像素(如一只眼低 1px 或嘴角多 1px),不许鬼脸。背景透明(alpha=0)。PNG 必须精确尺寸(上面列出)。

2. 瓦片 ID 表(tileset.pngid = 行×16+列;地图数据 gid = id+1

id 名称 id 名称
0 草地 1 草+花A 2 草+花B 3 深草
4 土路 5 石路 6 石路alt 7 广场砖
8 湖水A 9 湖水B 10 水岸北 11 水岸南
12 水岸西 13 水岸东 14 花坛粉 15 花坛黄
16 莓果丛(满) 17 莓果丛(空) 18 树左上 19 树右上
20 树左下 21 树右下 22 圆灌木 23 树桩
24 石头 25 路标 26 栅栏横 27 栅栏竖
28 路灯 29 彩带红 30 彩带黄 31 彩带蓝
32 奶油墙 33 奶油墙+窗 34 木板墙 35 橙屋顶左
36 橙屋顶中 37 橙屋顶右 38 门 39 门框顶
40 窗 41 烟囱 42 井左上 43 井右上
44 井左下 45 井右下 46 邮筒 47 公告板
48 木地板A 49 木地板B 50 室内墙 51 室内墙+窗
52 床左上 53 床右上 54 床左下 55 床右下
56 柜台左 57 柜台右 58 烤炉 59 空货架
60 满货架 61 桌子 62 椅子 63 地毯
64 日历 65 配方纸 66 文件纸 67 野餐布左上
68 野餐布右上 69 野餐布左下 70 野餐布右下 71 长椅左
72 长椅右 73 凳子 74 柜子 75 冰箱
76 水槽 77 盆栽 78 木桶 79 木箱
80 雾A 81 雾B 82 雾C 83 白色地板
84 白色墙 85 白墙板 86 白门 87 白床左上
88 白床右上 89 白床左下 90 白床右下 91 白柜
92 白监护仪 93 白窗帘 94 白地板alt 95 白天花灯
96 梯子 97 桥横 98 桥竖 99 蘑菇
100 花红 101 花黄 102 花蓝 103 蜂蜜罐
104 面包 105 信件 106 书 107 蜡烛
108 篮子 109 水桶 110 扫帚 111 蛋糕
112 红屋顶左 113 红屋顶中 114 红屋顶右 115 蓝屋顶左
116 蓝屋顶中 117 蓝屋顶右 118 粉墙 119 粉墙+窗
120 石墙 121 石墙+窗 122 遮阳篷 123 钟
124 旗 125 花环角 126 花环中 127 空(透明)

3. 地图契约(Tiled 导出 JSON;图层固定三层)

每层地图固定:

  • ground(瓦片层,无碰撞;必须全部用不透明瓦片——透明装饰瓦片只能放 obstacles 层,否则透明处露出黑底。大花 100-102、蘑菇 99、门 38 已在 tileset 里烘焙不透明底,可放 ground)
  • obstacles(瓦片层,带图层自定义属性 collides=true(bool);代码对该层整层碰撞,但彩带(瓦片 29-31)在代码里排除碰撞、可穿过)
  • objects(对象层,点对象)

对象约定(name + 自定义属性):

  • name="spawn",属性 id(string):出生点。各图必有 default;室内图另有 from_plazaplaza 有 from_house/from_bakery/from_shop/from_clinic/from_lake/from_field/from_well/from_fog
  • name="door",属性 target(string, 目标地图 key) + spawn(string, 目标 spawn id)。双向成对。
  • 落点安全:spawn 与任一门判定点的距离(以玩家测量点 y-8 计)必须 >14px(惯例 ≥24px);运行时另有门武装机制兜底——落到门旁时须先离开感应圈(>18px)才允许触发,且触发即消耗武装(须再次离开 >18px 才可重新触发),防止落点回弹与对话关闭帧的重复触发。
  • 2x2 拼块瓦片(树 18-21、井 42-45、床 52-55、野餐布 67-70、白床 87-90):四个象限占用连续 4 个 id;tileset 生成时把下半两个象限从自然落点(id+16/id+17)复制到 id+2/id+3。
  • name="npc",属性 id(string)afu / yuanyuan / dabao / mimi / fuyisheng / guiyeye / bird / squirrel / hedgehog / fox / pig / chick。
  • name="interact",属性 id(string):见下表。

10 张地图(尺寸 = 瓦片数;场景 key 同文件名):

key 文件 尺寸 必填对象
house maps/house.json 12×8 spawn(default,from_plaza)、door→plaza、interact:bed、interact:calendar
plaza maps/plaza.json 30×20 spawn(default,from_house,from_bakery,from_shop,from_clinic,from_lake,from_field,from_well,from_fog)、door×8(house/bakery/shop/clinic/lake/field/well/fog)、npc:afu(公告板旁)、npc:bird(树上)、npc:squirrel、npc:fox、interact:noticeboard、interact:flowerbed_1/2/3
bakery maps/bakery.json 15×10 spawn(default,from_plaza)、door→plaza、npc:yuanyuan、interact:counter_bread、interact:recipe_paper
shop maps/shop.json 15×10 spawn(default,from_plaza)、door→plaza、npc:mimi、npc:hedgehog、interact:shelf_count
clinic maps/clinic.json 12×8 spawn(default,from_plaza)、door→plaza、npc:fuyisheng、interact:doctor_desk
lake maps/lake.json 24×16 spawn(default,from_plaza)、door→plaza、npc:guiyeye(长椅旁)、npc:pig、interact:picnic_mat
field maps/field.json 20×14 spawn(default,from_plaza)、door→plaza、npc:chick、interact:berry_1..5、interact:water_point
well maps/well.json 14×10 spawn(default,from_plaza)、door→plaza、interact:ancient_well
fog maps/fog.json 20×8 spawn(default,from_plaza)、door→plaza、interact:fog_wall
white maps/white.json 20×14 spawn(default);上半 20×6 走廊 + 左下 10×8 病房,内部连通;白色层瓦片(83–95)

地图要点(PRD §10):plaza 为枢纽,含公告板(47)、花坛(14/15)、双子鸟树(18-21)、庆典空地;室外场景用草地/石路/栅栏/房屋装饰铺满,lake 有大片湖水(8-13)与长椅(71/72)field 有莓果丛(16)well 有古井(42-45)fog 边缘铺雾(80-82)white 全用白色层瓦片(走廊+病房,病房内放白床 87-90 与白监护仪 92)。室内场景:墙(50/51)围边、木地板(48/49)、家具按 PRD §10 要点摆放。house 必须有床(52-55)与日历(64)bakery 有柜台(56/57)、烤炉(58)、配方纸(65)shop 有货架(59/60)clinic 有桌子(61)+文件纸(66)。

4. 代码架构契约(Phaser 3 + TS + Vite

  • src/main.tsPhaser config,逻辑 320×180pixelArt:truescale:{ mode: Phaser.Scale.NONE, autoCenter: CENTER_BOTH, parent:'game' } + window resize 监听做整数倍放大(canvas style 宽高 = 320k×180kk = max(1, floor(min(winW/320, winH/180))))。
  • 场景 keyBootScene / TitleScene / HouseScene / PlazaScene / BakeryScene / ShopScene / ClinicScene / LakeScene / FieldScene / WellScene / FogScene / WhiteScene / UIScene / DreamScene / EndingScene
  • 地图场景共用基类 MapScenesrc/scenes/MapScene.ts):子类只提供 mapKey 与少量钩子。基类负责:tilemap 加载(this.load.tilemapTiledJSONtileset 名 candy 对应 assets/tileset.png)、player96 px/s、四向动画 walk-down/up/left/right + 待机帧、obstacles 层碰撞、相机跟随+钳制)、≤24px 出「按 E」提示(交 UIScene 显示)、door 切换(fade out/in + 目标 spawn)、NPC/群演实例化与表情档切换、注册 CorruptionDirector 的视听参数(相机 postFXColorMatrix 饱和度 / Vignette 晕影 / 负片单帧)。
  • CorruptionDirectorsrc/core/CorruptionDirector.ts):普通 TS 单例 + Phaser.Events.EventEmitter。状态:playerName、startTime(ms)、day(1-7)、happiness(0-100)、awareness(0-100)、clues(Set)、tasksDone(Set)、refusedTasks(Set)、escapedCelebrations、stage 派生值。事件名:happiness awareness stage day clue task celebration ending。是全部状态唯一真源;提供 debug 用 setter。
  • 对话:UIScene 底部通栏框(说话者名 + 头像 + 文本,E 推进;选项三选:温柔/迟疑/否定);文本腐蚀在渲染层按 §6.5 表替换(原文案干净)。头像用 portraits/。
  • 音频:src/audio/AudioEngine.ts,WebAudio 程序合成(无音频文件)。
  • Debugsrc/debug/DebugPanel.ts,反引号键开关的 DOM 面板(默认隐藏、无 UI 痕迹):设幸福/觉知/日(1-7)/传送场景/置齐线索/强制结局 E1-E4。
  • 数值与台词逐字逐数实现 PRD §5–§9。

5. 验收相关硬约束

  • npm run buildtsc --noEmit + vite build)必须零错误;npm run preview 可玩。
  • 无 localStorage、无外部网络请求、无外部音频/字体文件。
  • 控制台零报错。