下载 App
庇护所游戏公社的开发日志【第一篇】
3 小时前17 次播放综合
开发日志【第一篇】
今天是公布TapTap 聚光灯 GameJam 的主题的第三天
项目绑定出了点小问题。一直拖到了现在才发布日志~
1.对于我来说,本身就有制作游戏的计划,借活动要求产出开发内容的机会同步制作教程,教程同步制作的游戏会直接上架,后续会通过视频更新向关注我的小伙伴公布数据。
2.看到平台有发帖的需求,我想既然要写文章做视频,为什么不借此机会做一个taptap制造的游戏制作教程呢?这样既完成了平台的任务,又给自己和平台做了宣传。所以本人打算做一期【taptap制造的游戏开发系列教程】,并将教程发布于全平台,后续我会公布策划案,完善数值系统。宣传内容,教程主打低门槛,只要跟着操作就能做出游戏,全程可用AI实现功能,不仅教游戏制作还覆盖商业化变现全流程,目前倾向于免费开放。
- 目标:全面战争(局外)+骑马砍杀(局内)+ 三国 + 抽象穿越
- 规则:游戏没有任务,没有剧情。将所有的事件数值封装成参数。会考虑接入AI大语言模型接管游戏世界,从简到繁给玩家涌现不一样的游戏体验!
- 挑战:玩家不会扮演刘备等历史角色,将从平民到小兵,到将军,到匡扶汉室的历史使命。
- 反馈:我希望我的游戏能让玩家不再是历史人物的陪衬。在游戏的体验过程中,能体会到每一个士兵,不再是一串冰冷的数字,他们是兄弟,是家人,是陪你同生共死的坚定战友!是跟你从一个村子里走出去,并且能与你一起见证辉煌的伙伴。
现有局内技术架构(核心设计)
┌─────────────────────────────────────┐
│ NanoVG 层(每帧) │ 2D 序列帧士兵 / 轨迹 / 粒子 / UI
├─────────────────────────────────────┤
│ 3D 视口(renderer:SetViewport) │ lowpoly 地面+树木+城市,65° 正交相机
└─────────────────────────────────────┘
- 底层 3D:只负责“环境”——贴图地面、树石花羊,纯装饰不参与逻辑
- 上层 NanoVG:负责所有“游戏对象”——士兵、箭矢、指令轨迹、UI,全部是 2D
- 两层对齐的命脉:common.viewSquash = 0.906(=sin65°),w2s/s2w 里 y 轴乘/除这个系数,让 2D 逻辑坐标精确落在 3D 地面上;3D 相机每帧从 2D 相机同步(orthoSize = sh/zoom,位置 (cam.x, 453, -cam.y - 211))
- 这个设计的意图:士兵用像素序列帧保证美术风格可控,环境用 3D lowpoly 保证氛围,两边解耦,互不干扰。

3.一下是复刻此效果的提示词,游戏源码大概率也会全部开源,供大家参考和利用,希望大家三连击加关注!
下面是完整文档,直接复制发给那个 AI 即可:


《穿越之我在三国匡扶汉室》局内系统 — 提示词
面向 AI 的还原指南:按本文档主题逐章实现,即可还原“军团遭遇战”局内。 所有数值均为原项目真实参数,直接照抄;标注 ★ 的是踩过坑的关键点。
0. 总览与技术栈
| 项 | 内容 |
|---|---|
| 引擎 | UrhoX(Lua 5.4),渲染 = 3D 视口(底层)+ NanoVG(上层) |
| 文件 | main.lua(入口/事件)、common.lua(相机/输入/绘制辅助)、res.lua(精灵切帧)、audio.lua(声音)、battle3d.lua(3D 战场)、battle.lua(战斗核心)、gen_field.lua(离线纹理工具) |
| 渲染分层 | 3D 正交相机画 lowpoly 地面与装饰(视口 0)→ NanoVG 在其上画 2D 序列帧士兵、轨迹、UI。两层像素级对齐 |
| 核心铁律 | ★ 所有输入物理像素先 /dpr 转逻辑坐标;★ nvgCreateFont 只调一次;★ 事件数据用 eventData:GetInt("X") 形式 |
1. 局内地图(3D lowpoly 战场)
尺寸:战场逻辑区域 5500 × 2800。世界 (x,y) 映射到 3D 为 Vector3(x, h, -y)(3D z = -y,y+ 是“南”=屏幕下方)。
地面(两层):
- 外圈深绿大地:Box.mdl,颜色 (0.32, 0.46, 0.28),scale (5500*2.4, 1, 2800*2.4),y = -0.55(挡住贴图外视野)
- 主地面:Plane.mdl + 贴图 Textures/field_map.png(1024×512,一像素≈5.37 世界单位),技法 Techniques/Diff.xml,scale (w, 1, h),y = 0.1。★ 不要用 Box 当贴图地面(六面 UV 不可靠);贴图缺失时兜底纯色草地材质
地表纹理(gen_field.lua,headless 生成,固定种子保证每次一致):
- 底色暖黄绿 RGB(0.495, 0.660, 0.385)+ 大尺度明暗波(两重 sin 叠加,幅度 0.03/0.02)
- 云状斑:5 个超大层次云(r 320~540,alpha 0.20)+ 14 个深绿主斑(r 110~300,alpha 0.75,feather 0.55)+ 8 个浅斑(r 70~180,alpha 0.42)——全部用 smoothstep 径向渐变叠加
- 3 个湖(纹理坐标 (135,80,r48)、(775,385,r34)、(935,135,r38)):外围 70px 浅滩雾 → 米黄沙岸圈 → 边缘 14px 羽化的青色水面(中心更深)
- 细噪点:每像素亮度 ±0.0225,营造颗粒感
- 运行:UrhoXRuntime gen_field.lua -tapcode_dir=... -tool_mode -graphicssurfaceless,产物移入 assets/Textures/
装饰物(全部纯色 PBR 材质 PBRNoTexture.xml,金属度 0 粗糙度 0.9,同色缓存复用;math.randomseed(777) 固定布局):
| 物件 | 数量 | 构成与配色 |
|---|---|---|
| 阔叶树 | 70 | 棕干圆柱(0.40,0.28,0.19)高 s*1.05 + 球簇冠(0.36/0.44, 0.62/0.68, 0.30/0.34 两种绿)+ 55% 副冠球 + 35% 第三球 + 28% 挂 2 颗红果(0.80,0.28,0.20);s∈[30,52] |
| 针叶树 | 60 | 短干 + 大圆锥(0.25,0.51,0.33)+ 顶部小圆锥(0.30,0.58,0.38);s∈[28,46] |
| 石头 | 20 | 压扁灰球 (0.66,0.67,0.63),纵向压到 0.48 倍,s∈[6,17] |
| 草丛 | 55 | 深绿小球 (0.27,0.47,0.26),s∈[4,9] |
| 白花簇 | 12 组 | 每组 4-5 朵小白球 (0.94,0.95,0.90) 散布半径 ±5.5 + 中心黄芯 (0.92,0.82,0.35) |
| 红蘑菇 | 20 | 白柄圆柱 (0.90,0.86,0.78) + 红帽扁球 (0.78,0.30,0.22) + 白点,s∈[4,7.5] |
| 白羊 | 3 | 白球身体 (0.93,0.93,0.90) 10×7×7 + 黑头球 (0.30,0.28,0.26),随机朝向 |
- 每棵树/石下方放贴地假影盘:半透明扁圆柱 PBRNoTextureAlpha.xml 色 (0.05,0.10,0.04,0.28),scale (1.8r, 0.35, 1.8r),位置偏移 (-0.12r, +0.06r)
- ★ 装饰物不参与碰撞,单位可从树旁穿过
- ★ Scene 必须 CreateComponent("Octree") 且手动设置包围盒覆盖全图(默认 ±1000 会剔除界外树):BoundingBox(Vector3(-w,-100,-(h+2000)), Vector3(w,900,h+2000))
2. 视角与相机(65° 斜俯视,全系统对齐的命脉)
视角定义:65° 俯角 + 正交投影(能看到树干、树冠悬浮在前的“休闲手游 3/4 视角”,非 90° 正俯视)。
坐标换算(NanoVG 层与 3D 层对齐的唯一真相):
luacommon.viewSquash = 0.906 -- = sin(65°), 地面深度在屏幕上的压缩系数 -- s2w: (sx-sw/2)/zoom + cam.x , (sy-sh/2)/(zoom*viewSquash) + cam.y -- w2s: (wx-cam.x)*zoom + sw/2 , (wy-cam.y)*zoom*viewSquash + sh/2
★ 压缩系数是 sin(俯角),不是 cos!cos65°≈0.42 是“高度抬升”系数——弄反 = 树冠朝下、画面南北颠倒。
3D 相机:
luacamNode.position = Vector3(cam.x, 453, -cam.y - 211) -- 453=sin65°*500, 211=cos65°*500 camNode:LookAt(Vector3(cam.x, 0, -cam.y), Vector3(0, 1, 0)) -- ★ 必须在战场南侧(z 取负), up 用世界 Y camera.orthographic = true; camera.nearClip = 1; camera.farClip = 2500
每帧同步:camera.orthoSize = common.sh / cam.zoom(orthoSize 是视野全高)。
行为:
- 初始 zoom = 屏幕高/780,相机对准 (1250,1400)(蜀军后侧)
- 平滑跟随蜀军存活单位质心:插值系数 k = min(1, dt*2.5)
- 边界 clamp:横向 vw = sw/zoom,纵向 vh = sh/(zoom*viewSquash)(★ 纵向要除 squash);视野小于战场时 cam 限制在 [vh/2, FIELD-vh/2],超出则居中
- 缩放:滚轮 ±10%、双指 pinch(两指距离比,以中点为锚),zoom 范围 0.18~2.5;缩放锚点算法 = 先记锚点世界坐标 → 改 zoom → 反推 cam 使锚点屏幕位置不变
- 桌面调试:右键拖动平移、ESC 判负
光照(★ 全文档最大的坑):必须用引擎 LightGroup 预设——cache:GetResource("XMLFile", "LightGroup/Daytime.xml") 然后 节点:LoadXML(lgFile:GetRoot()),再把 Zone 包围盒扩到战场外并设 fog(fogStart=1800, fogEnd=6000)。手搓 DirectionalLight/Zone 在本引擎不生效,会退化为相机默认点光 = 全屏黑圆光斑。
3. 兵种与武将数据
兵种数值表(speed 单位 px/s,全部单位共用一套状态机):
| type | 显示高h | hp | dmg | 射程 | 攻击间隔 | 速度 | 视野 | 远程 | 攻击动画时长 |
|---|---|---|---|---|---|---|---|---|---|
| spear 枪兵 | 36 | 120 | 13 | 26 | 1.1 | 86 | 640 | 否 | 0.4 |
| blade 刀盾 | 36 | 180 | 11 | 24 | 1.0 | 78 | 640 | 否 | 0.4 |
| bow 弓兵 | 34 | 80 | 10 | 210 | 1.7 | 82 | 700 | 是 | 0.55 |
| cav 骑兵 | 56 | 160 | 17 | 32 | 1.2 | 185 | 800 | 否 | 0.45 |
| hero 武将 | 70 | 700 | 28 | 38 | 0.9 | 160 | 840 | 否 | 0.45 |
布阵(cx, cy = 营中心,人数,列数;行距 14+随机6、列距 16+随机6,形成松散方阵):
蜀军(玩家,左侧,朝右):
枪兵营 1350,1150 ×28(6列) 刀盾营 1350,1700 ×20(5列)
弓兵营 1050,1400 ×18(4列) 骑兵营 850, 950 ×12(4列)
关羽 1500,1330 张飞 1500,1480 → 共 80 人
魏军主力(右侧,朝左,大营决战):
枪兵 4200,1150 ×26(6列) 刀盾 4200,1750 ×20(5列)
弓兵 4550,1420 ×18(4列) 骑兵 4750, 950 ×10(4列)
曹操 4000,1430 → 共 75 人(顶部血条按 80/76 计)
攻城战(可选):守军数量由外部配置 {spear=9, blade=7, bow=7, cav=0} 在
(4150,1300)/(4180,1560)/(4450,1420)/(4700,1000) 生成(3列),可选守城武将 (4050,1430)初始扰动:攻击冷却 rand0.5(错开出手)、扫描计时 rand0.2、动画相位 rand*2。
4. 小兵实现(状态机 + 战斗 AI)
单位结构:x/y/side/type/def/hp/maxhp/legion/sheet/state/face(1右-1左)/animT/atkAnimT(-1=未攻击)/shotDone/atkCd/tx/ty/hasCmd/moveSpd/foffX/foffY(初始阵位偏移,敌军推进复用)/target/dead/deadT/selected/hurtT/scanT。
更新优先级(每帧,从高到低):
- 攻击动画期(atkAnimT≥0):完全不动不索敌;推进动画;弓兵在动画过半(atkDur*0.5)时发射追踪箭(每箭只发一次);动画结束回 -1
- ★ 移动命令最高优先级(hasCmd=true):坚决走向 (tx,ty),不索敌、不还手、不自动接战——这就是“交战中也能把部队拉走”的手感来源;用编队统一速度 moveSpd(而非自身速度);距离 <2.5 判到达,清 hasCmd/moveSpd 恢复自动战斗。下令瞬间强制收招(atkAnimT=-1, shotDone=false)
- 自动战斗:每 0.25+rand*0.2 秒扫描一次(省性能);目标死亡或超出视野²×1.6 则丢弃;无目标则取视野内最近敌人
- 有目标:距离 ≤ 射程 → 站定、转向、冷却好了就开攻(近战立即结算伤害+2颗星芒;弓兵只起手动画,放箭在过半帧);距离 > 射程 → 以自身速度直追
- 无目标:idle,动画慢放(animT += dt*0.4)
- 边界 clamp:x∈[30, W-30],y∈[40, H-20]
移动:无寻路——moveToward 直线奔向目标点(归一化方向×速度×dt),|dx|>3 时更新朝向;到达阈值 2.5。
寻路说明(重要):本作没有 A*/导航网格。战场无障碍物(树石纯装饰),移动 = 直线求位;单位间靠分离力防重叠:双重循环,半径近战 8/远程 7,最小间距 = 两半径之和,重叠时沿连线各退重叠量的一半。这个“直线 + 分离”的组合在军团混战里观感自然,不需要真寻路。
死亡:hp≤0 → dead=true、军团存活数 -1、计杀数(蜀方击杀/魏方击杀)、死亡点 5 颗星芒;渲染时倒地(旋转 1.35rad × 朝向)并在 2.5s 内渐隐。
5. 武将实现
- 数值用 hero 行(hp 700,肉度=6 个小兵),不编入军团(独立单位),可被框选/受指挥
- 头顶标识:26px 圆角红框头像(portraits.png 2×2:0关羽 1张飞 2刘备 3曹操;外框红 (230,60,80) 圆角 0.22)+ 金色名字 13px(255,235,180)
- 血条恒显(小兵只在受伤后 3s 内显示):20×3,黑底 130a + 红条 (230,70,60)
- 阵亡:头顶飘红字“关羽 阵亡!”(0.9s 上飘)
- 四武将骑马精灵图(2×2 帧),渲染 dispH=70
6. 控制与输入(手游单指/双指 + 桌面鼠标)
指针抽象(common.lua):统一单指主指针 {x,y,downX,downY,dragging,...} + touches 表管理多指。
- 拖拽阈值:移动曼哈顿距离 >10 才算 dragging(否则是 tap)
- 第二根手指按下 → 取消单指拖拽,进入 pinch:两指距离比 → 以中点为锚缩放,中点位移 → 平移
- 桌面:左键=主指针,右键拖=平移相机,滚轮=以鼠标位置为锚 ±10% 缩放,ESC=判负
战斗内手势(battle.onPointer):
| 手势 | 行为 |
|---|---|
| 按下命中 UI | "选择全部"按钮 → 全选蜀军;兵种卡 → 选中该营全部存活单位(设 uiHit 屏蔽后续) |
| 按下命中战场 | 无动作,等待 up |
| 抬起 & 未拖拽(tap) | 屏幕→世界坐标,半径 34/zoom + 26(随缩放自适应)找最近敌军:找到→对选中部队下令攻击;没找到→下令移动到该点 |
| 抬起 & 拖拽(框选) | 框两端转世界坐标取 min/max,选中矩形内所有蜀方存活单位 |
命令标记:下令移动同时在目标点放“旗标”(双层脉动黄色椭圆 14×6,存活 1.2s)。
7. 编队与指令(平移阵型,本作手感核心)
下令移动的算法(★ 逐条照做):
1. 计算选中单位质心 (cx,cy)
2. slowest = 选中单位里最慢的 def.speed(编队统一速度,枪兵拖骑兵)
3. 偏移 off = 目标点 - 质心
4. 每个单位:tx = clamp(自身x + offX), ty = clamp(自身y + offY)
→ 阵型"整体平移",保持当时相对队形(不是重排格点!)
5. hasCmd=true、moveSpd=slowest、收招(见 §4)
6. 每个士兵记录一条轨迹 {x0,y0 → x1,y1, t},生命 2.4s轨迹渲染(全军平移时一束平行轨迹,视觉签名):
- 淡入淡出 alpha = min(1,t×6) × min(1,(2.4-t)×1.5)
- 虚线:所有轨迹的虚线段合并成一个 path 一次描边(段长 10、间隔 8、两端各留 6/12),色 (150,205,255) 175a,线宽 2
- 终点圈 = “小兵脚下的黄圈”:所有圈合并一个 path,半径 7×(1+0.06sin(5t)) 脉动,贴地椭圆纵横比 0.8(65° 俯角),色 (255,210,60) 220a,线宽 2.5——与选中圈同款
- 起点圈:小实心圆 r2.6,(150,205,255) 150a
敌军 AI:大营决战开战 4s 后一次性触发——目标=蜀军存活质心,魏军各单位 tx=cmdX+初始阵位偏移foffX, ty=cmdY+foffY*0.6(纵向压缩 0.6,整阵压上);攻城战守军原地不推进。
胜负:任一方全灭即 win/lose,停 BGM,3.2s 后置 finished+result 交外部场景处理。
8. 美术规格(精灵图 / 色调 / 风格 / 生成方法)
8.1 精灵图(序列帧)
- 规格:网格表,第一行 = walk 循环帧,第二行 = attack 帧,横向排帧、透明背景
- 枪/刀盾/弓:1536×1024 整图 = 4×2 帧(单帧 384×512,walk 4 帧 + atk 4 帧)
- 骑兵/武将:768×1152 整图 = 2×2 帧(单帧 384×576,各 2 帧)
- 头像 portraits.png:1024×1024 = 2×2(0关羽 1张飞 2刘备 3曹操),红框在代码里画
- 来源:原项目为 AI 像素画生成(角色概念图 → sprite-gen 序列帧化)。★ 最稳做法:直接拷贝原项目的 PNG。若要重生成,按此规格投喂 sprite-gen:Q版 2 头身像素小人、纯侧视、~2px 像素颗粒、透明底
- 风格基调:蜀军 = 青蓝布甲+绿意点缀;魏军 = 黑铁甲+暗红点缀;双方同骨架不同配色,远看蓝红阵营分明
8.2 切帧绘制(res.lua,逐字照抄的算法)
lua-- dispW = dispH * (帧宽/帧高);(cx,by)=脚底中心,y = by-dispH -- 帧取样:nvgImagePattern(ctx, x - col*帧宽*sc, y - row*帧高*sc, 整图宽*sc, 整图高*sc, 0, img, 1) -- 再 nvgRect(x,y,dispW,dispH) + nvgFillPaint + nvgFill -- 水平翻转:nvgTranslate(ctx, 2*cx, 0) + nvgScale(ctx, -1, 1) -- 动画帧号:atk = floor(atkAnimT/攻击时长 × atk帧数);move = floor(animT*8);idle = floor(animT*2)
显示高度:小兵 34~36、骑兵 56、武将 70;朝向 flip = face<0。
8.3 色调总表(全部 RGBA,照抄即还原)
| 用途 | 颜色 |
|---|---|
| 草地底色/深斑/浅斑 | (0.495,0.660,0.385) / (0.335,0.525,0.300) / (0.560,0.700,0.430) |
| 单位脚下假影 | (30,40,20) 60a |
| 选中圈/轨迹终点圈 | (255,210,60) 220a,呼吸 6% |
| 轨迹虚线/起点圈 | (150,205,255) 175a / 150a |
| 顶部蓝条/红条/底槽 | (70,130,220) / (220,70,60) / (40,34,30,200) |
| 人数牌 | (240,120,100),中间击杀框 (35,30,28,235) |
| 卡片体/描边/选中态 | (244,228,196) / (120,96,60) / (255,170,40) 3px |
| 全选按钮 | 底 (232,214,178) 字 (80,60,36) |
| 卡片人数牌 | (60,110,200);军团血条 (80,170,80) |
| 单位血条 | 底黑 130a + (230,70,60) |
| 结算大字 | 胜 (255,215,90)"大 捷" / 负 (235,90,70)"败 退",52px |
| 横幅标题/副标题 | (255,240,200) 26px / 白 15px |
| 星芒粒子 | 纯白 8 角星(外角 r、内角 r*0.35 交替) |
| 血滴 | 蜀 (90,120,200) / 魏 (200,90,80) |
| 武将名 | (255,235,180) |
| 命令旗标 | (255,220,80) 双层脉动 |
9. UI 布局(屏幕层,精确数值)
- 顶部兵力条:宽 min(sw×0.56, 560) 高 14,y=20 居中;底槽圆角 8;蓝红条各占条宽×0.485×(存活/初始 80|76);中央 68px 击杀框显示“我方杀敌 | 我方阵亡”;两端 48px 人数牌
- 底部卡片栏:卡 88×62、间距 10、“选择全部” 104 宽,整排水平居中,距底 18;每卡:上方 44×18 蓝牌显示存活数、卡内左侧兵种立绘(walk 第 0 帧,高 46)、底部存活率绿条;该营有选中单位时描边变橙 3px
- 框选框:白 26a 填充 + 白 200a 1.4px 描边
- 开场横幅:前 2.6s,标题在屏幕高 24% 处
- NanoVG 世界变换:translate(sw/2, sh/2) → scale(zoom) → translate(-cam.x, -cam.y);世界层内文字想保持屏幕等大就再 scale(1/zoom) 补偿(飘字这么做)
10. 粒子 / 箭矢 / 飘字
- 星芒(打击反馈):击杀 5 颗、命中 2 颗;初速横向 ±45 纵向 -30~-90,重力 140/s²,生命 0.35~0.6s,白色 8 角星渐隐
- 血滴:4 颗圆点,生命 0.4s,同上重力
- 追踪箭(弓):出手位置 (x+朝向×8, y-16),速度 430、寿命 2.2s、命中半径 12,每帧更新瞄准点(目标活着就追踪 target.y-12);渲染 = 棕色杆(90,66,40)+灰白三角头(200,200,200),按 atan2 旋转朝向
- 飘字:0.9s、上移 26/s;武将阵亡红字
11. 音频(audio.lua)
- 防 GC:音频对象放模块级隐藏 Scene(Listener + BGM 节点 + 6 个 SoundSource 轮换池 gain 0.5)
- 资源:Sounds/sfx_melee.wav、sfx_arrow.wav、sfx_horn.wav、bgm_battle.wav
- 播放:轮换池取源 + 随机音调抖动(近战 0.85~1.15、放箭 0.9~1.1)+ 节流(近战 0.13s、放箭 0.18s),近战 gain 0.42、放箭 0.35、号角 0.7
- BGM:looped,gain 0.35,进战斗播、结算停
- 进战斗:号角 + BGM 同起
12. 陷阱清单(全部真实踩过,实现时逐条检查)
- Octree 包围盒必须手动覆盖全图(§1)
- 3D 相机必须在战场南侧(z = -cam.y - 211),LookAt up 用 (0,1,0)
- 深度压缩 = sin(俯角),高度抬升 = cos——弄反 = 树倒置/南北颠倒
- 光照必须 LightGroup/Daytime.xml 预设,手搓光 = 黑屏圆光斑(§2)
- 贴图地面用 Plane 不用 Box
- 程序化材质只用 PBRNoTexture / PBRNoTextureAlpha;同色缓存复用
- orthoSize 是视野全高;相机 clamp 纵向要除 viewSquash
- 贴地椭圆(选中圈/轨迹终点圈)纵横比 0.8(65° 俯角透视),不是 0.45
- 世界层飘字要 1/zoom 缩放补偿
- 音频隐藏 Scene 防 GC;轮换池 + pitch 抖动 + 节流
- 输入 /dpr;第二指按下取消单指拖拽进入 pinch
- 事件数据 eventData:GetInt("X");nvgCreateFont 只调一次
- 移动命令必须压过自动战斗(§4 优先级 2),否则"打起来拉不动"
- 编队移动 = 平移(保持相对队形)+ 最慢速度,不是格点重排
- 敌军推进目标 = 对方存活质心,纵向压缩 0.6 保持阵型感
- 工作流:改码 → LSP 诊断 0 Error → 构建 → 截图核对(软渲染下首次加载 240+ 模型的 >500ms 帧尖峰属正常)
13. 验收清单(逐项截图核对)
- [ ] 开场全景:蓝军 80(四营+关羽张飞红圈头像+名字)居左,魏军 75+曹操居右;65° 视角树干可见、树影贴地、云状色斑+湖泊
- [ ] 顶部蓝红兵力条 + 击杀数 + 两侧人数牌;2.6s 开场横幅;4s 后魏军整阵压上
- [ ] 框选白框 + 单位脚下黄色呼吸椭圆圈;卡片选中态橙描边
- [ ] 点地面:每兵一条「起点圈—蓝虚线—终点黄圈」轨迹,整阵平移、速度=最慢兵种、目标点脉动旗标
- [ ] 移动最高优先级:交战中拉得走,行军不还手,到位自动恢复
- [ ] 点敌军冲锋;弓兵动画中段放追踪箭;近战星芒+血滴;武将阵亡红字
- [ ] 单位 y 排序、分离不重叠、死亡倒地渐隐;受伤显血条、武将血条恒显
- [ ] 全灭结算 3.2s 大字;双指缩放/框选/右键平移/ESC 判负;音效齐活
- [ ] 攻城战:守军按配置生成、原地防守、胜利“攻 陷”











