Is it still possible to scroll sprite textures with material.mainTextureOffset?

I found tons of old tutorials that achieved a scrolling/parallaxy background effect by making a single sprite background to cover the screen, then every frame doing something like this:

Vector2 offset = new Vector2(Time.time * speed, 0);
GetComponent<Renderer>().material.mainTextureOffset = offset;

Whenever I try to do the same, I get an error on my sprite renderer, “Material texture property _MainTex has offset/scale set. It is incompatible with SpriteRenderer”. The only disparity I can find between my implementation and everyone else’s is that older tutorials change their scrolling sprite’s Texture Type from Sprite to Texture. I don’t have a Texture option on my dropdown, and as far as I can tell that particular option was revised out of Unity quite a while ago.

So is there an alternative implementation I’m missing? Messing with the offsets like this seems so useful, I can’t imagine it was made completely impossible by the change.

Try using the Default texture type.

I haven’t used offset with SpriteRenderer, since sprite slicing being added in Unity 4 took away the only reason I ever had to apply an offset to a sprite texture. It’s too bad that that doesn’t work…

Hmm, it looks like Default is intended to be used as a shader/material on 3d meshes- that’s the only context in which I can get it to apply, it looks like Default textures can’t be set as sprites in a 2d sprite renderer.

@Sendatsu_Yoshimitsu

Sorry, I meant to not use a sprite renderer at all. As the fault describes, setting offset at the texture is apparently not compatible with SpriteRenderer. You can still use a textured quad (for instance) if you still want to use offset, perhaps as part of a UI canvas for scaling purposes.

1 Like

Ooh I see what you mean, that’s a really good idea; thanks for the suggestion! :slight_smile:

1 Like

Digging up this thread as it was a top search result when I was attempting to scroll a SpiteRenderer’s material without success.

I believe the best approach is to copy the default Sprite shader and modify it with your own scrolling texcoord offsets, which is what I’ve done below. I wish there was a proper way to “extend” or inherit from shaders, but the only way I know how to do this is copy the code and modify what you need. Fortunately, this is relatively straightforward.

// A copy of builtin_shaders-2019.1.7f1/DefaultResourcesExtra/Sprites-Default
Shader "Custom/Sprite Scrolling" {
    Properties {
        [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
        _Color("Tint", Color) = (1,1,1,1)
        _ScrollSpeed("Scroll Speed", Vector) = (0,0,0,0)
        [MaterialToggle] PixelSnap("Pixel snap", Float) = 0
        [HideInInspector] _RendererColor("RendererColor", Color) = (1,1,1,1)
        [HideInInspector] _Flip("Flip", Vector) = (1,1,1,1)
        [PerRendererData] _AlphaTex("External Alpha", 2D) = "white" {}
        [PerRendererData] _EnableExternalAlpha("Enable External Alpha", Float) = 0
    }

    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 SpriteVertScrolling
            #pragma fragment SpriteFrag
            #pragma target 2.0
            #pragma multi_compile_instancing
            #pragma multi_compile_local _ PIXELSNAP_ON
            #pragma multi_compile _ ETC1_EXTERNAL_ALPHA
            #include "UnitySprites.cginc"

            float2 _ScrollSpeed;

            // A copy of SpriteVert() from builtin_shaders-2019.1.7f1/CGIncludes/UnitySprites.cginc
            v2f SpriteVertScrolling(appdata_t IN) {
                v2f OUT;

                UNITY_SETUP_INSTANCE_ID(IN);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);

                OUT.vertex = UnityFlipSprite(IN.vertex, _Flip);
                OUT.vertex = UnityObjectToClipPos(OUT.vertex);
                OUT.texcoord = IN.texcoord;
                OUT.texcoord.x += _Time * _ScrollSpeed.x;
                OUT.texcoord.y += _Time * _ScrollSpeed.y;
                OUT.color = IN.color * _Color * _RendererColor;

                #ifdef PIXELSNAP_ON
                OUT.vertex = UnityPixelSnap(OUT.vertex);
                #endif

                return OUT;
            }

            ENDCG
        }
    }
}

Then you can update the shader param as usual, eg:

[RequireComponent(typeof(SpriteRenderer))]
public class ScrollingTexture : MonoBehaviour {
    public Vector2 ScrollSpeed;

    private void OnEnable() {
        GetComponent<SpriteRenderer>().material.SetVector("_ScrollSpeed", ScrollSpeed);
    }
}

Example Output
dirtyfarhart

9 Likes

Does your solution also happen to tile/repeat @Pawl ? Wondering if there’s a way to get that working

Not sure I fully understand your question, but I believe so. I was able to dynamically adjust the size of the pattern. The texture I used was of course four way symmetrical.

This worked great for me, except the alpha remained in place while the texture scrolled—any way to get the alpha scrolling as well?

Thank you. This shader helped me closer to fixing my problem with Sprite Shape.

@opertoonist Did you ever resolve the alpha of the texture not scrolling? I’m running into something similar now.

Not with a SpriteRenderer. I ended up using a MeshRenderer and Material.SetTextureOffset.

It is not working on android/ios ,any one tells me why?

Still relevant in 2022, thanks for the pointer!

This solution worked for me, but now I’m struggling with zSorting. I know you can use renderer.sortingLayerID with sprite textures – is there a way to do that with the default texture?

Still relevant in 2023, seems like the best solution as of now.