下载 App
《刀光剑影》开发者日志
10/21
音效补齐、动态难度,和一次 OOM 黑屏排查 本期进展
- 打击音效从 0 到 8 个
上一期只有 BGM,没有任何音效——出刀、命中、击杀全靠视觉反馈。这期补上了完整的听觉层:
音效 触发时机 接入点 sfx_swing 出剑 StrokeCombat.Fire() sfx_hit 普通命中 Feedback() sfx_crit 暴击命中 Feedback() sfx_kill 击杀 Feedback() sfx_hurt 玩家受伤 main.HandleUpdate sfx_upgrade 进入三选一 Run.NextChoices() sfx_select 点击按钮 Theme.Button(统一入口) sfx_boss 首领登场 Director 清场前 播放走 8 个 SoundSource 轮转复用,连击时不新建对象。两个设计细节:
lua
复制 -- 1) 素材未就绪时静默跳过,不写负缓存(否则音效永久失效) local sound = cache:GetResource("Sound", SFX_PATH .. name .. ".mp3") if sound and Ready(sound) then sfxSounds_[name] = sound; return sound end return nil
-- 2) Play 的频率参数是绝对 Hz 不是倍率,必须先乘素材采样率 local frequency = (pitch and pitch ~= 1.0) and sound.frequency * pitch or 0 第 2 条是文档里明确警告过的坑。每次播放音高随机 ±6~12%,避免连击时同一声反复播放的机械感。
- 动态难度:怪物跟着玩家强度涨
原来的怪物强度只看幕数,玩家变强后怪物相对变弱,后期会失去挑战。
新增 roguelike/DynamicDifficulty.lua,把玩家强度综合成 0~1 分,再映射成三档缩放:
缩放项 开局 第 6 幕 满配 上限 同屏怪数量 1.02× 1.22× 1.33× 1.45× 精英出现率 +0.5% +6.9% +10.2% +14% 怪物血量 1.01× 1.20× 1.29× 1.40× 怪物伤害 不变 不变 不变 — 关键设计取舍:不缩放怪物伤害。如果直接乘血量/伤害,玩家"变强"会被抵消,变成负反馈;改成只加"威胁密度",变强就读作"面对更猛的敌潮"。
强度评分四个维度加权:
维度 权重 内容 DPS 34% 伤害 ÷ 出刀间隔 等效生命 30% 生命上限 ÷ (1−减伤) × 续航系数 清群 26% 连锁 + 爆炸 + 分光 + 剑气宽度 暴击期望 10% 暴击率 × 暴击倍率 第一版算错了:直接用绝对基准,算出来开局强度分就有 0.50——凭空加难。改成"每个维度先减去开局基线再归一化"后,开局降到 0.036。
两个安全机制:
平滑过渡(0.35/秒):刚选完强化卡不会瞬间加难 受挫回落:12 秒内受伤 3 次以上,强度分下调 0.06,避免"越强越难→越难越死"的死亡螺旋 首领不吃这套缩放,保持设计值。
- 数值重平衡:整体调难一档
上一轮验算发现怪偏软(1.7~2.4 刀),这轮上调:
幕 怪血 怪伤 首领血 首领伤 初锋 70 → 105 6 → 7 500 → 640 12 → 15 百战 95 → 140 8 → 9 850 → 1050 16 → 20 破境 125 → 180 10 → 11 1300 → 1600 20 → 26 霜临 160 → 225 12 → 13 1900 → 2300 24 → 32 血夜 200 → 275 14 → 16 2700 → 3200 28 → 38 终焉 250 → 335 16 → 19 3800 → 4500 32 → 45 玩家侧同步:基础伤害 20 → 22,经验曲线 12+5L+0.35L² → 16+6L+0.45L²(把首幕升级数从 11 次压到 7 次,不再开局连做三选一)。
平衡后验算:刀数 2.18~3.02、首领战 6.6~19.1 秒、硬撑时间 9.0 → 3.3 秒递减。
- 连击门槛从 100 降到 35
代码注释自己承认过:“默认档位门槛是100连击,3秒窗口下很难触及”。3 秒窗口要打满 100 连击需要每秒 33 次命中——同屏最多 32 只怪,物理上做不到。所以 4 张连击卡 + 1 张突破卡开局基本是废牌。
改成门槛 35、窗口 3.5 秒,正常清怪节奏下可达。
- 受击特效放大一倍
原来的圆环不够醒目。先加了三层结构(斩击裂纹 + 四色分层 + 加密火花),用户反馈"还是小",最后把 BASE_SIZE 从 2.8 一路提到 11.2——击杀时环直径 16.2m,超过相机可见高度(11.5m)。
裂纹的关键实现:沿刀刃方向劈开。剑气飞行方向的法线就是刀口走向:
lua
复制 -- 顶点色 a 通道传斩击角度(r=年龄, g/b=色调, a=角度,四通道全占满) local slash = math.atan(dx or 0.0, dz or 0.0) + math.pi * 0.5 池容量 8 → 12,每帧预算 6环/48粒子 → 12环/64粒子。
- 摇杆调大
用户反馈"左下角移动轮盘太小"。查代码发现视觉外环被刻意缩小了——r * (active and 0.82 or 0.62),可见圈只有判定范围的 62%。
改成未激活 88%、激活 100%(可见范围 = 手指能拨的范围),判定半径 0.16 → 0.20,摇杆头 22% → 30% 并加描边。核对过出剑禁区只从 25% 涨到 28% 屏宽,右侧仍留 72% 可出剑。
问题与风险 ① 一次真实的 OOM 黑屏
用户连续反馈"游戏黑屏了进不去了"。排查过程走了几条弯路:
错误判断 1:看到日志里两条 i18n 资源 cannot resolve 警告,断言"旧 UUID 失效导致预载死锁"。查证后发现文件没失效,CDN 上 HTTP 200、CRC 与清单一致——只是没被纳入项目 manifest。 错误判断 2:因为日志里没有 [Startup] 打印,推断"入口没执行"。实际上 Web 默认 -log=WARNING,print 属 INFO 级,看不到很正常。 真实根因是引擎 WASM 内存耗尽——/opt/log/dev/engine.log 的唯一内容就是 OOM。而构建日志显示 17 张 4096×4096 贴图因超过 2048 上限压缩失败,只能以未压缩形式入包,仅基础层就约 1.09GB 显存。
处理:全部降到 2048 以内(原图备份到 _source_backup/),启动预载组显存 515MB → 235MB,全项目 1674MB → 705MB。另外修了 4 张法线贴图的无效压缩配置(format="none" → ASTC_6X6)。
教训:日志缺失不等于代码没执行——先确认日志级别,再下结论。
② 宣传素材被 4MB 上限拒了
发布时 TapTap 报 400 Bad Request - banner 大小需在 4MB 以内。查下来宣传主图 5.16MB,顺带发现两张截图更是 10.35MB 和 11.62MB(它们没报错只是因为主图先被拒)。
全部转 JPEG(quality 92)压到 1MB 以内,画质肉眼无损。
③ 截图不符合 TapTap 规范
用户发来规范后逐条核对,发现两个问题:
宽高比不一致:第 3 张是 1.022(其他 1.778)——规范要求"所有截图宽高比必须保持一致" 实机画面占比不足:第 3 张是纯强化界面截图,而规范要求"实机画面必须超过 50%" 处理:统一裁成 1920×1080,第 3 张换成实机战斗画面,并补到 4 张。
④ 建筑加太多,撞上硬编码上限
加完 15 座建筑后游戏直接报错退出:
[TerrainHeight]:129: [TerrainHeight] 最多注册十四块行走平台 RegisterPad 里有条 assert(#pads < 14)。原地图 11 块平台,新增建筑后到 26 块。查证这条是项目自设的保守值,不是引擎限制——平台查询走 16m 格索引,实测 26 块时单格最多只有 2 个候选。移除上限后无性能影响。
我的疏漏:上一轮加建筑时没先核对 TerrainHeight 里已有的注册上限。
⑤ 宣传片受软件渲染限制
想做 15 秒实机宣传片,但软件渲染约 2 秒/帧,录满 15 秒需要约 5 小时。最终只能录到 95 帧(3.2 秒),其余用截图 + ffmpeg 动效(推近、炫彩字幕)补足。
炫彩字幕做法:文字做灰度遮罩 → 叠四色渐变(青→蓝→紫→金)→ 加同色发光描边。第一次合成时背景变黑(透明通道没生效,四角是不透明黑),用 PIL 把近黑像素 alpha 归零修好。
下期计划 实机试玩动态难度:确认"变强→敌人变多"的手感是否成立,尤其第 6 幕同屏 27 只是否过密 实机试玩新数值:确认前两幕难度是否合适(这轮上调幅度不小) 连击卡实机验证:门槛降到 35 后,连击加成是否终于能触发 跨局解锁(死了留点什么)——"上瘾"的另一半仍未做 把 assets/image/devlog/ 从构建打包范围排除(已欠四期,当前 91MB / 41 个文件仍打进包)





![《刀光剑影》开发者日志
第一期:让刀光"是画出来的",而不是贴上去的
本期进展
1. 核心玩法跑通了
玩家在屏幕上画一笔,这一笔的形状直接变成一道飞出去的刀光。画横线出横劈,画弧线出月牙,画爱心就出爱心刀光。笔画越长刀波越大,画得越快伤害越高。
技术上走的是「屏幕笔画 → 归一化形状 → 程序化几何体」这条路:把玩家的采样点降采样、居中、按最大跨度归一化到 [-0.5, 0.5],再沿着轨迹生成带截图](https://img2-tc.tapimg.com/moment/etag/FvfPjVihDzWqTvVHoCG78xT5dKzo_20261009184001.png/_tap_ugc_m.jpg)