ブラウザ3Dゲームを作る作戦その3: カメラで追従する
今回は3人称視点を目指して少しずつ変更していきます。
わかりやすいように床を入れる
これはとても簡単にできました。画像はカメラの位置を適当に調整してあります。
// 床グリッドを追加 var gridHelper = new THREE.GridHelper(500, 100); scene.add(gridHelper);
マウスイベントを取得する
マウスが動いたとかクリックされたなどは、今回は描画画面が監視してくれる関数を使いました。
// マウス情報 var mousex, mousey; var mouseDrag=0; // マウスが動いたとき renderer.domElement.addEventListener('mousemove', e => { //描画画面中心からの差をマウスの x, y とする mousex = e.clientX - renderer.domElement.offsetWidth/2 - renderer.domElement.offsetLeft; mousey = e.clientY - renderer.domElement.offsetHeight/2 - renderer.domElement.offsetTop; }); // マウスが押されたとき renderer.domElement.addEventListener('mousedown', e => { mouseDrag = 1; console.log( mousex +','+mousey ); }); // マウスが離されたとき renderer.domElement.addEventListener('mouseup', e => { mouseDrag = 0; });
描画画面の実体は renderer.domElement のようです。試しに console.log で表示して中を覗いてみると、
offsetWidth と offsetHeight というものがあったのでこの半分を画面の真ん中として使いました。
canvasの表示位置が少し下なのでその分を offsetTop で調整しています。
ここまでを実行するとクリックで console に場所が表示されるようになりました。
向いている方向に進む
カメラはプレイヤーについてくるので、先にプレイヤー操作をもう少し改造します。
オブジェクトを作ってみる
今まで前後左右に動くだけでしたが、向きなども入ってくるので変数をいくつかまとめたオブジェクトを作りたいと思います。
// object をつくる var MyAgent = function(){ this.position = new THREE.Vector3(0, 0, 0); // 場所 this.viewVect = new THREE.Vector3(0, 0, 0); // 向いている方向 this.rotationUp = 0; // 向いている上下の角度 this.rotationRight = THREE.Math.degToRad( -90 );// 向いている水平方向の角度 // 向いている角度から向いている方向を計算する this.updateView = function(){ var y_ = Math.sin(this.rotationUp); var x_ = Math.cos(this.rotationUp) * Math.cos(this.rotationRight); var z_ = Math.cos(this.rotationUp) * Math.sin(this.rotationRight); this.viewVect = new THREE.Vector3(x_, y_, z_); } } // player という名前で作ったobject を1つ用意する player = new MyAgent(); console.log( player ); player.position = cube.position;
console.log で表示してみると、
こんな感じで作ることができました。
向いている方向を基準に前後左右させる
今までの時点ではWASDで xと zが増減しましたが、向きを考えてどれぐらい x と z に値を加えるかを変更します。
さらに player と動かす cube を連動させたいので、キーボードの反映の関数をざっくり書き換えて、
- player の位置をキーボードで動かす
- cube の位置と向きをplayerと同じにする
ように変更します。これで playerが変わっても書き換えが楽になるかと思います。
//プレーヤーの移動 function userMove(frameTime, mesh, agent) { // 向きを更新 agent.updateView(); //w (前進) if(key_on[87]>0){ agent.position.z += frameTime * 5 * agent.viewVect.z; agent.position.x += frameTime * 5 * agent.viewVect.x; } //a if(key_on[65]>0){ agent.position.z -= frameTime * 5 * agent.viewVect.x; agent.position.x += frameTime * 5 * agent.viewVect.z; } //s (後退) if(key_on[83]>0){ agent.position.z -= frameTime * 5 * agent.viewVect.z; agent.position.x -= frameTime * 5 * agent.viewVect.x; } //d (右) if(key_on[68]>0){ agent.position.z += frameTime * 5 * agent.viewVect.x; agent.position.x -= frameTime * 5 * agent.viewVect.z; } // 反映する mesh.rotation.y = -agent.rotationRight; mesh.position.x = agent.position.x; mesh.position.y = agent.position.y; mesh.position.z = agent.position.z; }
向きに対して前進・後退か直角のどちらに進むかといった感じですね。
これを animation の部分で userMove(frameTime, cube, player); のように呼び出せばOKです。
今のところプレイヤーの向いている角度が変わらないので、ここはやっつけで勝手に回転させることにします。
// 時計 clock = new THREE.Clock(); function animate() { window.requestAnimationFrame( animate ); // 現在の1フレームの長さを取得 var frameTime = clock.getDelta(); // とりあえず回転させる player.rotationRight += frameTime * THREE.Math.degToRad( 20 ); userMove(frameTime, cube, player); // シーンを描画 renderer.render( scene, camera ); } animate();
カメラを動かす
カメラの動かし方はいろいろあるとは思いますが、基本的にプレイヤーの後ろからシーンを眺めているようにしたいです。概要としては、
- プレイヤーが向いている方向の背後にカメラの位置を設定する
- プレーヤーと同じ方向をカメラも向く
//カメラの再設置 function cameraUpDate(agent) { // カメラの場所をプレイヤーの後ろに camera.position.x = agent.position.x -agent.viewVect.x *5; camera.position.y = agent.position.y -agent.viewVect.y *5+2; camera.position.z = agent.position.z -agent.viewVect.z *5; // プレイヤーの方向を向く camera.lookAt( agent.position); }
これで cameraUpDate(player); と呼べばカメラ位置が更新されるようになります。
マウスで向きが変わる&カメラが追従する
せっかくなので勝手に回転するのはやめて、やっつけでマウスを押すと方向転換するようにします。
function userMove(frameTime, mesh, agent) { // マウス位置で方向転換 canvasWidth = renderer.domElement.offsetWidth; canvasHeight = renderer.domElement.offsetHeight; dest_angleRight = Math.atan2(mousex, canvasWidth); dest_angleUp = Math.atan2(mousey, canvasHeight); // 水平方向(マウスを押しているとき) if( dest_angleRight && mouseDrag>0){ player.rotationRight += frameTime * dest_angleRight *5; } // 上下(マウスを押しているとき) if( Math.abs(dest_angleUp) > THREE.Math.degToRad( 5 ) && Math.abs(dest_angleRight) < Math.abs(dest_angleUp) && mouseDrag>0){ player.rotationUp -= frameTime * dest_angleUp *4; if(player.rotationUp >= THREE.Math.degToRad( 45 )){player.rotationUp= THREE.Math.degToRad( 45 );} if(player.rotationUp <= THREE.Math.degToRad( -60 )){player.rotationUp= THREE.Math.degToRad( -60 );} } // 向きを更新 agent.updateView(); //w (前進) if(key_on[87]>0){ …以下略
サンプル
違和感はありますがやっとそれらしくなってきましたね。
https://iwanaboz.github.io/html/hogehoge6.html