質問: new Vector3 (0.0f, dirRot, 0.0f); ``` 長くなりましたが、どちらかでもいいので、ご教授いただけますと幸いです。
回答: カメラが初期位置から移動しなかったのは、オフセットがUpdate中の`offset = transform.position - player.position;`のために毎回「その時点のカメラの位置 - その時点のプレイヤーの位置」に更新されてしまっていたためかと思います。 ご参考として修正案を考えてみましたが、いかがでしょうか? なるべく元のコードを残そうとしたつもりでしたが、ご質問者さんの意図する動きを想像しながらいじっているうちに、結構変わってしまいました。すみません... 左右上下キー押下時のプレイヤー移動方向の切り替え案として、基準とするべき右方向・前方向をカメラに持たせておいて、プレイヤーはそれを見て進む方向を決めるようにしてみました。 スクリプト名はご質問者さんのものに合わせて読み替えてください。変数などをいくらか追加しましたが、名前がちょっと冗長な気がするので、お好きな名前を付けていただくといいかと思います。 カメラのスクリプト(LookRotation版) ``` using UnityEngine; public class CameraController : MonoBehaviour { private const float CameraDeltaOffsetSqrMagnitudeThreshold = 0.0001f; // キャラクターのTransform public Transform player; // カメラの移動スピード public float cameraMoveSpeed; // カメラの回転スピード public float cameraRotateSpeed; // カメラのキャラクターからの相対値を指定 private Vector3 offset; // Start時のキャラクターに対するカメラの相対位置をプレイヤーのローカル座標系で保存 private Vector3 initialPlayerLocalOffset; public Vector3 CameraRight { get; private set; } // 変更...バッキングフィールドとしてcameraRightを用意するより、CameraRightをCameraControllerだけから設定可能なプロパティとした方がコードがシンプルになるかと思いました public Vector3 CameraForward { get; private set; } // 変更...CameraRightと同じく、CameraControllerだけから設定可能なプロパティとしました bool flag = false; void Start () { //プレイヤーとカメラの距離を計算する offset = transform.position - player.position; transform.rotation = Quaternion.LookRotation(-offset); // offsetが更新されたので、カメラ回転も更新 initialPlayerLocalOffset = player.InverseTransformDirection(offset); // offsetをプレイヤーローカル座標系に直すパターンを採用 CameraRight = transform.right; // カメラの右方向を設定 var newForward = transform.forward; // 追加 newForward.y = 0.0f; // 追加...カメラの前方向をXZ平面上に投影 newForward.Normalize(); // 追加...正規化し長さを1に CameraForward = newForward; // 変更...新しい前方向ベクトルを設定 } void Update () { //ALtキーを押すとカメラの位置をプレイヤーの真後ろへリセットする if (Input.GetKeyDown (KeyCode.LeftAlt) || Input.GetKeyDown (KeyCode.RightAlt)) { flag = true; } if (flag == true) { //プレイヤーの背後へ移動 Vector3 destinationOffset = player.TransformDirection(initialPlayerLocalOffset); // Start時にプレイヤーの座標系で見たカメラの位置を保存しておいたので、そこからカメラの目標オフセットを求める // offset = Vector3.Slerp (offset, destinationOffset, cameraMoveSpeed * Time.deltaTime); // 変更...LerpをSlerpにして、カメラの動きが球面上の軌道を取るようにしてみました(Lerpの方がお好みでしたら、そちらでも問題ありません) offset = InterpolateOffset(offset, destinationOffset, cameraMoveSpeed * Time.deltaTime); // 追加...Slerpの代わりにカスタム補間メソッドを使った場合 transform.rotation = Quaternion.LookRotation(-offset); // offsetが更新されたので、カメラ回転も更新 //回転が終わったら通常の追従状態に戻る? if ((offset - destinationOffset).sqrMagnitude < CameraDeltaOffsetSqrMagnitudeThreshold) { offset = destinationOffset; // 目標オフセットに完全に移動する transform.rotation = Quaternion.LookRotation(-offset); // offsetが更新されたので、カメラ回転も更新 CameraRight = transform.right; // カメラの右方向を更新、以後プレイヤーは新しい右方向を基準に移動するようになる var newForward = transform.forward; // 追加 newForward.y = 0.0f; // 追加...カメラの前方向をXZ平面上に投影 newForward.Normalize(); // 追加...正規化し長さを1に CameraForward = newForward; // 変更...新しい前方向ベクトルを設定、以後プレイヤーは新しい前方向を基準に移動するようになる flag = false; } } transform.position = player.position + offset; } // 追加...offsetの補間用メソッド static Vector3 InterpolateOffset(Vector3 a, Vector3 b, float t) { var aXZ = new Vector2(a.x, a.z); // 真上から見たa var aR = aXZ.magnitude; // aのY軸からの距離 var aRIsNotZero = aR > 1E-05; // aRが(ほぼ)0より大きいか? var aD = aRIsNotZero ? aXZ / aR : Vector2.zero; // 真上から見たaの方角、aRがほぼ0ならVector2.zeroとして無回転にする var bXZ = new Vector2(b.x, b.z); // 真上から見たb var bR = bXZ.magnitude; // bのY軸からの距離 var bD = (bR > 1E-05) ? bXZ / bR : Vector2.zero; // 真上から見たbの方角、bRがほぼ0ならVector2.zeroとして無回転にする var theta = Mathf.Sign(aD.x * bD.y - bD.x * aD.y) * Mathf.Acos(Mathf.Clamp(Vector2.Dot(aD, bD), -1.0f, 1.0f)); // aDとbDがなす弧度法による符号付き角度 var alpha = aRIsNotZero ? Mathf.Atan2(aD.y, aD.x) : Mathf.Atan2(bD.y, bD.x); // 基準の角度...aDとX軸のなす角(aRがほぼ0ならbDとX軸のなす角) var newTheta = Mathf.Lerp(0.0f, theta, t); // aDとbDがなす角度を線形補間 var newPhi = alpha + newTheta; // 基準の角度に先ほど線形補間した角度を足して、これを真上から見たときの新たな方角とする var newR = Mathf.Lerp(aR, bR, t); // Y軸からの距離を線形補間 var newX = Mathf.Cos(newPhi) * newR; // 新しいX座標 var newY = Mathf.Lerp(a.y, b.y, t); // 新しいY座標(aとbのY座標を線形補間) var newZ = Mathf.Sin(newPhi) * newR; // 新しいZ座標 return new Vector3(newX, newY, newZ); } } ``` プレイヤーのスクリプト ``` using UnityEngine; public class PlayerController : MonoBehaviour { public CameraController cameraController; // 追加...カメラの右・前方向を知りたいので、カメラのスクリプトを保持するようにしました(インスペクタでカメラをセットしておいてください) // Use this for initialization private void Start() { } // Update is called once per frame private void Update() { float dirH, dirV; float dirSpeed = 0.1f; // これは使用していないようですが、一応残しています dirH = Input.GetAxis ("Horizontal"); dirV = Input.GetAxis ("Vertical"); // 削除...移動はvecを求めてから行う /* transform.position = new Vector3 (transform.position.x - dirH * 0.1f, //横移動 transform.position.y, transform.position.z - dirV * 0.1f); //奥移動 */ var vec = cameraController.CameraRight * (dirH * 0.1f) + cameraController.CameraForward * (dirV * 0.1f); // 変更...入力された移動量について、水平はCameraRight、垂直はCameraForwardの向きへの移動と考え、これをvecとする transform.Translate(vec, Space.World); // 追加...位置をvecだけ移動 // 追加...立ち止まったときに初期の向きに戻ってしまうのを防ぐため、入力がゼロ以外の時しか向きを回転しない // カメラのスクリプトでやったのと同じく、vec.sqrMagnitudeが一定値未満かどうかで判定すれば、入力の有無の判定にあそびを設けることもできると思います if (vec != Vector3.zero) { // ここから... vec.Normalize (); //方向を求める var dirRot = Mathf.Atan2 (vec.x, vec.z) / 3.14159265f * 180.0f; transform.eulerAngles = new Vector3 (0.0f, dirRot, 0.0f); // ここまでの代わりに、下記のような回転方法もアリかと思います // transform.rotation = Quaternion.FromToRotation(Vector3.forward, vec); } } } ```