Empowerment Tween-animations in NGUI

1. Animation and anchors

TweenPosition component does not stick to the anchor. I mean I have a widget, which is attached to anchor the top left corner of the screen. Changing the screen resolution and aspect ratio of my widget carefully maintained its position and everything looks great.

When I tried to use TweenPosition animation pulling out of widget from the edge of the screen, I realized that nothing had come of it. TweenPosition uses Vector3 position to indicate the start and end positions of the animation. For example, we set the following values for the animation:

When I change the aspect ratio, widget continues to move to the coordinates stored in TweenPosition, which do not correspond to its new coordinates



I tried to write a script that calculates the displacement vectors to the From and To, but it didn't work with Anchor and animation turned into hell. It's no good.

In NGUI have a wonderful component TweenTransform, which allows you to move an object from point A to point B, where A and B - transform'y any objects. Here's an example:

Remove the anchor from the widget and set anchor in our game object A and B

Take a look what we get:




Now at any resolution and aspect ratio animation retains its form.
Well, it works fine, at least the expected result is achieved. But we want to automate the process. Elements UI, requiring animation, I have a lot in the project and each of them to carry out the operations described above terribly boring. Writing a simple script that will help us. Call it TweenTransformHelper.

public class TweenTransformHelper : MonoBehaviour {
    public GameObject FromAnchor;
    public GameObject ToAnchor;

I know the above script surely impress you with its complexity, so the next you do not scare: TweenTransformHelperEditor.cs

[CustomEditor(typeof (TweenTransformHelper))]
public class TweenTransformHelperEditor : Editor {

    private TweenTransformHelper _tweener;

    private void Awake() {
        _tweener = (TweenTransformHelper) target;

    public override void OnInspectorGUI() {
        if (GUILayout.Button("Create from anchor")) {
        if (GUILayout.Button("Destroy")) {
        _tweener.FromAnchor = (GameObject) EditorGUILayout.ObjectField(_tweener.FromAnchor, typeof (GameObject));

        if (GUILayout.Button("Create to anchor")) {
        if (GUILayout.Button("Destroy")) {
        _tweener.ToAnchor = (GameObject) EditorGUILayout.ObjectField(_tweener.ToAnchor, typeof (GameObject));

    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) {

    private void DestroyAnchor(GameObject gameObj) {
        if (gameObj == null) {

    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;

Now, if you add a script to the widget, we will see just such a panel:

Create anchorTo and anchorFrom and click «Apply to tween» (it will automatically fill in the appropriate fields in TweenTransform). Now it is small, customize the binding to the edges of the screen to anchorTo and anchorFrom, pre-exposing them to the desired position.

On this issue is resolved. Go ahead.


 2 Animation sequence

But what if we want to make a chain of moving objects? With the help of NGUI make it simple. Each component is a field TweenTransformer On Finished, which may contain any of the methods of any component, with the proviso that the method is public. Added methods are invoked immediately after the animation has abandoned during play. For example, to make a sequence of moving objects we like so:

Now when you run the animation we see the next:

When activated, the screen elements go and it looks good. But what if we want to play the animation in reverse order when the deactivation of the screen?

Existing functional class events OnFinished UITweener (successor is TweenTransform) is not enough. It is called when the animation plays from the beginning to the end and vice versa. It is impossible to determine in which direction to play the animation, before you finish. If it were possible, my problem would be solved. As a result, I decided to empower NGUI. In fact, the changes that should be made in class UITweener, minimal.

In UITweener.cs add the following fields:

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

A method Update

if (onFinished != null)
mTemp = onFinished;

we add

if (onFinishedForward != null && direction == Direction.Forward) {
    mTempForward = onFinishedForward;
    onFinishedForward = new List<EventDelegate>();

    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>();

    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;

    We go in and makes UITweenerEditor.cs a few lines of code to advanced class UITweener displayed in the editor.  


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

We add

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

  As a result of these manipulations TweenTransform window now looks like this:


Having the opportunity to know in which direction to finish the animation, I can build a sequence that can correctly reproduce both forward and backward. It becomes simple:

To play the animation forward, you need to call a method in PlayForward first element chain, and to lose it in the reverse order, call PlayReverse at the last element. Now we get the expected result:

NGUI - a great plugin for Unity, with great potential, but he, like any complex tool has its flaws or defects. But, having left a little time and desire, they are easy to fix and achieve the desired result.

All data posted on the site represents accessible information that can be browsed and downloaded for free from the web.



User replies

No replies yet