Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

SpriteManager - draw lots of sprites in a single draw call!

Discussion in 'iOS and tvOS' started by Brady, Jan 15, 2009.

  1. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    That's very strange. I doubt it would be something in SM itself since it looks like you're doing everything correctly and that's pretty straight-forward stuff. Are you setting alpha on the letters anywhere else? Or could there be another GO that is accidentally holding on to an old sprite reference that it could be altering the alpha? I tend to think at this point that it's got to be something going on elsewhere in the game logic.
     
  2. Quickfingers

    Quickfingers

    Joined:
    Aug 29, 2009
    Posts:
    268
    ok thankyou brady I will re go through my code again.
    Also one other thing while I have your attention :)
    I noticed some strange outlining on some sprites with alphas like this



    notice the white outline on the sprite, this is not in the sprite sheet.
    Have you seen this before?
    Promise to stop bothering you after this one :)

    edit : I have tried a couple of shaders, including particles - alpha blended and transparent - diffuse, and they all show up with this line
     
  3. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    I'm not sure what that could be. I'd just make 100% sure of the alpha values right there at the edges. On second thought, it does occur to me that this could be an artifact of using mipmaps. If you have mipmapping turned on, it could be the result of the mipmapping's attempt to down/up sample the alpha pixels in the original sprite sheet. Try turning mipmapping off and see if that affects it.
     
  4. jtbentley

    jtbentley

    Joined:
    Jun 30, 2009
    Posts:
    1,397
  5. Quickfingers

    Quickfingers

    Joined:
    Aug 29, 2009
    Posts:
    268
    JTBentley you were absolutely correct, thankyou! I didn't realise that psd format didn't automatically premultiply the alpha, converting down to a png worked.

    all the best
     
  6. jbud

    jbud

    Joined:
    Jan 6, 2009
    Posts:
    49
    Does anyone know if it's faster performance wise to use dynamically batched quads or sprite manager??

    I might do a few tests to find out.

    But thought someone out there might already know the answer.


    Cheers
     
  7. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    Yes, I'd be very interested to see any such test results.

    It may depend on how you're doing things. For instance, perhaps lots of new instantiations might tip the balance in favor of one method while heavy re-use of existing objects might tip the other way. I'm not sure.

    Of course, SM provides you with sprite animation tools. I'm planning on leveraging the dynamic batching with SM's sprite animation in a future release. But I'll be very interested to see your test results first to see where the strengths lie.
     
  8. stringa

    stringa

    Joined:
    Aug 28, 2009
    Posts:
    8
  9. raleighr3

    raleighr3

    Joined:
    Jul 9, 2008
    Posts:
    106
    More Sprite Rotation woes here:
    I'm setup with a top-down view, YZ oriented and I have everything mostly working here except I'm experiencing an issue with when the rotation of a Sprite crosses the vertical axis the Sprite flips. I'll attach a couple pngs to illustrate the issue.
    • Image 'A' shows the sprite as it would appear in the Texture.
      Image 'B' shows the sprite rotating ccw approaching the vertical axis (Y in my scenario)
      Image 'C' shows the sprite having rotated past vertical and now flipped (notice the dots)

    While this might not be so bad if the sprite is symmetrical, the ones I am actually using are not. Also there is a sudden jerky blip right at the point it flips which I would like to eliminate.
    So here is the code that I am using to do the rotation:

    Code (csharp):
    1.  
    2. if(faceHeading  distance > 1.1f)
    3. {
    4.         rot = Quaternion.LookRotation(currentHeading, Vector3.up);
    5.  
    6.         transform.rotation =  rot;
    7. }
    currentHeading is a Vector3 towards which the sprite is moving

    Code (csharp):
    1.  
    2. targetHeading = ((Vector3)waypoints[targetwaypoint]) - transform.position;
    3. currentHeading = Vector3.Lerp(currentHeading, targetHeading, damping*Time.deltaTime);
    So what am I doing wrong here? is SM flipping the sprite or what is something else going funky?
     

    Attached Files:

  10. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    I believe this is an artifact of using LookRotation(). To fully understand why, you need to do some study on the math involved (mostly cross products). This would happen even if you weren't using sprites or SpriteManager. If you look at the transform of the GO in question, you'll see when it crosses vertical, one of its axes will reverse direction.

    You may want to consider using something other than LookRotation(), such as just applying local rotations as needed. I don't know if that's really an option in your situation or not, but the bottom line is, it is a transform math issue, not a SpriteManager issue.
     
  11. raleighr3

    raleighr3

    Joined:
    Jul 9, 2008
    Posts:
    106
    Thanks Brady. guess I'll need to dust off the old maths books, my vector math is pretty weak right now :-(
     
  12. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    I can give you a basic description of the problem: LookRotation() uses the "up" vector to cross product against the "forward" vector, in order to calculate the "right" vector (the three vectors you need to define the transformation). This works because the cross product of two vectors is a vector aiming at a right angle to the other two. But for that to work, the first two vectors have to be sufficiently separate.

    If the "up" and "forward" vectors are too close to being parallel, the math won't yield a good result for the "right" vector. Moreover, after you've crossed vertical, the "right" vector will be pointing the other way since the directional relationship of "up" to "forward" is now reversed from what it was.

    Though it won't entirely fix the problem of when the "up" and "forward" vectors are coincident, you may just put some logic in that checks the direction of the "right" vector, and if it comes out reversed, "manually" rotate the GO 180 degrees about its local "forward" (Z) axis.
     
  13. selece

    selece

    Joined:
    Jul 8, 2009
    Posts:
    8
    A question regarding animations and display sizes...

    If I have a spritesheet as follows for a single character...

    (A and B are frames of an animation for a single character).

    A A A A A
    A A A A A
    A A B B B
    B B B B

    1. Do animations A and B need the same UV size?
    2. Do A and B need to be in separate "grids" as as below?

    A A A A A
    A A A A A
    A A

    B B B B B
    B B

    I'm having sprite display issues (incorrect animations scrolling into weird parts of the master atlas and odd stretching/distortions), so any help would be appreciated.

    Thanks!
     
  14. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    First, make sure the display problems aren't being caused by bad UV coordinates by making sure you use PixelSpaceToUVSpace() and PixelCoordToUVCoord() correctly ("space" is for widths and heights, "coord" is for coordinates, such as the lower-left coordinate).

    For BuildUVAnim(), the frames must each be the same size and if you are doing 2 separate animations, they will need to be in separate grids like so:

    Code (csharp):
    1.  
    2. AAAAA
    3. AAAAA
    4. AA
    5. BBBBB
    6. BB
    7.  
    8. Or,
    9.  
    10. AAAAA
    11. AAAAA
    12. AABBB
    13.   BBB
    14.   BB
    15.  
    In other words, it will "wrap around" to the X position of the first sprite in the sequence, not to the left-most edge of the texture.

    All of that having been said, this limitation does not apply if you define each animation frame individually rather than using BuildUVAnim(). For instance, you can do the following:

    Code (csharp):
    1.  
    2. Vector2[] frames = new Vector2[5];
    3. UVAnimation anim = new UVAnimation();
    4.  
    5. // Be sure to use Pixel*ToUV* here!
    6. frames[0] = Vector2(...);
    7. frames[1] = Vector2(...);
    8. ...
    9. frames[4] = Vector2(...);
    10.  
    11. anim.SetAnim(frames);
    12.  
    This means each sprite frame can be anywhere on the texture. Currently, however, UVAnimation doesn't support differently sized sprites automatically yet. I'll put that on the list of things to add right away.
     
  15. selece

    selece

    Joined:
    Jul 8, 2009
    Posts:
    8
    Thanks for the answer. :)

    Is there a timeline for support for sprites with animations that have different UVsizes?

    Otherwise in the meantime, I guess I can do one of two things:

    1. Resize all frames of a character to use the larger of the animation frame sizes.
    2. Can I do something weird where I create two sprite objects, one with animation A, one with B. Then, set A to A's framesize, B to B's framesize. If I need animation A to show, hide B and only show A and vice versa?

    Yeah, #2 sounds more complex, but I don't REALLY relish rebuilding the sheet again, but I can if it's the best way to go about it for now.
     
  16. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    Yeah, either one of those could work. I can't promise any timeline at the moment as I'm deep into some other projects. But I will eventually come around to it as part of this project.
     
  17. selece

    selece

    Joined:
    Jul 8, 2009
    Posts:
    8
    Questions questions!

    Thanks for the support on the UVsizes in animations, it's all worked out now. :)

    Is there a way to offset the sprite relative to the client gameObject?

    i.e. Is there a way for me to draw the sprite not at (0,0,0) relative to the GO, but rather say (0, x, 0)?

    Thanks!
     
  18. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    Yes, just use .offset (member of Sprite). Though currently you'll have to call SetSize**() afterward for it to apply to the vertices. So for example:

    Code (csharp):
    1.  
    2. sprite.offset = Vector3.right; // 1,0,0
    3. sprite.SetSizeXY(sprite.width, sprite.height);
    4.  
     
  19. bobber205

    bobber205

    Joined:
    May 12, 2009
    Posts:
    139
    I plan on giving this SM business a shot soon. To help with the process, are there any example projects that are *not* for iphone that I can run with Indie I can look at and see how it's done?

    Thanks! :)
     
  20. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    The example in the first post will work just fine on regular Unity. SM is not specific to platform - it should work the same for all.
     
  21. bobber205

    bobber205

    Joined:
    May 12, 2009
    Posts:
    139
    I get this message when trying to import the package from Assets menu.

     
  22. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    Try re-downloading it. I just tried it with Unity 2.5 and it worked fine.
     
  23. selece

    selece

    Joined:
    Jul 8, 2009
    Posts:
    8
    Brady, thanks for the constant support!
    However, I'm back to bug you for another question ... :oops:

    Is there any reason why when calling Pause/Unpause for animations that the sprite framerate seems to double (or at least increase) every time this happens?

    I'm using the PauseAnim() / UnpauseAnim() functions.

    Thanks in advance!
     
  24. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    Yes, but that was fixed a version or two ago. Are you using the latest version?
     
  25. bobber205

    bobber205

    Joined:
    May 12, 2009
    Posts:
    139
    Not sure what you mean.
    I am using 2.5.1f and I downloaded the package off the first page.
     
  26. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    Okay, I just went ahead and opened it in 2.5 (not sure why it's not working for you) and re-exported it to a package from 2.5. This should work. If it doesn't, there's probably something wrong with your Unity install (try-reinstalling).

    Edit: Bear in mind, this version doesn't include a demonstration of the animation features. But the animation routines are documented on the wiki, and you should see some good examples earlier in this thread.
     

    Attached Files:

  27. bobber205

    bobber205

    Joined:
    May 12, 2009
    Posts:
    139
    I'm sorry to cause so much trouble.
    I am using WinRAR to extract it the .gz file I am downloading.

    I am attaching what I have tried to import. I am receiving the same error.
     

    Attached Files:

  28. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    That seemed to work for me, except with one caveat: for some reason, it was missing SpriteManager.cs. For that, just use the latest version and drop it in the Scripts folder.

    Wait, something just occurred to me... you're using WinRAR, which means you're on Windows. I'm testing it with 2.5 on the Mac. That shouldn't make a difference (I think), but that could be the problem.

    I'm attaching the demo script that puts it all together so you can at least see the syntax, etc.
     

    Attached Files:

  29. bobber205

    bobber205

    Joined:
    May 12, 2009
    Posts:
    139
    Thanks for that cs file. I am still having alot of trouble getting it to export correctly.

    Can you post the materials as just a regular zip file and not the unity format? Thanks! :)

    Is there anyone using the Windows client that could try exporting it for me? I tried using my mac to expand it but it ends up as a folder. I have named the folder on windows to end with .unitypackage but that didn't work. :(
     
  30. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    Okay, here's a zip with the whole project (including library. Some of it will undoubtedly not work for you (the reason for packages), but the texture and such are all there.
     

    Attached Files:

  31. bobber205

    bobber205

    Joined:
    May 12, 2009
    Posts:
    139
    Worked like a charm. Very fun demo!

    Thanks! :D
     
  32. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    Heh, can't beat soccer/foot balls and soda cans! :)
     
  33. selece

    selece

    Joined:
    Jul 8, 2009
    Posts:
    8
    The headers on the files read as follows:


    LinkedSpriteManager v0.632 (7-24-2009)
    SpriteManager v0.633 (8-02-2009)
    Sprite (part of SpriteManager) v0.633 (8-03-2009)

    These were downloaded from the OP of the topic.
     
  34. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    In that case, no, I'm not sure why that would be. I've tested it on my end and there doesn't seem to be any increase. Make sure there are only 1 Unpause call for every Pause call. Maybe put a Debug.Log() in the Pause and Unpause routines and make sure they're getting called 1:1. The reason it was speeding up in a prior release was due to the fact it wasn't being removed from the animation list, so each time you Unpaused, it got added to the list a second time, meaning it was updating twice per frame, and then if you unpaused again, it was being updated three times per frame and so on... Perhaps you're unpausing more than once? I'll throw some code in to prevent multiple unpauses in my internal version to prepare for the next release.
     
  35. codepunk

    codepunk

    Joined:
    Dec 10, 2008
    Posts:
    68
    I think I'm doing something wrong. If I call translate or something simlar on the GameObject containing the sprite, the GameObject bounding box moves, but the Sprite stays put. It only seems to move when I call spriteMan.Translate(sprite). However, when I read the doc's I see a big ol' performance warning when doing that. What is the correct way to move a sprite?
     
  36. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    Actually, you move a sprite by moving the GameObject to which it is associated (as opposed to the one that contains it, or the one containing the SpriteManager itself).

    So for instance, when you add a sprite with AddSprite(), one of the parameters you pass is the GameObject that should be the "client" of the sprite. This means then that the sprite will be translated/rotated according to that GameObject's transform.

    Also be sure the object containing the SpriteManager is at the origin (0,0,0).

    To get a better idea of how sprites are moved about, see the demo project attached to the original post of this thread.
     
  37. codepunk

    codepunk

    Joined:
    Dec 10, 2008
    Posts:
    68
    Ok, so what you're saying is that I shouldn't need to call SpriteManager.Translate or SpriteManager.UpdatePositions then. I simply need to move the GameObject that the sprite is attached to.

    The demo project doesn't really help in this instance. It's just letting the physics engine do all the work whereas I need to actually update positions as the game goes along (i.e. gameObject.Translate).
     
  38. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    Correct. Basically, what the demo project shows is that it is merely the motion of the GameObjects that automatically translates/rotates (transforms) the sprites. Whether you're manually moving your GOs or you have rigidbodies that are doing it, either way, there's nothing more for you to do to make the sprites follow suit.
     
  39. aerende

    aerende

    Joined:
    Apr 27, 2009
    Posts:
    316
    When the sprites move, they appear to move with the corner of the sprite as the center of the sprite. How can I make the center of the sprite be the center of the sprite when the sprite moves?
     
  40. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    I'm not 100% sure what you mean. If you mean you want the sprite centered at the position of its client GameObject, that is the default behavior. It will only do otherwise if you are using an offset. Make sure you are not specifying an offset and it should be centered.
     
  41. aerende

    aerende

    Joined:
    Apr 27, 2009
    Posts:
    316
    Where would the offset be specified?

    Currently I am instantiating the GOs and adding them to the Sprite Manager via the following code:

    Code (csharp):
    1.  
    2.  
    3.         Sprites.Add(Instantiate(prefabGO, location[m], Quaternion.identity));
    4.         SMGO = GameObject.Find("SpriteManagerGameObject");
    5.         LSP = SMGO.GetComponent("LinkedSpriteManager");
    6.  
    7.         s = LSP.AddSprite(Sprites[m],  // The game object to associate the sprite to
    8.                          90.0,        // The width  of the sprite
    9.                         170.0,        // The height of the sprite
    10.                         110,        // Left pixel
    11.                         128,        // Bottom pixel
    12.                          50,        // Width in pixels from Left pixel
    13.                         128,        // Height in pixels
    14.                         false);     // Billboarded?
    15.  
    16.  
    17.  
     
  42. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    That is specified by:

    sprite.offset = new Vector(1,2,3);

    Then to make it take effect, you must call SetSize**().

    So if you're not doing any of that, then the sprite should be centered on the GameObject. Also, make sure your SpriteManager object is at the origin (0,0,0).
     
  43. aerende

    aerende

    Joined:
    Apr 27, 2009
    Posts:
    316
    The SpriteManager Game Object is at (0, 0, 0). It appears that the Game Object is located at the bottom left hand corner of the sprite.

    I wasn't using offset or SetSizeXZ before. But now when using offset and SetSizeXZ, changing the X and Z positions in the offset both by negative values did move the sprite more on top of the Game Object.

    Anything else that could be causing this default offset between the sprite and the Game Object?
     
  44. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    Not that's coming to mind, no... That's very strange.

    I guess just make sure your UVs are set correctly (lower-left set to the lower-left pixel location, etc).
     
  45. iphonefreak

    iphonefreak

    Joined:
    Feb 25, 2009
    Posts:
    157
    Quick question about SpriteManager. Is it suitable to for creating UI elements (e.g. screen space icons and buttons etc)?
     
  46. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    Yes, certainly. Though it doesn't provide any built-in touch detection. It's just a way to display 2D graphics onscreen without lots of drawcalls and with added sprite animation features. If it's reducing drawcalls you're after, 1.5 has batching now and you can just use separate meshes. But if you're wanting sprite animation features, it's still very useful (I'm currently working on a new version of SpriteManager that uses batching but adds sprite animation capabilities).
     
  47. Mishiza

    Mishiza

    Joined:
    Jul 20, 2009
    Posts:
    10
    Heya!

    It's me again with my no0b questions, I've looked through the code and i cant get my head around this.

    My game is using about 100-200 sprites on screen (actually Im only using sprites and nothing else ^^) and I'm trying to make things go a bit faster by disabling the Auto update.

    My game now only updates the bounds when there is a change in the sprites, however...

    My main character and enemies are always moving...

    So my question is there a way to update bounds for specific sprites,

    or do i need to make another spritemanager in my game in order to only always update those sprites i know that will be needing it, that is to say one for my level (updated every 10th frame) and one for my characters (updated every frame)..?

    Thanks for your answer!
     
  48. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    Actually, the bounds are per-SpriteManager as opposed to per-sprite. A SpriteManager is basically one huge mesh, and each sprite is just a piece of that mesh. For that mesh to display, Unity must know that it is in view, and to do that, it tests the camera frustum against the mesh's bounding volume. When a SpriteManager starts, it has zero sprites, and therefore its default bounding volume is set to something like 1x1x1 world units (maybe less). So if that 1x1x1 area around the origin goes out of view (because the camera moves, etc), then all the sprites that are part of that mesh will disappear because Unity thinks they aren't visible to the camera anymore. This is why bounds recalculation is needed.

    If you know for sure that at least some of your sprites will always be in view, and therefore you never want the camera to cull the SpriteManager altogether, then I suggest the easiest solution to your issue is to do this when your scene first loads:
    For a 2D game, create 4 "dummy" sprites (for 3D, create 8 ), and then place them at the far corners of your game world, beyond where the camera is going to go, then manually recalculate bounds. Then, immediately remove these sprites (as they only served the purpose of setting the bounding volume), and you never need to recalculate bounds again. The bounding volume will always be inside the frustum, so your sprites will always be rendered.

    But with SpriteManager, there's no way to calculate bounds just for certain sprites since Unity works at the mesh level, and sprites are just parts of a larger mesh.

    I hope that helps!
     
  49. Mishiza

    Mishiza

    Joined:
    Jul 20, 2009
    Posts:
    10
    Dude, you are so awesome I could kiss you!

    Thanks a lot dude!
     
  50. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    No problem! And to clarify, by "manually recalculate bounds", I of course just mean calling "UpdateBounds()" once, as opposed to letting it auto-recalculate.

    Have fun!