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

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

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

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

Unity

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

Q&A

解決済

2回答

2644閲覧

Unityでのコルーチンを用いて別々に処理を行いたい

kaz2zak

総合スコア36

C#

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

Unity

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

0グッド

0クリップ

投稿2018/04/19 03:11

#実現したいこと
カメラのズームと移動を切り離して実行したい。

#問題点
現在、GearVRのコントローラーのタッチパッドからの入力を使用して、カメラが物体の回りを球体座標内で回転・ズームすることができる、カメラの動きに関するスクリプトを書いています。

回転:タッチパッドをクリックしている間、4分割した方向に合わせて回転
ズーム:トリガー+上クリックでズームイン、トリガー+下クリックでズームアウト
※タッチパッドは上・下・右・左に相当するように4分割にしています

ズームインをしようとトリガーと上クリックしても、回転処理が優先され、回転の動きが止まってから(y軸方向はClampで制限)ズームの処理が始まってしまいます。
そのため、カメラが物体の正面方向を向いている状態でズームインをしても、物体の上方向を向いてからのズームとなり、意図したズームの動きとは異なってしまいます。

#試したこと
コルーチンを用いて、回転とズームを切り離して処理を行うようにすれば解決できるのではないかとスクリプトを書いてみましたが、一向に改善されません。
該当スクリプトは以下の通りです。
プログラミング初心者になるので見にくいかもしれませんがご容赦ください。
解決方法だけでなく、現在のスクリプトでもっとこうしたら良くなるとかもありましたらご教示いただければ幸いです。
よろしくお願い致します。

C#

1public class CoroCam : MonoBehaviour 2{ 3 4 public Transform target; 5 public float spinSpeed = 1f; 6 public float distance = 1.2f; 7 public float diffR = 0.1f; 8 9 Vector3 nowPos; 10 Vector3 pos = Vector3.zero; 11 public Vector2 pad = Vector2.zero; 12 13 Transform currentTarget; 14 Vector3 currentTargetCenter; 15 Vector2 touchPosition; 16 17 bool isZooming = (OVRInput.Get(OVRInput.Button.PrimaryIndexTrigger)); 18 19 20 void Start() 21 { 22 // 初期位置の取得 23 nowPos = transform.position; 24 } 25 26 27 void Update() 28 { 29 StartCoroutine(CamZoom()); 30 StartCoroutine(CamRoatation()); 31 32 UpdateCurrentTarget(); 33 transform.position = pos + currentTargetCenter; 34 transform.LookAt(currentTargetCenter); 35 } 36 37 38 39 40 IEnumerator CamRoatation() 41 { 42 if(isZooming) 43 yield break; 44 45 46 if (!isZooming) 47 { 48 if (OVRInput.Get(OVRInput.Button.PrimaryTouchpad)) 49 { 50 if (OVRInput.Get(OVRInput.Touch.PrimaryTouchpad)) 51 { 52 touchPosition = OVRInput.Get(OVRInput.Axis2D.PrimaryTouchpad); 53 54 55 //極座標系内での移動 56 if (touchPosition.y / touchPosition.x > 1 || touchPosition.y / touchPosition.x < -1) 57 { 58 if (touchPosition.y > 0) 59 { 60 pad.y += spinSpeed / 60; 61 } 62 else 63 { 64 pad.y -= spinSpeed / 60; 65 } 66 } 67 else 68 { 69 if (touchPosition.x > 0) 70 { 71 pad.x += spinSpeed / 60; 72 } 73 else 74 { 75 pad.x -= spinSpeed / 60; 76 } 77 } 78 79 } 80 } 81 82 } 83 84 85 86 pad.y = Mathf.Clamp(pad.y, 0.1f, 0.9f); 87 88 pos.x = distance * Mathf.Sin(pad.y * Mathf.PI) * Mathf.Cos(pad.x * Mathf.PI); 89 pos.y = distance * Mathf.Cos(pad.y * Mathf.PI); 90 pos.z = -distance * Mathf.Sin(pad.y * Mathf.PI) * Mathf.Sin(pad.x * Mathf.PI); 91 92 pos *= nowPos.z; 93 pos.y += nowPos.y; 94 95 yield return null; 96 } 97 98 99 100 IEnumerator CamZoom() 101 { 102 if (OVRInput.Get(OVRInput.Button.PrimaryTouchpad)) 103 { 104 if (OVRInput.Get(OVRInput.Touch.PrimaryTouchpad)) 105 { 106 touchPosition = OVRInput.Get(OVRInput.Axis2D.PrimaryTouchpad); 107 108 if ((OVRInput.Get(OVRInput.Button.PrimaryIndexTrigger) && ((touchPosition.y / touchPosition.x > 1 || touchPosition.y / touchPosition.x < -1)))) 109 { 110 if (touchPosition.y < 0) 111 { 112 distance += diffR * Time.deltaTime; 113 } 114 if (touchPosition.y > 0) 115 { 116 distance -= diffR * Time.deltaTime; 117 } 118 } 119 } 120 } 121 distance = Mathf.Max(distance, 0.1f); 122 123 yield return null; 124 } 125

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

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

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

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

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

guest

回答2

0

ベストアンサー

まず、やりたいことは下記のコードでできそうでしょうか。試してみてください。
GearVRを持っていないのでキーボード操作に置き換えています。

動画1
動画2(上下移動の制限をつけたもの)

C#

1using UnityEngine; 2 3// カメラにアタッチする 4public class CameraController : MonoBehaviour 5{ 6 // 各種パラメーターはインスペクターから設定する 7 public Transform target; // カメラはこの物体の周りを回転する 8 public float zoomSpeed = 10f; // ズームの速度 9 public float minimumDistance = 1f; // 物体との最小距離(これ以上接近しない) 10 public float rotateSpeed = 50f; // 回転の速度 11 public float verticalMoveSpeed = 10f; // 上下移動の速度 12 public float yMin = 0f; // 【追記】上下移動の下限 13 public float yMax = 6f; // 【追記】上下移動の上限 14 15 private void Update () 16 { 17 // 対象の物体とカメラとの相対座標 18 var diff = target.position - transform.position; 19 20 // Aキーで物体に接近する(最小距離よりは接近しない) 21 if ( Input.GetKey ( KeyCode.A ) && diff.sqrMagnitude > Mathf.Pow ( minimumDistance, 2 ) ) 22 { 23 transform.position = Vector3.MoveTowards ( transform.position, target.position, zoomSpeed * Time.deltaTime ); 24 } 25 // Sキーで物体から遠ざかる 26 else if ( Input.GetKey ( KeyCode.S ) ) 27 { 28 // カメラを挟んで、物体とは反対側の座標 29 var otherSide = target.position - diff * 2; 30 31 transform.position = Vector3.MoveTowards ( transform.position, otherSide, zoomSpeed * Time.deltaTime ); 32 } 33 34 // 左右キーで物体の周りを回転する 35 if ( Input.GetKey ( KeyCode.LeftArrow ) ) 36 { 37 transform.RotateAround ( target.position, Vector3.up, rotateSpeed * Time.deltaTime ); 38 } 39 else if ( Input.GetKey ( KeyCode.RightArrow ) ) 40 { 41 transform.RotateAround ( target.position, -Vector3.up, rotateSpeed * Time.deltaTime ); 42 } 43 44 //上下キーでカメラを上下に移動する 45 if ( Input.GetKey ( KeyCode.UpArrow ) ) 46 { 47 if ( transform.position.y < yMax ) // 【追記】ifブロック追加 48 { 49 transform.Translate ( Vector3.up * verticalMoveSpeed * Time.deltaTime ); 50 } 51 } 52 else if ( Input.GetKey ( KeyCode.DownArrow ) ) 53 { 54 if(transform.position.y > yMin ) // 【追記】ifブロック追加 55 { 56 transform.Translate ( -Vector3.up * verticalMoveSpeed * Time.deltaTime ); 57 } 58 } 59 60 // 物体を見る 61 transform.LookAt ( target ); 62 } 63}

もともとのスクリプトで気になった点をいくつか挙げておきます。

  • 上記のとおり、今回はおそらくコルーチン不要だと思いますが、Update内で毎フレーム無条件にコルーチンを呼び出すような書き方は意図したとおり動作しないことが多いです。
  • if 文が重なって深い階層になっているので、階層を減らす工夫をすると読みやすくなるかもしれません。

参考:コードのネストを深くするな

以上です。

投稿2018/04/19 07:36

編集2018/04/20 09:24
negitama

総合スコア943

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

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

kaz2zak

2018/04/19 14:26

negitama様 回答ありがとうございます。 少し苦戦しましたが、ズームをしても、回転の処理が加わることなく、別個に処理することが可能になりました。 ありがとうございます! Update内でのコルーチンは良くないと見たので気になってははいたのですが、そもそもコルーチンをする必要がなかったのですね! 3点質問があります。 ①このスクリプトはどこがミソになるのでしょうか。  Update内でズーム→左右移動→上下移動の順番で処理をすれば、ズーム処理の際に上下移動が干渉しないかと、コルーチンを使わず書き直してみたのですが、元と同じでした。。そんな安直な理由ではないのですね。。 ②ズームが上手く挙動しません。  targetのpivotの座標の影響を受けているのか、ズーム自体はできるのですが、オブジェクトの上に吸い寄せられるようにズームインされてしまいます。  以前は、対象のオブジェクトのpivotでの中心座標が物体よりも随分と上方にあるので、ポリゴンの中心を新たに求めて、ポリゴンの中心にカメラが向くように処理をしていました。 現在のスクリプトでは、ズームの処理だけtargetの計算前のデータを用いて計算していることが原因ではないかと思うのですが、解決できません。 ③上下方向の移動の制限を設けたいのですが、どうすれば良いでしょうか。  現在のスクリプトでは真上・真下まで移動するので制限を設けたく、Vector3.upをClampで範囲を指定できないものかと試みましたが、エラーが出てしまいました。 質問ばかりで申し訳ありません。回答頂ける質問だけでも大変ありがたいです。 よろしくお願い致します。 解決方法の方に現在のスクリプトを貼りますので、見て頂ければ幸いです。
negitama

2018/04/19 14:46 編集

まず、さきほどの回答にこちらで録画した動画へのリンクを追加したので、イメージどおりか確認してみてください。
negitama

2018/04/19 15:07 編集

取り急ぎ、①と③に回答します。 回答 ① ズーム・左右移動・上下移動をスクリプト内に順序は関係ありません。今回の場合、それぞれ独立して動作しているためです。 回答 ③ まずは上限・下限の値を設定します。 public float yMin = 0f; // 上下移動の下限 public float yMax = 6f; // 上下移動の上限 カメラの上下動の箇所で、if 文で制御するほうがシンプルかもしれません。 if ( transform.position.y < yMax ) { transform.Translate ( Vector3.up * verticalMoveSpeed * Time.deltaTime ); }
negitama

2018/04/19 15:37

質問② については内容をよく理解しきれていませんが、Update文の中での UpdateCurrentTargetの呼び出しをもっと前に、Updateの直後の行などにもってきても変化ありませんか?
kaz2zak

2018/04/20 03:36

negitama様 早速の回答ありがとうございます。 朝からドツボにはまっています。。 動画ありがとうございます。まさに自分がやりたい動きと同じです。 回答①に対して 毎回transformで計算をし独立して動いているからなんですね。 回答②に対して UpdateCurrentTargetをUpdate文の始めに移動してみましたが、改善されませんでした。。 上の自己解決の方に動画をアップしますので見て頂けたらと思います。 再生後、ズーム、元の位置に戻って上に移動となっています。 回答③に対して 制限した範囲に行ってしまうと、そこから上下の移動が全く効かなくなってしまいます。 また、制限をかけないと真上・真下に来た際に激しく回転を繰り返してしまいます。
negitama

2018/04/20 09:32

もとの回答のほうに上下移動の制限をつけてみた動画へのリンクを追加したので確認願います。 スクリプト内で上下移動の制限のために追記した箇所を【追記】として示しています(4箇所、昨夜)ので、再確認願います。 そのうえで、制限をつけてみたときのスクリプトを見せてください。 ※ なおこちらはGearVRを持っていないため、GearVR用のスクリプトを実際に動かして確認することができません。掲題の質問内容からも離れてきていますので、この質問に関するコメントは次あたりで切り上げさせていただきます。
kaz2zak

2018/04/21 08:04

大変遅れて申し訳ありません。 色々とやってみて、問題はキーの共有によるものだと分かりました。 親身に答えてくださってありがとうございました。
guest

0

追記
上段への追記となり申し訳ありません。
再生した際の挙動です。
[https://twitter.com/kazu_bou21/status/987174234538455041]

階層が深く見にくくなってしまい申し訳ありません。
今後見直しますので、今回は見逃してください。。

c#

1public class TeratailCam : MonoBehaviour { 2 3 public Transform target; // カメラはこの物体の周りを回転する 4 public float zoomSpeed = 0.01f; // ズームの速度 5 public float minimumDistance = 0.1f; // 物体との最小距離(これ以上接近しない) 6 public float maxDistance = 5f; 7 public float rotateSpeed = 60f; // 回転の速度 8 public float verticalMoveSpeed = 100f; // 上下移動の速度 9 10 public Vector2 pad = Vector2.zero; 11 Vector2 touchPosition; 12 Transform currentTarget; 13 Vector3 currentTargetCenter; 14 15 private void Update() 16 { 17 // 対象の物体とカメラとの相対座標 18 var diff = target.position - transform.position; 19 20 21 //A,Sキーでの入力をトリガー+上or下パッドでの入力に変換 22 if (OVRInput.Get(OVRInput.Button.PrimaryTouchpad)) 23 { 24 if (OVRInput.Get(OVRInput.Touch.PrimaryTouchpad)) 25 { 26 touchPosition = OVRInput.Get(OVRInput.Axis2D.PrimaryTouchpad); 27 28 if ((OVRInput.Get(OVRInput.Button.PrimaryIndexTrigger) && ((touchPosition.y / touchPosition.x > 1 || touchPosition.y / touchPosition.x < -1)))) 29 { 30 //遠ざかる 31 if (touchPosition.y < 0 && diff.sqrMagnitude > Mathf.Pow(minimumDistance, 0.1f)) 32 { 33 34 var otherSide = currentTarget.position - diff * 2; 35 36 transform.position = Vector3.MoveTowards(transform.position, otherSide, zoomSpeed * Time.deltaTime); 37 } 38 //近づく 39 if (touchPosition.y > 0 && diff.sqrMagnitude < Mathf.Pow(maxDistance, 5f)) 40 { 41 42 transform.position = Vector3.MoveTowards(transform.position, currentTarget.position, zoomSpeed * Time.deltaTime); 43 44 } 45 } 46 } 47 } 48 49 50 51 52 // 上下左右キーでの回転をコントローラーのタッチパッド入力に変換 53 54 if (OVRInput.Get(OVRInput.Button.PrimaryTouchpad)) 55 { 56 if (OVRInput.Get(OVRInput.Touch.PrimaryTouchpad)) 57 { 58 touchPosition = OVRInput.Get(OVRInput.Axis2D.PrimaryTouchpad); 59 60 61 //上下パッドでの移動 62 if (touchPosition.y / touchPosition.x > 1 || touchPosition.y / touchPosition.x < -1) 63 { 64 if (touchPosition.y > 0) 65 { 66 transform.Translate(Vector3.up * verticalMoveSpeed * Time.deltaTime); 67 } 68 else 69 { 70 transform.Translate(-Vector3.up * verticalMoveSpeed * Time.deltaTime); 71 } 72 } 73 } 74 } 75 76 77 //左右パッドでの移動 78 if (OVRInput.Get(OVRInput.Button.PrimaryTouchpad)) 79 { 80 if (OVRInput.Get(OVRInput.Touch.PrimaryTouchpad)) 81 { 82 touchPosition = OVRInput.Get(OVRInput.Axis2D.PrimaryTouchpad); 83 84 85 //上下パッドでの移動 86 if (touchPosition.x / touchPosition.y > 1 || touchPosition.x / touchPosition.y < -1) 87 { 88 if (touchPosition.x > 0) 89 { 90 transform.RotateAround(currentTarget.position, -Vector3.up, rotateSpeed * Time.deltaTime); 91 } 92 else 93 { 94 transform.RotateAround(currentTarget.position, Vector3.up, rotateSpeed * Time.deltaTime); 95 } 96 } 97 } 98 } 99 100 101 UpdateCurrentTarget(); 102 // 物体を見る 103 transform.LookAt(currentTargetCenter); 104 105 106 } 107 108 void UpdateCurrentTarget() // 109 { 110 if (target != currentTarget) 111 { 112 currentTarget = target; 113 114 RecalculateCurrentTargetCenter(); // 中心点再計算部分を別メソッドに分離 115 } 116 } 117 118 // 中心点再計算部分を別メソッドに分離 119 void RecalculateCurrentTargetCenter() 120 { 121 if (currentTarget == null) 122 { 123 currentTargetCenter = Vector3.zero; // ターゲットがnullなら(0, 0, 0)を中心とする 124 } 125 else 126 { 127 Renderer[] renderers = currentTarget.GetComponentsInChildren<Renderer>(); 128 int rendererCount = renderers.Length; 129 if (rendererCount > 0) 130 { 131 Bounds unitedBounds = renderers[0].bounds; 132 133 for (int i = 1; i < rendererCount; i++) 134 { 135 unitedBounds.Encapsulate(renderers[i].bounds); 136 } 137 138 currentTargetCenter = unitedBounds.center; // 結合したバウンディングボックスの中心を新たな中心点とする 139 } 140 else 141 { 142 currentTargetCenter = currentTarget.position; // レンダラーがないオブジェクトの場合、positionを中心とする 143 } 144 } 145 } 146}

投稿2018/04/19 14:31

編集2018/04/20 03:44
kaz2zak

総合スコア36

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.51%

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

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

質問する

関連した質問