Three.js - 通过键盘控制模型移动和攻击(二十五)

「这是我参与2022首次更文挑战的第25天,活动详情查看:2022首次更文挑战

简介

上节加载了模型和模型动画,本节使用键盘控制模型移动和连贯动画实现攻击动作。

实现

基础模板

  • 上节讲述加载了模型,并实现了动画。本节在上节的基础上进行修改。

修改灯光并添加阴影

  1. 开启阴影渲染。
  // 开启阴影
  renderer.shadowMap.enabled = true
复制代码
  1. 添加方向光,开启阴影投射。
  let dLight = null
  {
    const light = new THREE.DirectionalLight(0xaaaaaa)
    light.position.set(0, 200, 100)
    light.lookAt(new THREE.Vector3())

    light.castShadow = true
    light.shadow.camera.top = 300
    light.shadow.camera.bottom = -300
    light.shadow.camera.left = -300
    light.shadow.camera.right = 300

    // 开启阴影投射
    light.castShadow = true
    dLight = light
    scene.add(light)
  }
复制代码
  1. 在地面网格上开启阴影接收。
mesh.receiveShadow = true
复制代码
  1. 修改模型网格对象,开启阴影投射。并设置方向光的焦点为模型网格对象,用于移动模型时方向光同步移动。
    // 设置模型的每个部位都可以投影
    mesh.traverse(function (child) {
      if (child.isMesh) {
        child.castShadow = true
        child.receiveShadow = true
      }
    })

    // 设置光线焦点模型
    dLight.target = mesh
复制代码
  1. 取消OrbitControls控件修改全局相机位置,用于模型网格对象移动时相机同步移动。
// camera.position.set(1000, 500, 1500)
camera.position.set(-1000, 1000, 100)
  
// 控制相机
// const controls = new OrbitControls(camera, canvas)
// controls.update()
复制代码

1.gif

修改键盘监听事件

  • 在浏览器中两个按键一起按下时,两个事件都会被监听到,但是只有后面一个事件会被响应。就是说keydown只会持续响应最后一个按下的键。
  • 我们需要对控制模型移动的键添加状态,keydown触发时修改为true按下状态,keyup触发时修改为false非按下状态。以此来判断按下了那几个建。

1.监听W、A、S、D来控制方向。

      // 监听键盘是否按下
      let keyCodeW = false
      let keyCodeS = false
      let keyCodeA = false
      let keyCodeD = false
      let keyCodeK = false // 攻击
      document.addEventListener(
        'keydown',
        (e) => {
          var ev = e || window.event
          switch (ev.keyCode) {
            case 87:
              keyCodeW = true
              break
            case 83:
              keyCodeS = true
              break
            case 65:
              keyCodeA = true
              break
            case 68:
              keyCodeD = true
              break
            case 75:
              keyCodeK = true
              attack()
              break
            default:
              break
          }
        },
        false
      )
      document.addEventListener(
        'keyup',
        (e) => {
          var ev = e || window.event
          switch (ev.keyCode) {
            case 87:
              keyCodeW = false
              break
            case 83:
              keyCodeS = false
              break
            case 65:
              keyCodeA = false
              break
            case 68:
              keyCodeD = false
              break
            default:
              break
          }
        },
        false
      )
复制代码
  1. 根据按键控制模型移动,控制模型的的朝向。同时控制方向光和相机一起跟随模型移动。
      // 控制 移动
      function onCodeMove(mesh) {
        if (keyCodeW) {
          mesh.position.x += 2
          camera.position.x += 2
          dLight.position.x += 2
          mesh.rotation.y = Math.PI * 0.5
        }
        if (keyCodeA) {
          mesh.position.z -= 2
          camera.position.z -= 2
          dLight.position.z -= 2
          mesh.rotation.y = Math.PI
        }
        if (keyCodeS) {
          mesh.position.x -= 2
          camera.position.x -= 2
          dLight.position.x -= 2
          mesh.rotation.y = Math.PI * 1.5
        }
        if (keyCodeD) {
          mesh.position.z += 2
          camera.position.z += 2
          dLight.position.z += 2
          mesh.rotation.y = Math.PI * 2
        }

        if (keyCodeW && keyCodeD) {
          mesh.rotation.y = Math.PI * 0.25
        }
        if (keyCodeW && keyCodeA) {
          mesh.rotation.y = Math.PI * 0.75
        }
        if (keyCodeA && keyCodeS) {
          mesh.rotation.y = Math.PI * 1.25
        }
        if (keyCodeS && keyCodeD) {
          mesh.rotation.y = Math.PI * 1.75
        }

        if (keyCodeK) {
        } else {
          resetMove()
        }
      }

      let moveNum = false
      // 重置移动
      function resetMove() {
        // 按下键盘 跑步动画
        if (keyCodeW || keyCodeS || keyCodeA || keyCodeD) {
          gui['action'](3)
          moveNum = true
        } else {
          // 只执行一次
          if (moveNum) {
            moveNum = false
            gui['action'](24)
          }
        }
      }
复制代码
  1. 修改模型为全局变量,修改渲染函数。
...
let meshHY = null
...
    // 设置光线焦点模型
    dLight.target = mesh
    meshHY = mesh
...
复制代码
// 渲染
function render() {
...
    if (meshHY) {
      onCodeMove(meshHY)
    }
...
}
复制代码

1.gif

添加攻击动作

  • 在前面键盘监听事件可以看到K的监听进行了特殊处理。当按下K时取消移动动作,

并调用attack()方法。

  • 攻击动作需要的全局变量
      let attackList = [12, 8, 9, 10] // 连招的循序
      let attackCombo = true
      let skills = 0 // 下标
      let clickNum = 0 // 点击次数
复制代码
  1. 攻击动作是多个动画组合来的,创建一个数组来保存要执行的动画。
  2. 还要设置attackCombo状态,保证上一次动画未执行完之前不重复执行。
  3. 根据skillsclickNum判断执行几个动画和动画执行后重置参数。
  // 模型攻击
  function attack() {
    clickNum++
    if (attackCombo) {
      attackCombo = false

      // 执行第一个动画
      gui['action'](attackList[skills])
      timeCallback()
    }
  }

  function timeCallback() {
    setTimeout(function () {
      // 进行下一个动作
      skills++
      // 判断点击次数是否还有下一个动作,如果全部动作完成结束循环
      if (skills === clickNum || skills > attackList.length - 1) {
        skills = 0
        clickNum = 0
        attackCombo = true
        keyCodeK = false
        moveNum = true
        resetMove()
      } else {
        gui['action'](attackList[skills])
        timeCallback()
      }
    }, meshHY.animations[attackList[skills]].duration * 1000)
  }
复制代码

1.gif

Supongo que te gusta

Origin juejin.im/post/7066680443940110366
Recomendado
Clasificación