尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

剑与盾的Unity武器系统实战:建模、材质、判定与优化

剑与盾的Unity武器系统实战:建模、材质、判定与优化 在Unity里做剑与盾算是动作游戏、RPG项目里绕不开的一个基础模块。很多刚入行的朋友拿到“做一个战士武器模型”这种需求第一反应是去Asset Store拖一套资源或者盯着建模软件发愁——但实际做下来你会发现模型本身只是冰山一角后面的材质处理、导入配置、装备逻辑、碰撞判定、受击反馈每一环都有坑。这篇就把我从模型到能上手砍怪的完整流程拆开讲讲包含建模思路、Unity导入设置、装备脚本和优化技巧希望对正在做战士角色或武器系统的朋友有实际帮助。1. 先聊清楚剑与盾在Unity里到底算什么活1.1 一个“战士道具”背后的完整技术链路单看标题你可能觉得这活儿不大——不就是一把剑加一块盾牌吗但放到Unity工程里它至少要穿过四条链路才能变成一个真正能用的战士道具第一是美术链路模型做多精细、拓扑怎么走、UV怎么拆、贴图风格跟项目整体搭不搭。这条链路直接决定道具的视觉上限也决定了后续性能优化要花多少力气。第二是资源链路FBX导出设置、模型缩放比例、材质球选什么渲染管线、贴图格式用不用压缩。很多团队在这一步就翻了车——模型导入Unity后材质发灰、法线异常、阴影满脸噪点十有八九是导入参数和管线配置没对齐。第三是逻辑链路装备到角色身上、切换持握状态、攻击时生成判定、盾牌格挡时计算伤害减免这些都是脚本层面的活。一个道具要“用起来像那么回事”逻辑链路才是核心。第四是表现链路挥剑的刀光、命中瞬间的停顿、盾牌格挡的碰撞火花、音效触发时机。表现链路决定打击感打击感是动作游戏玩家的第一体感做不好整个手感就飘。所以在动手之前先想清楚你在这个项目里到底要交付到什么程度。如果只是场景里的装饰摆件那做到第二链路收工就行如果是主角的常驻武器那四链路全得走完。1.2 三种实现路径别一上来就问“用不用建模”很多朋友会卡在“我不会建模能不能直接拿现成的”这个选择题上。我的建议是看情况定没有标准答案但有三条常见路径可以选路径一是全流程自建用Blender或3ds Max从零建模、拆UV、画贴图再导入Unity。优势是可控性最强风格完全贴合项目模型面数和拓扑结构心里有数缺点是门槛高建模新手要花不少时间。如果你只是做Demo验证玩法在这个环节耗太久不划算。路径二是用现成资源改造Asset Store或Sketchfab上搜“sword shield”能搜出一大堆选一个面数合理、风格接近的模型自己重新调材质或改贴图。优势是能快速跑通逻辑验证缺点是版权要注意尤其商用时看授权方式改造空间也受原模型拓扑限制。路径三是纯代码/程序化生成用Unity的ProBuilder建一个粗模当占位或者干脆用Cube/Capsule拼一个抽象武器先把攻击判定、格挡逻辑跑通。优势是零美术成本、迭代快适合玩法原型期缺点是没法直接当最终资源用后续还是要替换。我的习惯是原型阶段走路径三确认玩法后换路径二等项目美术风格定了再考虑要不要走路径一重做。这样每个阶段的时间成本都压在关键验证点上不会一上来就陷进建模细节里出不来。2. 剑与盾模型设计的核心细节与材质坑2.1 建模思路从概念设计到拓扑落地的关键考量如果确定要走自建路线那先说建模阶段的几个关键决策。以一把中世纪风格的双手长剑和一面圆盾为例我通常会在Blender里先把项目的参考图找好——不要凭空想找几张真实武器的照片或者游戏原画把轮廓、比例、关键结构标出来。剑的结构一般分为剑身、护手、剑柄、配重球四段盾牌分为盾面、盾缘、背部握把三段。分段建模方便后续分别调材质、挂碰撞体。剑身可以做一点中脊线凸起这样在Unity里打光时高光会有明显转折金属质感会更好——纯平面的剑身打光后像贴纸这是很多新手容易忽略的点。拓扑上注意几点第一剑刃边缘需要卡线Crease或Edge Bevel否则导入Unity后渲染出来刃口是圆的第二盾面如果要做弧形用循环边控制曲率别加太多细分面数一高后面优化就得重来第三所有部件的法线保持统一方向尤其是盾牌内外侧法线反了会出现“透亮”或“黑面”问题这个在Blender里按快捷键显示翻转法线就能提前发现。一套适合多数项目的目标面数给各位参考中等品质的剑约1500到3000三角面盾牌约2000到4000三角面。这个范围在手机端和PC端都能跑画面里同时出现几十个这样的道具也不会卡。如果做次世代主机项目再往上加面数但也不要无脑加后面还有法线贴图可以补细节。2.2 PBR材质金属不是刷个灰就完事模型做完导出前有一件事必须想清楚你项目的渲染管线是内置管线还是URP或者HDRP这个决定你在Unity里用什么Shader、怎么做材质球也反过来决定建模软件里导出贴图时要导哪些通道。以标准的PBR流程来说剑和盾需要的贴图通道至少有四张Albedo基础颜色决定金属本身的颜色比如钢铁偏冷灰、青铜偏黄绿、镀金偏亮黄。Metallic金属度剑身和盾面金属部分设为接近1剑柄缠绳、盾背衬垫这类非金属部分设为0。Smoothness光滑度控制高光反射强度剑身抛光的部位可以到0.8以上做旧的划痕区域局部降到0.3左右。Normal法线给剑身增加磕碰凹痕和拉丝纹理让光照在表面产生细节变化。这里最容易被忽略的是“金属度不是全图统一”的。一把用旧的剑刃口磨损后会露出更亮的金属护手缝隙里会有锈迹和污垢这些细节都要在贴图里用笔刷一点点画出来或者用程序化纹理遮罩叠加。全图刷一个灰值出来的效果就像塑料玩具。贴图尺寸建议一把主武器在PC端用2048x2048完全够移动端建议降到1024。盾牌如果占据画面面积更大同样2048否则1024。贴图尺寸每翻一倍显存占用涨四倍一个界面同时出现多把武器时差别还是很明显的。2.3 UV展开与贴图细节把“旧物流传感”做出来UV拆分这块我直接说几个实操心得第一剑身和盾面这种大面积平面尽量保持UV的连续性和方向一致性不要为了省空间把UV切得稀碎否则木纹、拉丝这类方向性纹理在拼接处会看到明显的转折断裂。第二盾牌的UV里正面、背面、侧缘要分开区域摆放。纹理密度尽量一致否则正面细节拉满、背面糊成一团转动视角时穿帮会很明显。第三利用好第二套UV做磨损遮罩。很多项目会在模型上开一套Lightmap UV或者自定义UV2用来叠加污渍、战损等Mask。比如盾牌底部靠近地面的区域可以单独刷一层泥土Mask配合Smoothness降低让泥土区域不发亮水渍区域反而更亮——这些真实感细节就是靠Mask层一层层叠出来的。实战里画贴图时我还有个习惯材质球的Smoothness优先用贴图驱动而不是走全局滑块。如果直接给一个固定值那金属高光的分布会很死板。用贴图驱动后我可以把高光集中在剑刃和盾心边缘自然衰减看起来才像被手长期摩挲过的武器。3. 模型导入Unity的关键配置与渲染处理3.1 FBX导入参数从建模软件到Unity的“最后一公里”模型在Blender里做得再漂亮导入Unity时参数没设对照样能给你来一套“毁容”三连模型灰蒙蒙、比例大变、法线乱七八糟。我每次导入FBX后都会重点检查以下五个配置项第一是Scale Factor缩放系数。Blender默认单位是米Unity也是米但很多从3ds Max导出的模型单位是厘米导入后会在Unity里发现模型跟地面和角色相比莫名其妙大了100倍——别怀疑是模型做错了先查这里。我习惯在建模软件里就把单位统一成米导出前把模型摆到世界原点模型中心放到脚底方便后续在Unity里对齐。第二是Read/Write Enabled。如果你打算在代码里动态改模型的Mesh比如做武器变形、刀刃融化特效就勾上这个选项如果不勾运行时读Mesh顶点数据会被引擎拦截。不过勾选这个会增加内存开销不是所有模型都要开。第三是Normals导入模式。多数情况下保持Import保持原样即可。如果发现模型表面出现硬边/软边异常可以在建模软件里重新调整平滑组或法线尽量不要在Unity里靠Import Normals重新计算——重新计算的代价你可能要花一晚上调软硬边还不一定比软件里调得好。第四是Material Creation Mode。这里有几个选项默认的Standard管线会为每个材质球分配Standard Shader如果你用的是URP建议导入后手动把材质Shader换成URP/Lit否则渲染出来会出现材质发紫或高光丢失。我项目里有美术同学经常忘了这步所以我自己写了个导入后处理脚本自动把材质球替换为项目的默认Shader——这个后面在自动化工具里详细说。第五是Animation Type。如果你只是导入静态武器选None即可如果要给剑加刀光动画、给盾牌加受击抖动动画选Legacy或Generic都行但注意不要选Humanoid——Humanoid动画系统是给人形角色用的给武器强设为Humanoid会导致导入报错或动画异常。3.2 材质管线选择URP还是内置管线别用错Shader现在的Unity项目URP已经是绝对主流HDRP在高端PC和主机项目中也越来越常见。我的建议是新项目直接用URP哪怕你以前熟悉内置管线的Standard也强迫自己切换到URP/Lit因为它性能更好、支持SRP Batcher、色彩空间处理也更现代。在URP下给剑和盾做材质球时最常用的Shader是URP/Lit。注意几个参数Surface Type如果选Transparent模型会出现半透明混合处理不当会出现排序问题——武器一般用Opaque。如果你希望刀光、护手宝石这类部件能自发光URP/Lit里也有Emission通道配合数值抬升可以实现简单的“发光剑”效果不用额外挂粒子。如果要让武器在暗处也有微弱的环境光反射依靠Lighting Mode和反射探针场景中要有Reflection Probe否则金属材质在纯黑环境中会直接闷下去。还有一个细节容易被忽视Shader的Metallic和Smoothness滑块虽然有默认值但当你加载带贴图的材质球时如果你没有把Metallic和Smoothness的来源设为贴图的对应通道Metallic贴图的A通道通常是Smoothness你在滑块上怎么调都调不出贴图里画好的效果。记得把Metallic Map指定到对应贴图并把Smoothness Source设为Metallic Alpha。3.3 光照与阴影问题剑身发黑和盾牌投射大片阴影怎么解热词里有不少朋友问“Unity阴影问题”这里就展开说一下武器道具最容易碰到的三个阴影情况。第一个是剑身大面积发黑。常见原因是场景里没有足够的环境光金属的反射完全依赖光源和反射探针光线不足就会闷黑。处理方式有几个给场景加一个强度合适的Environment Lighting环境光或者给角色挂在移动光源也可以在材质上适当提高环境反射强度或者干脆在剑身边缘位置安排一个补光。动作游戏里主角身上的主光、补光、轮廓光要做好武器作为主角最近的道具受光效果直接影响画面观感。第二个是武器影子的“脏影”问题。Unity的实时阴影在低分辨率阴影贴图下小物件表面会出现明显的锯齿和边缘爬动。剑和盾相比地面这种大物件阴影细节更需要照顾把阴影贴图分辨率适度调高或者给武器单独设置更高精度的Shadow Map再或者给武器一个自定义ShadowOnly平面来模拟接触阴影——这个技巧在角色脚底和武器贴地时非常实用。第三个是透明刀的阴影穿帮。如果你做的是“能量刃”这种半透明材质刀身默认的阴影投射会把整个刀身当成实心物体投出一个大黑块。处理办法是把材质球ShadowCastingMode设为Off或者用一个透明的ShadowCaster2D/Unlit Shader来替代投射让能量刃不投射硬阴影只有贴近地面时投接触阴影。4. 从静态模型到可装备武器的实战流程4.1 装备与卸下逻辑剑怎么挂到角色手上模型导入配置好、材质也调好了接下来就是把它变成战士手里能用的家伙。先说最高频的“装备/卸下”需求。我常用的武器挂点方案是这样在角色的骨骼层级里准备两个空节点分别叫 WeaponMount_R 和 WeaponMount_L右手挂剑、左手挂盾位置在掌心附近。代码里把剑和盾分别作为这两个挂点的子物体这样它们就能跟随手的骨骼运动不用手动每帧同步坐标。装备的脚本逻辑大致如下public class WeaponEquipController : MonoBehaviour { public Transform rightHandMount; public Transform leftHandMount; public GameObject sword; public GameObject shield; public void EquipSword() { sword.transform.SetParent(rightHandMount, false); sword.transform.localPosition Vector3.zero; sword.transform.localRotation Quaternion.identity; sword.SetActive(true); } public void UnequipSword() { // 如果角色有背剑槽可以先挪到背上再隐藏 sword.SetActive(false); } public void EquipShield() { shield.transform.SetParent(leftHandMount, false); shield.transform.localPosition Vector3.zero; shield.transform.localRotation Quaternion.identity; shield.SetActive(true); } }SetParent的worldPositionStays参数建议传false因为你挂到骨骼下是为了让骨骼带动它局部坐标归零就好用世界坐标反而会出现抖动和偏位。装备动画的话动作系统里做一个Equip状态切到该状态时播放“从背上拔剑/举盾”的动画动画事件在中间帧触发EquipSword()这样看起来才是真的在“拿武器”。这里提醒一点武器挂点不要直接绑在骨骼节点如Hand_R上因为手部骨骼在动画里会有旋转和位移直接挂武器会导致武器的朝向跟手不一致。中间的挂点节点最好做好轴向调整例如剑的默认前向Z轴要跟手臂握持方向对齐——不然后面每次都要在代码里补旋转值各种别扭。4.2 攻击与防御判定碰撞体你真的放对位置了吗装备模型仅仅是起点真正让“战士道具”产生玩法价值的是攻击与防御判定。剑的攻击判定常见做法是在剑身上挂一个Trigger碰撞体Box Collider设Is Trigger攻击动画播放到特定帧时启用碰撞体判定结束禁用。代码结构大概是这样的public class SwordDamage : MonoBehaviour { public float damage 25f; public float attackDuration 0.15f; public Collider hitCollider; private HashSetGameObject alreadyHit new HashSetGameObject(); public void BeginAttack() { alreadyHit.Clear(); hitCollider.enabled true; Invoke(nameof(EndAttack), attackDuration); } public void EndAttack() { hitCollider.enabled false; } private void OnTriggerEnter(Collider other) { if (alreadyHit.Contains(other.gameObject)) return; // 只攻击敌人层避免砍到空气也触发反馈 if (other.gameObject.layer LayerMask.NameToLayer(Enemy)) { var health other.GetComponentHealth(); if (health ! null) { health.TakeDamage(damage); } alreadyHit.Add(other.gameObject); } } }SetParent里那段我已经给出来了。这个类里有几个设计细节要展开第一为什么用HashSet保存已命中目标因为一把剑挥过去碰撞体可能在几帧内反复碰到同一个敌人如果不做去重敌人一帧掉三格血就直接秒杀了。攻击判定期间把所有命中过的对象记录起来一次挥击只结算一次伤害这是武器判定的基本功。第二碰撞体的位置和大小。很多朋友直接把Box Collider套在剑的Mesh上那把剑从盾牌边上扫过也会触发判定——碰撞体最好缩窄只覆盖剑刃部分长度略长于剑身这样可以减少“明明没砍到却算命中”的违和感。盾牌同理它的碰撞体主要用来挡飞行道具和近战攻击范围比盾牌模型略大一点点就够了太大容易“空气格挡”被玩家骂。第三攻击帧的触发。不建议在Update里检测动画是否播放到了攻击帧更规范的做法是在Animation Event里调用BeginAttack()。Animator的动画剪辑可以在指定帧添加事件精确到帧比每帧判断动画状态靠谱得多。盾牌的防御判定常见做法是盾牌挂一个Box ColliderTag设为“Shield”。当持盾状态下可以写一个Lookup判定逻辑比如敌人攻击打到玩家时先判断攻击方向与盾牌朝向前方之间的夹角如果夹角小于某个阈值比如60度则触发格挡伤害减半或完全格挡。夹角判断参考这样写public bool IsBlocking(Vector3 attackDirection) { // 盾牌前方与攻击方向的夹角 float angle Vector3.Angle(shield.forward, attackDirection); return angle blockAngleThreshold; }这里盾牌的正前方向量其实是盾面法线也就是盾面朝外的那个方向我习惯在预制体上用空子物体画出Forward箭头方便可视化调参。4.3 表现层细节刀光、火花与受击反馈判定了伤害不代表打击感就到位。一套合格的武器表现至少包含三样命中特效、音效、镜头或动画顿帧。命中特效层面剑挥砍时可以在剑身上挂一个Trail Renderer动态生成刀光拖尾。做刀光的关键参数是Time拖尾持续时间设在0.08到0.15之间太短看不见太长糊成一片Width Curve用从大到小再归零的曲线表现出“挥出去时最亮、收尾时淡出”材质用Additive混合模式颜色选亮白或亮蓝配合Emission会有光刃感。盾牌格挡时可以在盾面和攻击武器的接触点附近生成一个碰撞火花粒子系统。这个触发点不一定要特别精确的物理位置在盾牌前方大概一个拳头距离处放一个空节点命中时在该节点位置Play火花即可。注意粒子的转向让火花四溅方向与攻击方向相反看起来更像被弹开。顿帧Hit Stop是打击感里性价比最高的一招。当剑命中敌人时让Time.timeScale短暂降到0.05~0.1再快速恢复。实现如下public static class TimeScaleUtils { public static void HitStop(float duration) { Time.timeScale 0.05f; Time.fixedDeltaTime 0.02f * Time.timeScale; CoroutineRunner.Instance.StartCoroutine(Resume(duration)); } private static IEnumerator Resume(float duration) { yield return new WaitForSecondsRealtime(duration); Time.timeScale 1f; Time.fixedDeltaTime 0.02f; } }注意这里用的是WaitForSecondsRealtime因为timeScale已经变了正常WaitForSeconds会跟着变速导致实际暂停时间比预期长很多。顿帧时长一般不要超过0.05到0.1秒超过就变成卡顿了。动画层面如果想让挥砍更有力可以在剑命中瞬间人形Animator里加一个短暂的上半身动画层LayerBase Layer优先级不受影响播一个0.06秒左右的受击停顿动画。这个动画层权重可以靠脚本控制打完立刻权重回0避免影响后续移动动作。5. 性能优化与常见问题排查实录5.1 武器相关的性能优化重点武器道具做好之后性能优化容易被忽略但在大型场景里满地敌人人手一把剑、背后一堆盾牌的时候优化不到位就会暴露问题。第一个优化点LOD多细节层次。在Unity里给武器做两个LOD层级LOD0是高模给近距离看LOD1是低模面数去掉一半左右给中距离LOD2甚至可以是一个简化的板片给远距离。主要通过LOD Group组件挂载距离系数按项目情况调。特效里有很多敌人扎堆的时刻远距离的武器面数减半能省出一大截Draw Call。第二个优化点贴图压缩与尺寸。移动端项目里Albedo贴图用ASTC或ETC2压缩Normal贴图用BC5或ASTC不要全程用RGBA32未压缩的格式。一张2048的RGBA32贴图在显存里占16MB换成ASTC 6x6能压到2MB出头差距非常明显。训练用的内存在移动端异常宝贵别在这种细节上摆烂。第三个优化点静态合批和SRP Batcher。URP下材质球尽量复用不要每个实例都复制一个材质球——比如100个敌人如果共用一把剑的材质只要不动态改材质属性它们就能走SRP Batcher合批效率很高。如果你在运行时改了某个材质的颜色SRP Batcher会认为材质发生了变化被迫把该物体踢出合批性能就会退化。这个坑特别隐蔽我看到过不少团队因为这个导致合批失效。第四个优化点不要把武器碰撞体当成物理体一直Compute。武器的Trigger碰撞体平时尽量禁用只有攻击帧才启用否则场景里几十把剑同时带着Trigger碰撞体物理引擎每帧要多次检测重叠白白耗性能。我做过一个极端测试同样20个敌人武器碰撞体全开与仅在攻击帧开启物理耗时相差接近3倍。5.2 高频问题速查从“模型发黑”到“刀穿过盾牌”做武器这套流程我自己踩过不少坑也帮别人排查过不少问题把高频问题整理成表方便各位对号入座。问题现象常见原因解决方法剑身/盾面发黑发暗环境光不足、反射探针缺失、材质Smoothness太低调高Environment Lighting室内加Reflection Probe检查材质Smoothness材质球发紫Shader不兼容当前渲染管线如内置管线材质用在URP手动替换为URP/Lit或写导入后处理自动替换模型导入后巨大/微小建模软件单位不统一米/厘米检查Import Settings里的Scale Factor统一以米为单位剑刃看起来是圆的建模时刃口没有卡线Blender里对刃口边缘增加Crease或Bevel攻击判定重复伤害没有做命中去重用HashSet记录已命中对象一次挥击只结算一次盾牌“空气格挡”碰撞体范围过大缩小盾牌碰撞体碰撞判定结合角度阈值刀光拖尾糊成一片Trail Renderer的Time太长或材质混合不对Time设0.08~0.15材质用Additive装备后武器乱飘SetParent时worldPositionStays传了true传false局部坐标归零同一个武器模型不同敌人都带一份材质运行时实例化了材质导致合批失效尽量复用材质球不动态改共享材质表格之外再单独说两个“看起来没问题但实际很坑”的场景。第一个是“武器穿模”。剑背在背上时装进剑鞘或者穿过披风这种普遍是刚体碰撞没做好或者挂点位置不对。可以做一层低模碰撞体比如剑鞘和披风各一个Box Collider在装备动画播放时让挂点沿骨骼位移走一个弧形路径从背后绕到手上避免直线插过去穿模。第二个是影子在Wall上乱跳。武器离墙近时实时阴影会在墙面上出现闪烁噪点这时一般是Shadow Near Plane Offset太小的原因可以把该值略微调大或者给武器单独设置ShadowDistance但注意OverrideShadowDistance是全局的要用分层阴影或Cinematic Shadow来精确控制。5.3 一个小工具自动替换导入后的材质Shader最后分享一个提升效率的小工具。前面提到URP项目的材质Shader替换问题我一般写一个Editor脚本挂在Project窗口批量处理指定目录下所有FBX/材质把Standard Shader自动替换成URP/Litusing UnityEditor; using UnityEngine; using UnityEngine.Rendering; public static class MaterialShaderReplacer { [MenuItem(Tools/Replace Standard to URP/Lit)] public static void ReplaceShaders() { string[] guids AssetDatabase.FindAssets(t:Material, new[] { Assets/Art/Weapons }); int count 0; foreach (string guid in guids) { string path AssetDatabase.GUIDToAssetPath(guid); Material mat AssetDatabase.LoadAssetAtPathMaterial(path); if (mat ! null mat.shader.name Standard) { mat.shader Shader.Find(Universal Render Pipeline/Lit); count; } } AssetDatabase.SaveAssets(); Debug.Log($替换完成共处理 {count} 个材质球); } }脚本里FindAssets指定目录避免全工程扫描处理速度会快很多。AssetDatabase.SaveAssets务必调用否则改了Shader不会落盘切一下焦点又变回去了。这个脚本我每次项目导入外部资源包后都会跑一遍能省掉大量手动调材质的时间。结尾剑与盾这套东西说到底是Unity武器系统里的一个标准样本。它不复杂但链条长——建模、UV、材质、导入、装备、判定、特效、优化任何一环掉链子最终手感都会打折。我自己做了这么多次武器道具之后最大的体会是一定要在项目早期把武器的挂点规范、材质管线和碰撞体设计定下来否则后期模型一多每个人一套规范光是统一配置就能让人崩溃。最后再分享一个小技巧做武器的攻击判定时不要光盯着代码逻辑多去实际场景里挥几下感受一下。把碰撞体调短一点、把攻击判定帧往前挪两帧、把顿帧时间改成0.06秒这些参数看起来不起眼但组合起来就是“手感”本身。如果你正在做战士职业的武器系统我建议你在原型阶段就反复调这些手感参数调顺了再做美术细化这样返工成本最低玩家体验也最稳。
返回列表