Author Topic: Filled Sprite Texture Offset  (Read 14148 times)

BeShifty

  • Jr. Member
  • **
  • Thank You
  • -Given: 5
  • -Receive: 7
  • Posts: 52
    • View Profile
Filled Sprite Texture Offset
« on: January 03, 2013, 05:51:36 PM »
I'd like to create an effect similar to this loading bar: , where a repeating texture has its UV coordinates animated to create a scrolling effect. I understand that for the repeating part I should use a UITiledSprite, but that doesn't help me with the UV offsetting. Is there any way of assigning an offset manually, or will I have to modify the UITiledSprite class?

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Filled Sprite Texture Offset
« Reply #1 on: January 03, 2013, 06:20:49 PM »
You can only scroll tileable textures, and sprites are not tileable. You have to use a UITexture with a custom shader to do this.

BeShifty

  • Jr. Member
  • **
  • Thank You
  • -Given: 5
  • -Receive: 7
  • Posts: 52
    • View Profile
Re: Filled Sprite Texture Offset
« Reply #2 on: January 03, 2013, 06:27:56 PM »
What about modifying the UV coordinates of the mesh? I'd need the wrapping texture to repeat once to allow full looping, but the built-in shader supports UV coordinates, correct?

BeShifty

  • Jr. Member
  • **
  • Thank You
  • -Given: 5
  • -Receive: 7
  • Posts: 52
    • View Profile
Re: Filled Sprite Texture Offset
« Reply #3 on: January 03, 2013, 06:53:34 PM »
This would probably work if I extended UITiledSprite, if I understand your code correctly (not at home atm):
  1. private Vector2 uvOffset;
  2. public Vector2 UVOffset {
  3.     get {
  4.         return uvOffset;
  5.     }
  6.     set {
  7.         if( value != uvOffset ) {
  8.             uvOffset = value;
  9.             MarkAsChanged();
  10.         }
  11.     }
  12. }
  13.  
  14. /* Down in OnFill */
  15. // Replace
  16. uvs.Add(new Vector2(clipped.x, 1f - min.y));
  17. uvs.Add(new Vector2(clipped.x, 1f - clipped.y));
  18. uvs.Add(new Vector2(min.x, 1f - clipped.y));
  19. uvs.Add(new Vector2(min.x, 1f - min.y));
  20. // with
  21. uvs.Add(new Vector2(clipped.x + uvOffset.x, 1f - min.y - uvOffset.y));
  22. uvs.Add(new Vector2(clipped.x + uvOffset.x, 1f - clipped.y - uvOffset.y));
  23. uvs.Add(new Vector2(min.x + uvOffset.x, 1f - clipped.y - uvOffset.y));
  24. uvs.Add(new Vector2(min.x + uvOffset.x, 1f - min.y - uvOffset.y));
  25.  

Edit: You'd have to manually edit the looping texture's sprite to half its original width in the atlas in order to wrap correctly, and then the animation code would just be
  1. public class ScrollSpriteTexture : MonoBehaviour {
  2.     public UITiledSpriteWithOffset sprite;
  3.     public float speed;
  4.     private float spriteWidth;
  5.     void Start() {
  6.         spriteWidth = sprite.innerUV.width;
  7.     }
  8.     void Update() {
  9.         Vector2 oldUVs = sprite.UVOffset;
  10.         oldUVs.x += Time.deltaTime * speed;
  11.         if( oldUVs.x > spriteWidth )
  12.             oldUVs.x = 0;
  13.         sprite.UVOffset = oldUVs;
  14.     }
  15. }
  16.  
« Last Edit: January 03, 2013, 07:32:45 PM by BeShifty »

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Filled Sprite Texture Offset
« Reply #4 on: January 04, 2013, 05:51:24 AM »
Sprite UVs are somewhere in the atlas. For example, a sprite can have a rect of (0.1, 0.1, 0.2, 0.2) within an atlas. You can't simply adjust the UVs as then you will start seeing sprites around your chosen sprite instead of tiling it. This is why I said you can't tile sprites. Tiled Sprite works by creating geometry rather than changing UVs.

That said, you can still create a version of a tiled sprite that will take an offset and start creating geometry in a specific fashion.

BeShifty

  • Jr. Member
  • **
  • Thank You
  • -Given: 5
  • -Receive: 7
  • Posts: 52
    • View Profile
Re: Filled Sprite Texture Offset
« Reply #5 on: January 04, 2013, 11:41:06 AM »
Ah alright, I misunderstood the sprite editing fields in the altas inspector. I'll just modify the code above to use a Rect instead of a Vector2 for the UVs and allow the UVs to be scaled as well as offset. Thanks!

BeShifty

  • Jr. Member
  • **
  • Thank You
  • -Given: 5
  • -Receive: 7
  • Posts: 52
    • View Profile
Re: Filled Sprite Texture Offset
« Reply #6 on: January 05, 2013, 02:39:41 PM »
It looks like my new scripts work as intended. Here's a small demo: http://youtu.be/2CBTFqWioD4.
I created 3 new classes: UITiledOffsetSprite, which extends UITiledSprite and adds a UV modifier field which shifts and scales the UV coordinates of the sprite's mesh, UITiledOffsetSpriteInspector (I know, long name), and UITextureScroller which is just an interaction script which takes a UITiledOffsetSprite and animates the sprite's texture offsets with a given speed.
I would give out the code but I'd like ArenMook's approval first since UITiledOffsetSprite's OnFillBuffers function is almost copy/pasted from UITiledSprite and I don't want to give out his work without his permission.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Filled Sprite Texture Offset
« Reply #7 on: January 05, 2013, 03:38:01 PM »
Looks great! Ship it! :D

If something is copy/pasted then you can probably derive from the tiled sprite.

BeShifty

  • Jr. Member
  • **
  • Thank You
  • -Given: 5
  • -Receive: 7
  • Posts: 52
    • View Profile
Re: Filled Sprite Texture Offset
« Reply #8 on: January 05, 2013, 04:13:41 PM »
Unfortunately it's the OnFill function. The only way I could reuse the tiled sprite function would be to call it like this:
  1. public override OnFill(BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color> cols) {
  2.     Rect rect = mInner;
  3.     rect.width *= uvModifiers.width;
  4.     rect.height *= uvModifiers.height;
  5.     Vector2 offsetInUVs = new Vector2( uvModifiers.x * rect.width / tex.width, -uvModifiers.y * rect.height / tex.height );
  6.  
  7.     BetterList<Vector2> tempUVs = new BetterList<Vector2>();
  8.     base.OnFill(verts, tempUVs, cols);
  9.     foreach( Vector2 uv in tempUVs ) {
  10.         uvs.Add( uv + offsetInUVs );
  11.     }
  12. }
  13.  

If that code doesn't smell too bad to you, I'll post the full package.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Filled Sprite Texture Offset
« Reply #9 on: January 05, 2013, 04:35:06 PM »
Creating a new array every OnFill function is generally not a good idea.

Cripple

  • TNP Alpha
  • Full Member
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 117
    • View Profile
Re: Filled Sprite Texture Offset
« Reply #10 on: January 05, 2013, 05:39:01 PM »
ArenMook is right, cache the array and clear() it when you want to use it again otherwise it will garbage a lot.

Graphicstream Dev.

BeShifty

  • Jr. Member
  • **
  • Thank You
  • -Given: 5
  • -Receive: 7
  • Posts: 52
    • View Profile
Re: Filled Sprite Texture Offset
« Reply #11 on: January 05, 2013, 06:24:22 PM »
Alright I'm fairly satisfied using TiledSprite's function, although I did have to make a few more adjustments. Here's a zip with the 3 files I mentioned, and here's the UITiledOffsetSprite.cs code for your perusal.
  1. public class UITiledOffsetSprite : UITiledSprite
  2. {
  3.         public Rect uvModifiers = new Rect(0, 0, 1, 1);
  4.         Rect uvModifiersOld;
  5.        
  6.         /// <summary>
  7.         /// Fill the draw buffers.
  8.         /// </summary>
  9.  
  10. #if UNITY_3_5_4
  11.         public override void OnFill (BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color> cols)
  12. #else
  13.         public override void OnFill (BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color32> cols)
  14. #endif
  15.         {
  16.                 Texture tex = material.mainTexture;
  17.                 if (tex == null) return;
  18.                
  19.                 Rect rect = mInner;
  20.                
  21.                 rect.width *= uvModifiers.width;
  22.                 rect.height *= uvModifiers.height;
  23.                
  24.                 int oldNumVerts = uvs.size;
  25.                
  26.                 //Tiled Sprite needs the scaled uv coordinates
  27.                 Rect realInner = mInner;
  28.                 mInner = rect;
  29.                 base.OnFill(verts, uvs, cols);
  30.                 mInner = realInner;
  31.                
  32.                 if (atlas.coordinates == UIAtlas.Coordinates.TexCoords)
  33.                 {
  34.                         rect = NGUIMath.ConvertToPixels(rect, tex.width, tex.height, true);
  35.                 }
  36.                
  37.                 Vector2 offsetInUVs = new Vector2( uvModifiers.x * rect.width / tex.width, -uvModifiers.y * rect.height / tex.height );
  38.                
  39.                 for( int i = oldNumVerts; i < uvs.size; i++ )
  40.                         uvs[i] += offsetInUVs;
  41.                
  42.                 if( uvModifiers != uvModifiersOld ) {
  43.                         MarkAsChanged();
  44.                         uvModifiersOld = uvModifiers;
  45.                 }
  46.         }
  47. }
  48.  

Edit: I should add that, again, the sprite texture will need to be the same image repeated twice in the direction you want to loop. The texture in the demo is
« Last Edit: January 05, 2013, 06:47:29 PM by BeShifty »

Irin1178

  • Guest
Re: Filled Sprite Texture Offset
« Reply #12 on: August 14, 2013, 01:58:17 PM »
Did this get integrated into NGUI or is it available somewhere else? The link appears to be broken and I'd like to not reinvent the wheel.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Filled Sprite Texture Offset
« Reply #13 on: August 15, 2013, 07:22:00 AM »
This effect and more is trivial to do in the upcoming GUI. ;)

Quite painful in NGUI, unless you just use a UITexture with a custom material that simply blends two textures.