You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于相机朝向旋转剑组,实现随面向方向挥剑?

解决方案

核心思路

把剑单独放在一个子容器中,让父容器(你的group)负责跟随相机的位置和朝向,子容器专门处理挥剑的局部旋转,避免直接修改父容器旋转导致的四元数冲突问题。同时用时间驱动的动画循环实现完整的"向下挥-向上抬"动作,保证不同帧率下动作平滑一致。

具体实现步骤

  1. 调整模型层级
    给剑新增一个子容器,用来隔离挥剑旋转和相机跟随逻辑:

    // 你的原有父容器
    const group = new THREE.Group();
    scene.add(group);
    
    // 新增剑容器,负责局部挥剑旋转
    const swordContainer = new THREE.Group();
    // 设置剑的初始位置偏移(替换成你原来的偏移值)
    swordContainer.position.set(0.5, -0.2, -1);
    // 如果剑初始需要倾斜角度,在这里设置(比如初始向上抬一点)
    // swordContainer.rotation.x = Math.PI / 12;
    group.add(swordContainer);
    
    // 将剑模型添加到子容器
    const swordMesh = yourSwordModel; // 替换成你的剑模型实例
    swordContainer.add(swordMesh);
    
  2. 添加动画状态管理
    用状态变量控制挥剑流程,避免按键重复触发:

    let isSwinging = false; // 是否正在挥剑
    let swingTimer = 0; // 动画计时
    const swingTotalTime = 0.6; // 挥剑全程时长(秒,可调整)
    const maxSwingAngle = Math.PI / 2; // 向下挥的最大角度(90度,可调整)
    const clock = new THREE.Clock(); // 用于获取帧时间差
    
  3. 在动画循环中处理挥剑逻辑
    替换你原来的keys.z判断逻辑,改用时间驱动的动画:

    function animate() {
      requestAnimationFrame(animate);
      const deltaTime = clock.getDelta();
    
      // 父容器始终跟随相机位置
      group.position.copy(camera.position);
      // 非挥剑状态下,父容器跟随相机朝向
      if (!isSwinging) {
        group.quaternion.copy(camera.quaternion);
      }
    
      // 触发挥剑:按下Z键且未在挥剑时启动动画
      if (keys.z && !isSwinging) {
        isSwinging = true;
        swingTimer = 0;
      }
    
      // 执行挥剑动画
      if (isSwinging) {
        swingTimer += deltaTime;
        const progress = swingTimer / swingTotalTime;
    
        // 动画结束:重置状态和剑的角度
        if (progress >= 1) {
          isSwinging = false;
          swordContainer.rotation.x = 0; // 回到初始角度,若有初始偏移则替换成对应值
        } else {
          // 分阶段计算旋转角度:前半段向下挥,后半段向上抬
          let swingAngle;
          if (progress <= 0.5) {
            // 向下挥:用缓动函数让动作更自然(easeIn)
            const downProgress = progress * 2;
            swingAngle = maxSwingAngle * downProgress * downProgress;
          } else {
            // 向上抬:用缓动函数让动作减速收尾(easeOut)
            const upProgress = (progress - 0.5) * 2;
            swingAngle = maxSwingAngle * (1 - upProgress * upProgress);
          }
          // 将角度应用到剑容器的局部X轴(保证和相机朝向一致)
          swordContainer.rotation.x = swingAngle;
        }
      }
    
      renderer.render(scene, camera);
    }
    

关键细节说明

  • 为什么用子容器?父容器group只负责同步相机的位置和朝向,子容器swordContainer在父容器的局部空间内旋转,完全不会影响相机跟随逻辑,解决了你之前直接修改group.rotation导致的冲突问题。
  • 时间驱动的优势:相比你原来的Math.PI/100帧驱动方式,时间驱动能保证在不同帧率的设备上,挥剑速度完全一致,动作更平滑。
  • 缓动函数:示例中用了二次缓动,你可以根据需求替换成其他缓动(比如Math.sin(progress * Math.PI / 2)),让挥剑动作更符合真实物理感。

内容的提问来源于stack exchange,提问作者Blaze Branham

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 10:23:23