Author Topic: Manually Using TweenAlpha Screws Up Button Tweens  (Read 9184 times)

fizzd

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 4
    • View Profile
Manually Using TweenAlpha Screws Up Button Tweens
« on: August 27, 2013, 10:47:54 AM »
So I bought NGUI, I love it, and now I am at the point of pulling my hair out.

I'm making a dynamic main menu that incorporates many menus, fading submenus in and out of the screen.

First of all, I made this class a while back that lets me Hide and Show any object or group of objects.:

  1. using UnityEngine;
  2. using System.Collections;
  3.  
  4. public class scrFadeChildren : MonoBehaviour {
  5.  
  6.         // Use this for initialization
  7.         static public void show(GameObject go, float time)
  8.         {
  9.                         Transform[] arrChildren = go.GetComponentsInChildren<Transform>();
  10.                         foreach (Transform child in arrChildren)
  11.                         {
  12.                         if (child.gameObject.GetComponent<BoxCollider>() != null)
  13.                         child.gameObject.GetComponent<BoxCollider>().enabled = true;
  14.                         TweenAlpha ta = TweenAlpha.Begin(child.gameObject,time,1);
  15.                         ta.tweenGroup = 99;
  16.                         ta.eventReceiver = child.gameObject;
  17.                         ta.unclickableOnFinish = false;
  18.                        
  19.                         }
  20.                
  21.                
  22.         }
  23.        
  24.        
  25.                
  26.         static public void hide(GameObject go,float time)
  27.         {
  28.                         Transform[] arrChildren = go.GetComponentsInChildren<Transform>();
  29.                         foreach (Transform child in arrChildren)
  30.                         {
  31.                         TweenAlpha ta = TweenAlpha.Begin(child.gameObject,time,0);
  32.                         ta.tweenGroup = 99;
  33.                         ta.eventReceiver = child.gameObject;
  34.                         ta.unclickableOnFinish = true;
  35.                         }
  36.         }
  37. }
  38.  

Unclickableonfinish is a modification that lets me disable the box colliders when an object is being hidden, such that it can't be clicked. (This is preferable to just disabling the object, because disabling prevents the object from being found again using GameObject.Find, which I have had to use plenty of in my situation.)

Usage is as simple as something like:

  1. scrFadeChildren.hide(_guiPlanet,2f);
  2. scrFadeChildren.show(_guiHome,2f);
  3.  


This works absolutely fine and looks wonderful! HOWEVER: if I use this script on buttons, the buttons' will fade out on hover! And not only that, the colors in the Button Color change to black, such that when hovering over, the button color becomes (0,0,0,1).

And so the crux of this problem, as I seem to have worked out, is that TweenAlpha.Begin will screw up the TweenColors that are used by the Button Color and Button Tween scripts.

My question is just why does this happen? I'd think it's something about the Alpha and Color tweens being related to each other. Things i've tried:

I've tried destroying the TweenAlpha that is created on TweenAlpha.Begin, but even when I destroy it, it appears that there is some memory of the Alpha left because the ButtonTween colors still glitch out on hover.

These little niggles in NGUI are killing me. Any help would be appreciated! Please don't advise to just not use TweenAlpha.Begin and use an animation or whatnot - I don't see how I can have an all purpose Hide and Show function without TweenAlpha.Begin.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Manually Using TweenAlpha Screws Up Button Tweens
« Reply #1 on: August 27, 2013, 12:15:16 PM »
Buttons enter the disabled state when the collider is disabled. Check UIButton.isEnabled flag -- it straight up checks the collider's state. The easiest way to make something not clickable is to have two layers for your UI -- your normal UI layer, and the "NonClickableUI". Set the UICamera's event mask to only "UI", but have the camera draw both layers. Switch the window from one layer to another as you need, and you won't have to disable any colliders.

Furthermore, don't tween individual objects. TweenAlpha.Begin on the panel drawing the window.

fizzd

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 4
    • View Profile
Re: Manually Using TweenAlpha Screws Up Button Tweens
« Reply #2 on: August 27, 2013, 12:42:48 PM »
Quote
Furthermore, don't tween individual objects. TweenAlpha.Begin on the panel drawing the window.

Yeah I thought you'd say that. You would agree that's something of a compromise, yes? Surely the most basic function for a tween is to be able to tween properties of individual objects!

I'm just wondering why it is that TweenAlpha.Begin doesn't work well on individual objects. It seems like the presence of a tween when a *.Begin function is called (as it is in the ButtonTweens onHover) is what messes things up.

For example, in order to be able to edit my tweens with the visual editor (drawing animation curves etc) BUT still be able to call them from code, I made this function, which returns the Tween with a certain group number:

  1.         public static UITweener GetTweenOfGroup(GameObject go,int _tweengroup,bool includeChildren)
  2.         {
  3.                 UITweener[] mTweens = includeChildren ? go.GetComponentsInChildren<UITweener>() : go.GetComponents<UITweener>();
  4.                 UITweener tww = null;
  5.                 for (int i = 0, imax = mTweens.Length; i < imax; ++i)
  6.                         {
  7.                                 UITweener tw = mTweens[i];
  8.                                 // If the tweener's group matches, we can work with it
  9.                                 if (tw.tweenGroup == _tweengroup)
  10.                                 {
  11.                                         tww = tw;
  12.                                 }
  13.                         }
  14.                 return tww;
  15.                
  16.         }
  17.  

Once again, this works completely fine! I had a Camera object with three different TweenTransforms attached, with groups 51,52, and 53 respectively.

BUT it seems like the moment I call a TweenTransform.Begin in the code, instead of making a separate TweenTransform, it modifies one of the TweenTransforms that I had already made and just plays that one instead, therefore screwing up everything.

Question: why does this happen? And what would be the solution to having a simple all-purpose Hide and Show (with fade, I mean) function that can be used on any gameObject, then, if the Tweens are weird like this?

Quote
Buttons enter the disabled state when the collider is disabled. Check UIButton.isEnabled flag -- it straight up checks the collider's state. The easiest way to make something not clickable is to have two layers for your UI -- your normal UI layer, and the "NonClickableUI". Set the UICamera's event mask to only "UI", but have the camera draw both layers. Switch the window from one layer to another as you need, and you won't have to disable any colliders.

Disabling colliders is not the problem; I've already solved that with my tweak, unless there is a reason why I would particularly want to avoid disabling them.

fizzd

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 4
    • View Profile
Re: Manually Using TweenAlpha Screws Up Button Tweens
« Reply #3 on: August 28, 2013, 05:16:38 AM »
Aha! Ahahahahaha I've solved it! Or rather I've realised the problem thanks to this link:

http://www.tasharen.com/forum/index.php?topic=507.msg2460#msg2460

The problem in the way the Button Tween remembers what the 'normal' colour of an object is. The way it does it is it takes a snapshot of the sprite colour at the start of the program, the moment it is activated (or rather the Box Collider is activated) for the first time. AFTER that it doesn't matter if you deactivate and reactivate shiz, it still uses that original snapshot.

And so the problem was that at the start of the scene I deactivated all the Inactive GUIs, and the script that did that must have run BEFORE the snapshot was taken. Thus it took a snapshot of it when alpha = 0, and set that alpha =0 colour to the default colour to fade out to.

The solution? Instead of calling my fade function with duration 0 at the start, I call it with duration 0.1 or whatnot. This ensures the snapshot is still taken when the alpha is 1. Turns out its nothing to do with Tweens, and the Tweens on individual objects work perfectly fine in conjunction with Buttons (..I think). Thanks ArenMook, and hope this helps anyone!

Nicki

  • Global Moderator
  • Hero Member
  • *****
  • Thank You
  • -Given: 33
  • -Receive: 141
  • Posts: 1,768
    • View Profile
Re: Manually Using TweenAlpha Screws Up Button Tweens
« Reply #4 on: August 28, 2013, 05:03:38 PM »
The reason you'd want to tween the panel is for performance purposes.

DEngland

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 9
    • View Profile
Re: Manually Using TweenAlpha Screws Up Button Tweens
« Reply #5 on: February 06, 2014, 09:51:44 PM »
I'm bumping this thread because this issue still exists and it was a pain to deal with. Basically, it seems that you can't put a TweenAlpha on any UIWidget that is also a UIButton, or you can get unexpected results. For me, having a TweenAlpha on my button causes it to fade out when I hover off of it, and alpha back up when I hover on. This ignores the colour values I have set in the UIButton component and as fizzd has pointed out, seems to be connected to the alpha my button was at when the widget was activated.

The simplest workaround seems to be putting your button widget inside another widget and putting the TweenAlpha on that, so for anyone seeing similar issues I recommend giving that a try.
In the meantime, is there a fix planned for this bug? Surely it's valid to want to animate a button by itself.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Manually Using TweenAlpha Screws Up Button Tweens
« Reply #6 on: February 06, 2014, 09:55:35 PM »
UIButton uses TweenColor, which modifies the alpha. And so does TweenAlpha. If you have both, they conflict with each other. If you want to fade out your button, put TweenAlpha on a parent object. Not sure why you would want to fade out just one button -- generally you'd fade out an entire window instead, and alpha is cumulative.

DEngland

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 9
    • View Profile
Re: Manually Using TweenAlpha Screws Up Button Tweens
« Reply #7 on: February 06, 2014, 10:11:24 PM »
Thanks for the quick response ArenMook.

Okay, I understand that both UIButton and TweenAlpha use TweenColor (I observed this in the hierarchy when running the game and was wondering what was instantiating the TweenColor). However, why doesn't the UIButton remember what its user-defined setting is for the Normal colour and use that when animating the hover? It seems that it's instead looking at the widget's colour at/near activation time and deciding that's the new Normal, which intuitive or properly controllable.

In my case, the issue wouldn't arise if there was a checkbox for whether or not there should be a hover animation at all, which sounds like a nice thing to have as an alternative to animating from one colour to the same colour.

Regarding animating buttons individually, that comes down to visual design. In my case I want a row of buttons to animate in with slightly offset timing, so they each need their own tweens.

Thanks again for the responses.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Manually Using TweenAlpha Screws Up Button Tweens
« Reply #8 on: February 06, 2014, 10:14:28 PM »
"Normal color" points to the sprite's color. The button doesn't actually have a "normal" color. It just records the starting color when activated. That's where the trouble lies. The idea is that the color should only be set in one place -- and in this case it's on the sprite itself.

P.S. You can change UIButtonColor's Start() function to be Awake() instead and see if that helps.

DEngland

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 9
    • View Profile
Re: Manually Using TweenAlpha Screws Up Button Tweens
« Reply #9 on: February 06, 2014, 10:27:34 PM »
Okay, that explains it. I think it would make sense for the UIButton to store the Normal colour set in the editor, and that should prevent any such conflicts. But maybe you know of some other edge cases introduced by setting it up that way, I don't know.

I'll continue with the workaround of using a container widget but hopefully this conflict can be removed at some point, either by storing the user-specified normal colour or something else. And it would be nice to be able to disable animations like Hover or Pressed if you know you aren't going to use them (esp. on mobile).

Thanks for the details on this,
-Devan

[ELine]asilva

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 5
    • View Profile
Re: Manually Using TweenAlpha Screws Up Button Tweens
« Reply #10 on: March 06, 2014, 12:37:27 PM »
ArenMook, one thing that it seems as though still isn't answered from this thread is that if I have a UIButton and a manually added tween attached to an object, when the Hover/Click/etc. tweens happen from the UIButton, the UITweener.Begin<> function looks for a tween that is already attached to the game object to use, instead of creating a new one.  This makes sense if all you have is a UIButton attached, but if you've manually setup specific tweens to happen at different times (using different group numbers) the first of those will get overwritten by the code in UITweener.Begin<>.

One way to solve this is to reserve a special tween group for tweens that are created by UITweener.Begin<>.  I have made those changes locally (using the special tween group of -1) - I was wondering if you might consider pulling this into your codebase so we don't have to redo it every time we get up to date?

(FYI, the differences are before the #if where we look for components with the reserved tween group and the line towards the bottom where we set the tweenGroup for the starting tween to the reserved group of -1)

  1. static public T Begin<T> (GameObject go, float duration) where T : UITweener
  2. {
  3.         T comp = null;
  4.         T[] comps = go.GetComponents<T>();
  5.        
  6.         foreach (T tweener in comps)
  7.         {
  8.                 if ( tweener.tweenGroup == -1 )
  9.                 {
  10.                         comp = tweener;
  11.                         break;
  12.                 }
  13.         }
  14. #if UNITY_FLASH
  15.         if ((object)comp == null) comp = (T)go.AddComponent<T>();
  16. #else
  17.         if (comp == null) comp = go.AddComponent<T>();
  18. #endif
  19.         comp.mStarted = false;
  20.         comp.duration = duration;
  21.         comp.mFactor = 0f;
  22.         comp.mAmountPerDelta = Mathf.Abs(comp.mAmountPerDelta);
  23.         comp.style = Style.Once;
  24.         comp.animationCurve = new AnimationCurve(new Keyframe(0f, 0f, 0f, 1f), new Keyframe(1f, 1f, 1f, 0f));
  25.         comp.eventReceiver = null;
  26.         comp.callWhenFinished = null;
  27.         comp.enabled = true;
  28.         comp.tweenGroup = -1;
  29.        
  30.         if (duration <= 0f)
  31.         {
  32.                 comp.Sample(1f, true);
  33.                 comp.enabled = false;
  34.         }
  35.         return comp;
  36. }

Thanks for your time!
« Last Edit: March 06, 2014, 02:34:31 PM by [ELine]asilva »

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Manually Using TweenAlpha Screws Up Button Tweens
« Reply #11 on: March 07, 2014, 10:41:23 AM »
The problem with this approach is that you have two conflicting tweens on the same object. What is your usecase, why do you need two tweens of the same type on the same object?

[ELine]asilva

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 5
    • View Profile
Re: Manually Using TweenAlpha Screws Up Button Tweens
« Reply #12 on: March 07, 2014, 12:31:20 PM »
In theory, they should never play at the same time (although I'll grant you that they obviously could).  Our use case is the artists/designers setup the tween(s) they want to play with specific parameters, etc. with each one on a different group (mostly, there are 2 groups; one for tweening in and one for tweening out - we were seeing issues with playing them in reverse - not resetting properly, etc. - could have been something we did wrong).  Then they use uscript (our visual scripting system of choice) to kick off whichever tween group they want to play on which object(s).

Like I said, I'll grant you that having multiple tweens could conflict with each other, but that seems like a developer-solvable problem, whereas UIButton overwriting manually added tweens isn't (without the code above).  Also, the problem of having multiple tweens on a single object already exists and developers are already responsible for mitigating this issue.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Manually Using TweenAlpha Screws Up Button Tweens
« Reply #13 on: March 08, 2014, 12:04:51 PM »
I'd be more comfortable this this modification that treats group ID of 0 as an unset group ID, and doesn't do the expensive GetComponents<> unless it has to.
  1.         static public T Begin<T> (GameObject go, float duration) where T : UITweener
  2.         {
  3.                 T comp = go.GetComponent<T>();
  4.                
  5.                 // Find the tween with an unset group ID (group ID of 0).
  6.                 if (comp != null && comp.tweenGroup != 0)
  7.                 {
  8.                         comp = null;
  9.                         T[] comps = go.GetComponents<T>();
  10.                         for (int i = 0, imax = comps.Length; i < imax; ++i)
  11.                         {
  12.                                 comp = comps[i];
  13.                                 if (comp != null && comp.tweenGroup == 0) break;
  14.                                 comp = null;
  15.                         }
  16.                 }
  17. #if UNITY_FLASH
  18.                 if ((object)comp == null) comp = (T)go.AddComponent<T>();
  19. #else
  20.                 if (comp == null) comp = go.AddComponent<T>();
  21. #endif
  22.                 comp.mStarted = false;
  23.                 comp.duration = duration;
  24.                 comp.mFactor = 0f;
  25.                 comp.mAmountPerDelta = Mathf.Abs(comp.mAmountPerDelta);
  26.                 comp.style = Style.Once;
  27.                 comp.animationCurve = new AnimationCurve(new Keyframe(0f, 0f, 0f, 1f), new Keyframe(1f, 1f, 1f, 0f));
  28.                 comp.eventReceiver = null;
  29.                 comp.callWhenFinished = null;
  30.                 comp.enabled = true;
  31.  
  32.                 if (duration <= 0f)
  33.                 {
  34.                         comp.Sample(1f, true);
  35.                         comp.enabled = false;
  36.                 }
  37.                 return comp;
  38.         }

[ELine]asilva

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 5
    • View Profile
Re: Manually Using TweenAlpha Screws Up Button Tweens
« Reply #14 on: April 18, 2014, 03:04:44 PM »
Sorry for the slow response on this - been crunching quite a bit lately.

Putting the GetComponents inside the if check is fine, but using group 0 for the "automatic" tweens used by UIButton seems bad to me since whenever I add a new tween to an object its default tween group is 0 (which would mean any that you add will be overwritten by the UIButton tween unless you change the group every time you add one). Also, think of all the users out there that already have tweens attached to their objects and haven't changed the group from the default of 0 - all of their tweens would be broken - that's why I recommended -1 - it's an int, so -1 is not illegal or anything and it has a much lower likelihood of being already in use by current users.

Thoughts?