ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

武士 (Samurai) 第三人称游戏 Three.js 开源

武士 (Samurai) 第三人称游戏 Three.js 开源 https://github.com/achrefelouafi/SamuraiThirdPersonTemplateThreeJSGitHub - achrefelouafi/SamuraiThirdPersonTemplateThreeJS: SamuraiThirdPersonTemplateThreeJS · GitHub武士 (Samurai) — 一个第三人称 Three.js 模板浏览器中一个完整的第三人称动作场景一个绑定好骨骼的武士站在一片无尽的程序化夜间景观上具备运动系统、动作扭曲近战、召唤、飞行、布娃娃死亡、一把燃烧的武士刀以及一个用于给角色穿戴装备的装备工作室。无引擎无物理库无资源管线。仅使用 Three.js、Vite以及一个被所有模块每帧读取的配置文件。场景npm install npm run dev # http://127.0.0.1:5173 npm run build操作说明按键动作WASD / 方向键​移动。Shift​ 奔跑。空格键​跳跃​ — 奔跑时是长距离跳跃速度较慢时则是在原地小跳。在空中时会释放刀刃。E · R · T​踢 · 斩击 · 滑斩。三种战斗技巧。V​影分身​ — 标记两个敌人你的影子会分别冲向它们。C​天罚​ — 标记一个敌人一只巨拳将穿过法阵从天而降。X​飞刃​ — 离开地面标记敌人来为每个目标锻造一把刀刃按空格键释放它们。Tab​打开装备工作室。拖拽 · 滚轮​环绕旋转 · 缩放。G · F · P​编辑器​ · 帧率统计 · 暂停。这些招式也会绘制在屏幕底部每种类型一个面板而它们中的每一条都只是src/config/abilities.js中的一行——重新绑定按键、重命名招式或添加新招式只需编辑该文件无需改动其他任何地方。core/Input.js从中构建按键映射ui/ActionHUD.js从中绘制面板而core/App.js每帧根据 id 解析状态。世界黑夜一个高度场、一片天空、一团空气屏幕上没有任何东西被允许与其中任何一个产生分歧。系统文件说明地形​src/world/Terrain.js世界的高度场也是唯一被允许回答“这里的地面有多高”的东西。查表驱动的值噪声在 GPU地面、落叶和 JS角色、相机、阴影焦点上以完全相同的方式评估。每个旋钮都是一个实时 uniform。地面​src/world/Ground.js一个跟随角色的网格在其顶点着色器中根据高度场进行位移因此没有可以走下去的世界边缘。平铺通过纹理偏移与世界锁定着色器所做的一切都是世界位置的函数因此只有光照池在移动。天空​src/world/Sky.js一个全屏着色器一个地平线为雾色的渐变、一个细胞哈希的星空以及一个月亮。它决定了月亮的位置——sky.moon.azimuth/elevation解析为frame.uLightDir因此光斑、眩光、雾的散射瓣和地面薄雾的受光面永远不会不一致。月亮​src/world/Moon.js作为实体而非点乘结果的月亮。一个携带真实月球表面材质的球体在对象空间中进行三平面投影——地图是 1024² 的地面正方形如果包裹在球体 UV 上会导致正面接缝和两极陨石坑的收缩。浮雕是几何体的高度图使顶点位移因此陨石坑边缘会打破轮廓而不是仅仅作为阴影投射在一个圆上。当这个实体月亮出现时天空会隐藏其自带的圆盘。空气​src/world/Atmosphere.js替代 Three.js 线性雾的东西一个带有月光前向散射瓣的分析式高度雾积分因此雾气聚集在凹陷处并在朝向月亮的方向上发光。比线性雾每个片段只多了一个 exp 计算。地面雾​src/world/GroundFog.js柔软的布告板 puff顺风飘动随年龄增长而扩散读取地面自身的烘焙高度场因此它们紧贴地面并融入其中。整个轨迹在顶点着色器中以封闭形式存在——CPU 只写入本帧过期的少数几个槽位。落叶​src/world/Leaves.js两种群体一种外观5600 片叶子躺在地上LeafLitter260 片在空中LeafDrift共用一张包含九片叶子的贴图具有统一的渐变、背光和风力。以不透明和 Alpha 测试而非混合方式绘制——它们写入深度无需排序可以以任何顺序绘制这是它们每个只消耗一次绘制调用的唯一原因。穿过落叶堆会将其打散身体的位置和地面速度直接传入因此冲刺会扬起叶子而静止不动则不会扰动任何东西。光照​src/world/Environment.js一个带有 4096² 阴影贴图的冷色调关键光跟随角色重新居中、一个在其后方的冷色调轮廓光、一个深蓝色的天空填充光以及来自地面的浅色反弹光。关键光和轮廓光仅照亮角色Three.js 没有逐对象的光照过滤因此世界自身的表面被修补以丢弃所有方向光。HDR 探针仅保留作为暗淡的镜面反射响应绝不是可见的天空。接触阴影​src/world/ContactShadows.js太阳阴影贴图无法解析的脚下紧密暗部。相机​src/core/CameraRig.js环绕控制器其距离始终解析回settings.camera.distance因此滚轮和设置文件永远不会冲突。后处理​src/postprocessing/PostProcessing.js泛光 → 色调映射 → 一个调色通道色差、对比度/饱和度/色温、晕影、颗粒。地形以及为什么噪声是一张表src/shaders/lib/terrain.glsl.js中的terrainHeightAt(vec2)是世界表面。地面网格在其顶点着色器中由其位移落叶躺在其上而src/world/Terrain.js在 JS 中镜像了相同的算术以便角色、相机锚点、接触阴影和太阳阴影焦点都站在你确切能看到的地面上。这种镜像就是为什么噪声是查表驱动的而不是通常的snoise。程序化哈希fract(sin(...) * 43758.5)在 GPU 和 JS 上的计算结果并不完全相同——它只精确到几位小数这相当于地形的几厘米误差也就是角色会陷入山丘。一个使用NEAREST采样、256×256 字节的表在两侧都精确返回byte / 255其下游所有东西都是普通的浮点数学误差在微米之内。然后必须处理两件事否则地面会“沸腾”网格跟随Swimming地面平面跟随角色因此顶点的世界位置——以及其高度——每帧都会在其下方改变。Ground#setCenter将平面捕捉到整个顶点间距因此每个顶点始终保持落在相同的世界位置上网格在从不移动的表面下滑动。法线通过中心差分从场中解析得出精确使用半个顶点间距因此着色永远不会声称三角形无法显示的细节。平铺自身的法线贴图叠加在其上。settings.terrain中的所有内容都是一个实时 uniform因此可以在行走时重新调整景观amplitude: 0amplitude: 3.4默认amplitude: 15, ridge: 0.85平坦起伏山峰两个例外是seed重新洗牌表格和segments重建地面网格。octaves是真正的性能开销旋钮。amplitude设为 0 会将整个场景免费折叠回y 0的平坦平面。角色刀刃身体及其动作存在于不同的文件中。public/models/tpose.fbx是皮肤一个带纹理的蒙皮网格绑定到 Mixamo 的mixamorig:骨骼处于 T-pose没有自己的动画。CharacterController将其标准化到settings.character.targetHeight将材质转换为 PBR并保留导出时附带的所有贴图和颜色。public/animations/*.fbx是仅包含骨骼的导出——只有关节没有网格。因为两个文件对其关节的命名相同_retarget()从每个文件中提取第一个剪辑并将其绑定到身体并丢弃该骨骼没有的轨道。在此过程中会发生两种修正单位位移轨道按两个绑定姿势在臀部测量所暗示的比例进行重新缩放因此以米为单位的身体可以接受以厘米创作的剪辑而不会被发射到空中。旋转不需要。根运动控制器拥有身体的位置因此臀部的水平位移被冻结在第一帧剪辑原地播放。垂直位移被保留——那是步态的上下摆动而不是位移。在ROOT_MOTION_CLIPS中命名的剪辑其水平位移在通过时会被记录而不是仅仅被丢弃以便其他东西可以将其重放到根节点上。添加状态只需在ANIMATION_URLS中添加一行并在Locomotion中添加一个权重。移动ThirdPersonController将输入积分成一个速度而不是将其应用为位置增量并将身体转向其前进方向而不是相机的视角。Locomotion读取该速度并从中混合 空闲 → 行走 → 奔跑。所有三个剪辑永久播放只有它们的权重在移动因此启停输入永远不会让身体卡在淡入淡出之间行走是主步态奔跑从属于其归一化相位这阻止了混合中期的“滑行”。播放速率是身体的真实速度除以剪辑自身覆盖的速度因此提高walkSpeed或runSpeed会相应地加快腿部运动而不是让脚滑。从奔跑起跳——绝不能从行走或站立起跳——使身体进入Jump。这是一个已提交的动作摇杆从起跳到落地前都无效弧线自身的位移就是携带角色的东西。settings.jump.distance将该位移重新归一化为精确的米数。在速度较慢时同一个类会播放原地跳跃settings.hop它永远不会接管控制——gaitBleed是下方仍播放的行走或奔跑的量因为完全采用站立跳跃姿势会让腿在身体继续前进时定住。战斗判定一次按键会命中谁目标锁定一个敌人头上出现圆环是因为按键锁定了它——而不是因为它碰巧站在某个锥体内旁边还有另外三个挥击永远够不到的家伙。这个问题每次只针对一个招式提出是招式自己的问题findTarget使用其射程和锥角这正是ThirdPersonController在按下按键时调用的方法。两个圆环可能同时出现当它们出现时它们说的是实话踢击和斩击锁定了不同的敌人头顶的标记说明了哪个按键对应哪里。一个答案提供给三样东西——头顶的标记、底部的面板以及按键本身——因此面板永远不会在按键无法触及的身体上亮起。为面向他人的动画进行瞄准踢击是针对一个精确距离和精确角度的假想对手创作的而玩家永远不会正好站在那里。只有三种出路其中两种是错的用缓动将身体滑过去看起来像脚滑让脚在差一英尺的地方挥空看起来像 bug。Attack​ 做了第三件事——动作扭曲motion warping。按下按键时目标被锁定剪辑所需的位置在该身体前方standoff米处面向它被解析一次角色在剪辑自身的逼近阶段被移动到该位置——先转身再步进turnAt因为这是人类实际做的顺序。到hitAt时身体正好位于动画师假设它所在的位置脚也落下了。这个类从不写入变换。像Jump一样它只解析身体应该在的位置而ThirdPersonController——位置的唯一管理者——将其放在那里。它的时钟是动作自身的时钟因此整个动作服从animationSpeed、暂停键和命中停顿hit-stop。踢击E、斩击R和滑斩T是这一个类的三个实例仅因它们被赋予的剪辑和读取的设置块而不同。在射程内按下按键但什么都没有发生感觉像是坏了而挥空也是一种信息。接触三件事在同一帧发生而且三件事都是同一个节拍身体被交给布娃娃世界速度降至hitStopScale持续hitStop秒镜头产生震动。单独任何一个都像是故障三者一起就感觉到了重量。命中停顿是对dt的缩放而不是暂停因此动画、布娃娃和薄雾一起变慢——而震动以真实时间运行因此镜头在世界静止时仍在移动。这三者都从命中的招式中读取这是招式在撞击瞬间的全部区别踢击是一个短暂的平推斩击是更长的冻结和断成两截的身体。布娃娃布娃娃系统布娃娃没有添加物理引擎因为布娃娃根本不需要。眼睛所看到的身体倒下无非是在重力作用下、有地面阻挡的、骨骼长度永远不变且四肢不能反向弯曲——这三者都是距离约束。因此骨骼变成每个关节一个粒子骨骼变成约束整个事情在每次子步中松弛几次。求解是基于位置的position-based而不是普通的 Verlet预测、投影然后从校正中读回速度这种形式可以承受命中停顿和暂停的时钟。三件事阻止它看起来像一根绳子质量骨盆和胸部很重手和脚很轻以逆质量inverse mass保持。校正在此比率下分配因此手臂从几乎注意不到的躯干上甩出。支撑Bracing仅靠骨骼长度会给出一条可以平折的链。骨盆、胸部和脊柱上的十几个额外约束给了身体一个在其它部分乱甩时试图保持的形状。限制膝盖双向弯曲是最明显的破绽因此髋部到脚踝的距离有下限和上限。将点重新放回骨骼上是另一半。每根骨骼都被转向以瞄准其子粒子这使其绕自身轴的扭曲与死亡姿势完全一致。骨盆和胸部有两个独立的方向可用因此它们的完整方向是从该帧重建的——没有它面朝上和面朝下的身体是相同的瞄准向量尸体每次都会侧身落地。交接不是混合。混合器在帧中间停止求解器的第一个姿势就是打击连接时剪辑显示的姿势这是让死亡看起来像是发生在同一个身体上的唯一方法。敌人尸体五具尸体站在玩家半径范围内不小于minRadius分布在该环的面积上而不是半径上并相互排斥因此没有两具共享一块地面。下载一个骨骼每个敌人都是它的SkeletonUtils.clone每个都以自己的轻微速度在剪辑的同一相位上空闲——五具身体同步呼吸是人群最不自然的事情。尸体不占用槽位 refill 计时器在身体死亡时开始因此当旧尸体还躺在那里时环已经恢复到满员。它停留corpseTime然后在dissolveTime内通过从脚部升起的噪声溶解烧掉——使用discard而不是 alpha因此躺在地面雾中的尸体永远不需要与其排序。导出根本不带纹理因此外观是创作的而不是导入的冷色调近黑的身体带有余烬菲涅尔边缘这是唯一一种在二十米外的蓝色夜晚保持清晰的组合。三个技能所有三个技能都通过先标记一个敌人来瞄准而瞄准TargetMarking在每个技能自己的设置块上都是相同的机制。它是屏幕空间而不是世界空间这就是全部当身体靠近玩家瞄准的屏幕点时它就是候选者最近的获胜。容差是屏幕高度的一小部分因此这是“看”而不是“像素级狩猎”。瞄准点是光标它从屏幕中心开始——对于环绕相机这是同一个手势因此无需解释。影分身 — V影分身骨骼的两个克隆——身体、盔甲、武器一切都挂在骨骼上——每个材质都被替换为一个黑色表面。相同的轮廓相同的帧另外两个你站在你不在的地方。每个执行一次任务然后结束。它诞生于角色内部处于完全相同的姿势每个克隆节点的局部变换都从它所克隆的节点复制因此转身或步态混合在落在影子的同一帧落在身体上然后滑出到标记旁边它在那里蹲下片刻——召唤的全部意义在于两样东西从你身上走出安顿下来标记目标然后才移动它各自猎杀一个敌人绝不会两次选中同一个它用玩家自己的攻击之一进行打击使用该招式的数值因此影子的滑斩将身体撕成两半就像你做的一样。但故意不在同一个节拍上没有命中停顿只有一半的震动。命中停顿是玩家自己的打击反馈为世界冻结一个由三十米外不是你的东西抛出的斩击读起来像是卡顿。天罚 — C法阵打开 - 拳头穿过六个节拍法阵在空中写下自己从顶部逆时针完整一圈它充能保持收紧并变亮而其他什么都没发生——这个节拍让打击变得不可避免而不是突然没有它整个动作读起来就像抛射物拳头落下在 t² 上加速这是下落物体实际做的事情它落地一切都在同一帧发生——身体倒下世界几乎停止镜头被踢地面裂开并抛出它的组成部分它停留坐在结果上因为让打击感觉沉重的最廉价方式就是将落下的东西在停止的地方停留半秒钟然后它缩回法阵法阵折叠。整个事情依赖的技巧一个从圆圈中落下的拳头除非圆圈是个洞否则就是个道具。因此拳头的材质丢弃法阵平面上方的每个片段并在交叉处烧出一条线前臂在顶点着色器中被拉伸因此无论法阵挂多高它总是能到达那个平面。屏幕上永远不是一只漂浮的手臂——而是一只从某物中穿过的手臂在任何高度在下落的任何时刻没有任何关键帧。飞刃 — X从空中标记 - 齐射唯一一个作为模式而不是招式的技能也是唯一一个排除其他技能的模式。起飞同时做三件事它们必须一起开始否则读起来就像三个事件身体离开地面瞄准出现因此下一次点击就是标记所有属于地面的东西都被收起。当你在空中时你点击的每个敌人都会锻造一把刀刃——角色自己的武士刀从那一刻实际装备的任何东西克隆出来穿着自己的材质和贴图。它是武士持有的同一把剑相同的钢相同的烧刃线相同的握把包裹被同一轮月亮照亮。召唤添加的是一个菲涅尔边缘和将其写入空气的阈值。一个你认得的物体正在发生一些事情读起来是剑在做某事替换材质读起来是一个碰巧是剑形的道具。每把刀锻造一个阈值带着参差不齐的噪声前沿扫过模型——是绘制出来的而不是切换的在旋转的环中保持位置并充能在释放时向后远离其标记穿过为其锻造的敌人身体并插在击杀后方的地面中像被敲击的音叉一样鸣响然后按写入的方式烧回去。它们一次离开一把。这里的瞄准在每次点击时重新武装因为在此模式下标记是玩家正在做的事情而不是他们所处的模式。空格键释放落地释放任何仍然悬挂的刀刃而不是丢弃它们因为玩家标记了那些敌人在下降途中把齐射扔掉等于收回。燃烧的武士刀WeaponFire是由一个体积而非骨骼来定位的box是武器自身坐标系中的定向框模型内部该框内的每个面都在发射。它从模型自身的边界框开始因此整把剑都在燃烧并且已经与刀片在任何手掌握持的角度对齐沿着刀片缩小它让握把保持冰冷。表面采样器将其保留的面展平为三角形池其他一切都只是读取该同一池的不同方式DistanceField将其烘焙为“这里的钢有多远”VolumetricFireMaterial通过它光线追踪黑体体积——这就是让这把剑燃烧而不是它周围的一团火。EmberSystem按面积从中挑选三角形并从中抛出火花因此每个余烬都离开模型的一个实际面。BladeHeat使框内的钢发出白热光因此武器是一个光源而不是站在火焰中的黑暗道具。这三者都通过相同的blackbody()辐射温度在同一尺度上因此冷却的火花、羽流的边缘和发光的脊柱在温度相同时是相同的颜色。这就是让效果读起来像一个物体而不是三层的原因。装备工作室 — Tab工作室按 Tab 将角色身体移出游戏场景进入一个为一件事而建的场景观察它并给它穿衣服。这是一个模式而不是场景图技巧——进入会交换相机、光照和调色并将指针交给检查环绕。没有东西被复制相同的骨骼、相同的混合器和相同的装备挂载点在两个地方都在屏幕上这就是为什么在这里调整的装备在世界中已经是正确的。部分文件说明场景​src/world/StudioStage.js关键光双重——一个承载阴影的聚光灯和一个相同角度的矩形区域柔光箱用于包裹和镜面滚动——加上来自相反四分之一区域的面积填充光、冷色调轮廓光和每个肩膀后面的暖色调踢光故意不匹配相等的边缘读起来像错误不同的读起来像房间、一个头发光以及一个每个帧从视图向量放置光环的背景板因此剪影总是被墙的明亮部分框住。相机​src/screens/StudioCamera.js自由检查环绕——拖拽旋转滚轮推拉右键拖拽平移——加上在指针触摸画布时滑动并放弃自身的取景预设。模式​src/screens/CharacterScreen.js拥有切换后处理堆栈绘制哪个场景、哪个相机、哪个调色块以及运行哪个更新路径。面板​src/ui/CharacterScreenUI.js纯 DOM。不保持状态每个值都从管理器重新读取因此小工具和数字框永远不会不一致。骨骼覆盖​调整放置选择一个类别点击一个物品装备它然后设置关节并微调偏移。视口中的移动/旋转小工具写入与检查器滑块相同的数字——拖动箭头滑块跟随在框中输入部件移动。骨骼透过盔甲绘制骨骼关节标记显示部件锚定的位置动作播放行走和奔跑因此可以在身体移动时判断装备。看起来正确后“复制默认值”将位置放在剪贴板上作为片段粘贴到目录中的默认值上。“保存”将装备保存在 localStorage 中“导出”将其写为 JSON。目录是完整的内容层添加一把刀只需在src/equipment/EquipmentCatalog.js中添加一条记录其他地方无需代码{ id: sword, name: Katana, category: weapons, url: ./models/weapons/sword.glb, defaults: { bone: RightHand, position: [-0.051, 0.102, 0.052], rotation: [-168.3, 84, -0.8], scale: 1 } }weapons和attachments是故意分开的。weapons是以后会添加规则的类别——拔剑和入鞘、必须握在手中的手、伤害attachments是装饰性的永远不会有规则。现在拆分它们以后的工作只需更改一个类别而不是过滤平面列表。模型在载入过程中会发生两件事它是惰性加载的。在有人请求之前什么都不会下载——引导路径不受任何大小的目录影响。其余的在屏幕实际打开后在后台预取。它使用身体的材质。这些导出中的每一个都嵌入了角色调色板已经携带的相同的四张 1024² 贴图它们来自同一个 Blender 场景。因此材质通过名称针对MaterialLibrary进行解析导出的材质被释放——零额外纹理内存并且装备由与盔甲完全相同的材质照亮。挂载点以及为什么偏移量以米为单位EquipmentManager将每个部件父级到挂载点mount而不是直接到关节挂载点的比例是关节世界比例的反比。骨骼是 Mixamo FBX——以厘米为单位创作按fbxScale缩放并再次缩放以达到targetHeight——因此关节的世界比例约为 0.01直接父级到它的对象会小一百倍偏移量也相应匹配。挂载点精确地抵消了这一点其中的一切都以米为单位无论骨骼以何种单位导出并且当targetHeight移动时数字保持有效。直接到骨骼仍然有效适用于不想要自己放置的代码app.character.attach(sword, RightHand); const head app.character.getBone(Head);骨骼在原始名称和去除命名空间后的名称下都被索引因此只需询问普通关节。父级到骨骼的任何东西都可以免费骑在骨骼上。编辑器 — G项目中每个可调整的数字都存在于src/config/settings.js中而src/ui/Editor.js是一个直接绑定到它的 lil-gui 面板。没有控制器有onChange处理程序因为不需要灯光、地面着色器、叶子、骨骼和后期处理堆栈都在每帧采样这些字段因此滑块在下一帧重新照亮场景无需重建无需着色器重新编译。这在时钟暂停P时也成立此时姿势才真正值得打光。系统可能只采样这些值——永远不要在构造时将其中一个复制到记录中然后再读回。这一条规则是整个实时编辑故事所依赖的。预设是 localStorage 中整个树的快照可作为 JSON 导出和导入并可以重置为出厂默认值。加载会合并到活动对象中而不是替换它们因此着色器或灯光持有的绑定保持有效。相同的字段也在window上因此控制台也可以工作settings.terrain.amplitude 14; // 山脉同一帧可行走 settings.terrain.ridge 1; // 尖锐的山脊而不是起伏的丘陵 settings.terrain.amplitude 0; // 免费回到平坦平面 settings.leaves.drift.count 900; // 狂风落叶 settings.fire.intensity 0; // 熄灭武士刀 settings.environment.sunIntensity 4; // 下一帧重新照亮 settings.environment.floorTextureSet stone; // 将土壤换成石板 settings.global.timeScale 0.25; // 子弹时间一切同时变慢 settings.camera.distance 8; // 控制器滑出 // 以及应用程序本身 app.toggleCharacterScreen(); // 与按 Tab 相同 app.enemies.respawnAll(); // 重新生成一圈敌人 await app.characterScreen.equipment.equip(scabbard); app.characterScreen.equipment.setBone(scabbard, Spine); console.log(app.characterScreen.equipment.snippet()); // 粘贴覆盖目录默认值帧统计 — F帧率、平均和峰值帧时间、绘制调用和三角形数在半秒窗口上平均。计数器在下一帧的顶部采样在那里一帧肯定会结束而不是在几个可能结束的地方之一。上面的数字是完整场景——地形、落叶、飘叶、雾、五个敌人、燃烧的武士刀和整个后处理链——大约 66 次绘制调用。架构core/App.js构建每个子系统然后只做一件事按顺序执行每帧更新。布线被故意做成单向的没有子系统回连到 App。frame()中的顺序是整个架构每一步的存在都是因为下游的东西读取了它写下的内容Terrain​ — 此帧移动的任何滑块都在这里生效在任何东西读取高度之前。Air, sky, moon​ — 一次外观一起重新读取月亮将自己挂在天空刚刚解析的光照方向上。Controller​ — 先移动它设置混合动画朝向的航向和速度。它只接触 XZ这就是为什么身体可以落在地面上而控制器不需要知道地面的存在。Ground height character​ — 项目中拥有身体高度的唯一地方。飞行的悬停是离地几米因此飞过山丘会爬升。Enemies → target rings → marks​ — 此帧击倒的身体在同一帧失去其圆环和标记。Equipment → weapon fire → shadows → judgement → blades​ — 每个都挂在之前事物的最终姿势上。Floor → ground fog → leaves​ — 雾和落叶站在地面刚刚刷新的高度场烘焙上。Camera​ — 在真实时间上因此在暂停时环绕仍然响应。Shadow map, grade, post。有两种模式切换它们的只有一样东西characterScreen.active决定后处理管道绘制哪个场景、通过哪个相机绘制、哪个调色块生效以及运行两个更新路径中的哪一个。两种模式互不感知。目录结构src/ core/ 渲染器、时钟、环绕控制器、输入、共享帧 uniform world/ 地形、地面、天空、月亮、空气、地面雾、落叶、光照、工作室场景 animation/ 角色骨骼、重定向、运动、跳跃、飞行、攻击 combat/ 敌人、布娃娃、目标标记 vfx/ 武器火焰、影分身、天罚、刀刃风暴、标记、血液 equipment/ 目录、惰性库、挂载管理器 screens/ 角色屏幕及其相机 postprocessing/ 泛光、色调映射、调色 ui/ 动作 HUD、编辑器、统计、提示、加载遮罩 config/ settings.js — 每个数字 — 和 abilities.js — 每个招式 shaders/lib/ 噪声、地形高度场、黑体辐射构建工具three ^0.185 · lil-gui ^0.21 · vite ^8.1 · 无其他运行时依赖。致谢角色模型​ — dark_igorek on Sketchfab动画​ — Mixamo纹理​ — ambientCGHDRI​ — Poly Haven
返回列表