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

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

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

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

Unity

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

Q&A

解決済

1回答

6281閲覧

Unity 球体座標で動くカメラ移動について

kaz2zak

総合スコア36

C#

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

Unity

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

0グッド

1クリップ

投稿2018/03/22 13:59

#実現したいこと
CTで撮影したDICOM画像を3Dに変換し、任意の臓器をどの角度からも自由に観察できるようにしたい

#現段階で実現できていること
ターゲットとなるオブジェクトを、任意の角度から観察できる。
球体座標を使用して、ターゲットを中心にカメラを回転させています。

#困っていること
球体上での移動ではなく、ターゲットに対して平行に、x軸とy軸を動かしたい。
例えば、uparrowを押すと、今見ている角度はそのまま、オブジェクトの上の方を見られるようになるなどです。
(分かりにくい説明で申し訳ございません)
ターゲットに移動のスクリプトを書くことでやりたい動きは実現しましたが、これから臓器や血管も増やしていくことを考えると得策ではないかと思っています。

今書いているスクリプトだと一瞬、縦横ともに移動するのですがすぐに元に戻ってしまいます。
球体上での移動ではないのでlocalPositionかと思って書いてみたのですが、恐らくここの変換が上手くいってないんだろうなぁと思っていますが、上手く書けません。

C#

1public class NewCamera : MonoBehaviour { 2 public Transform target; 3 public float spinSpeed = 1f; 4 float distance = 3f; 5 public float LateMove = 1f; 6 public float diffR = 0.01f ; 7 8 Vector3 nowPos; 9 Vector3 pos = Vector3.zero; 10 public Vector2 mouse = Vector2.zero; 11 12 Transform currentTarget; 13 Vector3 currentTargetCenter; 14 15 void Start() 16 { 17 // 初期位置の取得 18 nowPos = transform.position; 19 } 20 21 void Update() 22 { 23 // マウスの移動の取得 24 if (Input.GetMouseButton(0)) 25 { 26 mouse += new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")) * Time.deltaTime * spinSpeed; 27 } 28 29 mouse.y = Mathf.Clamp(mouse.y,0f, 1f); 30 31 // 球面座標系変換 32 pos.x = distance * Mathf.Sin(mouse.y * Mathf.PI) * Mathf.Cos(mouse.x * Mathf.PI); 33 pos.y = -distance * Mathf.Cos(mouse.y * Mathf.PI); 34 pos.z = -distance * Mathf.Sin(mouse.y * Mathf.PI) * Mathf.Sin(mouse.x * Mathf.PI); 35 36 pos *= nowPos.z; 37 pos.y += nowPos.y; 38 39 //ズームの設定 40 if (Input.GetKey(KeyCode.Q)) 41 { 42 distance += diffR * Time.deltaTime; 43 } 44 else if (Input.GetKey(KeyCode.W)) 45 { 46 distance -= diffR * Time.deltaTime; 47 } 48 49 50 UpdateCurrentTargetCenter(); 51 52 // 座標の更新 53 transform.position = pos + currentTargetCenter; 54 transform.LookAt(currentTargetCenter); 55 56 //カメラの平行移動 57 if (Input.GetKey(KeyCode.UpArrow)) 58 { 59 transform.localPosition += new Vector3(0, LateMove, 0); 60 } 61 else if (Input.GetKey(KeyCode.DownArrow)) 62 { 63 transform.localPosition -= new Vector3(0, LateMove, 0); 64 } 65 else if (Input.GetKey(KeyCode.LeftArrow)) 66 { 67 this.transform.localPosition -= new Vector3(LateMove, 0, 0); 68 } 69 else if (Input.GetKey(KeyCode.RightArrow)) 70 { 71 this.transform.localPosition += new Vector3(LateMove, 0, 0); 72 } 73 } 74 75 76 // ターゲットが変わった場合、中心点を再計算するメソッド 77 void UpdateCurrentTargetCenter() 78 { 79 if (target != currentTarget) 80 { 81 currentTarget = target; 82 83 if (currentTarget == null) 84 { 85 currentTargetCenter = Vector3.zero; // ターゲットがnullなら(0, 0, 0)を中心とする 86 } 87 else 88 { 89 Renderer[] renderers = currentTarget.GetComponentsInChildren<Renderer>(); 90 int rendererCount = renderers.Length; 91 if (rendererCount > 0) 92 { 93 Bounds unitedBounds = renderers[0].bounds; 94 95 for (int i = 1; i < rendererCount; i++) 96 { 97 unitedBounds.Encapsulate(renderers[i].bounds); 98 } 99 100 currentTargetCenter = unitedBounds.center; // 結合したバウンディングボックスの中心を新たな中心点とする 101 } 102 else 103 { 104 currentTargetCenter = currentTarget.position; // レンダラーがないオブジェクトの場合、positionを中心とする 105 } 106 } 107 } 108 } 109}

また、ズームの Time.deltaTime をスクリプトに加えてから、ズーム後に回転させようとせると重いのか一呼吸動き出しまでに時間がかかります。
これは
Time.deltaTime の計算に時間がかかっているからなのでしょうか。

Unity、C#言語ともに初心者になります。易しくご教示頂ければ幸いです。

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

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

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

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

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

guest

回答1

0

ベストアンサー

一瞬移動してもすぐ戻ってしまう件ですが、これはカメラの位置は「座標の更新」部分で毎フレーム更新されているため、localPositionを操作しても次のフレームには再び書き換わってしまうことになるためかと思います。

カメラ位置の更新は「座標の更新」部分だけにしておいて、カメラの位置を直接変えようとするのではなく、「注視点を平行移動させる」ととらえるのはどうでしょうか?

C#

1using UnityEngine; 2using System.Collections; 3 4public class NewCamera : MonoBehaviour 5{ 6 public Transform target; 7 public float spinSpeed = 1f; 8 float distance = 3f; 9 public float LateMove = 1f; 10 public float diffR = 0.01f; 11 12 Vector3 nowPos; 13 Vector3 pos = Vector3.zero; 14 public Vector2 mouse = Vector2.zero; 15 16 Transform currentTarget; 17 Vector3 currentTargetCenter; 18 19 void Start() 20 { 21 // 初期位置の取得 22 nowPos = transform.position; 23 } 24 25 void Update() 26 { 27 // マウスの移動の取得 28 if (Input.GetMouseButton(0)) 29 { 30 mouse += new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")) * Time.deltaTime * spinSpeed; 31 } 32 33 mouse.y = Mathf.Clamp(mouse.y, 0f, 1f); 34 35 // 変更...ズーム設定部分の位置を「球面座標系変換」よりも手前に移動(この変更はあまり重要ではありません) 36 // 実際の動作にほとんど違いはないでしょうが、「ズームの設定」でdistanceを変更し「球面座標系変換」で新しいdistanceを使用する...という流れの方がロジック的に自然かな?と思ったためです 37 // また、QWキーを「else if」で排他的にするよりも、同時押しを許容する方が個人的に好みだったので、勝手ながら変えてしまいました... 38 //ズームの設定 39 if (Input.GetKey(KeyCode.Q)) 40 { 41 distance += diffR * Time.deltaTime; 42 } 43 if (Input.GetKey(KeyCode.W)) 44 { 45 distance -= diffR * Time.deltaTime; 46 } 47 48 // 追加...お節介かもしれませんが、distanceを適当な範囲にクランプしてやるといいかもしれません 49 // 特に、distanceがマイナスになるとカメラがオブジェクトを貫通してしまうことになるので、少なくともdistanceの最小値は設けてやるのがいいかと思います 50 distance = Mathf.Max(distance, 1.0f); // distanceが1以下にならないようにする(最小値のみ設定する)場合 51 // distance = Mathf.Clamp(distance, 1.0f, 10.0f); // distanceを1〜10におさめる(最小値と最大値を設定する)場合 52 53 // 球面座標系変換 54 pos.x = distance * Mathf.Sin(mouse.y * Mathf.PI) * Mathf.Cos(mouse.x * Mathf.PI); 55 pos.y = -distance * Mathf.Cos(mouse.y * Mathf.PI); 56 pos.z = -distance * Mathf.Sin(mouse.y * Mathf.PI) * Mathf.Sin(mouse.x * Mathf.PI); 57 58 pos *= nowPos.z; 59 pos.y += nowPos.y; 60 61 UpdateCurrentTarget(); 62 63 // 変更...カメラ平行移動を「座標の更新」よりも手前に移動し、操作対象をlocalPositionではなくcurrentTargetCenterにしました 64 // さらに、マウス操作やズームは移動量にTime.deltaTimeを掛けてフレームレート非依存にしているので、こちらもそれらに合わせてTime.deltaTimeを掛けるようにしています 65 // また、上下左右キーも同時押しを許容するように変えました 66 //カメラの平行移動 67 Vector2 cameraTranslation = Vector2.zero; // カメラの移動量 68 if (Input.GetKey(KeyCode.UpArrow)) 69 { 70 cameraTranslation.y += LateMove * Time.deltaTime; 71 } 72 if (Input.GetKey(KeyCode.DownArrow)) 73 { 74 cameraTranslation.y -= LateMove * Time.deltaTime; 75 } 76 if (Input.GetKey(KeyCode.LeftArrow)) 77 { 78 cameraTranslation.x -= LateMove * Time.deltaTime; 79 } 80 if (Input.GetKey(KeyCode.RightArrow)) 81 { 82 cameraTranslation.x += LateMove * Time.deltaTime; 83 } 84 // transform.rightでカメラ右方向、transform.upでカメラ上方向が得られるので、currentTargetCenterをその方向に移動 85 currentTargetCenter += transform.right * cameraTranslation.x + transform.up * cameraTranslation.y; 86 87 // さらにもうひとつお節介を加え、スペースキー押下で強制的に中心点再計算を行い、currentTargetCenterをターゲット中心に戻すようにしました 88 if (Input.GetKeyDown(KeyCode.Space)) 89 { 90 RecalculateCurrentTargetCenter(); 91 } 92 93 // 座標の更新 94 transform.position = pos + currentTargetCenter; 95 transform.LookAt(currentTargetCenter); 96 } 97 98 99 // ターゲットが変わった場合、中心点を再計算するメソッド 100 void UpdateCurrentTarget() // 変更...メソッドの機能の観点から、メソッド名を変更 101 { 102 if (target != currentTarget) 103 { 104 currentTarget = target; 105 106 RecalculateCurrentTargetCenter(); // 変更...中心点再計算部分を別メソッドに分離 107 } 108 } 109 110 // 追加...中心点再計算部分を別メソッドに分離 111 void RecalculateCurrentTargetCenter() 112 { 113 if (currentTarget == null) 114 { 115 currentTargetCenter = Vector3.zero; // ターゲットがnullなら(0, 0, 0)を中心とする 116 } 117 else 118 { 119 Renderer[] renderers = currentTarget.GetComponentsInChildren<Renderer>(); 120 int rendererCount = renderers.Length; 121 if (rendererCount > 0) 122 { 123 Bounds unitedBounds = renderers[0].bounds; 124 125 for (int i = 1; i < rendererCount; i++) 126 { 127 unitedBounds.Encapsulate(renderers[i].bounds); 128 } 129 130 currentTargetCenter = unitedBounds.center; // 結合したバウンディングボックスの中心を新たな中心点とする 131 } 132 else 133 { 134 currentTargetCenter = currentTarget.position; // レンダラーがないオブジェクトの場合、positionを中心とする 135 } 136 } 137 } 138}

平行移動部分以外にも、個人的な好みから蛇足的な変更を加えていますが、これらはご質問者さんの好みに合わせて変えてしまうといいかと思います。

ズーム部分を修正したら引っかかりが生じるようになったとのことですが、私の試したところでは目に見える違いが現れず、よくわかりませんでした...
プロファイラーで見てみても、さほどコード部分が時間を消費している様子はなく、deltaTimeを掛けたことが原因とは考えにくいように思います。
モデルがCTスキャン画像を立体化したものとのことですが、想像するにかなり高密度のポリゴンメッシュなのではないでしょうか?もしかするとズームによる描画面積の変化が引っかかりを誘発しているのかもしれませんが、はっきりとしたアドバイスはできずすみません...
【Unity】CPUプロファイラでパフォーマンスを改善する 前編 - テラシュールブログなどをご参考に負荷の変化を調べてみますと手掛かりが得られるかもしれません。

投稿2018/03/22 22:32

Bongo

総合スコア10807

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

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

kaz2zak

2018/03/24 04:03

Bongo様、度々ご回答いただきありがとうございます。 Bongo様天才すぎます!! お陰様で、無事に思い描いていたカメラの動きをしてくれるようになりました。 ズームはClampを用いて最小・最大値の範囲をしていすることにしましてた。臓器を貫通するのは良くないのでアドバイスを頂き感謝申し上げます。 Bongo様のご尽力によって完成したカメラの挙動を見て頂きたく、YouTubeにアップロードしました(笑)見て頂ければ幸いです。 https://www.youtube.com/watch?v=3mbmGUh8xd4 カメラの動きにタイムラグが発生する原因をCPUプロファイラで調べてみたのですが、Renderingで30FPSを割ることが多々あり、大半が60FPSを割っていました。 他の項目では60FPSを割ることはなかったのでご指摘の通りの原因のようです。。 今後、他の臓器を加えていくので、ポリゴンメッシュの密度も考えながら、軽くしていこうと思います。 多岐にわたるアドバイスを頂きありがとうございました。 また、分からないことがあり、質問するかもしれませんが、どうぞよろしくお願い致します。
Bongo

2018/03/24 04:33

ご評価いただきまして恐縮です... 本筋とは関係ないのですが、あらためてコードを見直してみたところ気付いたこととして、マウス操作による回転量算出の部分のdeltaTime乗算は不要だったかもしれません。 他の部分ではフレームレート非依存にするためにdeltaTimeを掛けるのは妥当な方法だと思いますが、Input.GetAxis("Mouse X")、Input.GetAxis("Mouse Y")に関しては、得られる値は前回のフレームからのマウス移動量でしょうから、すでにフレームレート非依存性を有していると考えられます(同じ速度でマウスを動かしたとすると、高フレームレート環境ほどフレーム時間が短いため小さな値になるでしょう)。 これにさらにdeltaTimeを掛けてしまうと、フレームレートの大きい高性能な環境ほど逆に回転が鈍くなるといった現象が出てくるかもしれないですね。 https://docs.unity3d.com/ja/current/ScriptReference/Input.GetAxis.html の例を見てみると、Input.GetAxis("Vertical")やInput.GetAxis("Horizontal")に由来する値にはdeltaTimeを掛けているのに対し、Input.GetAxis("Mouse X")、Input.GetAxis("Mouse Y")に由来する値にはdeltaTimeを掛けていないようです。
kaz2zak

2018/03/24 06:25

早速コードを書き直して試してみたのですが、 mouse += new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")) * Time.deltaTime * spinSpeed; の*Time.deltaTimeを削除すると、カメラの動きが速すぎ思ったように動かせませんでした。 UnityのHPでもInput.GetAxisは独立したフレームレートで動いていると書いてあるのになぜなのでしょうか。。 コードを戻せば、イメージ通りの挙動をしてくれるので、問題はないのですが。
Bongo

2018/03/24 06:46

deltaTimeを削除した場合、それに見合った程度にspinSpeedを小さくしてやる必要があります。もし平均的な実行速度が約60FPSだったとすると、deltaTimeを削除したことによって回転量が約60倍になりますので、そのままでは速すぎるかと思います。 spinSpeedのスケールを変えずにちょうどいい速さにしたいとしたら、基準の速度を60FPSとする場合、係数を60で割って mouse += new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")) * (spinSpeed / 60.0f); としてみるのはどうでしょう?
kaz2zak

2018/03/25 03:52

deltaTimeを加える意味については考えていましたが、削除した場合は目から鱗の内容でした。 60で割ってみましたら調度良いカメラの動きとなりました。 ありがとうございます!Bongo様のお陰で多少は理解しながらスクリプトを書くことができました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問