はじめに
Three.js にハマり今日も開発を続行、ブロック崩しゲームの開発が止まりそうな勢いであれこれ試してます。
昨日、衝突判定を作りましたが、壁に当たった後、進行を止める処理をやってなかったので実装。
ただ、普通に判定処理をすると壁にめり込んで、身動きが出来なくなる問題があり、思い付きで先読み処理など追加で何とか動くようになったので、メモも含めて記事に残しておきます。
実際の動作画面は以下で今日も、GIFアニメにしてみました。
![[JavaScript] Three.jsを使った3Dキャラクター移動と衝突判定の実装方法](https://humanxai.info/images/uploads/javascript-threejs-character-movement-collision.gif)
その他、画像のようにBlenderを使った、キャラクター&アニメーションも実装したので、それも別の記事でまとめる予定です。
一度挫折したBlenderをまた触ると思いませんでした・・・。
3Dはモデリングの処理まで手を出す必要があるので、物凄く大変ですね。。
AIに聞きながらなんとかアニメーションを実装しています。
それ以外にも、カメラの回転とそれにあわせたキャラクターの進行方向を変えるなど、四苦八苦しながら実装しています。
ただ、モデリングのanimationや表示まで出来るようになったのでもう、ここまでのスキルでかなりの事が出来るようになったと思います。
1. キャラクターの移動処理
今回は、Three.jsを使った3Dキャラクターの移動と衝突判定を実装する方法について解説します。特に、衝突時の反発処理と先読み衝突判定を利用して、キャラクターが壁にめり込まないようにする方法に焦点を当てます。
この記事では、以下の2つの主要な要素を実装しました:
- キャラクターの移動処理
- 衝突判定とその後の反発処理
まず、基本的な移動処理を実装しました。キャラクターはWASDキーで進行方向を指定し、カメラの向きに応じて動きます。移動は、THREE.Vector3を使ってベクトル計算で行い、移動速度を掛けてキャラクターを動かします。
export function moveCharacter() {
let moveDirection = new THREE.Vector3(0, 0, 0); // 移動方向のベクトル
// カメラとキャラクターの位置差分を計算
const directionToCamera = new THREE.Vector3();
directionToCamera.subVectors(config.camera.position, config.character.box.position);
directionToCamera.y = 0; // 上下方向は無視(平面移動のみを考慮)
// 移動方向を設定(カメラの位置に基づいて)
if (config.keyState["ArrowUp"] || config.keyState["KeyW"]) {
moveDirection = directionToCamera.clone().negate(); // カメラ方向の逆
}
if (config.keyState["ArrowDown"] || config.keyState["KeyS"]) {
moveDirection = directionToCamera.clone(); // カメラ方向に進む
}
// 進行方向が計算されたら、移動ベクトルを正規化して速度を掛けて移動
if (moveDirection.length() > 0) {
moveDirection.normalize().multiplyScalar(config.character.speed);
config.character.box.position.add(moveDirection);
// キャラクターの回転
const moveAngle = Math.atan2(moveDirection.x, moveDirection.z);
config.character.box.rotation.y = moveAngle;
}
}
ここでは、カメラの向きに合わせてキャラクターが移動するようにしています。directionToCameraでキャラクターとカメラの位置差を計算し、そのベクトルに基づいて進行方向を決めています。
2. 衝突判定と反発処理
次に、衝突判定を追加しました。キャラクターが壁に衝突した場合に壁にめり込むのを防ぐため、衝突が発生した時に反発ベクトルを計算して、キャラクターを壁から押し戻します。
先読み衝突判定
先に進行方向を予測し、衝突するかどうかを事前に確認することで、めり込みを回避します。この方法では、移動する前に衝突判定を行い、衝突がない場合にのみキャラクターを移動させます。
function checkPreCollision(moveDirection) {
const predictedPosition = config.character.box.position.clone().add(moveDirection);
const predictedBox = new THREE.Box3().setFromObject(config.character.box); // 現在の衝突範囲を取得
predictedBox.setFromCenterAndSize(predictedPosition, new THREE.Vector3(1, 2, 1)); // 仮の位置に衝突範囲を設定
// 衝突する場合は進まない
if (predictedBox.intersectsBox(wallBox)) {
return false; // 衝突あり、移動しない
}
return true; // 衝突なし、移動可能
}
この関数では、移動先の位置を予測し、その位置で衝突判定を行っています。衝突しない場合はキャラクターが進み、衝突する場合は移動がキャンセルされます。
衝突後の修正
もし衝突が予測された場合、キャラクターを反発方向に少し戻すことで、壁にめり込まずに進むようにします。
if (checkPreCollision(moveDirection)) {
config.character.box.position.add(moveDirection);
} else {
// 衝突した場合、進行方向の逆方向に少し戻す
const reverseDirection = moveDirection.clone().negate();
config.character.box.position.add(reverseDirection.multiplyScalar(0.5)); // 少しだけ戻す
}
これで、キャラクターは壁に衝突した場合でもめり込まず、壁の隙間に少し戻ることで自然に動き続けることができます。
3. アイテム取得用の衝突判定
衝突判定はアイテムの取得にも利用できます。壁との衝突判定と似たように、アイテムがキャラクターと衝突した場合にアイテムを取得する処理を追加しました。
export function checkItemCollision() {
if (characterBox.intersectsBox(itemBox)) {
const characterCenter = new THREE.Vector3();
const itemCenter = new THREE.Vector3();
characterBox.getCenter(characterCenter);
itemBox.getCenter(itemCenter);
const direction = new THREE.Vector3();
direction.subVectors(characterCenter, itemCenter);
// アイテム取得処理
console.log("アイテム取得!");
return true;
}
return false;
}
この関数では、キャラクターがアイテムに衝突したかを判定し、衝突した場合はアイテム取得処理を実行します。
まとめ
このプロジェクトでは、Three.jsを使ったキャラクターの移動と衝突判定を実装しました。以下が主なポイントです:
- 移動処理:カメラの向きに合わせてキャラクターを移動。
- 先読み衝突判定:移動前に衝突を予測して、キャラクターのめり込みを防ぐ。
- 反発処理:壁に衝突した際にキャラクターを反発させる。
- アイテム取得:衝突判定を使って、アイテムを取得する処理。
これで、めり込みや壁の貫通を回避しながら、スムーズにキャラクターが移動できるようになりました。また、アイテム取得にも衝突判定を活用できることが分かりました。
次のステップ
- さらに複雑な衝突判定や、物理エンジンを使ったリアルな挙動を追加することができます。
- アニメーションやカメラ制御の改善もできるので、次のステップとして追加していくと良いでしょう。
💬 コメント