質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

1回答

1831閲覧

カメラの追従と、位置リセットを実装したいが追従しない + リセット後プレイヤーの正面方向を奥方向としたい

toricotori

総合スコア14

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2017/11/22 09:40

質問失礼いたします。
現在、「プレイヤーの位置から一定距離保ちながらカメラが追従し、一定のキーを押すとプレイヤーの向いている方向にあわせてカメラが回転する」というスクリプトを書いています。
以下の通りに書いてみたのですが、まず、カメラがプレイヤーに追従しません。
何が問題なのかご教授いただきたいです。

C#

1// キャラクターのTransform 2public Transform player; 3// カメラの移動スピード 4public float cameraMoveSpeed; 5// カメラの回転スピード 6public float cameraRotateSpeed; 7// カメラのキャラクターからの相対値を指定 8private Vector3 offset; 9// カメラの回転値を保存 10Quaternion camRot; 11//  12bool flag = false; 13float timer = 0; 14 15void Start () 16{ 17 //プレイヤーとカメラの距離を計算する 18 offset = transform.position - player.position; 19  //距離分離す 20 transform.position = player.position + offset; 21  //初期回転値を保存 22 camRot = transform.rotation; 23} 24 25void Update () 26{ 27  //通常はプレイヤーの位置に一定距離を保って追従する 28 if (flag == false) 29 { 30 transform.rotation = camRot; 31 offset = transform.position - player.position; 32 transform.position = player.position + offset; 33 } 34  //ALtキーを押すとカメラの位置をプレイヤーの真後ろへリセットする 35 if (Input.GetKeyDown (KeyCode.LeftAlt) || Input.GetKeyDown (KeyCode.RightAlt)) 36 { 37 flag = true; 38 } 39 if (flag == true) 40 { 41 timer += Time.deltaTime; 42    //プレイヤーの背後へ移動 43 transform.position = Vector3.Lerp (transform.position, player.position + (-player.forward * offset.z) + (Vector3.up * offset.y * 1.0f), cameraMoveSpeed * Time.deltaTime); 44    //プレーヤーの方向へ回転 45 transform.rotation = Quaternion.Slerp (transform.rotation, Quaternion.LookRotation (player.position - (transform.position - new Vector3 (0.0f, offset.y, 0.0f))), cameraRotateSpeed * Time.deltaTime); 46    //回転が終わったら通常の追従状態に戻る? 47 if (timer >= cameraRotateSpeed * Time.deltaTime) 48 { 49 camRot = transform.rotation; 50 timer = 0; 51 flag = false; 52 } 53 } 54}

最初はoffsetをpublic変数にして距離を指定していたのですが、
ワールド座標としての位置関係になってしまったため、
距離を計算する方法にしたのですが、うまくいきませんでした。

また、プレイヤーの移動スクリプトは以下のようになっています。
上下キーで奥移動、左右キーで横移動となっていますが、
例えば右を向いてカメラをリセットした際(カメラがプレイヤーの背後に来た際、画面奥をキャラクターが向いているように映した際)、このままだと左右キーで奥移動、上下キーで横移動となってしまうので、
カメラがリセットされると上下キーで奥移動、左右キーで横移動となるようこちらもリセットされるようにしたいと思っています。

C#

1float dirH, dirV; 2loat dirSpeed = 0.1f; 3dirH = Input.GetAxis ("Horizontal"); 4dirV = Input.GetAxis ("Vertical"); 5 6transform.position = new Vector3 (transform.position.x - dirH * 0.1f, //横移動 7               transform.position.y, 8       transform.position.z - dirV * 0.1f); //奥移動 9vec = new Vector3 (-dirH, 0.0f, -dirV); 10vec.Normalize (); 11//方向を求める 12dirRot = Mathf.Atan2 (vec.x, vec.z) / 3.14159265f * 180.0f; 13transform.eulerAngles = new Vector3 (0.0f, dirRot, 0.0f);

長くなりましたが、どちらかでもいいので、ご教授いただけますと幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

カメラが初期位置から移動しなかったのは、オフセットがUpdate中のoffset = transform.position - player.position;のために毎回「その時点のカメラの位置 - その時点のプレイヤーの位置」に更新されてしまっていたためかと思います。

ご参考として修正案を考えてみましたが、いかがでしょうか?
なるべく元のコードを残そうとしたつもりでしたが、ご質問者さんの意図する動きを想像しながらいじっているうちに、結構変わってしまいました。すみません...
左右上下キー押下時のプレイヤー移動方向の切り替え案として、基準とするべき右方向・前方向をカメラに持たせておいて、プレイヤーはそれを見て進む方向を決めるようにしてみました。
スクリプト名はご質問者さんのものに合わせて読み替えてください。変数などをいくらか追加しましたが、名前がちょっと冗長な気がするので、お好きな名前を付けていただくといいかと思います。

カメラのスクリプト(LookRotation版)

C#

1using UnityEngine; 2 3public class CameraController : MonoBehaviour 4{ 5 private const float CameraDeltaOffsetSqrMagnitudeThreshold = 0.0001f; 6 7 // キャラクターのTransform 8 public Transform player; 9 // カメラの移動スピード 10 public float cameraMoveSpeed; 11 // カメラの回転スピード 12 public float cameraRotateSpeed; 13 // カメラのキャラクターからの相対値を指定 14 private Vector3 offset; 15 // Start時のキャラクターに対するカメラの相対位置をプレイヤーのローカル座標系で保存 16 private Vector3 initialPlayerLocalOffset; 17 18 public Vector3 CameraRight { get; private set; } // 変更...バッキングフィールドとしてcameraRightを用意するより、CameraRightをCameraControllerだけから設定可能なプロパティとした方がコードがシンプルになるかと思いました 19 public Vector3 CameraForward { get; private set; } // 変更...CameraRightと同じく、CameraControllerだけから設定可能なプロパティとしました 20 21 bool flag = false; 22 23 void Start () 24 { 25 //プレイヤーとカメラの距離を計算する 26 offset = transform.position - player.position; 27 transform.rotation = Quaternion.LookRotation(-offset); // offsetが更新されたので、カメラ回転も更新 28 29 initialPlayerLocalOffset = player.InverseTransformDirection(offset); // offsetをプレイヤーローカル座標系に直すパターンを採用 30 31 CameraRight = transform.right; // カメラの右方向を設定 32 33 var newForward = transform.forward; // 追加 34 35 newForward.y = 0.0f; // 追加...カメラの前方向をXZ平面上に投影 36 newForward.Normalize(); // 追加...正規化し長さを1に 37 CameraForward = newForward; // 変更...新しい前方向ベクトルを設定 38 } 39 40 void Update () 41 { 42 //ALtキーを押すとカメラの位置をプレイヤーの真後ろへリセットする 43 if (Input.GetKeyDown (KeyCode.LeftAlt) || Input.GetKeyDown (KeyCode.RightAlt)) 44 { 45 flag = true; 46 } 47 if (flag == true) 48 { 49 //プレイヤーの背後へ移動 50 Vector3 destinationOffset = player.TransformDirection(initialPlayerLocalOffset); // Start時にプレイヤーの座標系で見たカメラの位置を保存しておいたので、そこからカメラの目標オフセットを求める 51 52 // offset = Vector3.Slerp (offset, destinationOffset, cameraMoveSpeed * Time.deltaTime); // 変更...LerpをSlerpにして、カメラの動きが球面上の軌道を取るようにしてみました(Lerpの方がお好みでしたら、そちらでも問題ありません) 53 offset = InterpolateOffset(offset, destinationOffset, cameraMoveSpeed * Time.deltaTime); // 追加...Slerpの代わりにカスタム補間メソッドを使った場合 54 transform.rotation = Quaternion.LookRotation(-offset); // offsetが更新されたので、カメラ回転も更新 55 //回転が終わったら通常の追従状態に戻る? 56 if ((offset - destinationOffset).sqrMagnitude < CameraDeltaOffsetSqrMagnitudeThreshold) 57 { 58 offset = destinationOffset; // 目標オフセットに完全に移動する 59 transform.rotation = Quaternion.LookRotation(-offset); // offsetが更新されたので、カメラ回転も更新 60 CameraRight = transform.right; // カメラの右方向を更新、以後プレイヤーは新しい右方向を基準に移動するようになる 61 62 var newForward = transform.forward; // 追加 63 64 newForward.y = 0.0f; // 追加...カメラの前方向をXZ平面上に投影 65 newForward.Normalize(); // 追加...正規化し長さを1に 66 CameraForward = newForward; // 変更...新しい前方向ベクトルを設定、以後プレイヤーは新しい前方向を基準に移動するようになる 67 flag = false; 68 } 69 } 70 transform.position = player.position + offset; 71 } 72 73 // 追加...offsetの補間用メソッド 74 static Vector3 InterpolateOffset(Vector3 a, Vector3 b, float t) 75 { 76 var aXZ = new Vector2(a.x, a.z); // 真上から見たa 77 var aR = aXZ.magnitude; // aのY軸からの距離 78 var aRIsNotZero = aR > 1E-05; // aRが(ほぼ)0より大きいか? 79 var aD = aRIsNotZero ? aXZ / aR : Vector2.zero; // 真上から見たaの方角、aRがほぼ0ならVector2.zeroとして無回転にする 80 var bXZ = new Vector2(b.x, b.z); // 真上から見たb 81 var bR = bXZ.magnitude; // bのY軸からの距離 82 var bD = (bR > 1E-05) ? bXZ / bR : Vector2.zero; // 真上から見たbの方角、bRがほぼ0ならVector2.zeroとして無回転にする 83 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がなす弧度法による符号付き角度 84 var alpha = aRIsNotZero ? Mathf.Atan2(aD.y, aD.x) : Mathf.Atan2(bD.y, bD.x); // 基準の角度...aDとX軸のなす角(aRがほぼ0ならbDとX軸のなす角) 85 var newTheta = Mathf.Lerp(0.0f, theta, t); // aDとbDがなす角度を線形補間 86 var newPhi = alpha + newTheta; // 基準の角度に先ほど線形補間した角度を足して、これを真上から見たときの新たな方角とする 87 var newR = Mathf.Lerp(aR, bR, t); // Y軸からの距離を線形補間 88 var newX = Mathf.Cos(newPhi) * newR; // 新しいX座標 89 var newY = Mathf.Lerp(a.y, b.y, t); // 新しいY座標(aとbのY座標を線形補間) 90 var newZ = Mathf.Sin(newPhi) * newR; // 新しいZ座標 91 92 return new Vector3(newX, newY, newZ); 93 } 94}

プレイヤーのスクリプト

C#

1using UnityEngine; 2 3public class PlayerController : MonoBehaviour 4{ 5 public CameraController cameraController; // 追加...カメラの右・前方向を知りたいので、カメラのスクリプトを保持するようにしました(インスペクタでカメラをセットしておいてください) 6 7 // Use this for initialization 8 private void Start() 9 { 10 11 } 12 13 // Update is called once per frame 14 private void Update() 15 { 16 float dirH, dirV; 17 float dirSpeed = 0.1f; // これは使用していないようですが、一応残しています 18 dirH = Input.GetAxis ("Horizontal"); 19 dirV = Input.GetAxis ("Vertical"); 20 21 // 削除...移動はvecを求めてから行う 22 /* 23 transform.position = new Vector3 (transform.position.x - dirH * 0.1f, //横移動 24 transform.position.y, 25 transform.position.z - dirV * 0.1f); //奥移動 26 */ 27 28 var vec = cameraController.CameraRight * (dirH * 0.1f) + cameraController.CameraForward * (dirV * 0.1f); // 変更...入力された移動量について、水平はCameraRight、垂直はCameraForwardの向きへの移動と考え、これをvecとする 29 30 transform.Translate(vec, Space.World); // 追加...位置をvecだけ移動 31 32 // 追加...立ち止まったときに初期の向きに戻ってしまうのを防ぐため、入力がゼロ以外の時しか向きを回転しない 33 // カメラのスクリプトでやったのと同じく、vec.sqrMagnitudeが一定値未満かどうかで判定すれば、入力の有無の判定にあそびを設けることもできると思います 34 if (vec != Vector3.zero) 35 { 36 // ここから... 37 vec.Normalize (); 38 //方向を求める 39 var dirRot = Mathf.Atan2 (vec.x, vec.z) / 3.14159265f * 180.0f; 40 transform.eulerAngles = new Vector3 (0.0f, dirRot, 0.0f); 41 // ここまでの代わりに、下記のような回転方法もアリかと思います 42 // transform.rotation = Quaternion.FromToRotation(Vector3.forward, vec); 43 } 44 } 45}

投稿2017/11/23 12:02

編集2017/11/24 21:30
Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

toricotori

2017/11/23 14:16

詳しくご回答いただきまして、ほんとうにありがとうございます。 実行してみましたところ、カメラのスクリプトのほうで、Altキーを押したときに向いていてほしい方向と真逆の方向を向いてしまうようです。 initialCamRotOffset, destinationRotation等をいじってみたのですが、私の力では解決しませんでした。 こちらの解決方法もご教授いただけますと幸いです。
Bongo

2017/11/23 15:42

プレイヤーの初期の向きをカメラの初期位置・初期回転に反映しようとしたのがまずかったかもしれません... カメラのスクリプトを変更して、プレイヤーの最初の向きを考慮しないようにしてみましたが、こちらだとどうでしょうか? ※結局、initialCamRotOffsetは当初ご質問者さんが作成されたcamRotと同じ形に戻りましたね。
toricotori

2017/11/24 06:10

ありがとうございます。 ご指示くださった、初期位置、初期回転のパターン2を適用したところ、正しい挙動になりました。ほんとうに助かりました。 できればプレイヤーの方向へ回転する際、プレイヤーを画面に収めながら回転させたいと思っているのですが、 Quaternion.LookRotationを活用する形になるでしょうか。 Quaternion.Slerp(tranform.rotation, Quaternion.LookRotation(player.position - transform.position), cameraRotateSpeed * Time.deltaTime); と書いてみたのですが、プレイヤーを見ながら回転後、カクッと回転Xが正しい値に戻るようになってしまいました。 回転中、回転Xが変化してしまうようです。 至らないところが多く申し訳ありません。 こちらも、少しでもご教授いただけますと幸いです。
Bongo

2017/11/24 13:35

仕事終わりまでお返事できずすみませんでした。パターン2が有効でしたか。カメラ位置に関してはこれでよさそうですね。 カメラの向きに関してですが、このさいoffsetに変化があるたびにカメラを-offsetの方向に向けるというのはどうでしょう。カメラ位置はなめらかに移動しているので、あとは常時カメラをプレイヤーに向ければ、レンダリングされた映像にも不自然な途切れはなくなるかと思います。このパターンを、以前のカメラのスクリプトの後に追記しました。 なお、私の実験したパターンではLookRotationを使うとカメラがキャラクターを少し見下ろすような形になってしまい、カメラのforwardがXZ平面上から外れてしまうため、これをそのままCameraForwardとしてしまうとキャラクターの前後移動が正常に行われなくなってしまったため(地面にめり込んだり宙に浮いたり)、forwardをXZ平面上に投影する処置を加えています。ご質問者さんの場合はもしかするとこれは不要かもしれません。適宜書き換えてみてください。
toricotori

2017/11/24 15:04

お忙しい中回答いただきまして、ありがとうございます。 -offset方向を見ながらというのは思いつきませんでした。理想的な形になり感激しております。 offsetの移動をSlerpにした場合、プレイヤーが手前を向いているときにAltキーで位置リセットを行うと、カメラがプレイヤーの頭上を移動するようなのですが、プレイヤーの横を通るようにすることは可能でしょうか。 Slerpのやり方など調べてみたのですが、やはり自分だけでは解決できず…… もう少々お力添えしていただけると幸いです。
Bongo

2017/11/24 21:25

Slerpの置き換え案として、大円の代わりに緯線に沿った軌道で補間されるようなメソッドを追加してみました(珍妙なコードですみません...)。こんな動きでいかがでしょうか? 投稿字数の制限を超えてしまった様子ですので、旧カメラスクリプト(非LookRotation版)を削除しました。もしそちらのコードをご覧になりたい場合は編集履歴をご参照ください。
toricotori

2017/11/25 12:22

お返事遅れまして申し訳ございません。 適用しましたところ、理想の動きになりました。100点では足りません。 このたびは、お忙しい中何度も詳しくご回答いただき、感謝してもしきれないくらいです。 ほんとうにほんとうに、ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問