DAY 25
3
Modern Web

## 用 Three.js 來當個創世神 (24)：專案實作#13 - 子彈射擊效果

Photo by rawpixel on Unsplash

• 左鍵射擊效果
• 右鍵疊磚功能
• 苦力怕剛體化

## 專案實作

### 1. 射擊效果與疊磚

``````// shooting related settings
const ballShape = new CANNON.Sphere(0.2)
const ballGeometry = new THREE.SphereGeometry(ballShape.radius, 32, 32)
let shootDirection = new THREE.Vector3()
const shootVelo = 15
let raycaster = new THREE.Raycaster() // create once
let mouse = new THREE.Vector2() // create once
``````
``````// shooting event
if (controls.enabled == true) {
let ammoShape
let ammoGeometry
let ammoMass
let ammoColor
switch (e.which) {
case 1: // 左鍵射擊
ammoShape = ballShape
ammoGeometry = ballGeometry
ammoMass = 20
ammoColor = 0x93882f
break
case 3: // 右鍵疊磚
ammoShape = boxShape
ammoGeometry = boxGeometry
ammoMass = 50
ammoColor = 0x0f0201
default:
break
}

// 取得目前玩家位置
let x = playerBody.position.x
let y = playerBody.position.y
let z = playerBody.position.z

// 子彈剛體與網格
const ammoBody = new CANNON.Body({ mass: ammoMass })
const ammoMaterial = new THREE.MeshStandardMaterial({ color: ammoColor })
const ammoMesh = new THREE.Mesh(ammoGeometry, ammoMaterial)
ammos.push(ammoBody)
ammoMeshes.push(ammoMesh)
getShootDir(e, shootDirection) // 取得射擊方向
ammoBody.velocity.set(
shootDirection.x * shootVelo,
shootDirection.y * shootVelo,
shootDirection.z * shootVelo
)
// Move the ball outside the player sphere
ammoBody.position.set(x, y, z)
ammoMesh.position.set(x, y, z)
}
})
``````

``````function getShootDir(event, targetVec) {
// 取得滑鼠在網頁上 (x, y) 位置
mouse.x = (event.clientX / window.innerWidth) * 2 - 1
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1

// 透過 raycaster 取得目前玩家朝向方向
raycaster.setFromCamera(mouse, camera)

// 取得 raycaster 方向並決定發射方向
targetVec.copy(raycaster.ray.direction)
}
``````

``````function render() {
requestAnimationFrame(render)
...
for (let i = 0; i < ammos.length; i++) {
ammoMeshes[i].position.copy(ammos[i].position)
ammoMeshes[i].quaternion.copy(ammos[i].quaternion)
}
...
renderer.render(scene, camera)
}
``````

### 2. 苦力怕剛體化

``````const headShape = new CANNON.Box(
new CANNON.Vec3(2 * sizeScale, 2 * sizeScale, 2 * sizeScale)
)
mass: 5 * massScale,
position: new CANNON.Vec3(0, 12 * sizeScale, 0)
})
``````

``````creeperObj.head.position.copy(creeperObj.headBody.position)
``````

### 3. 苦力怕連結處約束效果

`creeperModule.js`

``````// Neck joint
``````

`index.js`

``````function createCreeper() {
...
...
}
``````

Cannon.js 中的約束有很多種，筆者測試到後來發現 `LockConstraint` 是可以順利完成我們要的效果的，加上去之後就會發現苦力怕成功地連結在一起剛體化了，可以透過子彈將它擊倒啦！

## 開發後記 - 遇到的問題

### 2. 苦力怕剛體化之約束問題

trace 了一下 code，發現原來是需要加上「約束」啊！就依樣畫葫蘆試著加上了：

``````this.neckJoint = new CANNON.ConeTwistConstraint(
this.bodyBody,
{
pivotA: new CANNON.Vec3(0, 6, 0),
pivotB: new CANNON.Vec3(0, 4, 0),
axisA: CANNON.Vec3.UNIT_Y,
axisB: CANNON.Vec3.UNIT_Y,
angle: Math.PI / 4,
twistAngle: Math.PI / 8
}
)
``````