Preface

After finishing the latest map-editor re-write, I realized something. I still needed one more additional internal tool. I need a powerful sprite editor that handles the job of importing textures from unused sprite sheets, and allows me to add and modify the sprite meta data that unity uses for its sprite meta files.

There were a lot of similar concepts that I could use from the map editor I had just finished writing, so that’s where I started. Here are some of the things that remained the same:

  • EditorWindow manages the UI elements, and sets up the initialization of the sprite editor.
  • A SpriteEditorManager manages the dependencies between the different classes that actually do the work.
  • A SpriteEditorDataSet manages the shared data.
  • A SpriteEditorConfig manages all the strings, floats, and colors that are abstracted out into the config.

So, thats where I started. About a week of development time went by, and I was getting pretty close to the end. That was 2 days ago. Then… disaster struck!

Obstacle

Disaster came in the form of a realization that the very basic performance that I expect from the editor, couldn’t be achieved. Why, well, it boils down to this: If you display a huge spritesheet as a single texture, in a sprite, then if you make a change to the texture, you have to re-create the sprite. This means, every time we move a sprite from one sheet to the other, we have to re-create both of the sprites for both of those spritesheets. This ends up being about a 8-10 second wait time for every time we want to move a sprite or sprite group.

Is there a solution? Yes. Instead of representing an entire spritesheet as a single sprite, managed by a single sprite renderer, we should just create every sprite as its own object, with its own sprite renderer.

Scope

So rather than just dive into a re-write again, I’m going to put my ‘thoughts on paper’ and really try to define a scope of work for this editor. Lets try to walk through how the program might work.

  1. We click to open the editor window.
  2. We click start to start the editor.
  3. We click ‘load source spritesheet’.
    1. We then see all the sprites loaded, and the camera moves to the default center position.
  4. We click ‘load target spritesheet’.
    1. We then see all the sprites loaded, and the camera moves to the default center position.
  5. We click ‘swap sheet’, to look at the source sheet again.

So that’s pretty straight-forward. We’re loading a bunch of sprites. We’re able to flick back and forth between the two. Now lets get into potential operations. What do we actually want to do with this program, and why do we want to do it?

  • We want to be able to move sprites from the source sheet, to the target sheet.
    • Why: because manually importing sprites using something like photoshop is terribly inefficient.
  • We want to be able to move sprites from one location in the target sheet, to another location.
    • Why: because sometimes its smart to group sprites by their similarities. Like, putting all the trees together. 
  • We want to be able to view and change the following properties for sprites, width, height, pivot, and name.
    • Why: because sometimes sprite names need to be in sequential order, and because some sprites are not just 32×32, and because some sprites require a custom pivot (such as creatures). 

Now lets simplify those three actions into less words, and add sub-actions.

  • Import Sprites (from source)
    • Select Sprites (from source)
    • Paste Sprites (to target)
  • Move Sprites (within target)
  • Modify Sprites (within target)

Now lets first talk about importing sprites.

  • Q: What is the input procedure for selecting sprites
    • A: The user will move their mouse over the sprites. When the user clicks, we check if the mouse position is within the bounds of a sprite. If it is, we add it to our selection. We also add a color tint to the selected sprites. The first sprite selected will be orange, and other sprites will be yellow. The reason the first one is a different color, is because this is our ‘anchor’, and other sprites use relative positioning to it.
  • Q: What is the input procedure for pasting sprites?
    • A: The user will first move over to the target sheet. Then the user will position their mouse  over grid. Gizmos will draw (in the same color scheme), an outline of where the sprites will go.
  • Q: What happens when the user clicks to confirm a paste operation?
    • A: First there is some validation. We need to check to make sure we’re not out of bounds. Then we need to check against other sprites that are there to prevent them from being over-written. Then we need to move the sprites from one list (in memory) to another, and we need to modify their positions as well. This completes the import operation.

Now, lets talk about moving sprites.

  • Q: Is it possible to move multiple sprites at once?
    • A: No. For simplicity there’s no reason for this to occur. Lets limit this to single sprites at a time.
  • Q: What is the input procedure for moving sprites?
    • A: The input procedure is similar to the import operation above. The user clicks on the sprite, it is marked as orange. The user moves the mouse and gizmos will draw a orange box to indicate where the sprite will go.
  • Q: What happens when the user clicks to confirm the move?
    • A: The same validation occurs as before, we check against boundaries, and we check against preexisting sprites. Then the sprite is moved.

Now lets talk about modifying sprites.

  • Q: What is the input procedure for modifying sprites?
    • A: The user will click on a sprite to select it, similar to the above move operation. The editor window will have a region to display meta information for that sprite. This will contain input fields for the width, height, pivot, and name. The user will then modify the information there.

Lastly, lets talk about saving.

  • Q: What does the interface for saving look like?
    • A: There is a save button in the editor window that can be clicked at any time.
  • Q: What happens when the save button is clicked?
    • A: All of the data stored in memory is saved to the disk and data that exists on the disk is overwritten.
  • Q: How do we know where to save the files?
    • A: The path that the file was originally loaded from is saved in the object in memory. We re-use this information to save to the same location at any time.
  • Q: How do we know what meta data to write?
    • A: The non-sprite meta data is also kept in memory, similar to the path information. The meta data about the sprites is stored in the sprites we keep in memory. We just dump all of that to the files.

Conclusions

Now that I’ve gone through the mental exercise of writing this informal ‘scope of work’ document, lets summarize some of the things we’ve discovered along the way.

  • We don’t need two different sprite sheet objects. We can use the larger, more inclusive of the two, and just specify if its a source sheet, or a target sheet.
  • We can store sprite information in the sprite sheet objects.
  • Sprite objects should themselves contain the information that they are selected or not (this works because we can still distinct between the selection of source sprites, and the selection of target sprites).
  • We don’t need to draw error textures to show out of bounds or overlap. Single-validation is all we need.

I feel fairly confident going into this next re-write. Lets get started!

Additional Scope of Work

About 2 days ago, I finished all of the aforementioned work. The following day, I took it easy, and just tried to use the sprite editor in a real-world scenario. I tried to use it to usefully import many sprites from my unused sprite sheets, to my actual sprite sheets. I discovered a number of problems, and that’s why there is going to be additional scope of work. Lets list some things that are wrong or features that are missing.

 

New Features

Sprite Deletion

When looking at the target sheet, it should be possible to delete a sprite. This means it should be possible to delete the sprite with the texture, or to delete the sprite and leave the texture behind. Originally, I was going to use this to turn 4 sprites of size 32×32 into 1 sprite of size 64×64, but an idea just occurred to me as I’m writing this.

Sprite Merge

It would make so much sense to select 4 sprites of size 32×32, and then hit a button that says “Merge’. This then removes 3 of the sprites (leaving the texture), and expands one of them to encompass the other. If we merge sprites that arn’t in a regular shape ,we could solve this by prompting how many units across and how many units high the final sprite will be. So we could do 2×2  for a 64×64, or a 1×5 for an odd, long sprite.

Sprite Copy

Because of the way in which we use sprites in unity, as opposed to how Tibia did it, we need the option to copy sprites to create alternate versions of that sprite.

Multi-Select

We need the option to select multiple sprites. This applies to the source, and the target sheets. The reason for this is because, lets say we’re working on the source, and we want to select 60 sprites. We currently need to click 60 times, instead of click and drag. Now lets say we’re on the target sheet and we want to move some sprites to another location, if we want to move 60 sprites, we need to click 60 times. I imagine that this feature will have the most cascading changes, and will be the hardest to write.

Old Feature Fixes/Changes

Import Auto-Transition

Suppose we want to move some sprites. We go to the source sheet, and then click on our sprites. Go to the target sheet, and then click to place. However, we’re still in import mode, and we can’t just click and drag the sprites as we see fit. So, after a successful import, lets have a toggle that lets us auto-transition into edit mode.

Edit Mode Select

When in edit mode, currently when you have a sprite selected and click on another sprite, nothing happens because we consider it a failed move attempt. However, in the future, we should just select that sprite instead. This makes the process much more intuitive.

Target-To-Target Previews

When we have a sprite in the target sheet selected and we’re wanting to move it, we should get a preview of where the sprites will be moved to.

Source/Target UI Cleanup

The current layout of the buttons is confusing and should be reworked. Rather than having a horizontal layout, we should switch to a two-column vertical layout with the source on the left, and target on the right.

Unity Reload Issue

Currently when we make changes to a sprite sheet, unity will take some time to re-import and process the sprites. This means we can’t use the editor during that time. In addition, the changes we’ve made to sprites before the reload happens, isn’t noticed yet by unity. So its possible to load something that doesn’t exist anymore because unity hasn’t updated yet. This is going to be a tricky one.