3

I wrote the following script:

public class SpriteUV : MonoBehaviour 
{
    private SpriteRenderer _spriteRenderer;
    private Sprite _sprite;

    [SerializeField] public Vector2[] _uv = new Vector2 [4]
    {
        new Vector2(0.4f, 0.5f),
        new Vector2(0.6f, 0.5f),
        new Vector2(0.4f, 0.35f),
        new Vector2(0.6f, 0.35f)
    }; 

    void Start ()
    {
        _spriteRenderer = GetComponent<SpriteRenderer>();
        _sprite = _spriteRenderer.sprite;
    }

    // Update is called once per frame
    void Update ()
    {
        _sprite.uv = _uv;
    }
}

BUT this has an error, it turns that Sprite.uv has no setter (not obvious from the documentation) How can I change the sprite to map different part of the texture?

Narek
  • 38,779
  • 79
  • 233
  • 389
  • You can't change `uv` directly. From https://docs.unity3d.com/ScriptReference/Sprite.OverrideGeometry.html : `Sprite UVs are calculated automatically by mapping the provided geometry onto the Sprite texture.` – derHugo Sep 18 '18 at 15:11
  • Can I create a material that does change the UVs or something similar? I am new to Unity but I believe there should be a normal way of doing that. – Narek Sep 18 '18 at 15:15
  • I don't know to be honest. But I think you can not do that in Unity that easely. Unity is no 3D modelling program. Instead you could try to load your models into e.g. [Blender](https://www.blender.org/) and create the different materials / uv maps there and than import it into Unity – derHugo Sep 18 '18 at 15:18
  • 1
    What is need to do is to change UVs programmatically based on some input. This is not a static setting. – Narek Sep 18 '18 at 15:19
  • I believe the sprite UV's would be stored on the material that is in the sprite renderer... can you give a brief explanation of what you are trying to do? like why are you trying to change the sprite UV's? – AresCaelum Sep 18 '18 at 15:21
  • 1
    To render the part of the texture I want :) – Narek Sep 18 '18 at 15:24
  • One hacky way might be to use a particle system. It can adjust which part of a sprite sheet it's using, very quickly, very leanly. – Confused Sep 18 '18 at 15:31
  • @Narek that sounds like you dont really understand how the SpriteRenderer works... for example why not use a normal material on a quad then? by setting the tiling and uv offsets on that material instance? The SpriteRenderer is designed to work a set of predetermined sprites. You would go into your texture itself, set it to multiple sprite mode, crop out the sprites. doing this Unity will create a sprite for each of those, and then using either code, or an animator you show which ever sprite from that texture you want. – AresCaelum Sep 18 '18 at 15:32
  • The spriteRenderer creates a sort of mesh for each individual sprite you make, and doesnt have a set of 4 uv positions. It can 3 or more. – AresCaelum Sep 18 '18 at 15:34
  • 1
    [Here is a workaround using a MeshRenderer on a plane](https://forum.unity.com/threads/spriterenderer-changing-texture-uv-offset.366688/) instead; [Here](https://answers.unity.com/questions/605905/dynamic-offset-tiling-for-spriterenderer.html) is a bit more complex solution including a custom shader that might be cabable of what you are trying .. didn't test it – derHugo Sep 18 '18 at 15:34
  • @Confused I understand how people expect UV's to work. I fell into this pitfall, that is how I came to understand how Unity's SpriteRenderer Works. Don't get my understand of how Unity's tool work confused with my understanding of how UV's work in a typical project. – AresCaelum Sep 18 '18 at 15:36
  • Another hack way to achieve the desired goal: two sprites, one masking the other, the one masked having the full texture, and moved, scaled and rotated underneath the mask as you desire/need. – Confused Sep 18 '18 at 15:36

3 Answers3

4

This is a solution that works with a SpriteRenderer for atleast selecting a rectangle part of the Sprite you want to show. (How Programmer stated out orrectly in the comments this can not "deform" your UV if you need a complete different mapping.)

  1. Create a new Shader and call it BlendVertexColorWithUV

  2. Open it in VisualStudio (or any text editor) and past in the following code
    Source

     // unlit, vertex color, alpha blended, offset uv's
     // cull off
    
     Shader "BlendVertexColorWithUV" 
     {
         Properties 
         {
             _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
         }
    
         SubShader
         {
             Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
             ZWrite Off Lighting Off Cull Off Fog { Mode Off } Blend SrcAlpha OneMinusSrcAlpha
             LOD 110
    
             Pass 
             {
                 CGPROGRAM
                 #pragma vertex vert_vct
                 #pragma fragment frag_mult 
                 #pragma fragmentoption ARB_precision_hint_fastest
                 #include "UnityCG.cginc"
    
                 sampler2D _MainTex;
                 float4 _MainTex_ST;
    
                 struct vin_vct 
                 {
                     float4 vertex : POSITION;
                     float4 color : COLOR;
                     float2 texcoord : TEXCOORD0;
                 };
    
                 struct v2f_vct
                 {
                     float4 vertex : POSITION;
                     fixed4 color : COLOR;
                     float2 texcoord : TEXCOORD0;
                 };
    
                 v2f_vct vert_vct(vin_vct v)
                 {
                     v2f_vct o;
                     o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                     o.color = v.color;
                     o.texcoord = TRANSFORM_TEX (v.texcoord, _MainTex);;
                     return o;
                 }
    
                 fixed4 frag_mult(v2f_vct i) : COLOR
                 {
                     fixed4 col = tex2D(_MainTex, i.texcoord) * i.color;
                     return col;
                 }
    
                 ENDCG
             } 
         }
    
         SubShader
         {
             Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
             ZWrite Off Blend SrcAlpha OneMinusSrcAlpha Cull Off Fog { Mode Off }
             LOD 100
    
             BindChannels 
             {
                 Bind "Vertex", vertex
                 Bind "TexCoord", texcoord
                 Bind "Color", color
             }
    
             Pass 
             {
                 Lighting Off
                 SetTexture [_MainTex] { combine texture * primary } 
             }
         }
     }
    
  3. Create a new Material

  4. Drag the BlendVertexColorWithUV onto this material

  5. assign this material to the Object that uses the SpriteRenderer

  6. Set the SpriteRenderer's DrawMode to Tiled

  7. Set the TileMode to Continous

enter image description here

Note: I actually made a mistake while grabbing: You assign the Sprite to the SpriteRenderer not to the material! You can leave the material Blanc actually and just adjust the Tiling and Offset values.

Now you can adjust the offset of the sprite in the material e.g. by using a script

public Material textureToAnimate;
public Vector2 uvOffset;

....

textureToAnimate.mainTextureOffset = uvOffset;
derHugo
  • 83,094
  • 9
  • 75
  • 115
  • Do this with a sprite that has transparency in it. To make it work with that you will have to change the MeshType in the texture to be full rect instead of polygon. Which is what I was referring to in my comment above when I said that the sprites generate a sort of mesh. – AresCaelum Sep 18 '18 at 16:01
  • 1
    Setting uv offset is different from setting array of uv to a variable. I think OP wants to change the uv array.... – Programmer Sep 18 '18 at 16:04
  • 1
    In regards to what @Programmer stated, you would still need to change the size option to make it determine where the last 2 uv's are placed. – AresCaelum Sep 18 '18 at 16:06
  • @Eddge I actually made a mistake while grabbing: You set the Sprite to the `SpriteRenderer` not to the material! It worked just the same for me with a transparent sprite. It also didn't make any difference whether I choose `Simple` or `Polygon` in the Sprite settings. The size can be changed using the Tiling in the material as well – derHugo Sep 18 '18 at 16:07
  • @Programmer anyway ofcourse you are right and this might not work if you want to "deform" your UV mapping instead of just selecting which part shall be mapped – derHugo Sep 18 '18 at 16:11
  • Something you could do to programmatically determine where the other UVs would be would be to set your size to 1(Width and Height on the SpriteRenderer), and then modify the tiling... for example uvOffset = {0.2f, 0.2f} and you want the second set to be {0.3f, 0.35f} you size would be `determineTiling = secondSet - firstSet`, then `tiling = {1f/determineTiling.x, 1f/determineTiling.y}` but that is essentially a hack... – AresCaelum Sep 18 '18 at 16:18
0

I have just used Quad, for which you can set uvs, vertices and triangles. So basically got all the information (uv, vertices) from Sprite and transfered that to a Quad.

Narek
  • 38,779
  • 79
  • 233
  • 389
  • There are many things in Unity is referred as Quad. In Unity 2019, there is a 3D quad object. And also you could create a empty GameObject then add a mesh to it. Then add a Quad texture to that mesh. Which quad are you referring to? – Maxi Wu Jun 15 '20 at 05:43
0

There is a more direct way now if you add the additional Sprite2D package by Unity:

sprite.SetVertexAttribute<Vector2>(VertexAttribute.TexCoord0, uv);

Only caveat is it requires a native array but you can Dispose() it right after the call. Equivalently there's a Get() method.

DragonGamer
  • 834
  • 3
  • 9
  • 27