5
\$\begingroup\$

I'm trying to figure out how to do this but am coming up short. I'm making a slot machine in unity 2d and would like to clip the tile prefabs when they leave a given rectangular area.

I can't seem to figure out how to do this. All of the references I've found say to use a clipping shader with a mask texture, but it seems like massive overkill to use a masking texture when I just need an easy pre-defined rectangular area.

\$\endgroup\$
  • 2
    \$\begingroup\$ In the roadmap this is listed as "2D: Masking" unity3d.com/unity/roadmap it's planned for next spring sigh \$\endgroup\$ – jhocking Aug 28 '15 at 20:27
3
\$\begingroup\$

What your are looking for, is called scissor test in OpenGL/Direct3D vocabulary. But unfortunately you don't have direct access to that feature in unity. I've been looking for a solution to same problem a few weeks ago, and I came across people suggesting to use multiple cameras with different render area to simulate this behavior.

Here are some articles that I've found back then, which you can read for further explanations.

| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

If it's a 2D game and you want to clip to some rectangular area, why not just use an overlay with a cutout for the area you want exposed? There would be no runtime tests required. Just place all the tiles, then place your mask layer over top.

This is the same sort of technique that is used for most HUDs. Eg. A border around the edge of the screen.

The caveat would be that your mask would be a static image, so if you want the background outside of the clipping area to be dynamic (such as a generated or animated image), then it will be more complicated and you may need to use another method of clipping like what you or Ali.S has suggested.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ You know what, you're right. I've spent all day fighting with extra cameras and what the viewport units really mean and positioning everything the way I want. This is what I'm doing. It's just not worth it otherwise. \$\endgroup\$ – ReaperUnreal Aug 28 '15 at 18:54
1
\$\begingroup\$

I ended up doing something different. I used a shader to clip the sprites I was scrolling to a certain area of screen space. And here's the code for that shader since I know I was looking for it and could never find it.

Shader "Sprites/Clipped"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
        [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
        _ClipX ("Clip X", Range(0.0, 2000.0)) = 0
        _ClipY ("Clip Y", Range(0.0, 1000.0)) = 0
        _Width ("Width", Range(0.0, 2000.0)) = 2000
        _Height ("Height", Range(0.0, 1000.0)) = 1000
    }

    SubShader
    {
        Tags
        { 
            "Queue"="Transparent" 
            "IgnoreProjector"="True" 
            "RenderType"="Transparent" 
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Cull Off
        Lighting Off
        ZWrite Off
        Blend One OneMinusSrcAlpha

        Pass
        {
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile _ PIXELSNAP_ON
            #include "UnityCG.cginc"

            struct appdata_t
            {
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex    : SV_POSITION;
                fixed4 color     : COLOR;
                half2 texcoord   : TEXCOORD0;
                float2 screenPos : TEXCOORD1;
            };

            fixed4 _Color;

            v2f vert(appdata_t IN)
            {
                v2f OUT;
                OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex);
                OUT.texcoord = IN.texcoord;
                OUT.color = IN.color * _Color;
                #ifdef PIXELSNAP_ON
                OUT.vertex = UnityPixelSnap (OUT.vertex);
                #endif
                OUT.screenPos.xy = ComputeScreenPos(OUT.vertex).xy * _ScreenParams.xy;

                return OUT;
            }

            sampler2D _MainTex;
            float _Width;
            float _Height;
            float _ClipX;
            float _ClipY;

            fixed4 frag(v2f IN) : SV_Target
            {
                if ((IN.screenPos.x < _ClipX) || (IN.screenPos.x > _ClipX + _Width) || (IN.screenPos.y < _ClipY) || (IN.screenPos.y > _ClipY + _Height))
                {
                    fixed4 transparent = fixed4(0, 0, 0, 0);
                    return transparent;
                }

                fixed4 c = tex2D(_MainTex, IN.texcoord) * IN.color;
                c.rgb *= c.a;
                return c;
            }
        ENDCG
        }
    }
}
| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Thanks ReaperUnreal, your Shader solution is great. Unity changes the Sprites/Default shader with almost every release, but provides the source code available https://unity3d.com/get-unity/download/archive.

One improvement I would suggest... Conditionals in Shaders can be slow because of GPU reasons, so I rewrote the clip test as:

c *= step(_ClipX, IN.screenPos.x) * step(IN.screenPos.x, _ClipX+_Width) * 
    step(_ClipY, IN.screenPos.y) * step(IN.screenPos.y, _ClipY+_Height);

which of course happens just before the return c;

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.