Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Unity UI Hight light UI on tutorial system?

Discussion in 'UGUI & TextMesh Pro' started by dohaiha930, Oct 5, 2018.

  1. dohaiha930

    dohaiha930

    Joined:
    Mar 27, 2018
    Posts:
    55
    Hi, i'm creating tutorial system with simple objective "Click to here to do somethings". I try to find the way hight light UI with dark background like bellow image.

    I try to create 1 dark background, then can't find the way to hight light element or ui like this. The only way i think possible is have 1 image have hole, but i think it's suck. So....

    Did anyone do this before? Can you share to me how to do this?

    Many thank!

    Nox 2018-10-05 11-23-32-440.jpg
     
  2. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,833
    I can think of a few possible ways of doing this, but all of them are a bit inconvenient.

    #1

    You could create a transparent black overlay to make everything darker, but then give it a sprite that is transparent in one area to create a "hole" where the dimmer doesn't apply. This requires that you create a sprite that has the exact shape of the hole you want to create, and then position it so that it exactly lines up with whatever you want to show through.

    IMPORTANT NOTE: The "hole" only limits the visuals; it doesn't change whether or not UI events (like tapping the screen) can pass through or not. If you want taps to pass through your black overlay, you need to turn off "raycast target", but then they'll pass through everywhere, so you'll need some additional filtering mechanism to limit what the user is allowed to do in your game.

    #2

    You could create a transparent black overlay to make everything darker, and then create new buttons ON TOP of the overlay for what you want the player to actually do.

    In this case, you can have the overlay block everything, but you need to make sure the stuff you're creating on top of it looks exactly like the normal UI behind it that you want to appear to "shine through" the overlay. Also, since this is a whole new button, you'll need to attach appropriate behavior to it; the user won't actually be clicking on the normal UI (which is hidden behind the dimmer)...unless, again, you turn off "raycast target" on all of your visual effects, in which case the user could click anywhere in the normal UI.

    #3

    You could create a transparent black overlay to make everything darker, and then reparent the button you want the user to click on so that it is a child of the overlay (instead of wherever it normally exists in your object hierarchy), while keeping its world position constant. This will put the button "on top" of your overlay, so it is visible and clickable, and it will have its normal behavior.

    The big problem here is that you need to put the button back when you're done, and if you make any mistake that button could disappear forever! I worked on a project once that used this technique and had that exact problem (it was possible to exit the tutorial in an unusual way that failed to put the button back where it should go, completely breaking the UI).

    #4

    Instead of reparenting the existing button as in #3, you could create a copy of the button and reparent the copy. This will preserve your original UI in case of an error, though you'll still need to delete the copy at some point.

    Having 2 copies of some normal piece of your UI also might cause a variety of weird problems depending on the details of how exactly your UI works.

    #5

    Instead of having a transparent black overlay, you could modify the appearance of every single thing in your UI individually, one at a time, except for the one you want to look normal. (For instance, you could set "interactable" to false on all the buttons, and apply a color cross fade to all the non-interactive elements, like the map background.)
     
  3. dohaiha930

    dohaiha930

    Joined:
    Mar 27, 2018
    Posts:
    55
    Sheyrokee likes this.
  4. Antony-Blackett

    Antony-Blackett

    Joined:
    Feb 15, 2011
    Posts:
    1,772
    I've found another option that might be suitable for some situations.

    Add a Canvas component and a GraphicRaycaster component to the object you want to display on top of the full screen darken object. Set the canvas to override sorting and set the layer of the new Canvas to be higher than the Canvas used for the UI normally. Once the tutorial is over, remove these components.

    This should have the benefit of not messing with the hierarchy and any anchoring/layout/animation and references you have.

    Code (CSharp):
    1.  
    2.     Canvas tutorialCanvas;
    3.     GraphicRaycaster tutorialRaycaster;
    4.     void SetupTutorialMode()
    5.     {
    6.         tutorialCanvas = gameObject.AddComponent<Canvas>();
    7.         tutorialCanvas.overrideSorting = true;
    8.         tutorialCanvas.sortingOrder = 2;
    9.  
    10.         tutorialRaycaster = gameObject.AddComponent<GraphicRaycaster>();
    11.     }
    12.  
    13.     void CleanupTutorialMode()
    14.     {
    15.         if( tutorialCanvas != null )
    16.         {
    17.             Destroy( tutorialCanvas );
    18.         }
    19.         if( tutorialRaycaster != null )
    20.         {
    21.             Destroy( tutorialRaycaster );
    22.         }
    23.     }
     
    Last edited: Jul 2, 2020
  5. nicmarxp

    nicmarxp

    Joined:
    Dec 3, 2017
    Posts:
    404
    Thanks @Antony-Blackett, that was an excellen solution. Not sure if something changed, but for 2021.3.6 i had to wait one frame to destroy the canvas, or i would get "Can't remove Canvas because GraphicRaycaster (Script) depends on it".

    So you can do this with a coroutine to wait one frame.
    You also might need to add
    using System.Collections;

    I also added an if, in case you want to run this while not in play mode.

    Code (CSharp):
    1.         public void CleanupTutorialMode() {
    2.             if (tutorialRaycaster != null) {
    3.                 if (Application.isPlaying) {
    4.                     Destroy(tutorialRaycaster);
    5.                 } else {
    6.                     DestroyImmediate(tutorialRaycaster);
    7.                 }
    8.             }
    9.  
    10.             StartCoroutine(DestroyCanvasAfterOneFrame());
    11.         }
    12.      
    13.      
    14.         // Ienumerator to wait for a frame, then destroy canvas
    15.         IEnumerator DestroyCanvasAfterOneFrame() {
    16.             yield return null;
    17.             if (tutorialCanvas != null) {
    18.                 if (Application.isPlaying) {
    19.                     Destroy(tutorialCanvas);
    20.                 } else {
    21.                     DestroyImmediate(tutorialCanvas);
    22.                 }
    23.             }
    24.         }