Three.js r146 と cannon-es v0.20.0 でカメラ視点移動ボタン追加してみるテスト
タイトルにあるように、3D表示用のThree.jsと、物理エンジンcannon.jsからの開発を引き継いでいると思われるcannon-esで、3D表示と物理演算をする機能を実装し、さらにカメラ視点移動ができるようにしてみました。
ここにあるようなReact Three Fiberの実験からの流れで、物理演算をしようとするとcannon-esというものがでてきたので、その理解のため元となったcannon.jsからいろいろおさらいしてみた、といったところです。
この時点のThree.jsとcannon-esの連携だけで、また自分の技術的にアップアップになってきたので、React Three Fiberにもどってcannon-esの機能とうまく連携させられるか今から心配になってきましたw。
成果物概要
カメラを操作できるまでにした最終成果物は以下になります。 何か少し自分の予定していたカメラ操作機能(←モンハンのカメラの動きみたいな機能)と異なりますが、これ以上自分の技術や理解力では開発進みそうになかったので、妥協してこのあたりで開発打ち止めとしましたw。 カメラ機能実装の途中での打ち止め、ここと同じように毎度のイベントのようになってきました。。。
CodePen Home Three.js r146 + cannon-es v0.20.0 Test07 Camera Rotation Test(コード表示付き)
CodePen Home Three.js r146 + cannon-es v0.20.0 Test07 Camera Rotation Test( full page view )
操作方法としては以下のようになります。
「forward」「backward」ボタンで操作対象となる赤いワイヤーフレームBoxの前進、後退を、「rightRot」「leftRot」ボタンで右回転、左回転を行います。
カメラ位置の制御を行う「camLeftRot」「camRightRot」ボタンでカメラを操作Boxを中心に左回転、右回転させ、「camHeight」ボタンでカメラの高さ、「camDist」ボタンでカメラの操作Boxからの距離、を変更します。 「camReset」でカメラを(操作Boxに対して)最初の位置にもどします。
成果物ソースコード
自分が後で参照する用機能もかねて、ソースコードも載せておきます。 以下となります。
HTML
<html lang="en">
<head>
<meta charset="utf-8" />
</head>
<body>
<button id="forwardButton">forward</button>
<button id="leftRotButton">leftRot</button>
<button id="rightRotButton">rightRot</button>
<button id="backwardButton">backward</button>
<br>
<button id="camLeftRotButton">camLeftRot</button>
<button id="camRightRotButton">camRightRot</button>
<button id="camHeightButton">camHeight</button>
<button id="camDistButton">camDist</button>
<button id="camResetButton">camReset</button>
</body>
</html>
JavaScript
またいつものよく理解していないベクトルとクォータニオンをあれこれごちゃごちゃまぜて、なんとかそれなり回転にさせましたw。
目的の「モンハン」カメラ機能までには到達できてませんが、これまでやりたくてもできなかった、「camReset」ボタンによりカメラ位置が操作オブジェクトの真後ろになる機能だけ「達成した感」がありますw。
//three.jsと(cannon.jsベースの)cannon-esの連携をテストしてみました
//
//Boxの操作とカメラの操作ができるようにいろいろ試してみました
//
//自分の作成したものも含めて、以下サイト・コードを参考にさせていただきました
//
//
//
//WebGL開発に役立つベクトルの足し算・引き算 (Three.js編)
//https://ics.media/entry/15043/
//
//
//
//Three.js + Cannon.js Test05
//https://codepen.io/siouxcitizen/pen/xxzqmKW
//Three.js + Cannon.js Test06(失敗中)
//https://codepen.io/siouxcitizen/pen/ZERyoPX
//Three.js r146 + cannon-es v0.20.0 Test06
//https://codepen.io/siouxcitizen/pen/LYBYzjB
//
//
//
//Canno.jsオブジェクトの回転、移動について その1
//CANNON.js Cube Movement + Jump
//Adam James
//https://codepen.io/AdamJames93/pen/vYmJoxX
//
//
//
//Canno.jsオブジェクトの回転、移動について その2
//Example > ThreeJS FPS
//https://github.com/pmndrs/cannon-es
//https://github.com/pmndrs/cannon-es/tree/master/examples
//
//https://github.com/pmndrs/cannon-es/blob/master/examples/threejs_fps.html
//https://github.com/pmndrs/cannon-es/blob/master/examples/js/PointerLockControlsCannon.js
//
//https://github.com/pmndrs/cannon-es/blob/master/examples/js/dom-utils.js
//
//
//
//fixedRotation、angularDampingの設定について
//CannonJS lock rotation on specific axes
//https://stackoverflow.com/questions/42598275/cannonjs-lock-rotation-on-specific-axes
//
//
//
import * as CANNON from 'https://cdn.jsdelivr.net/npm/cannon-es@0.20.0/dist/cannon-es.min.js';
import * as THREE from 'https://unpkg.com/three@0.146.0/build/three.module.js';
// Cannon.js 変数
let world;
let phyGroundBox;
let phyGroundBoxMat
let phySphere;
let phySphereMat;
let phyBox;
let phyBoxMat;
let phyWireBox;
let phyWireBoxMat;
let phyWireBoxAndPhyGroundBoxCM;
// Three.js 変数
let camera, scene, renderer;
let viewGroundBox;
let viewSphere;
let viewBox;
let viewWireBox;
// 操作用ワイヤーフレームBox操作ボタン
const forwardButton = document.getElementById('forwardButton');
const leftRotButton = document.getElementById('leftRotButton');
const rightRotButton = document.getElementById('rightRotButton');
const backwardButton = document.getElementById('backwardButton');
// カメラ位置操作ボタン
const camLeftRotButton = document.getElementById('camLeftRotButton');
const camRightRotButton = document.getElementById('camRightRotButton');
const camHeightButton = document.getElementById('camHeightButton');
const camDistButton = document.getElementById('camDistButton');
const camResetButton = document.getElementById('camResetButton');
//ボタン押下状態保持用
let moveForward = false;
let rotateLeft = false;
let rotateRight = false;
let moveBackward = false;
let cameraLeftRot = false;
let cameraRightRot = false;
// 現フレームと前フレームとの時間の差deltaを取得のため使用
let clock = new THREE.Clock();
let delta;
// 操作用ワイヤーフレームBoxの角度と角度を加えた速度制御用
let inputVelocity = new THREE.Vector3();
let velocityFactor = 0.2;
let euler = new THREE.Euler();
//let pitchObject = new THREE.Object3D();
let yawObject = new THREE.Object3D();
let quaternion = new THREE.Quaternion();
// 操作用ワイヤーフレームBoxの角度を基準に
// カメラの角度を計算時に使用
let rot = 0; // 角度
let targetRot = 0;
// カメラの高さに関する変数・定数の設定
// 高さの最小値
const MIM_CAMERA_HEIGHT = 1;
// 高さの最大値
const MAX_CAMERA_HEIGHT = 15;
// 高さの初期値
const DEFFAULT_CAMERA_HEIGHT = 1;
// 高さの計算単位
const CAMERA_HEIGHT_CALC_UNIT = 3;
let cameraHeight = DEFFAULT_CAMERA_HEIGHT;
// カメラの操作対象3Dオブジェクトからの距離に関する変数・定数の設定
// 距離の最小値
const MIM_CAMERA_DIST = 15;
// 距離の最大値
const MAX_CAMERA_DIST = 30;
// 距離の初期値
const DEFFAULT_CAMERA_DIST = 15;
// 距離の計算単位
const CAMERA_DIST_CALC_UNIT = 5;
let cameraDistance = DEFFAULT_CAMERA_DIST;
// コード作業中の見た目のわかりやすさのため
// 操作用ワイヤーフレームBoxの進行方向に表示する矢印ヘルパーを作成
// 矢印を、Z軸方向のベクトル(進行方向のベクトル)に向ける用
let frontVector = new THREE.Vector3(0, 0, 1);
// ヘルパー作成
const helper = new THREE.ArrowHelper(
frontVector,
new THREE.Vector3(0, 0, 0),
10, // 矢印の長さを設定
);
initThree();
initCannon();
animate();
setupEventHandlers();
function initCannon() {
// Cannon.jsの物理空間を生成
world = new CANNON.World();
// 重力を設定
world.gravity.set(0, -9.82, 0);
// ぶつかっている「可能性のある」剛体同士を見つける作業
world.broadphase = new CANNON.NaiveBroadphase();
// 反復計算回数
world.solver.iterations = 5;
// 許容値
world.solver.tolerance = 0.1;
// 地面をBoxで作成
// 摩擦等の設定のための地面Boxのマテリアル
phyGroundBoxMat = new CANNON.Material('phyGroundBoxMat');
// Boxシェイプの剛体を質量0で生成、摩擦設定のためのマテリアルを指定
phyGroundBox = new CANNON.Body({
mass: 0,
material: phyGroundBoxMat
});
phyGroundBox.addShape(new CANNON.Box(new CANNON.Vec3(7.5, 0.1, 7.5)));
// 減衰率
phyGroundBox.angularDamping = 0.99;
// 物理世界に追加
world.addBody(phyGroundBox);
// Sphereを作成
// 摩擦等の設定のためのSphereマテリアル
phySphereMat = new CANNON.Material('phySphereMat');
// Sphereシェイプの剛体を質量1で生成、摩擦設定のためのマテリアルを指定
phySphere = new CANNON.Body({
mass: 1,
material: phySphereMat
});
phySphere.addShape(new CANNON.Sphere(1));
// 剛体の位置を設定
phySphere.position.set(-5, 10, 0);
// Z軸に-1の角速度を設定
phySphere.angularVelocity.set(0, 0, -1);
// 減衰率
phySphere.angularDamping = 0.1;
// 物理世界に追加
world.addBody(phySphere);
// Boxを作成
// 摩擦等の設定のためのBoxマテリアル
phyBoxMat = new CANNON.Material('phyBoxMat');
// Boxシェイプの剛体を質量50で生成、摩擦設定のためのマテリアルを指定
phyBox = new CANNON.Body({
mass: 50,
material: phyBoxMat
});
phyBox.addShape(new CANNON.Box(new CANNON.Vec3(1, 1, 1)));
// 剛体の位置を設定
phyBox.position.x = 5;
phyBox.position.y = 10;
//phyBox.position.z = 0;
// X軸に100の角速度を設定
phyBox.angularVelocity.set(100, 0, 0);
// 減衰率
phyBox.angularDamping = 0.99;
// 物理世界に追加
world.addBody(phyBox);
// Box(ワイヤーフレームBox用)を作成
// 摩擦等の設定のためのBoxマテリアル
phyWireBoxMat = new CANNON.Material('phyWireBoxMat');
// Boxシェイプの剛体を質量1で生成、摩擦設定のためのマテリアルを指定
phyWireBox = new CANNON.Body({
mass: 1,
material: phyWireBoxMat
});
const shape = new CANNON.Box(new CANNON.Vec3(1, 1, 1));
phyWireBox.addShape(shape);
// 剛体の位置を設定
phyWireBox.position.x = -1;
phyWireBox.position.y = 10;
//phyWireBox.position.z = 0;
//phyWireBox.fixedRotation = true;
// Y軸に1の角速度を設定
//phyWireBox.angularVelocity.set(0, 1, 0);
// 減衰率
//phyWireBox.angularDamping = 0.5;
phyWireBox.angularDamping = 1;
// 物理世界に追加
world.addBody(phyWireBox);
// Material同士の接触設定
// phyWireBoxとphyGroundBoxが接触した際のContactMaterialを生成
phyWireBoxAndPhyGroundBoxCM = new CANNON.ContactMaterial(
phyGroundBoxMat, //ひとつ目のマテリアル(地面用Boxのマテリアル)
phyWireBoxMat, //ふたつ目のマテリアル(操作用ワイヤーフレームBoxのマテリアル)
{
contactEquationRelaxation: 3, // 接触式の緩和性
contactEquationStiffness: 10000000, // 接触式の剛性
friction: 0.001, //摩擦係数
frictionEquationRelaxation: 3, // 摩擦式の剛性
frictionEquationStiffness: 10000000, // 摩擦式の緩和性
restitution: 0.1 // 反発係数
}
);
// 生成したContactMaterialをworldに追加
world.addContactMaterial(phyWireBoxAndPhyGroundBoxCM);
}
function initThree() {
// Three.jsのSceneを生成
scene = new THREE.Scene();
// Three.jsのカメラを生成
//camera = new THREE.PerspectiveCamera(30, 650 / 400, 1, 10000);
camera = new THREE.PerspectiveCamera( 30, window.innerWidth/window.innerHeight, 0.2, 5000 );
camera.position.set(0, 5, -25);
camera.lookAt(new THREE.Vector3(0, 0, 0));
scene.add(camera);
// Three.jsのライトを生成
let light = new THREE.DirectionalLight(0xffffff, 2);
light.position.set(10, 10, -10);
light.castShadow = true;
light.shadowMapWidth = 1024;
light.shadowMapHeight = 1024;
light.shadowCameraLeft = -10;
light.shadowCameraRight = 10;
light.shadowCameraTop = 10;
light.shadowCameraBottom = -10;
light.shadowCameraFar = 100;
light.shadowCameraNear = 0;
light.shadowDarkness = 0.5;
scene.add(light);
let amb = new THREE.AmbientLight(0x999999);
scene.add(amb);
// Three.jsのBoxで地面用Boxの見た目を作成
viewGroundBox = new THREE.Mesh(
new THREE.BoxGeometry(15, 0.2, 15),
new THREE.MeshLambertMaterial(
{
color: 0x00ff00
}
)
);
viewGroundBox.castShadow = true;
viewGroundBox.receiveShadow = true;
scene.add(viewGroundBox);
// Three.jsでSphereの見た目を作成
viewSphere = new THREE.Mesh(
new THREE.SphereGeometry(1, 50, 50),
new THREE.MeshLambertMaterial(
{ color: 0xff0000 }
)
);
viewSphere.castShadow = true;
viewSphere.receiveShadow = true;
scene.add(viewSphere);
// Three.jsでBoxの見た目を作成
viewBox = new THREE.Mesh(
new THREE.BoxGeometry(2, 2, 2),
new THREE.MeshLambertMaterial(
{ color: 0x0000ff }
)
);
viewBox.castShadow = true;
viewBox.receiveShadow = true;
scene.add(viewBox);
// Three.jsでワイヤーフレームのBoxの見た目を生成
const geometry = new THREE.BoxBufferGeometry(2, 2, 2);
const material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true });
viewWireBox = new THREE.Mesh(geometry, material);
scene.add(viewWireBox);
viewWireBox.add(helper);
// Three.js レンダラー設定
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener('resize', onWindowResize, false);
renderer.render(scene, camera);
}
function animate() {
requestAnimationFrame(animate);
// Cannon.jsの物理空間の時間を進める
world.fixedStep();
delta = clock.getDelta();
let rotateAngle = Math.PI / 2 * delta;
inputVelocity.set(0, 0, 0);
if( rotateLeft ) {
yawObject.rotation.y += rotateAngle;
}
if( rotateRight ) {
yawObject.rotation.y -= rotateAngle;
}
if( moveForward ) {
inputVelocity.z = velocityFactor * delta * 50;
}
if( moveBackward ) {
inputVelocity.z = -velocityFactor * delta * 50;
}
// ★カメラ操作の計算・設定 Start ★
// カメラ左回転ボタン押下時
if ( cameraLeftRot ) {
targetRot = targetRot - 0.05;
// カメラ右回転ボタン押下時
} else if ( cameraRightRot ) {
targetRot = targetRot + 0.05;
}
//rot += 0.5; // 毎フレーム角度を0.5度ずつ足していく
// イージングの公式を用いて滑らかにする
// 値 += (目標値 - 現在の値) * 減速値
rot += (targetRot - rot) * 0.05;
// Convert velocity to world coordinates
// ↓自分の理解でコメント
// オイラー角による操作用ワイヤーフレームBoxの角度をクォータニオンに変換して
// 速度のベクトルに適応させる(=速度のベクトルに掛ける?)
euler.y = yawObject.rotation.y;
euler.order = 'XYZ';
quaternion.setFromEuler(euler);
inputVelocity.applyQuaternion(quaternion)
// ↑上記の元となったプログラムでは、速度をワールド座標軸に変換、としている処理?
// ↓とりあえず自分理解でコメント
// オイラー角による、操作用ワイヤーフレームBoxの回転を基準にさらにカメラの回転を加えたものを
// クォータニオンへ変換
// 操作用ワイヤーフレームBoxのまわりを回るカメラの角度として使用する
let cameraEuler = new THREE.Euler();
let cameraQuaternion = new THREE.Quaternion();
cameraEuler.y = yawObject.rotation.y + rot;
cameraEuler.order = 'XYZ';
cameraQuaternion.setFromEuler(cameraEuler);
// Add to the object
phyWireBox.velocity.x += inputVelocity.x
phyWireBox.velocity.z += inputVelocity.z
// Cannon.jsの物理空間の演算によるSphereの位置・回転をThree.jsによる見た目に反映
viewSphere.position.copy(phySphere.position);
viewSphere.quaternion.copy(phySphere.quaternion);
// Cannon.jsの物理空間の演算によるBoxの位置・回転をThree.jsによる見た目に反映
viewBox.position.copy(phyBox.position);
viewBox.quaternion.copy(phyBox.quaternion);
// Cannon.jsの物理空間の演算によるワイヤーフレームBoxの位置・回転をThree.jsによる見た目に反映
// 回転はオイラー角を変換して求めたクォータニオンを適応
viewWireBox.position.copy(phyWireBox.position);
//viewWireBox.quaternion.copy(phyWireBox.quaternion);
viewWireBox.quaternion.copy(quaternion);
// カメラ設定
// カメラの位置をベクトルとクォータニオンで設定
// Z軸方向のベクトルを作成(進行方向のベクトルを作成)
let directionVec = new THREE.Vector3( 0, 0, 1 );
// Z軸方向のベクトル(進行方向のベクトル)にカメラの角度の元となるクォータニオンを適応
directionVec.applyQuaternion(cameraQuaternion);
// カメラの距離をベクトル的に掛け算
directionVec.multiplyScalar(cameraDistance);
// .negate()関数を使用してカメラの位置が操作Boxの背後となるように設定
let cameraDirection = directionVec.clone().negate();
// 操作Boxの位置をベクトルで取得
let viewWireBoxPosition = new THREE.Vector3( viewWireBox.position.x, viewWireBox.position.y, viewWireBox.position.z );
// カメラの高さを表すベクトルを取得
let camHeightVec = new THREE.Vector3( 0, cameraHeight, 0 );
// 操作Boxの位置ベクトル + カメラの(角度を含む)位置ベクトル + カメラの高さベクトル
// → 現在のカメラの位置ベクトルを演算
const cameraPosition = cameraDirection.add(viewWireBoxPosition).add(camHeightVec);
// カメラの位置ベクトルをカメラに設定
camera.position.copy(cameraPosition);
// カメラが操作Boxの方を見るように設定
camera.lookAt(new THREE.Vector3( viewWireBox.position.x, viewWireBox.position.y, viewWireBox.position.z ));
// Three.js レンダリング
renderer.render(scene, camera);
}
function setupEventHandlers(){
//リサイズイベント発生時に実行
window.addEventListener('resize', onWindowResize, false);
//★★★★★★★ ボタン処理スタート ★★★★★★★
//★★「forward」ボタン処理★★
//カーソルが「forward」ボタン上にあるとき
forwardButton.addEventListener('pointerenter', () => {
}, false);
//「forward」ボタンが押されたとき
forwardButton.addEventListener('pointerdown', () => {
moveForward = true;
}, false);
//「forward」ボタンが離されたとき
forwardButton.addEventListener('pointerup', () => {
moveForward = false;
}, false);
//カーソルが「forward」ボタン上から離れたとき
forwardButton.addEventListener('pointerleave', () => {
moveForward = false;
}, false);
//★★「leftRot」ボタン処理★★
//「leftRot」ボタンが押されたとき
leftRotButton.addEventListener('pointerdown', () => {
rotateLeft = true;
}, false);
//「leftRot」ボタンが離されたとき
leftRotButton.addEventListener('pointerup', () => {
rotateLeft = false;
}, false);
//カーソルが「leftRot」ボタン上から離れたとき
leftRotButton.addEventListener('pointerleave', () => {
rotateLeft = false;
}, false);
//★★「rightRot」ボタン処理★★
//「rightRot」ボタンが押されたとき
rightRotButton.addEventListener('pointerdown', () => {
rotateRight = true;
}, false);
//「rightRot」ボタンが離されたとき
rightRotButton.addEventListener('pointerup', () => {
rotateRight = false;
}, false);
//カーソルが「rightRot」ボタン上から離れたとき
rightRotButton.addEventListener('pointerleave', () => {
rotateRight = false;
}, false);
//★★「backward」ボタン処理★★
//「backward」ボタンが押されたとき
backwardButton.addEventListener('pointerdown', () => {
moveBackward = true;
}, false);
//「backward」ボタンが離されたとき
backwardButton.addEventListener('pointerup', () => {
moveBackward = false;
}, false);
//カーソルが「backward」ボタン上から離れたとき
backwardButton.addEventListener('pointerleave', () => {
moveBackward = false;
}, false);
//★★「camLeftRot」ボタン処理★★
//「camLeftRot」ボタンが押されたとき
camLeftRotButton.addEventListener('pointerdown', () => {
cameraLeftRot = true;
}, false);
//「camLeftRot」ボタンが離されたとき
camLeftRotButton.addEventListener('pointerup', () => {
cameraLeftRot = false;
}, false);
//カーソルが「camLeftRot」ボタン上から離れたとき
camLeftRotButton.addEventListener('pointerleave', () => {
cameraLeftRot = false;
}, false);
//★★「camRightRot」ボタン処理★★
//「camRightRot」ボタンが押されたとき
camRightRotButton.addEventListener('pointerdown', () => {
cameraRightRot = true;
}, false);
//「camRightRot」ボタンが離されたとき
camRightRotButton.addEventListener('pointerup', () => {
cameraRightRot = false;
}, false);
//カーソルが「camRightRot」ボタン上から離れたとき
camRightRotButton.addEventListener('pointerleave', () => {
cameraRightRot = false;
}, false);
//★★「camHeight」ボタン処理★★
//「camHeight」ボタンが押されたとき
camHeightButton.addEventListener('pointerdown', () => {
cameraHeight = cameraHeight + CAMERA_HEIGHT_CALC_UNIT;
if ( cameraHeight > MAX_CAMERA_HEIGHT ) {
cameraHeight = MIM_CAMERA_HEIGHT;
}
}, false);
//「camHeight」ボタンが離されたとき
camHeightButton.addEventListener('pointerup', () => {
}, false);
//カーソルが「camHeight」ボタン上から離れたとき
camHeightButton.addEventListener('pointerleave', () => {
}, false);
//★★「camDist」ボタン処理★★
//「camDist」ボタンが押されたとき
camDistButton.addEventListener('pointerdown', () => {
cameraDistance = cameraDistance - CAMERA_DIST_CALC_UNIT;
if ( cameraDistance < MIM_CAMERA_DIST ){
cameraDistance = MAX_CAMERA_DIST
}
}, false);
//「camDist」ボタンが離されたとき
camDistButton.addEventListener('pointerup', () => {
}, false);
//カーソルが「camDist」ボタン上から離れたとき
camDistButton.addEventListener('pointerleave', () => {
}, false);
//★★「camReset」ボタン処理★★
//「camReset」ボタンが押されたとき
camResetButton.addEventListener('pointerdown', () => {
targetRot = 0;
cameraHeight = DEFFAULT_CAMERA_HEIGHT;
cameraDistance = DEFFAULT_CAMERA_DIST;
}, false);
//「camReset」ボタンが離されたとき
camResetButton.addEventListener('pointerup', () => {
}, false);
//カーソルが「camReset」ボタン上から離れたとき
camResetButton.addEventListener('pointerleave', () => {
}, false);
//★★★★★★★ ボタン処理エンド ★★★★★★★
}
// リサイズイベント発生時に実行
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
最終成果物までの過程となったコードたち
最終成果物に辿りつくまで、結局cannon.jsをほぼ最初から復習する形となってしまった過程の記録です。 これまでの経験から今後CodePenに違う系統のコードを追加しているうちに他のコードに埋もれてしまいそうなので、リンク集を書き留めておく感じです。
内容はまったく整理されていなくて、あとで見返すかも精神でコメントアウトで書きかけコードや、書き換え前コードが残りまくってますw。 (←そしてそれをまた残して参考リンクとか後で見返すかも精神ですw)
過程コードその① Three.jsとCannon.jsの連携
Three.js + Cannon.js Test01
https://codepen.io/siouxcitizen/pen/vYrGpKW
Three.js + Cannon.js Test02
https://codepen.io/siouxcitizen/pen/oNyzMMr
Three.js + Cannon.js Test03
https://codepen.io/siouxcitizen/pen/LYrxGxw
Three.js + Cannon.js Test04
https://codepen.io/siouxcitizen/pen/vYrgqNr
Three.js + Cannon.js Test05
https://codepen.io/siouxcitizen/pen/xxzqmKW
Three.js + Cannon.js Test06(失敗中)
https://codepen.io/siouxcitizen/pen/ZERyoPX
過程コードその② Three.jsとcannon-esの連携
Three.js r146 + cannon-es v0.20.0 Test01
https://codepen.io/siouxcitizen/pen/PoaBKrP
Three.js r146 + cannon-es v0.20.0 Test02
https://codepen.io/siouxcitizen/pen/oNyMyWK
Three.js r146 + cannon-es v0.20.0 Test03
https://codepen.io/siouxcitizen/pen/eYKqdrW
Three.js r146 + cannon-es v0.20.0 Test04
https://codepen.io/siouxcitizen/pen/rNrNBwG
Three.js r146 + cannon-es v0.20.0 Test05
https://codepen.io/siouxcitizen/pen/oNMNNZN
Three.js r146 + cannon-es v0.20.0 Test06
https://codepen.io/siouxcitizen/pen/LYBYzjB
次回
この記事が気に入ったらサポートをしてみませんか?