「这是我参与2022首次更文挑战的第25天,活动详情查看:2022首次更文挑战」
简介
上节加载了模型和模型动画,本节使用键盘控制模型移动和连贯动画实现攻击动作。
实现
基础模板
- 上节讲述加载了模型,并实现了动画。本节在上节的基础上进行修改。
修改灯光并添加阴影
- 开启阴影渲染。
// 开启阴影
renderer.shadowMap.enabled = true
- 添加方向光,开启阴影投射。
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)
}
- 在地面网格上开启阴影接收。
mesh.receiveShadow = true
- 修改模型网格对象,开启阴影投射。并设置方向光的焦点为模型网格对象,用于移动模型时方向光同步移动。
// 设置模型的每个部位都可以投影
mesh.traverse(function (child) {
if (child.isMesh) {
child.castShadow = true
child.receiveShadow = true
}
})
// 设置光线焦点模型
dLight.target = mesh
- 取消
OrbitControls
控件修改全局相机位置,用于模型网格对象移动时相机同步移动。
// camera.position.set(1000, 500, 1500)
camera.position.set(-1000, 1000, 100)
// 控制相机
// const controls = new OrbitControls(camera, canvas)
// controls.update()
修改键盘监听事件
- 在浏览器中两个按键一起按下时,两个事件都会被监听到,但是只有后面一个事件会被响应。就是说
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
)
- 根据按键控制模型移动,控制模型的的朝向。同时控制方向光和相机一起跟随模型移动。
// 控制 移动
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)
}
}
}
- 修改模型为全局变量,修改渲染函数。
...
let meshHY = null
...
// 设置光线焦点模型
dLight.target = mesh
meshHY = mesh
...
// 渲染
function render() {
...
if (meshHY) {
onCodeMove(meshHY)
}
...
}
添加攻击动作
- 在前面键盘监听事件可以看到K的监听进行了特殊处理。当按下K时取消移动动作,
并调用
attack()
方法。 - 攻击动作需要的全局变量
let attackList = [12, 8, 9, 10] // 连招的循序
let attackCombo = true
let skills = 0 // 下标
let clickNum = 0 // 点击次数
- 攻击动作是多个动画组合来的,创建一个数组来保存要执行的动画。
- 还要设置
attackCombo
状态,保证上一次动画未执行完之前不重复执行。 - 根据
skills
和clickNum
判断执行几个动画和动画执行后重置参数。
// 模型攻击
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)
}
- 一个简单的模型控制就完成了。
- 代码地址