Tint multiple targets with single button

Hi,

How would I go about tinting multiple targets with a single click? For example if I have an Image (background) with a button component attached to it, and as a child of this image I have another image which displays an icon for the button. When I click on the button only the background will tint its color, not the icon.

Is there a way I can achieve this effect? I know this is not NGUI, but for reference I could add multiple button components to a single GameObject in NGUI and change the targets to achieve the effect.

Adam

Anyone got any suggestions on this? Still looking for an elegant solution

I would just create a new type of button that inherits from button, and Override DoStateTransition, then you can find all images in the parent or something, and loop through each of them on the transition

something like this

    protected virtual IEnumerator TweenColorFromCurrent (Color ToColor, float duration)
                {
                        for (float f = 0; f <= duration; f = f + Time.deltaTime) {

                                obUI.ForEach(x=>x.material.color = Color.Lerp (x.material.color, ToColor, f));
                                yield return null;

                        }
                        obUI.ForEach(x=>x.material.color = ToColor);
                }


                protected override void DoStateTransition (Selectable.SelectionState state, bool instant)
                {
                    obUI = this.GetComponentsInChildren<Renderer>().ToList();
                        Debug.Log ("inside state transition");

                        if (state == SelectionState.Pressed) {
                                StopAllCoroutines ();
                                StartCoroutine (TweenColorFromCurrent (this.colors.pressedColor, this.colors.fadeDuration));
                        }
          
                        if (state == Selectable.SelectionState.Highlighted) {
                                Debug.Log ("state was highlight");
                                //StopAllCoroutines();
                                StartCoroutine (TweenColorFromCurrent (this.colors.highlightedColor, this.colors.fadeDuration));
                        }
                        if (state == Selectable.SelectionState.Normal) {
                                Debug.Log ("state was normal");
                                StopAllCoroutines ();
                                StartCoroutine (TweenColorFromCurrent (this.colors.normalColor, this.colors.fadeDuration));
                        }
           
                }
        }
}
2 Likes

also, since the UI is open source… very useful
https://bitbucket.org/stramit/ui/src/4df4e3c1c521d620bacd4f5ce4f7e805c3447733/UnityEngine.UI/UI/Core/Selectable.cs?at=4.6

Thanks for the help! I did not know that the UI had gone open source already, I knew they was planning to but didnt see that post! Ill be checking it out for sure, thanks again!

I know this is an old Post but since google directed me here, probably others will come here aswell.

I found a simple solution the the scenario miminito describes.

  1. Create a Button and add your own button sprite as Source Image.
  2. Create another Button and use your background sprite (in my case a glowing outline) as Source Image for this one.
  3. Put the background as child of the first button

Both Buttons will detect if the mouse is hovering over it or if they are clicked.
Nonetheless when you are assigning your script to the “On Click ()” function:

  1. Put it on the last child in the hirachy

I’m not exactly sure why it can’t be on the parent, but somehow it then gets blocked by the child.
Guess the last child is on top of the others, even if they all have the same coords.

In case your button keeps highlighted after you clicked it:

  1. Put navigation on “None”
2 Likes

Based on @holyjewsus 's answer and the source of UI’s selectable I’ve came with a working solution.

using UnityEngine;
using UnityEngine.UI;

public class MultiImageButton : Button
{
    protected override void DoStateTransition(SelectionState state, bool instant)
    {
        var targetColor =
            state == SelectionState.Disabled ? colors.disabledColor :
            state == SelectionState.Highlighted ? colors.highlightedColor :
            state == SelectionState.Normal ? colors.normalColor :
            state == SelectionState.Pressed ? colors.pressedColor :
            state == SelectionState.Selected ? colors.selectedColor : Color.white;

        foreach (var graphic in GetComponentsInChildren<Graphic>())
        {
            graphic.CrossFadeColor(targetColor, instant ? 0f : colors.fadeDuration, true, true);
        }
    }
}
10 Likes

Using [SerializeField] Graphic[] _sourceImages; works, you just have to activate the inspector debug view to access the field (top right of the inspector panel, click on the three dot and check debug). You also can create a custom editor for this class to handle it without having to switch from normal to debug mode.

1 Like

There is an Animation transition type which allows to animate any objects for different states of the button (or any other control which supports transitions)

For anyone still interested, I reworked it so it’s a tad bit more user friendly inspector wise:

This is the MultiImageButton.cs

using UnityEngine;
using UnityEngine.UI;

[RequireComponent(typeof(MultiImageTargetGraphics))]
public class MultiImageButton : Button
{
    private Image[] targetImages;

    private MultiImageTargetGraphics targetGraphics;

    protected override void Start()
    {
        targetGraphics = GetComponent<MultiImageTargetGraphics>();

        targetImages = targetGraphics.GetTargetImages;

        base.Start();
    }

    protected override void DoStateTransition(SelectionState state, bool instant)
    {
        if (!targetGraphics) return;

        var targetColor =
            state == SelectionState.Disabled ? colors.disabledColor :
            state == SelectionState.Highlighted ? colors.highlightedColor :
            state == SelectionState.Normal ? colors.normalColor :
            state == SelectionState.Pressed ? colors.pressedColor :
            state == SelectionState.Selected ? colors.selectedColor : Color.white;

        foreach (var image in targetImages)
            image.CrossFadeColor(targetColor, instant ? 0 : colors.fadeDuration, true, true);
    }
}

Accompanied by MultiImageTargetGraphics

using UnityEngine;
using UnityEngine.UI;

public class MultiImageTargetGraphics : MonoBehaviour
{
    [SerializeField] private Image[] targetImages;

    public Image[] GetTargetImages => targetImages;
}

All you do is add MultiImageButton, replacing the standard button, and just add the target images on the MultiImageTargetGraphics that will be added with it. TargetGraphic of MultiImageButton is unused.

1 Like

@the1whom0x
I have rewritten your code a little bit, so that it works for all kinds of graphics and not only images.

This is the MultiImageButton.cs

using UnityEngine;
using UnityEngine.UI;

[RequireComponent(typeof(MultiImageTargetGraphics))]
public class MultiImageButton : Button
{
    private Graphic[] graphics;

    private MultiImageTargetGraphics targetGraphics;

    protected override void Start()
    {
        base.Start();
    }

    protected override void DoStateTransition(SelectionState state, bool instant)
    {
        //get the graphics, if it could not get the graphics, return here
        if (!GetGraphics())
            return;

        var targetColor =
            state == SelectionState.Disabled ? colors.disabledColor :
            state == SelectionState.Highlighted ? colors.highlightedColor :
            state == SelectionState.Normal ? colors.normalColor :
            state == SelectionState.Pressed ? colors.pressedColor :
            state == SelectionState.Selected ? colors.selectedColor : Color.white;

        foreach (var graphic in graphics)
            graphic.CrossFadeColor(targetColor, instant ? 0 : colors.fadeDuration, true, true);
    }

    private bool GetGraphics()
    {
        if(!targetGraphics) targetGraphics = GetComponent<MultiImageTargetGraphics>();
        graphics = targetGraphics?.GetTargetGraphics;
        return graphics != null && graphics.Length > 0;
    }
}

This is the MultiImageTargetGraphics.cs

using UnityEngine;
using UnityEngine.UI;

public class MultiImageTargetGraphics : MonoBehaviour
{
    [SerializeField] private Graphic[] targetGraphics;

    public Graphic[] GetTargetGraphics => targetGraphics;
}

Also fixed a tiny error. Enjoy!

17 Likes

It does not work with TMpro text, any fix/idea?

It should if you are using TextMeshProUGUI which inherits UnityEngine.UI.Graphic

Anybody knows how colors are being interpolated?
Becuse I’m getting weird result, which doesn’t match to setup colors (normal, highlighted etc)