-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from jordanbleu/initial
First Commit
- Loading branch information
Showing
2 changed files
with
224 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
--[[ | ||
Aseprite to Spine Exporter Script | ||
Written by Jordan Bleu | ||
https://github.com/jordanbleu/aseprite-to-spine | ||
]] | ||
|
||
-----------------------------------------------[[ Functions ]]----------------------------------------------- | ||
--[[ | ||
Returns a flattened view of | ||
the layers and groups of the sprite. | ||
parent: The sprite or parent layer group | ||
arr: The array to append to | ||
]] | ||
function getLayers(parent, arr) | ||
for i, layer in ipairs(parent.layers) do | ||
if (layer.isGroup) then | ||
arr[#arr + 1] = layer | ||
arr = getLayers(layer, arr) | ||
else | ||
arr[#arr + 1] = layer | ||
end | ||
end | ||
return arr | ||
end | ||
|
||
--[[ | ||
Checks for duplicate layer names, and returns true if any exist (also shows an error to the user) | ||
layers: The flattened view of the sprite layers | ||
]] | ||
function containsDuplicates(layers) | ||
for i, layer in ipairs(layers) do | ||
if (layer.isVisible) then | ||
for j, otherLayer in ipairs(layers) do | ||
-- if we find a duplicate in the list that is not our index | ||
if (j ~= i) and (otherLayer.name == layer.name) and (otherLayer.isVisible) then | ||
app.alert("Found multiple visible layers named '" .. layer.name .. "'. Please use unique layer names or hide one of these layers.") | ||
return true | ||
end | ||
end | ||
end | ||
end | ||
return false | ||
end | ||
|
||
--[[ | ||
Returns an array of each layer's visibility (true / false) | ||
layers: the flattened view of the sprite layers | ||
]] | ||
function captureVisibilityStates(layers) | ||
local visibilities = {} | ||
for i, layer in ipairs(layers) do | ||
visibilities[i] = layer.isVisible | ||
end | ||
return visibilities | ||
end | ||
|
||
--[[ | ||
Hides all layers and groups | ||
layers: The flattened view of the sprite layers | ||
]] | ||
function hideAllLayers(layers) | ||
for i, layer in ipairs(layers) do | ||
if (layer.isGroup) then | ||
layer.isVisible = true | ||
else | ||
layer.isVisible = false | ||
end | ||
end | ||
end | ||
|
||
--[[ | ||
Captures each layer as a separate PNG. Ignores hidden layers. | ||
layers: The flattened view of the sprite layers | ||
sprite: The active sprite | ||
outputDir: the directory the sprite is saved in | ||
visibilityStates: the prior state of each layer's visibility (true / false) | ||
]] | ||
function captureLayers(layers, sprite, outputDir, visibilityStates) | ||
hideAllLayers(layers) | ||
|
||
local separator = app.fs.pathSeparator | ||
|
||
for i, layer in ipairs(layers) do | ||
-- Ignore groups and non-visible layers | ||
if (not layer.isGroup and visibilityStates[i] == true) then | ||
layer.isVisible = true | ||
sprite:saveCopyAs(outputDir .. separator .. "images" .. separator .. layer.name .. ".png") | ||
layer.isVisible = false | ||
end | ||
end | ||
end | ||
|
||
--[[ | ||
Restores layers to their previous visibility state | ||
layers: The flattened view of the sprite layers | ||
visibilityStates: the prior state of each layer's visibility (true / false) | ||
]] | ||
function restoreVisibilities(layers, visibilityStates) | ||
for i, layer in ipairs(layers) do | ||
layer.isVisible = visibilityStates[i] | ||
end | ||
end | ||
|
||
|
||
-----------------------------------------------[[ Main Execution ]]----------------------------------------------- | ||
local activeSprite = app.activeSprite | ||
|
||
if (activeSprite == nil) then | ||
-- If user has no active sprite selected in the UI | ||
app.alert("Please click the sprite you'd like to export") | ||
return | ||
elseif (activeSprite.filename == "") then | ||
-- If the user has created a sprite, but never saved it | ||
app.alert("Please save the current sprite before running this script") | ||
return | ||
end | ||
|
||
local flattenedLayers = getLayers(activeSprite, {}) | ||
|
||
if (containsDuplicates(flattenedLayers)) then | ||
return | ||
end | ||
|
||
-- Get an array containing each layer index and whether it is currently visible | ||
local visibilities = captureVisibilityStates(flattenedLayers) | ||
|
||
-- directory where the sprite is saved | ||
local spritePath = app.fs.filePath(activeSprite.filename) | ||
|
||
-- Saves each sprite layer as a separate .png under the 'images' subdirectory | ||
captureLayers(flattenedLayers, activeSprite, spritePath, visibilities) | ||
|
||
-- Restore the layer's visibilities to how they were before | ||
restoreVisibilities(flattenedLayers, visibilities) | ||
|
||
--[[ | ||
Write out the json file for importing into spine. | ||
(sorry this is so ugly, I didn't want to include a full lua json library) | ||
]] | ||
local spriteFilename = app.fs.fileName(activeSprite.filename) | ||
local jsonFileName = spritePath .. "/" .. spriteFilename .. ".json" | ||
json = io.open(jsonFileName, "w") | ||
|
||
json:write('{') | ||
|
||
-- skeleton | ||
json:write([[ "skeleton": { "images": "images/" }, ]]) | ||
|
||
-- bones | ||
json:write([[ "bones": [ { "name": "root" } ], ]]) | ||
|
||
-- build arrays of json properties for skins and slots | ||
-- we only include layers, not groups | ||
local slotsJson = {} | ||
local skinsJson = {} | ||
local index = 1 | ||
|
||
for i, layer in ipairs(flattenedLayers) do | ||
|
||
if not layer.isGroup then | ||
local name = layer.name | ||
slotsJson[index] = string.format([[ { "name": "%s", "bone": "%s", "attachment": "%s" } ]], name, "root", name) | ||
skinsJson[index] = string.format([[ "%s": { "%s": { "x": 0, "y": 0, "width": 1, "height": 1 } } ]], name, name) | ||
index = index + 1 | ||
end | ||
|
||
end | ||
|
||
-- slots | ||
json:write('"slots": [') | ||
json:write(table.concat(slotsJson, ",")) | ||
json:write("],") | ||
|
||
-- skins | ||
json:write('"skins": {') | ||
json:write('"default": {') | ||
json:write(table.concat(skinsJson, ",")) | ||
json:write('}') | ||
json:write('}') | ||
|
||
-- close the json | ||
json:write("}") | ||
|
||
json:close() | ||
|
||
app.alert("Export completed! Use file '" .. jsonFileName .. "' for importing into Spine.") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,35 @@ | ||
# aseprite-to-spine | ||
This script will import pixel art created in aseprite into Esoteric Spine software for creating skeletal animation. | ||
# aseprite-to-spine | ||
|
||
## Lua Script for importing Aseprite projects into Spine | ||
|
||
## v1.0 | ||
|
||
### Installation | ||
|
||
1. Open Aseprite | ||
2. Go to **File > Scripts > Open Scripts Folder** | ||
3. Drag the included ```Prepare-For-Spine.lua``` file to this directory | ||
4. In Aseprite, click **File > Scripts > Rescan Scripts Folder** | ||
|
||
After following these steps, the "Prepare-For-Spine" script should show up in the list. | ||
|
||
### Usage | ||
|
||
1. Create your sprite just like you would in Photoshop. Each "bone" should be on its own layer. | ||
2. Keep in mind that layer "groups" are ignored when exporting. | ||
3. When you're ready to bring your art into Spine, save your project and run the ```Prepare-For-Spine``` script. This will create a .json file as well as an "images" folder in the directory your aseprite project is saved in. | ||
4. If you get a dialogue requesting permissions for the script, click "give full trust" (it's just requesting permission for the export script to save files). | ||
5. Open Spine and create a new project | ||
6. Click the Spine Logo in the top left to open the file menu, and click **Import Data**. | ||
7. Set up your Skeleton and start creating animations! | ||
|
||
### Known Issues | ||
* Hiding a group of layers will not exclude it from the export. Each layer needs to be shown or hidden individually (group visibility is ignored) | ||
* The Spine will be imported with a name of "{filename}.aseprite". Eventually i'll trim off the .asperite part when I get a sec. | ||
* Not as many options as the Photshop script. Maybe I'll add these in the future but honestly i've never used any of them so we will see. | ||
|
||
### Version History | ||
|
||
#### v1.0 | ||
|
||
Initial Release |