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

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

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

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

Unity

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

Q&A

解決済

1回答

4040閲覧

CinemachineのDolly Pathで1回転させたいが、Pathがねじれる

hogefugapiyo

総合スコア3302

C#

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

Unity

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

0グッド

0クリップ

投稿2020/01/24 02:26

お世話になっております。

現在、Cinemachine 2.2.9 を利用してDolly CartおよびDolly Pathを使ったカメラ移動を実現しようとしています。
通常の移動ならば問題なくパスを設定した通りに動いてくれるのですが、これらのパスが角度が一定以上だと表と裏がねじれてしまいます。

イメージ説明

Cinemachine Smooth Path にて反対のCの字のようなパスを作ろうとすると、1のところでパスがねじれてしまいます。(パスがX字に交差してねじれているのが分かる)

Rollの値を変更してみましたが、この値とこのねじれとは関係がなさそうでRollで与えた回転とは別にこのねじれが発生します。

理想としては下記のようにねじれないでパスが設定できることなのですが、これを実現するにはどのようにすれば良いでしょうか?(下記画像は、画像を加工したイメージ図となります)
イメージ説明

#環境
Unity 2018.4.2f1 LTS
Cinemachine 2.2.9

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

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

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

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

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

guest

回答1

0

ベストアンサー

設定をいじるだけでは無理かもしれません。どうやらパス上で姿勢を求める際に上方向をパスオブジェクトの上方向に決め打ちしているようで、ご質問者さんのように宙返り経路を作ろうとすると進行方向が垂直方向をまたぐタイミングで姿勢の飛びが起こってしまうようです。
このねじれはご提示いただいた図のように急激に発生するのに対し、Rollによるねじりはウェイポイント間でなめらかに起こるため、Rollの調整によってこの急激なねじれをほどこうとしてもうまくいかなかったものと思われます。

この上方向問題をどうしたものかと考えた結果、下記のようなスクリプトを作成して、ドリートラック(デフォルトの名前は「DollyTrack1」でしょうか)のCinemachineSmoothPathを削除し、代わりにこちらをアタッチすることにしました。
なお、これによってドリーカート(デフォルトの名前は「DollyCart1」でしょうか)側のパスへの参照が切れるでしょうから、あらためてセットしなおしてやる必要があるかと思います。

C#

1using System.Collections.Generic; 2using Cinemachine; 3using Cinemachine.Utility; 4using UnityEngine; 5 6[AddComponentMenu("Cinemachine/CinemachineVerticalLoopPath")] 7[SaveDuringPlay] 8public class CinemachineVerticalLoopPath : CinemachineSmoothPath 9{ 10 private readonly List<Vector3> waypointPositions = new List<Vector3>(); 11 private readonly List<Quaternion> waypointRotations = new List<Quaternion>(); 12 13 public override Quaternion EvaluateOrientation(float pos) 14 { 15 // まずは本来のEvaluateOrientationと同じように処理していく 16 var pathRotation = this.transform.rotation; 17 if (this.m_Waypoints.Length <= 0) 18 { 19 return pathRotation; 20 } 21 var worldForward = this.EvaluateTangent(pos); 22 if (worldForward.AlmostZero()) 23 { 24 return pathRotation; 25 } 26 27 // そして本来のEvaluateOrientationと同様にLookRotationによる回転を求める 28 var worldUp = pathRotation * Vector3.up; 29 var baseLookRotation = Quaternion.LookRotation(worldForward, worldUp); 30 31 // ここで、本来のEvaluateOrientationを実行して回転を得ておき... 32 var baseRotation = base.EvaluateOrientation(pos); 33 34 // そこからロール成分を抽出する 35 var rollRotation = Quaternion.Inverse(baseLookRotation) * baseRotation; 36 37 // 次に、現在のセグメントの両端のインデックスを得て... 38 var t = this.GetBoundingIndices(pos, out var indexA, out var indexB) % 1.0f; 39 40 // 各ウェイポイントでの姿勢を更新したのち、セグメント両端の回転を混合し... 41 this.UpdateWaypointRotations(); 42 var rotation = pathRotation * Quaternion.Slerp(this.waypointRotations[indexA], this.waypointRotations[indexB], t); 43 44 // その回転が指す上方向をを使ってLookRotationによる回転を求め、 45 // それをロール成分と組み合わせて返す 46 return Quaternion.LookRotation(worldForward, rotation * Vector3.up) * rollRotation; 47 } 48 49 // 各ウェイポイントにおける回転を求めるためのメソッドを用意する 50 private void UpdateWaypointRotations() 51 { 52 var waypointCount = this.m_Waypoints.Length; 53 var waypointCacheCount = this.waypointPositions.Count; 54 var needsUpdate = waypointCount > waypointCacheCount; 55 if (needsUpdate) 56 { 57 var waypointCountToAdd = waypointCount - waypointCacheCount; 58 for (var i = 0; i < waypointCountToAdd; i++) 59 { 60 this.waypointPositions.Add(new Vector3()); 61 this.waypointRotations.Add(new Quaternion()); 62 } 63 } 64 else 65 { 66 for (var i = 0; i < waypointCount; i++) 67 { 68 if (this.m_Waypoints[i].position != this.waypointPositions[i]) 69 { 70 needsUpdate = true; 71 break; 72 } 73 } 74 } 75 76 if (waypointCount < waypointCacheCount) 77 { 78 var elementCountToRemove = waypointCacheCount - waypointCount; 79 this.waypointPositions.RemoveRange(waypointCount, elementCountToRemove); 80 this.waypointRotations.RemoveRange(waypointCount, elementCountToRemove); 81 } 82 83 if ((waypointCount == 0) || !needsUpdate) 84 { 85 return; 86 } 87 88 // 最初のウェイポイントは本来のCinemachineSmoothPathと同様に 89 // Vector3.upを上方向とした回転を採用し... 90 var inversePathRotation = Quaternion.Inverse(this.transform.rotation); 91 var previousTangent = inversePathRotation * this.EvaluateTangent(0); 92 this.waypointPositions[0] = this.m_Waypoints[0].position; 93 this.waypointRotations[0] = Quaternion.LookRotation(previousTangent, Vector3.up); 94 for (var i = 1; i < waypointCount; i++) 95 { 96 // 以降のウェイポイントは一つ前のウェイポイントの回転に対して 97 // 前方向の変化から求めた差分回転を合成した回転を採用する 98 var tangent = inversePathRotation * this.EvaluateTangent(i); 99 this.waypointPositions[i] = this.m_Waypoints[i].position; 100 this.waypointRotations[i] = Quaternion.FromToRotation(previousTangent, tangent) * this.waypointRotations[i - 1]; 101 previousTangent = tangent; 102 } 103 } 104 105 // CinemachineSmoothPathのGetBoundingIndicesをそのまま持ってくる 106 private float GetBoundingIndices(float pos, out int indexA, out int indexB) 107 { 108 pos = this.StandardizePos(pos); 109 var numWaypoints = this.m_Waypoints.Length; 110 if (numWaypoints < 2) 111 { 112 indexA = indexB = 0; 113 } 114 else 115 { 116 indexA = Mathf.FloorToInt(pos); 117 if (indexA >= numWaypoints) 118 { 119 pos -= this.MaxPos; 120 indexA = 0; 121 } 122 123 indexB = indexA + 1; 124 if (indexB == numWaypoints) 125 { 126 if (this.Looped) 127 { 128 indexB = 0; 129 } 130 else 131 { 132 --indexB; 133 --indexA; 134 } 135 } 136 } 137 138 return pos; 139 } 140}

また、これだけだとインスペクター上でウェイポイントエディタが使えなくなってしまうようです。残念ながらCinemachineSmoothPathEditorは派生不許可のようなので、下記のようにCinemachineSmoothPathEditorを丸写ししたものをEditorフォルダに入れておくことにしました。
CinemachineSmoothPathEditorの属性を[CustomEditor(typeof(CinemachineVerticalLoopPath), true)]に変えて派生クラスでも使用できるようにするという手もあるかもしれませんが、これだとCinemachineのコードがパッケージから再読み込みされた際に元に戻ってしまう可能性があり、いまいちかな...と思いました。

C#

1using System.Collections.Generic; 2using Cinemachine; 3using Cinemachine.Editor; 4using UnityEditor; 5using UnityEditorInternal; 6using UnityEngine; 7 8[CustomEditor(typeof(CinemachineVerticalLoopPath))] 9internal sealed class CinemachineVerticalLoopPathEditor : BaseEditor<CinemachineVerticalLoopPath> 10{ 11 // 省略...内容はCinemachineSmoothPathEditor.csの丸写し 12 13 // ただし最後のDrawGizmosはCinemachinePathEditor.DrawPathGizmoを使っているため 14 // 丸写しのままでは動作しない 15 [DrawGizmo(GizmoType.Active | GizmoType.NotInSelectionHierarchy 16 | GizmoType.InSelectionHierarchy | GizmoType.Pickable, typeof(CinemachineSmoothPath))] 17 private static void DrawGizmos(CinemachineSmoothPath path, GizmoType selectionType) 18 { 19 DrawPathGizmo(path, 20 (Selection.activeGameObject == path.gameObject) 21 ? path.m_Appearance.pathColor : path.m_Appearance.inactivePathColor); 22 } 23 24 // そこで、CinemachinePathEditor.csからDrawPathGizmoを持ってきて、こちらを使う 25 private static void DrawPathGizmo(CinemachinePathBase path, Color pathColor) 26 { 27 // Draw the path 28 var colorOld = Gizmos.color; 29 Gizmos.color = pathColor; 30 var step = 1f / path.m_Resolution; 31 var lastPos = path.EvaluatePosition(path.MinPos); 32 var lastW = (path.EvaluateOrientation(path.MinPos) * Vector3.right * path.m_Appearance.width) / 2; 33 for (var t = path.MinPos + step; t <= (path.MaxPos + (step / 2)); t += step) 34 { 35 var p = path.EvaluatePosition(t); 36 var q = path.EvaluateOrientation(t); 37 var w = (q * Vector3.right * path.m_Appearance.width) / 2; 38 var w2 = w * 1.2f; 39 var p0 = p - w2; 40 var p1 = p + w2; 41 Gizmos.DrawLine(p0, p1); 42 Gizmos.DrawLine(lastPos - lastW, p - w); 43 Gizmos.DrawLine(lastPos + lastW, p + w); 44 #if false 45 // Show the normals, for debugging 46 Gizmos.color = Color.red; 47 Vector3 y = (q * Vector3.up) * width / 2; 48 Gizmos.DrawLine(p, p + y); 49 Gizmos.color = pathColor; 50 #endif 51 lastPos = p; 52 lastW = w; 53 } 54 55 Gizmos.color = colorOld; 56 } 57}

ちょっと試したかぎりではレールに異常なねじれは生じなくなったように見えましたが、いかがでしょうかね?

図

投稿2020/01/24 21:00

Bongo

総合スコア10807

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

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

hogefugapiyo

2020/01/27 04:00

お返事が遅くなり申し訳有りません。 早速スクリプトの内容を確認しながら手元でも実装チェックを行ってみました。 ご提示いただいた画像のようにしっかりとオブジェクトの上が維持されており、ねじれることがなくなりました。 スクリプト中に詳細なコメントまで記述いただいてありがとうございます。問題の起こった原因と理由を理解し、そしてそれをどう解決しているのかがわかりやすく非常に助かりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問