Hello Guest

Author Topic: Automatically transfer a sprite between atlases?  (Read 9275 times)

wiley.a

  • Newbie
  • *
  • Posts: 14
    • View Profile
Automatically transfer a sprite between atlases?
« on: October 08, 2012, 02:31:32 pm »
Hello

Is it possible to script the transfer of a sprite between atlases and then regenerate both?

Thanks

unikronsoftware

  • Administrator
  • Hero Member
  • *****
  • Posts: 9709
    • View Profile
Re: Automatically transfer a sprite between atlases?
« Reply #1 on: October 08, 2012, 07:34:08 pm »
You could if you wanted to and it would be fairly straightforward, but the references to existing sprites would be the tricky bit. You'll have to open up each scene (you could script all of this) find sprites and change them, and save the scene after that.

wiley.a

  • Newbie
  • *
  • Posts: 14
    • View Profile
Re: Automatically transfer a sprite between atlases?
« Reply #2 on: October 09, 2012, 05:15:18 pm »
I have it mostly working but "fairly straightfoward" wouldn't be how I describe it. Maybe I've missed something obvious. Anyway, the trouble I am having now is that when I am searching through all of the scene files to find sprites of the type that I have just moved (moved to the destination atlas without removing the source to avoid the collection and sprite names becoming invalid), I can correctly identify the ones I want to update but as I update the first one I come across, it causes other sprites in the scene to get changed. I've managed to track the problem down to it being that after I use SwitchCollectionAndSprite() the spriteIDs of the others in the scene (on the same collection) get reordered.

Is there a way to tell it to just add the new sprite with the ID of the sprite count in that collection? Or maybe you have written something to handle this already and I haven't spotted it?

Thanks

unikronsoftware

  • Administrator
  • Hero Member
  • *****
  • Posts: 9709
    • View Profile
Re: Automatically transfer a sprite between atlases?
« Reply #3 on: October 10, 2012, 12:26:51 am »
One cheeky (and really really quick) way to do this without writing complex code is something like this:

1. Write an editor script to print out the GUID of the selected object. Something like this will suffice.
Code: [Select]
[MenuItem("2D Toolkit/Debug/PrintGUID", false, 99999)]
static void PrintGUID()
{
if (Selection.activeObject == null)
return;
string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(Selection.activeObject));
Debug.Log(guid);
}

2. Select the sprite collection data (make sure its the data object and not the editor object) object of the source, and print the GUID (save it somewhere). Also note the sprite Id you'll be switching from.

3. Do the same with the sprite collection you want to switch to, and save that somewhere. Likewise, not the sprite Id you'll be switching to.

4. In tk2dSprite.cs, Awake() insert these lines (or something like this anyway)
Code: [Select]
string sourceGUID = UnityEditor.AssetDatabase.AssetPathToGUID(UnityEditor.AssetDatabase.GetAssetPath(collection));
if (sourceGUID == "source_guid_you_saved" && _spriteId == SOURCE_SPRITE_ID_YOU_SAVED)
{
collection = UnityEditor.AssetDatabase.LoadAssetAtPath(UnityEditor.AssetDatabase.GUIDToAssetPath("dest_guid_you_saved"), typeof(tk2dSpriteCollectionData)) as tk2dSpriteCollectionData;
if (collection == null) Debug.LogError("Don't save this scene.");
_spriteId = DEST_SPRITE_ID_YOU_SAVED;
}

Now load and save all your scenes. Don't forget to remove those lines after you're done.


p.s. to delete a sprite in a sprite collection, get the tk2dSpriteCollection object, find your sprite in texturerefs or textureParams.texture. Once you've found it, set textureRefs = null; textureParams = new tk2dSpriteCollectionDefinition(); After that call tk2dSpriteCollectionBuilder.Rebuild(spritecollection); Same logic to copy it, just insert a new one in both those entries, and copy values from the other before you wipe it. When deleting, never erase an entry, just null it out as described above to retain unique ids.


wiley.a

  • Newbie
  • *
  • Posts: 14
    • View Profile
Re: Automatically transfer a sprite between atlases?
« Reply #4 on: October 11, 2012, 11:57:04 am »
I am looking for a more permanent way of doing this and have nearly got it, just got one last (quite major) problem. So at the moment I have it so that you select two atlases (one source, one dest) and also the source sprite in the source atlas you wish to transfer. When I run it, it will correctly transfer the sprite between atlases and regenerates them (the atlas textures are correctly updated) and all occurences of the source sprite in the open scene are updated to use the new sprite in the dest atlas. However, when I try to move the moved sprite onto another atlas or even just close and reopen the scene the changed gameobjects return to the original atlas but just selects the first sprite in that atlas. Any ideas on this as I am sure I'm nearly there and its just frustrating me :(

Code: [Select]
public void Transfer()
    {
// Load the two collections
tk2dSpriteCollection sourceCol = AssetDatabase.LoadAssetAtPath(
"Assets/Main/Sprites/" + m_atlasNames[m_sourceAtlasIndex],
typeof(tk2dSpriteCollection)) as tk2dSpriteCollection;

tk2dSpriteCollection destCol = AssetDatabase.LoadAssetAtPath(
"Assets/Main/Sprites/" + m_atlasNames[m_destAtlasIndex],
typeof(tk2dSpriteCollection)) as tk2dSpriteCollection;

// ===============================

// Make a copy of the dest texture refs with a extra slot for the new one
Texture2D[] temp = new Texture2D[destCol.textureRefs.Length + 1];

for (int i = 0; i < destCol.textureRefs.Length; ++i)
temp[i] = destCol.textureRefs[i];

// The new sprite we are adding to the dest col
temp[temp.Length - 1] = sourceCol.textureRefs[m_sourceSpriteIndex];

// Set the copy
destCol.textureRefs = temp;

// Ensure the dest col is updated
tk2dSpriteCollectionBuilder.ResetCurrentBuild();
tk2dSpriteCollectionBuilder.Rebuild(destCol);

// =============================

// Make a copy of the texture params of the sprite that we are moving
tk2dSpriteCollectionDefinition defToCopy = sourceCol.textureParams[m_sourceSpriteIndex];

// Find the moved sprite in the dest col and then copy across the source params
for (int i = 0; i < destCol.textureParams.Length; ++i)
{
if (destCol.textureParams[i] == null)
continue;

if (destCol.textureParams[i].name == defToCopy.name)
destCol.textureParams[i].CopyFrom(defToCopy);
}

// Update the dest col
tk2dSpriteCollectionBuilder.ResetCurrentBuild();
tk2dSpriteCollectionBuilder.Rebuild(destCol);

// Now we need to go through the level and update any uses of the source sprite
FixupReferences();

// =============================

// Now that the sprite has been moved we need to null the reference in the source col
for (int i = 0; i < sourceCol.textureRefs.Length; ++i)
if (i == m_sourceSpriteIndex)
sourceCol.textureRefs[i] = null;

// Update the source col
tk2dSpriteCollectionBuilder.ResetCurrentBuild();
tk2dSpriteCollectionBuilder.Rebuild(sourceCol);

// Refresh sprite list
m_sourceSpriteIndex = 0;
PopulateSourceSprites();
    }

public void FixupReferences()
{
// Get the collection name we want to replace (used to search with)
string collectionNameToFind = "";

string[] split = m_atlasNames[m_sourceAtlasIndex].Split(new char[] { '/' });
collectionNameToFind = split[split.Length - 1].Split(new char[] { '.' })[0];

// Load the dest col
tk2dSpriteCollection destCol = AssetDatabase.LoadAssetAtPath(
"Assets/Main/Sprites/" + m_atlasNames[m_destAtlasIndex],
typeof(tk2dSpriteCollection)) as tk2dSpriteCollection;

// Load the scene to alter
if (EditorApplication.OpenScene("SCENE_NAME"))
{
// Read the contents of the hierarchy
object[] hierarchy = GameObject.FindSceneObjectsOfType(typeof(GameObject));

foreach (object o in hierarchy)
{
GameObject go = (GameObject)o;

// Try and get the sprite component
tk2dSprite sprite = go.GetComponent<tk2dSprite>();

// No sprite component so return
if (sprite == null)
continue;

// The sprites collection name isn't the one we want to alter so return
if (sprite.collection.spriteCollectionName != collectionNameToFind)
continue;

// If the sprite names match, switch the collection and the sprite
if (sprite.collection.spriteDefinitions[sprite.spriteId].name == m_sourceSpriteNames[m_sourceSpriteIndex])
sprite.SwitchCollectionAndSprite(destCol.spriteCollection, destCol.spriteCollection.spriteDefinitions.Length - 1);
}

// Save the scene
EditorApplication.SaveScene();
EditorApplication.SaveAssets();
}
else
{
Debug.LogError("Failed to find " + "World1Level1");
}
}

unikronsoftware

  • Administrator
  • Hero Member
  • *****
  • Posts: 9709
    • View Profile
Re: Automatically transfer a sprite between atlases?
« Reply #5 on: October 11, 2012, 12:12:25 pm »
Doesn't that imply the scene isn't correctly updated? Easy way to check is to switch to text serialization, open the scene and make sure its actually saved the changed guid reference in there.

Also try calling EditorUtility.SetDirty(sprite); in FixupReferences() - you're sure its actually finding everything and calling SwitchColectionAndSprite right?

I'm curious now - is there any reason in particular you're doing this?

wiley.a

  • Newbie
  • *
  • Posts: 14
    • View Profile
Re: Automatically transfer a sprite between atlases?
« Reply #6 on: October 11, 2012, 12:18:56 pm »
In a way yes the scene isn't correctly updated which is where the problem is. Once the code I have posted has finished running the gameobjects that were using the old sprite correctly have their sprite component's collection name and sprite names set to use the one in the new atlas. However, upon attempting to move the same sprite to yet another atlas or even just closing and reopening the level, they aren't as they were changed anymore.

I shall try and other thing you suggested in a moment. And yes I am sure it is correctly finding what it needs and callign SwitchCollectionAndSprite() on them.

Currently we have 40+ atlases which have got pretty unorganised. Sprites appear in multiple atlases as they different themes in our game when they should be in some global atlas and generally we have a lot of wasted sprite as they haven't been organised really. I am trying to provide a nice, clean and easy way for us to be able to sort the atlases out without having to rebuild any of the levels because of lost collection/sprite references.

unikronsoftware

  • Administrator
  • Hero Member
  • *****
  • Posts: 9709
    • View Profile
Re: Automatically transfer a sprite between atlases?
« Reply #7 on: October 11, 2012, 12:30:22 pm »
Thanks for explaining - The reason I was asking is it might be worth supporting an official path for this if it isn't just a one off thing.

wiley.a

  • Newbie
  • *
  • Posts: 14
    • View Profile
Re: Automatically transfer a sprite between atlases?
« Reply #8 on: October 11, 2012, 12:33:11 pm »
I think it would be a very nice feature to have and I'm sure you can do a much better job than I am.

wiley.a

  • Newbie
  • *
  • Posts: 14
    • View Profile
Re: Automatically transfer a sprite between atlases?
« Reply #9 on: October 11, 2012, 12:37:15 pm »
As far as I can tell placing
Code: [Select]
EditorUtility.SetDirty(sprite); straight after the call to SwitchCollectionAndSprite() seems to have worked!  ;D

wiley.a

  • Newbie
  • *
  • Posts: 14
    • View Profile
Re: Automatically transfer a sprite between atlases?
« Reply #10 on: October 11, 2012, 02:49:06 pm »
Is there a way to test if a sprite or number of sprites will fit into a atlas before doing anything to either the source or destination atlases?

unikronsoftware

  • Administrator
  • Hero Member
  • *****
  • Posts: 9709
    • View Profile
Re: Automatically transfer a sprite between atlases?
« Reply #11 on: October 11, 2012, 03:48:05 pm »
No there isn't I'm afraid, only thing to do is to add to dest, try building and if it fails, remove it.

nindim

  • 2D Toolkit
  • Newbie
  • *
  • Posts: 37
    • View Profile
Re: Automatically transfer a sprite between atlases?
« Reply #12 on: September 30, 2015, 04:35:40 am »
I know this is an old post, but I need to do the exact same thing as wiley.a...

Uikron, you mentioned perhaps supporting this officially, did that ever gain any traction? Seems like it would be a really useful feature!

unikronsoftware

  • Administrator
  • Hero Member
  • *****
  • Posts: 9709
    • View Profile
Re: Automatically transfer a sprite between atlases?
« Reply #13 on: October 04, 2015, 12:49:30 pm »
No it didn't and most of my available time for the project is spent on support right now, I don't have the resources available to build this.