NGUIでのTweenアニメーションの強化

こんにちは、私の名前はミハイル・クリコフです。私はゲーム開発者であり、Unityを難易度の高いクラフトで使用しています。 Unityが何であるか、またはUnityをエンジンとして使用し始めた段階に到達した方法については説明しません。 これは多くの長所と短所を備えた素晴らしいツールであるとしか言えず、主な欠点は、私の意見では、UIで快適に作業するためのツールがないことです。 Unity開発者がバージョン4.6でオープンベータとして提供したツールはカウントされません。 長い間NGUIプラグインを使用していたので、あまり詳しく説明しませんでしたし、あまり欲求もありません。 今日は、インターフェイスのトゥイーンアニメーションを作成し、これらの問題の解決策を作成しなければならなかった問題を共有したいと思います。



問題#1。 アニメーションとアンカー



または、私は何かを理解していないか、 TweenPositionコンポーネントがアンカーにまったく適合していません。 私は次の状況を意味します。 画面の左上隅にアンカーが付いたウィジェットがあります。 画面の解像度とアスペクト比を変更することで、私のウィジェットはその位置を維持し、すべてが見栄えよくなります。

4:3アスペクト比



16:9アスペクト比



TweenPositionを使用してウィジェットを画面の端から「移動」させてアニメーション化しようとすると、何も機能しないことがわかりました。 TweenPositionVector3座標を使用して、アニメーションの開始位置と終了位置を示します。 たとえば、アニメーションに次の値を設定します。







アスペクト比を変更すると、ウィジェットはTweenPositionに格納された座標に沿って移動し続けますが、この座標は新しい座標とはまったく一致しません

4:3アスペクト比



16:9アスペクト比



FromとToのベクトルのオフセットを計算するスクリプトを作成しようとしましたが、彼はアンカーと友達になりたくなかったので、アニメーションは地獄に変わりました。 これはダメです。 さらに考え始めます。



NGUIには、オブジェクトをポイントAからポイントBに移動できる素晴らしいTweenTransformコンポーネントがあります。AとBは、任意のオブジェクトの変換です。 以下に例を示します。







ウィジェットからアンカーを削除し、ゲームオブジェクトAおよびBにアンカーを配置します







何が起こったのか見てみましょう:

4:3アスペクト比



16:9アスペクト比



これで、解像度とアスペクト比を問わず、アニメーションの外観が保持されます。

まあ、それはうまく動作します、少なくとも期待される結果が達成されます。 しかし、私はプロセスを自動化したいです。 私のプロジェクトにはアニメーションを必要とするUI要素がたくさんありますが、それぞれについて上記の操作を実行するのは非常に退屈です。 私たちは私たちを助ける簡単なスクリプトを書いています。 TweenTransformHelperと呼びましょう。

//TweenTransformHelper.cs [RequireComponent(typeof(TweenTransform))] public class TweenTransformHelper : MonoBehaviour { public GameObject FromAnchor; public GameObject ToAnchor; }
      
      





上記のスクリプトは、おそらくその複雑さであなたを驚かせたので、以下はあなたを怖がらないでしょう。

TweenTransformHelperEditor.cs
 //TweenTransformHelperEditor.cs [CustomEditor(typeof (TweenTransformHelper))] public class TweenTransformHelperEditor : Editor { private TweenTransformHelper _tweener; private void Awake() { _tweener = (TweenTransformHelper) target; } public override void OnInspectorGUI() { EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Create from anchor")) { CreateAnchorFrom(); } if (GUILayout.Button("Destroy")) { DestroyAnchor(_tweener.FromAnchor); } EditorGUILayout.EndHorizontal(); _tweener.FromAnchor = (GameObject) EditorGUILayout.ObjectField(_tweener.FromAnchor, typeof (GameObject)); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Create to anchor")) { CreateAnchorTo(); } if (GUILayout.Button("Destroy")) { DestroyAnchor(_tweener.ToAnchor); } EditorGUILayout.EndHorizontal(); _tweener.ToAnchor = (GameObject) EditorGUILayout.ObjectField(_tweener.ToAnchor, typeof (GameObject)); CreateAndApplyTweener(); UpdateUI(); } private void CreateAndApplyTweener() { bool toAnchorNotEqualsNull = _tweener.ToAnchor; bool fromAnchorNotEqualsNull = _tweener.FromAnchor; if (!fromAnchorNotEqualsNull) { EditorGUILayout.HelpBox("From anchor not created!", MessageType.Warning); } else if (!toAnchorNotEqualsNull) { EditorGUILayout.HelpBox("To anchor not created!", MessageType.Warning); } else { if (GUILayout.Button("Apply to tween")) { var tweenComponent = _tweener.GetComponent<TweenTransform>() ?? _tweener.gameObject.AddComponent<TweenTransform>(); tweenComponent.from = _tweener.FromAnchor.transform; tweenComponent.to = _tweener.ToAnchor.transform; tweenComponent.enabled = false; } } } private void UpdateUI() { if (GUI.changed) { EditorUtility.SetDirty(_tweener); } } private void DestroyAnchor(GameObject gameObj) { if (gameObj == null) { return; } DestroyImmediate(gameObj); } private void CreateAnchorTo() { var anchor = CreateAnchor("$anchorTo"); _tweener.ToAnchor = anchor; } private void CreateAnchorFrom() { var anchor = CreateAnchor("$anchorFrom"); _tweener.FromAnchor = anchor; } private GameObject CreateAnchor(string anchorName) { var anchorGameObj = new GameObject(anchorName); anchorGameObj.transform.parent = _tweener.transform; anchorGameObj.transform.localPosition = Vector3.zero; anchorGameObj.transform.localScale = Vector3.one; var widgetScript = anchorGameObj.AddComponent<UIWidget>(); widgetScript.width = widgetScript.height = 100; return anchorGameObj; } }
      
      







スクリプトを詳細に説明する意味はわかりませんが、複雑なことは何もありません。 スクリプトをウィジェットに追加すると、次のパネルが表示されます。







$ anchorTo$ anchorFromを作成し、「Apply to tween」をクリックします(これにより、 TweenTransformの適切なフィールドに自動的に入力さます )。 事は小さくなりました。目的の位置に設定した後、$ anchorToと$ anchorFromの画面の端へのバインディングを調整します。







ここで問題1が解決されました。 どうぞ



問題#2一貫したアニメーション



しかし、オブジェクトのチェーンを残したい場合はどうでしょうか? NGUIを使用してこれを行うことは基本です。 各TweenTransformerコンポーネントにはOn Finishedフィールドがあり、このメソッドがパブリックであれば、任意のコンポーネントのメソッドを含めることができます。 追加されたメソッドは、アニメーションの再生が終了した直後に呼び出されます。 たとえば、次のような一連の出発を行うことができます。







アニメーションを開始すると、次の奇跡が表示されます。







画面がアクティブになると、要素が残り、見栄えが良くなります。 しかし、画面が非アクティブになっているときにアニメーションを逆方向に再生したい場合はどうでしょうか?



プラグインフォーラムでの議論で見つけたものに基づいて、このような問題に遭遇したのは私だけではありませんでした。 議論の参加者は、次の方法でこの問題を解決することを提案しました。どういうわけか、イベントコールのリストを取得し、それを逆にし、逆の順序でコールを並べます。 彼らが言うように、通常のヒーローは常に回っているので、この決定は複雑すぎるように思えました。 UITweenerクラスのOnFinishedイベント( TweenTransform継承 )の既存の機能では十分ではありません。 アニメーションが最初から最後まで、またはその逆に再生されるときに呼び出されます。 アニメーションが完了する前にどのように再生されたかを判断することはできません。 可能であれば、私の問題は解決されます。 最終的に、NGUIの機能を拡張することにしました。 ArenMookを許してください 。しかし、私は彼のコードを管理しなければなりませんでした 。 実際、 UITweenerクラスに加える必要がある変更最小限です。



UITweener.csで 、次のフィールドを追加します。

 List<EventDelegate> mTempForward = null; List<EventDelegate> mTempReverse = null; [HideInInspector] public List<EventDelegate> onFinishedForward = new List<EventDelegate>(); [HideInInspector] public List<EventDelegate> onFinishedReverse = new List<EventDelegate>();
      
      





そして、更新メソッドで

 if (onFinished != null) { mTemp = onFinished; … }
      
      





加える

 if (onFinishedForward != null && direction == Direction.Forward) { mTempForward = onFinishedForward; onFinishedForward = new List<EventDelegate>(); EventDelegate.Execute(mTempForward); for (int i = 0; i < mTempForward.Count; ++i) { EventDelegate ed = mTempForward[i]; if(ed != null && !ed.oneShot) EventDelegate.Add(onFinishedForward, ed, ed.oneShot); } mTempForward = null; } if (onFinishedReverse != null && direction == Direction.Reverse) { mTempReverse = onFinishedReverse; onFinishedReverse = new List<EventDelegate>(); EventDelegate.Execute(mTempReverse); for (int i = 0; i < mTempReverse.Count; ++i) { EventDelegate ed = mTempReverse[i]; if (ed != null && !ed.oneShot) EventDelegate.Add(onFinishedReverse, ed, ed.oneShot); } mTempReverse = null; }
      
      





UITweenerEditor.csに移動し、さらに数行のコードを追加して、 UITweenerクラスの高度な機能がエディターに表示されるようにします。





 NGUIEditorTools.DrawEvents("On Finished", tw, tw.onFinished);
      
      





加える

 NGUIEditorTools.DrawEvents("On Finished forward", tw, tw.onFinishedForward); NGUIEditorTools.DrawEvents("On Finished reverse", tw, tw.onFinishedReverse);
      
      





これらの操作の結果、TweenTransformウィンドウは次のようになります。







アニメーションの再生がどの方向に終わったかを知る機会を得て、順方向と逆方向の両方で正しく再生できるシーケンスを構築できます。 これは基本的な方法で行われます:







アニメーションを順方向に再生するには、チェーンの最初の要素でPlayForwardメソッドを呼び出す必要があり、逆の順序で再生するには、最後の要素でPlayReverseを呼び出す必要があります。 期待どおりの結果が得られました。







おわりに



NGUIはUnityの優れたプラグインであり、優れた機能を備えていますが、洗練されたツールと同様に欠点や欠点があります。 しかし、少し時間と欲求を保留しておくと、簡単に修正して目的の結果を達成できます。



All Articles