Hello Guest

Author Topic: [Creating a Customizable 2D Character] Creating Atlases Dynamically at Runtime  (Read 16691 times)

aikitect

  • 2D Toolkit
  • Newbie
  • *
  • Posts: 14
    • View Profile
Hello, I am trying to create a customizable 2D character that is comprised of 30 different sprites.  Assuming that there are 10 variations of each sprite, there would be 300 sprites in total.  I also want to have multiple characters on screen at one time.  As a result, I am trying to figure out what's the best way to render a given character at runtime if I know the sprite names that need to be shown.  There are three solutions:

  • Create an atlas for each of the 10 character variations offline.  However, this would require me to load all 10 atlases if the character uses a sprite from each character variation.  This will waste memory because I am loading a lot of sprites I will never use.
  • Create an atlas for each sprite.  This would require 30 draw calls per character.  If I have 5 characters on screen, that would require 150 draw calls which is too much.
  • Since I know which sprites the character will use, I can theoretically create an atlas at runtime and use that.  This will require 1 draw call per character.

I want to attempt the last solution.  However, I am trying to figure out how to do that with TK2D.  I have been reading other threads such as this one: http://2dtoolkit.com/forum/index.php/topic,2576.15.html.

However, I have a few questions about the workflow for this works.  From what I gather, it work as follows.

  • Use Texture2D.PackTextures to pack the correct textures into an atlas.
  • Use tk2dSpriteCollectionData.CreateFromTexture to create a tk2dSpriteCollectionData.
  • Use RenderTexture to actually display the character?  I'm looked at the Unity documentation for Render Texture and am not sure how it fits into the workflow.

Can you help advise on the workflow above or suggest something else completely if it is not correct.

unikronsoftware

  • Administrator
  • Hero Member
  • *****
  • Posts: 9709
    • View Profile
#3 is not necessary if you do #1 and #2. Once you get to that point, you'll be able to use the sprites directly.
The render texture solution is much more optimal alternative to PackTextures, which is really really slow.

aikitect

  • 2D Toolkit
  • Newbie
  • *
  • Posts: 14
    • View Profile
Can you explain the basic workflow of using render textures?  I watched a few tutorials and wasn't really able to piece it together.  Are you supposed to use RenderTexture to generate the atlas and then plug it into tk2dSpriteCollectionData.CreateFromTexture?  Is there sample code available for this?

unikronsoftware

  • Administrator
  • Hero Member
  • *****
  • Posts: 9709
    • View Profile
Yes, you use render textures to generate the atlas - this is a lot faster than writing into texture2D, and call CreateFromTexture using the render texture. I am not aware of any sample code for this.

At the end of the day though, if you need something and its not performance critical, the pack textures method will work fine.

aikitect

  • 2D Toolkit
  • Newbie
  • *
  • Posts: 14
    • View Profile
Ok, so if there's no sample code, can you give guidance on the following rough algorithm?

1) Create a RenderTexture called characterTexture.
2) Create a camera in the scene. (Should this camera be a tk2dCamera?)
3) Pack the relevant 2D sprites into the field of view of the camera.  Save the sprite names in names, save the width and height of each sprite in regions, and save the center (since they are center-anchored) of each sprite in anchors.
4) Call tk2dSpriteCollectionData.CreateFromTexture.

Code: [Select]
tk2dSpriteCollectionData.CreateFromTexture(
    characterTexture,
    tk2dSpriteCollectionSize.ForTk2dCamera(Camera.main),
    names,
    regions,
    anchors
);

Two more questions related to this.  Is there a packing algorithm that you suggest using (perhaps the one that tk2d uses)?  Also, how fast is tk2dSpriteCollectionData.CreateFromTexture?  Can it be done in realtime (e.g. if I swap out a red leg piece for a blue leg piece, can I refresh the atlas by calling CreateFromTexture quickly)?

unikronsoftware

  • Administrator
  • Hero Member
  • *****
  • Posts: 9709
    • View Profile
The camera doesn't need to be a tk2d camera, you will just need to arrange everything into this atlas, so whatever way you wanna do it is fine. Be sure to only draw this once and not repeatedly

tk2dSpriteCollectionData.CreateFromTexture can be updated at runtime, performance should be fine. You can also do this incrementally - look into the source code to this function to see how you can do modify things on the fly.

One thing to note - if your modify the atlas in such a  way that existign sprites will no longer be valid, you will need to find them and call ForceBuild to have them regenerate the geometry.

aikitect

  • 2D Toolkit
  • Newbie
  • *
  • Posts: 14
    • View Profile
Be sure to only draw this once and not repeatedly.  How do I ensure that I only draw the texture once?

One thing to note - if your modify the atlas in such a  way that existign sprites will no longer be valid, you will need to find them and call ForceBuild to have them regenerate the geometry.  What would I be calling ForceBuild on?
« Last Edit: June 30, 2014, 06:26:06 pm by aikitect »

unikronsoftware

  • Administrator
  • Hero Member
  • *****
  • Posts: 9709
    • View Profile
Be sure to only draw this once and not repeatedly.  How do I ensure that I only draw the texture once?
You will need to either call camera.Render manually or turn off the camera after a frame. You should read up about how all of this stuff works - this approach requires a fair bit of understanding about how unity works. Answers.unity3d.com will help. If you don't want to invest the time to understand all this use Texture2D.PackTextures.

One thing to note - if your modify the atlas in such a  way that existign sprites will no longer be valid, you will need to find them and call ForceBuild to have them regenerate the geometry.  What would I be calling ForceBuild on?
On the tk2dSprite class for all the existing sprites.

aikitect

  • 2D Toolkit
  • Newbie
  • *
  • Posts: 14
    • View Profile
Thanks, what exactly makes Texture2D.PackTextures slow in the first place?

unikronsoftware

  • Administrator
  • Hero Member
  • *****
  • Posts: 9709
    • View Profile
Its done in software, and requires copies of source and destination textures in main memory.

aikitect

  • 2D Toolkit
  • Newbie
  • *
  • Posts: 14
    • View Profile
Hey, so I'm in the middle of implementing RenderTexture-based atlases.  I have the following code that creates a RenderTexture:

Code: [Select]
    private Texture2D CreateAtlasTexture(int textureSize)
    {
    RenderTexture renderTexture = new RenderTexture(textureSize, textureSize, 24);
    Texture2D atlasTexture = new Texture2D(textureSize, textureSize, TextureFormat.ARGB32, false);
   
    RenderCamera.aspect = 1.0f;
    RenderCamera.targetTexture = renderTexture;
    RenderCamera.Render();
   
    RenderTexture.active = renderTexture;
                atlasTexture.ReadPixels(new Rect(0.0f, 0.0f, textureSize, textureSize), 0, 0);
                atlasTexture.Apply();
    RenderTexture.active = null;
    RenderCamera.targetTexture = null;
   
    return atlasTexture;
     }

However, when I run this code, and the camera preview looks like the screenshot below, I get a Texture2D that is distorted.  Can you help identify why this might be happening?  I feel like it is a camera setting (it seems like it is being previewed at 1024x768 and shrunk to 512x512), but I'm not sure how to fix this.  Thanks!


unikronsoftware

  • Administrator
  • Hero Member
  • *****
  • Posts: 9709
    • View Profile
Dont use a tk2dcamera for this. The tk2d camera is meant for screen cameras, its performing screen aspect ratio correction on this which you dont want.

aikitect

  • 2D Toolkit
  • Newbie
  • *
  • Posts: 14
    • View Profile
Great, I got the RenderTexture working.  Thank you so much!  Now the last part is trying to get the right regions and anchors for each of the sprites.  So using my packing algorithm, I get back a list of Rects that contain the bounds of each sprite.  For example, I will get back one Rect myRect that is:

x: 466.0
y: 84.0
width: 42.0
height: 34.0

(This is using the bottom left as the origin.)

I am trying to translate this into the the region and anchor (preferably centered) that is given to tk2dSpriteCollectionData.CreateFromTexture.  I have tried sending the following but they don't work:

Code: [Select]
region = myRect;
anchor = myRect.center;

region = myRect.NormalizeBy(100.0f); // This divides each dimension of the Rect by 100.0f.
anchor = myRect.center / 100.0f;

Also, my CreateFromTexture call is as follows:

Code: [Select]
spriteCollection = tk2dSpriteCollectionData.CreateFromTexture(atlasTexture,
tk2dSpriteCollectionSize.PixelsPerMeter(100.0f), allSprites, spriteRegions, spriteAnchors);
« Last Edit: July 20, 2014, 11:53:13 pm by aikitect »

unikronsoftware

  • Administrator
  • Hero Member
  • *****
  • Posts: 9709
    • View Profile
What do you mean by it dont work?

aikitect

  • 2D Toolkit
  • Newbie
  • *
  • Posts: 14
    • View Profile
I have a Spine SkeletonAnimation that uses this new SpriteCollection to generate a character using the sprites in the SpriteCollection.  When I use a pre-built SpriteCollection that I created via the Editor, the character shows up on the screen.  However, when I create the SpriteCollection via CreateFromTexture, nothing shows up at all on the screen.