はじめに
こんにちは!2019年度新入社員の飯塚です。
先日、Unityを用いた3Dアプリケーションの開発に初めて取り組みました。Unity公式のチュートリアルである「玉転がし」を通じてUnityの基本操作を勉強していきました。オブジェクト、コンポーネント、プレハブ、コライダーなど聞きなれない単語がたくさん登場してきましたが、なんとかUnity上の操作は行えるようになりました。
3Dオブジェクトを”魅せる”ためには、カメラ操作が欠かせません。3Dモデルの共有サイトとして有名な「TURBOSQUID」や「Free3D」では3Dモデルのスクリーンショットを掲載していますが、せっかく3Dのモデルを扱うのだから「Sketchfab」のように360°モデルを回転させて眺められる方が断然魅力的に感じます。手元でぐりぐり操作する感覚が心地よいですし、マテリアルの雰囲気やライトの具合などもリアルタイムに実感することができます。
さて、Unityを触り始めて3日の私が気になったのは、「UnityワークスペースのSceneビューでのカメラ操作感」と「Sketchfabでのカメラの操作感」が異なるという点でした。具体的には、Sceneビューではカメラを中心として視野が回転しますが、Sketchfabでは対象オブジェクトを中心にカメラの位置が回転移動するのです。今回は、Sketchfabのような「対象オブジェクト中心のカメラ回転」が簡単に実装できないかということにチャレンジしてみることにしました。
実装してみる
Sceneビューでのカメラ操作の実装についてはこちらの記事が参考になりました。これを元に、Sketchfabでのカメラ操作を再現してみます。
今回実装したいカメラ操作は「カメラはある点に注目し続ける」というのがポイントです。見たいオブジェクト自体に視点を固定してもいいですが、それではカメラの平行移動の処理を組み込みづらいです。なので見たいオブジェクトとは別に、カメラターゲット用のオブジェクトを置くことで実装します。
方針
- カメラとカメラターゲットを、ヒエラルキーの同階層に置く。
- カメラはUpdate()のたびにカメラターゲットの方を向く。
- カメラを平行移動させる場合は、カメラターゲットも一緒に平行移動させる。
- カメラを回転移動させる場合は、カメラターゲットも一緒に回転させる(位置は固定)。
- カメラを前進/後退させる場合は、カメラターゲットは固定。
スクリプト
Main Camera(Cameraオブジェクト)とCameraTarget(Emptyオブジェクト)を並列に置きます。
Main Cameraに「Add Component」から以下のスクリプトを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
using UnityEngine; public class CameraController : MonoBehaviour { public GameObject CameraTarget; [SerializeField, Range(1f, 500f)] private float wheelSpeed = 100f; [SerializeField, Range(0.1f, 1f)] private float moveSpeed = 0.3f; [SerializeField, Range(0.1f, 1f)] private float rotateSpeed = 1f; private Vector3 preMousePosition; private void Start() { LookCameraTarget(); } private void Update() { float scrollWheel = Input.GetAxis("Mouse ScrollWheel"); if(scrollWheel != 0.0f) { MouseWheel(scrollWheel); } if (Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1)) { preMousePosition = Input.mousePosition; } MouseDrag(Input.mousePosition); return; } private void MouseWheel(float delta)//前進/後退 { transform.position += transform.forward * delta * wheelSpeed; return; } private void MouseDrag(Vector3 mousePosition) { Vector3 diff = mousePosition - preMousePosition; if (diff.magnitude < Vector3.kEpsilon) { return; } float d = distance(); if (Input.GetMouseButton(0))//回転移動 { transform.Translate(-diff * Time.deltaTime * rotateSpeed * d); LookCameraTarget(); transform.position += transform.forward * ((transform.position - CameraTarget.transform.position).magnitude - d);//直線移動と曲線移動の誤差修正 } else if (Input.GetMouseButton(1))//平行移動 { transform.Translate(-diff * Time.deltaTime * moveSpeed * d); CameraTarget.transform.Translate(-diff * Time.deltaTime * moveSpeed * d); LookCameraTarget(); } preMousePosition = mousePosition; return; } private float distance() { return (transform.position - CameraTarget.transform.position).magnitude; } private void LookCameraTarget() { transform.LookAt(CameraTarget.transform); CameraTarget.transform.rotation = transform.rotation; return; } } |
スクリプトを追加したら、変数Camera TargetにCameraTarget(Emptyオブジェクト)を紐づけます。
結果
実行してみると、以下の操作を実現できました。
- マウスホイールの回転で、ズームイン/ズームアウト
- 左クリック→ドラッグで、カメラの回転移動
- 右クリック→ドラッグで、カメラの平行移動
解説
- カメラがカメラターゲットの方を向くメソッドは「LookCameraTarget()」です。TransformのLookAtメソッドを活用します。平行移動する際に、カメラの移動方向とカメラターゲットの移動方向が一致するように、カメラのRotationとカメラターゲットのRotationも一致させておきます。
- 左クリックは「Input.GetMouseButtonDown(0)」、右クリックは「Input.GetMouseButtonDown(1)」で入力を受け取れます。
- 前進/後退は、カメラの位置ベクトル「transform.position」にカメラの向いている方向を表すベクトル「transform.forward」を加算することで実現できます。
- 平行移動は、TransformのTranslateメソッドを活用します。マウスポインタが動いた分だけ平行移動させます。
- 回転移動にもTransformのTranslateメソッドを活用します。ただし、Translateは直線移動なので曲線移動らしく振舞うように誤差を修正する必要があります。
- 平行移動と回転移動はターゲットから遠ざかるほど移動が大変になるので、カメラとカメラターゲットとの距離(distance())を掛けることでターゲットに近いときと同様の操作感を実現しています。
まとめ
今回はUnityでSketchfabのようなカメラ操作を実装していきました。
ポイントは、カメラのターゲットとなるオブジェクトを用意して一緒に動かす、というところでした。今回書いたスクリプトは簡単なメソッドの組み合わせですので、誰でも簡単に実装できるかと思います。
Sketchfabが実際にどのようなロジックを用いているかは分かりません。Sketchfabではカメラ移動に慣性が働いているようにも感じられるので、もう少し工夫の余地があるかもしれませんね。またUnityを触る機会があれば掘り下げてみたいと思います。
最後までお読みいただきありがとうございました。