Search Unity

  1. Check out the Unite LA keynote for updates on the Visual Effect Editor, the FPS Sample, ECS, Unity for Film and more! Watch it now!
    Dismiss Notice
  2. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  3. Improved Prefab workflow (includes Nested Prefabs!), 2D isometric Tilemap and more! Get the 2018.3 Beta now.
    Dismiss Notice
  4. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice
  5. Want to see the most recent patch releases? Take a peek at the patch release page.
    Dismiss Notice

Render sprite in front of everything

Discussion in 'Scripting' started by santiagolopezpereyra, Nov 9, 2018.

  1. santiagolopezpereyra

    santiagolopezpereyra

    Joined:
    Feb 21, 2018
    Posts:
    57
    Firstly, to clarify, I place this thread here and not in the UI section because, having tested all the "direct" Unity solutions (those that involve tweaking values on the inspector, layers and so), and failed, I have come to the point where I think this would be more easily solvable using code.

    With that said, I have a quest marker; it appears a few units above the current quest objective. I want this quest marker to be visible regardless of what object it's behind of; this way, if the objective is in the following room, the player would be able to see the marker despite of the walls. The marker is a 2D sprite with a Sprite renderer and a script that updates its position to always stay on top of the objective.

    How could I make it always visible, regardless of what object is between it and the player?
     
  2. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    7,361
    You'd do this with layers. That's what they're for. You say you tried this, and failed... can you go into more detail about what you tried, and how it failed? I feel like I'm missing some important aspect of the problem.
     
  3. santiagolopezpereyra

    santiagolopezpereyra

    Joined:
    Feb 21, 2018
    Posts:
    57
    Sorry for the delayed answer, and thank you for replying.

    I have tried to tweak the sorting layer, creating a new sorting layer for my quest marker that's supposed to render before all the others. The problem with this approach is that apparently only affects the rendering order of the sprites of my game; this is, my quest marker renders before my other sprites, but not before game objects and meshes, and thus is still hidden if the marker is behind them.

    The other suggested solution was to use a specific shader, or to modify the renderQueue of the material; those were suggested here:

    https://support.unity3d.com/hc/en-u...e-render-in-front-of-everything-in-the-scene-

    But I know very little about shaders, let alone writing my own, and modifying my material's renderQueue value had no effect (I do it with this very basic code):

    Code (CSharp):
    1. void Start () {
    2.         m_SpriteRenderer = GetComponent<SpriteRenderer>();
    3.         m_SpriteRenderer.material.renderQueue = 4000;
    4.     }
    Having all this solutions failed I'm running out of options, that's why I came to the forum looking for answers :)
     
  4. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    7,361
    Ah, well you didn't say that your game had a mixture of sprites and other meshes (or if you did, I missed it). That does change things. 3D meshes write to the Z-buffer, whose job it is to determine what's in front of what so you only see what's in front. Monkeying around with the render queue doesn't change that.

    So, what I would do in this case is either (1) use a UI element in a screen-space canvas, or (2) use a Quad positioned just beyond the camera's near clipping plane. Option 2 is probably easier. Just get the vector between the "true" position where you want this thing to be, and the camera; rescale that vector to camera.nearClipPlane plus a little; add this to the camera position; and put the quad there.

    So it's a true 3D element, it's just that it's always close to the camera, like a HUD on an invisible helmet or something.
     
  5. santiagolopezpereyra

    santiagolopezpereyra

    Joined:
    Feb 21, 2018
    Posts:
    57
    Yeah, my first post was a little bit uninformative, which is very wrong and I don't tend to do that. I guess I was super tired from coding the whole day and a little bit frustrated by this sprite problem. I apologise for that. Thanks, JoeStrout!

    I will test your solution and tell you the result! :)
     
    JoeStrout likes this.
  6. santiagolopezpereyra

    santiagolopezpereyra

    Joined:
    Feb 21, 2018
    Posts:
    57
    This solution has gotten me closer to the expected result. Anyhow, I'm not sure how to get the vector between two objects; does this mean the difference among them (Vector3 a - Vector3 b)? What do you mean by "rescaling" a Vector?

    Excuse my ignorance, many things are still new to me. Thanks!
     
  7. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    7,361
    That's exactly right. You can think of this difference in vectors as a vector that points from b to a.

    In this case, what you want is a vector that points from the camera position to the target, so it'd be (targetPos - cameraPos).

    Just changing its length, i.e. v = v * desiredLength / v.magnitude. If (for example) desiredLength is 10 and the previous length was 30, then this will multiply the vector by 10/30, resulting in a new length of exactly 10.
     
  8. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,126
    It is indeed possible to modify a shader for that, in case the other solutions don't work as expected. The 2nd solution could work, but I can think off some edge cases that might cause flickering or other inconsistent rendering results (for instance changes of lighting in the camera's area, and you'll always need to ignore it when you raycast unless you use the default ignore raycast layer).

    So, in case you wanna try the shader solution, it is as follows:
    1) Download the built-in shaders for your Unity version, you'll find the sprite default & sprite diffuse shaders (among many others).
    2) Open the one that you need in an Editor (for example the diffuse shader).
    3) Along the lines of
    ZWrite Off
    , you need to add
    ZTest Always
    .
     
  9. santiagolopezpereyra

    santiagolopezpereyra

    Joined:
    Feb 21, 2018
    Posts:
    57
    This actually worked! Now I have the effect I expected. Joe's solution got me pretty close, but I guess I'll stick with this one since it's less work and already functions. I will leave here my custom shader script (it's exactly the same than the default sprite shader, but with the proper values added).

    Code (CSharp):
    1. // Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
    2.  
    3. Shader "Sprites/Default"
    4. {
    5.     Properties
    6.     {
    7.         [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
    8.         _Color ("Tint", Color) = (1,1,1,1)
    9.         [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
    10.         [HideInInspector] _RendererColor ("RendererColor", Color) = (1,1,1,1)
    11.         [HideInInspector] _Flip ("Flip", Vector) = (1,1,1,1)
    12.         [PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {}
    13.         [PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0
    14.     }
    15.  
    16.     SubShader
    17.     {
    18.         ZWrite Off
    19.         ZTest Always
    20.         Tags
    21.         {
    22.             "Queue"="Transparent"
    23.             "IgnoreProjector"="True"
    24.             "RenderType"="Overlay"
    25.             "PreviewType"="Plane"
    26.             "CanUseSpriteAtlas"="True"
    27.         }
    28.  
    29.         Cull Off
    30.         Lighting Off
    31.         ZWrite Off
    32.         Blend One OneMinusSrcAlpha
    33.  
    34.         Pass
    35.         {
    36.         CGPROGRAM
    37.             #pragma vertex SpriteVert
    38.             #pragma fragment SpriteFrag
    39.             #pragma target 2.0
    40.             #pragma multi_compile_instancing
    41.             #pragma multi_compile _ PIXELSNAP_ON
    42.             #pragma multi_compile _ ETC1_EXTERNAL_ALPHA
    43.             #include "UnitySprites.cginc"
    44.         ENDCG
    45.         }
    46.     }
    47. }
    Once you create a shader with this code, just create its own material and add it on the Material slot of the Sprite Renderer. That's it :)

    Thank you both very much; I've learned a lot. Joe, Suddoha, I'll see you guys around!

    EDIT: Just to be clear, I know the solution provided here is the same I linked a few posts above; I said I tried it and didn't work (in deed it was that way). What changed the game was how specific where Suddoha's instructions compared to the those given on the Unity Support page I referred before.
     
    Suddoha and JoeStrout like this.