More than 1 year has passed since last update.

はい、今週も週刊 WebでMinec◯aft的なものを作るの時間ですー!今週も司会・進行・実装担当の大学生プログラマ、うーぴょん(さすらいうさぎ)がお送りしていきたいと思います!

いやー、そろそろ視点を動かしてみたいですね!Minec◯aftといえば、Wで前、Aで左、Sで後ろ、Dで右に自分が移動することができます!(後、スペースキーで上、シフトキーで下に動けますね)
マウスを動かすと見る方向も変えられますよね!
今日はその実装を進めていこうと思います!

アジェンダ的な

  1. カメラを動かす!

Let's try!

カメラの位置を動かす。

STEP1 キー入力を受け付ける。

はい、同じみのonkeydownを使います!

js/applicaiton.js
window.onload = function() {
  var renderer = new THREE.WebGLRenderer({antialias: true});
  var scene = new THREE.Scene();
  var camera = new THREE.PerspectiveCamera(40, 800 / 480);
  var cube = new THREE.CubeGeometry(1, 1, 1);
  var light = new THREE.AmbientLight(0xffffff);
  var updateCanvas = function() {
    requestAnimationFrame(updateCanvas);
    renderer.render(scene, camera);
  }

  renderer.setSize(800, 480);
  renderer.setClearColor(0xffffff);
  document.getElementById("canvas_wrapper").appendChild(renderer.domElement);

  camera.position.set(0, 4, 15);
  camera.lookAt(new THREE.Vector3(0, 0, 0));

  for(var x = -3; x <= 3; ++x) {
    for(var z = -3; z <= 3; ++z) {
      var meshDirt = new THREE.Mesh(cube, Blocks.dirt.getMaterial());
      var meshRock = new THREE.Mesh(cube, Blocks.rock.getMaterial());
      var meshBedrock = new THREE.Mesh(cube, Blocks.bedrock.getMaterial());

      meshDirt.position.set(x, 0, z);
      meshRock.position.set(x, -1, z);
      meshBedrock.position.set(x, -2, z);

      scene.add(meshDirt);
      scene.add(meshRock);
      scene.add(meshBedrock);
    }
  }

  scene.add(light);

  document.onkeydown = function(e) {
    console.log(e.keyCode);
  }

  updateCanvas();
}

とりあえず、「W」「A」「S」「D」「space」のキーが使いたいのでkeyCodeをコンソールに出力して調べてみると、それぞれ87・65・83・68・32でした。ちなみに関数の引数eには、発生したイベントに応じて座標などの必要な情報が入っています

これを使って実装を進めていきます。

js/application.js
window.onload = function() {
  var posX = 0, posY = 0, posZ = 0;
  var renderer = new THREE.WebGLRenderer({antialias: true});
  var scene = new THREE.Scene();
  var camera = new THREE.PerspectiveCamera(40, 800 / 480);
  var cube = new THREE.CubeGeometry(1, 1, 1);
  var light = new THREE.AmbientLight(0xffffff);
  var updateCanvas = function() {
    requestAnimationFrame(updateCanvas);
    renderer.render(scene, camera);
  }

  renderer.setSize(800, 480);
  renderer.setClearColor(0xffffff);
  document.getElementById("canvas_wrapper").appendChild(renderer.domElement);

  camera.position.set(0, 4, 15);
  camera.lookAt(new THREE.Vector3(0, 0, 0));

  for(var x = -3; x <= 3; ++x) {
    for(var z = -3; z <= 3; ++z) {
      var meshDirt = new THREE.Mesh(cube, Blocks.dirt.getMaterial());
      var meshRock = new THREE.Mesh(cube, Blocks.rock.getMaterial());
      var meshBedrock = new THREE.Mesh(cube, Blocks.bedrock.getMaterial());

      meshDirt.position.set(x, 0, z);
      meshRock.position.set(x, -1, z);
      meshBedrock.position.set(x, -2, z);

      scene.add(meshDirt);
      scene.add(meshRock);
      scene.add(meshBedrock);
    }
  }

  scene.add(light);

  document.onkeydown = function(e) {
    if(e.keyCode == 87) {
      posX += 0.1;
    } else if(e.keyCode == 65) {
      posZ -= 0.1;
    } else if(e.keyCode == 83) {
      posX -= 0.1;
    } else if(e.keyCode == 68) {
      posZ += 0.1;
    } else if(e.keyCode == 32 && e.shiftKey) {
      posY -= 0.3;
    } else if(e.keyCode == 32) {
      posY += 0.3;
    }
  }

  updateCanvas();
}

キー入力のコード作成はこれで終了です。

STEP2 カメラを動かす

カメラの設置位置を動かして描画しなおせばいいだけです、はい。

js/application.js
window.onload = function() {
  var posX = 0, posY = 0, posZ = 0;
  var renderer = new THREE.WebGLRenderer({antialias: true});
  var scene = new THREE.Scene();
  var camera = new THREE.PerspectiveCamera(40, 800 / 480);
  var cube = new THREE.CubeGeometry(1, 1, 1);
  var light = new THREE.AmbientLight(0xffffff);
  var updateCanvas = function() {
    requestAnimationFrame(updateCanvas);

    camera.position.set(posX, posY + 1.6, posZ);
    camera.lookAt(new THREE.Vector3(posX + 7, posY, posZ));
    renderer.render(scene, camera);
  }

  renderer.setSize(800, 480);
  renderer.setClearColor(0xffffff);
  document.getElementById("canvas_wrapper").appendChild(renderer.domElement);


  for(var x = -3; x <= 3; ++x) {
    for(var z = -3; z <= 3; ++z) {
      var meshDirt = new THREE.Mesh(cube, Blocks.dirt.getMaterial());
      var meshRock = new THREE.Mesh(cube, Blocks.rock.getMaterial());
      var meshBedrock = new THREE.Mesh(cube, Blocks.bedrock.getMaterial());

      meshDirt.position.set(x, 0, z);
      meshRock.position.set(x, -1, z);
      meshBedrock.position.set(x, -2, z);

      scene.add(meshDirt);
      scene.add(meshRock);
      scene.add(meshBedrock);
    }
  }

  scene.add(light);

  document.onkeydown = function(e) {
    if(e.keyCode == 87) {
      posX += 0.1;
    } else if(e.keyCode == 65) {
      posZ -= 0.1;
    } else if(e.keyCode == 83) {
      posX -= 0.1;
    } else if(e.keyCode == 68) {
      posZ += 0.1;
    } else if(e.keyCode == 32 && e.shiftKey) {
      posY -= 0.3;
    } else if(e.keyCode == 32) {
      posY += 0.3;
    }
  }

  updateCanvas();
}

「W」で前、「A」で左、「S」で後ろ、「D」で右、「space」で上、「Shift + space」で下に動きます!
本当は下に動かすキーは、本家と同じように「shift」だけにしたかったのですが、押しっぱなしかどうかの判定が少々ややこしくなる(多分入力に関係ないキーだから優遇されてない?)ので、別のキーバインドに変更しました。

STEP3 マウスからの入力を受け付ける

これも同じみ、onmousemoveを使います!今回はカーソルの変化量を取りたいのですが、ブラウザによってプロパティ名が異なるので||でつないでいます!
||でつなぐことで左から順に式が評価され、null・undefinedじゃない最初の値を返します。

js/js/application.js
window.onmousemove = function() {
  var posX = 0, posY = 0, posZ = 0;
  //(中略)
  document.onmousemove = function(e) {
    var deltaX = e.momentX || e.webkitMovementX || e.mozMovementX || e.movementX;//カーソルのx軸方向の変化量が入っている変数はブラウザによって異なる
    var deltaY = e.momentY || e.webkitMovementY || e.mozMovementY || e.movementY;

    console.log("(" + deltaX + ", " + deltaY + ")");
  }

  updateCanvas();
}

コードが長くなったので必要な部分だけ抜粋しました。

STEP4 カメラの注視点を変える

STEP3の内容と組み合わせてカメラが見つめる位置、注視点を変えていきます!
ピッチ(縦方向の傾き)だけは、絶対値が90°を超えないようにしました。

js/application.js
window.onload = function() {
  var posX = 0, posY = 0, posZ = 0;
  var yaw = 0, pitch = 0;
  var sightX = 1, sightY = 0, sightZ = 0;
  //(中略)
  var updateCanvas = function() {
    requestAnimationFrame(updateCanvas);

    camera.position.set(posX, posY + 1.6, posZ);
    camera.lookAt(new THREE.Vector3(posX + sightX, posY + 1.6 + sightY, posZ + sightZ));
    renderer.render(scene, camera);
  }
  //(中略)
  document.onmousemove = function(e) {
    var deltaX = e.momentX || e.webkitMovementX || e.mozMovementX || e.movementX;
    var deltaY = e.momentY || e.webkitMovementY || e.mozMovementY || e.movementY;
    var radian = Math.PI / 180;

    yaw += deltaX / 0.8;
    pitch -= deltaY / 0.8;

    if(pitch > 90) {
      pitch = 90;
    } else if(pitch < -90) {
      pitch = -90;
    }

    sightX = Math.cos(radian * pitch) * Math.cos(radian * yaw);
    sightY = Math.sin(radian* pitch);
    sightZ = Math.cos(radian * pitch) * Math.sin(radian * yaw);
  }

  updateCanvas();
}

マウスの動きに合わせてピッチとヨー(回転)の角度をいじくって、視線のベクトルを計算しています。ベクトルの計算は描画速度を落としたくないのでonmousemove内で行っています。(多分、これぐらいならonmousemoveのパフォーマンスも落ちないはず)

STEP5 視線と動きの方向を合わせる

カメラの位置や向きをいじれるようになって、テンションが上がってきましたね!でも違和感を感じます・・・。あっ、視線と実際にカメラが動いていく方向が違うからおかしいんですね!
では、この2つの向きを合わせていきます!

js/js/application.js
window.onload = function() {
  var posX = 0, posY = 0, posZ = 0;
  var yaw = 0, pitch = 0;
  var sightX = 1, sightY = 0, sightZ = 0;
  var moveX = 1, moveZ = 0;
  //(中略)
  document.onkeydown = function(e) {
    if(e.keyCode == 87) {
      posX += 0.3 * moveX;
      posZ += 0.3 * moveZ;
    } else if(e.keyCode == 65) {
      posX += 0.3 * moveZ;
      posZ -= 0.3 * moveX;
    } else if(e.keyCode == 83) {
      posX -= 0.3 * moveX;
      posZ -= 0.3 * moveZ;
    } else if(e.keyCode == 68) {
      posX -= 0.3 * moveZ;
      posZ += 0.3 * moveX;
    } else if(e.keyCode == 32 && e.shiftKey) {
      posY -= 0.3;
    } else if(e.keyCode == 32) {
      posY += 0.3;
    }
  }

  document.onmousemove = function(e) {
    var deltaX = e.momentX || e.webkitMovementX || e.mozMovementX || e.movementX;
    var deltaY = e.momentY || e.webkitMovementY || e.mozMovementY || e.movementY;
    var radian = Math.PI / 180;

    yaw += deltaX / 0.8;
    pitch -= deltaY / 0.8;

    if(pitch > 90) {
      pitch = 90;
    } else if(pitch < -90) {
      pitch = -90;
    }

    sightX = Math.cos(radian * pitch) * Math.cos(radian * yaw);
    sightY = Math.sin(radian* pitch);
    sightZ = Math.cos(radian * pitch) * Math.sin(radian * yaw);
    moveX = Math.cos(radian * yaw);
    moveZ = Math.sin(radian * yaw);
  }

  updateCanvas();
}

またまた、ベクトルをゴリゴリと計算しています!
これで視線と移動方向が一致しました!

まとめ

座標計算にさえ詰まらなければ、ユーザからの入力に応じたカメラの移動は簡単!
onkeydownでキー入力が行われた時に関数を実行できる!
onmousemoveを使うとマウスが動いた時に関数を実行できる!

カメラ動かすことができたし、そろそろブロック置いたり壊したりしたいなぁと思ったところで時間が来てしまいました!では、また来週ー!

第2週目はこちら

週刊 WebでMinec◯aft的なものを作る! ~第2週目~

号外1はこちら

週刊 WebでMinec◯aft的なものを作る! ~号外1~

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.