• Editor
  • I tried to export with command line but not file is created.

  • Uređeno
Related Discussions
...

My command line is

Spine -i Kids.spine -e "toBinary.export.json" -o "./export_out"

Here is the console log:

Spine Launcher 3.8.82
Esoteric Software LLC (C) 2013-2020 | http://esotericsoftware.com
Windows 10 Enterprise x86 10.0
Spine 3.8.97 Professional
Licensed to: XXXXXX
Intel, Intel(R) UHD Graphics 630, 4.6.0 - Build 26.20.100.7263
Binary export: Kids
Complete.

Did I do something wrong?


Btw, why is export from command line so slow?
When exporting from UI, exporting without texture packing is almost immediately done.

The order of the parameters is important. Some parameters are used by later parameters, like -i and -o, while others are "terminal" and perform an action, like -e. When -e is encountered the export is performed, so it uses the output path in the .export.json file rather than the path specified with -o. We'll see about making it an error to specify non-terminal parameters at the end of the parameter list.

When Spine needs to render images it uses OpenGL (and similar technology) and has to setup nearly everything needed to run the editor as normal. When exports don't require OpenGL, they are run "headless" and exporting is a lot faster. Since you are packing an atlas, unfortunately the export takes long. It will be faster in v4 though.

Nice. I changed the order of parameters and now it works as expected.

making it an error to specify non-terminal parameters at the end of the parameter list.

You may want to consider reading all the parameters into a dictionary first and parse accordingly to eliminate the ordering restriction.

Can you add a new parameter, say -skin, to override the skin export checkbox values and have command line being able to export certain skin to certain folder? That would make things much easier because changing output path and the skin export check box values for each export is not that convenient in the editor. Saving them to export script file is more reliable.

Spine -i "Kids.spine" -o "./HomeTownKids" -skin "HomeTown_Boy, HomeTown_Girl"  -e "toBinary.export.json" 

Thanks
Looking forward to 4.0.

Nick wrote

You may want to consider reading all the parameters into a dictionary first and parse accordingly to eliminate the ordering restriction.

Order is important by design. You can write export scripts like this:
spine-runtimes/export.sh at 3.8

Nick wrote

Can you add a new parameter, say -skin, to override the skin export checkbox values and have command line being able to export certain skin to certain folder?

The could be useful. Maybe we could make it more general purpose, allowing any values in the settings JSON to be overwritten. Eg something like:

spine -i project.spine -o dir 

---

set skin=something 

---

set packAtlas/rotation=false -e settings.export.json

Order is important by design. You can write export scripts like this:
spine-runtimes/export.sh at 3.8

Ah I see. The arguments are read as a stream of command to have the app exporting multiple projects consecutively.
This is one way to go. Another approach is to let each command run its own spine.exe instance in parallel (may be cap to 4-8 instance to be safe) to make good use of the available system resources. We generally have lost of CPU core + VRAM on today's PC. That could make batch export much faster. Just an idea. Not that important as currently implementation will work too.

Nate wrote

The could be useful. Maybe we could make it more general purpose

Agree. That is more flexible. Just that don't forget to add the "skin" property when implementing this one as 'skin' prop is not exist in the json yet.

Thanks

Ah, I thought you meant to choose a skin for image of video export. You mean instead to export JSON or binary data, but only for a single skin? Exporting a partial skeleton is not a workflow we recommend. Can you please explain your needs for this?

I have characters that share the same skeleton and animations but in different skins.

The kids project i uploaded is one example. Those kids including boys and girls from different locations. In game, they will never be loaded at the same time. Thus I want to make things simple and export them to different folders instead of packing them into one big atlas set.

Example:
<Unity Asset Folder>/units/kingdom/kids
<Unity Asset Folder>/units/hometown/kids
<Unity Asset Folder>/units/harbor/kids

Note that being able to export them separately is also fundamental to create patch file for game update. I cannot ask player to download already downloaded characters again and again just because they shared the same spine project and I just updated very little thing with the project. Say, I fixed some issues with 1 character from a 5 character spine project. I would like to deliver a patch file with only the affected character to the game client.

I found that I could export it correctly by checking / unchecking the 'export' checkbox of the skin in the Spine editor.
However, this also involve setting the output path for each skin export. This not only require quit some effort but is also error prune when compared to doing it in a predefined script file.
That's why I want the script file being able to choose which skins to export. Current command line can already override the output folder. Just missing the skin override to have the magic happens.

For a project has many skins for different characters, if some shared modification happens, all the affected exports need to be updated. Doing the export in command line seems to be the best way to do. No need to change any config. Just run corresponding script file.

Your kids project binary export is 92KB and 56.7KB zipped. Exporting the same but just one skin is 13-33KB zipped. That is not much savings per skeleton and actually makes the total amount of data larger, as some data is duplicated in each skeleton. 56KB is already very small, eg it is common to see multiple images on a website that size or larger. Other projects may have larger exports, but are still likely to be small enough to not be worth the extra trouble of exporting the skeleton for each skin.

The skeleton data is very small and I haven't yet seen a use case where it makes sense to do a partial export of the skeleton.

Loading that much data at runtime is also unlikely to make a difference. Note you don't need to load the images for the unused skins.

Your kids project binary export is 92KB and 56.7KB zipped. Exporting the same but just one skin is 13-33KB zipped.

Can't just consider the skeleton export file size without the texture...Content updates could be including both binary data and texture. e.g. adding new characters.

Also its not just about the download file size but also the file placement design. In my game, almost each character, except those mix and match player heroes, are saved under its own folder along with their other configuration files. I just found this much easier to work when they are grouped properly.

<AssetFolder>/Units/Kingdom/A
<AssetFolder>/Units/Kingdom/B
<AssetFolder>/Units/Hometown/C
<AssetFolder>/Units/Hometown/D
<AssetFolder>/Units/Harbor/E
<AssetFolder>/Units/Harbor/F

vs

<AssetFolder>/Units/ABCDEF

As I mentioned before. I am already doing it this way and found it working very well. Just that I want an extra command (-skin) to further improve the workflow making it possible to export fully outside the spine editor. Please consider to take one step further to support it. It would be very appreciated if such command can be added.

Thanks.

Nick wrote

Can't just consider the skeleton export file size without the texture...Content updates could be including both binary data and texture.

Understood, but the skeleton data file is separate from the images. The images are generally much larger and it can make sense to break them up per skin, or however else is needed. That can be done while keeping a single skeleton data file, which I still think is the simplest and easiest solution.

Nick wrote

In my game, almost each character, except those mix and match player heroes, are saved under its own folder along with their other configuration files. I just found this much easier to work when they are grouped properly.

It makes sense to organized the images this way. Are there reasons using a single skeleton data file would be problematic with this organization?

I don't mean to be argumentative, I just want to fully understand the use case. Interrogation like this is helpful not just for considering this feature, but when thinking about the whole application.

Nick wrote

It would be very appreciated if such command can be added.

By adding such a feature, it will encourage the export of partial skeletons. For us to do that, it needs to be a workflow we want to encourage, or at the very least we think makes sense in some cases even if it is generally suboptimal in most.

For example, since the beginning of Spine we've gotten requests to export a skeleton to multiple files, for example each animation in a separate file. Usually the rationale is to be able to update a skeleton piecemeal. This is a Bad Idea for a number of reasons: it's a prematurely optimization since the entire skeleton data is small, but mostly that it will be difficult to manage (ie a huge mess) because the animations have dependencies on the skeleton data. We declined to add such an export and Spine's usage benefits from the simplicity.

Your exporting each skin as a separate skeleton is of course a different solution than that, it was just an example where sometimes it's better for us not to enable a workflow. If you are adamant about your solution, it may be easiest to write a tool that takes an export containing all the skins and saves a file for each skin that contains the entire skeleton but only that skin. Writing a tool for JSON is easier than binary, so you could process the JSON, then use the Spine CLI to convert the JSON files to binary. Here's some Java that manipulates skeleton JSON, it could be used as a base for such a tool.

Hold on, I am a little confused here. Need to confirm on something first.

It makes sense to organized the images this way. Are there reasons using a single skeleton data file would be problematic with this organization?

Before I try to export each skins of a spine project to its own skeleton and texture package using the editor, I actually tried to export them with two script files.

One script for exporting selected skins texture to their corresponding unit folders.
Another script for exporting the same skeleton binary file which include all the skins also to the unit folders.

The Output is something like:

Unit_A\Texture_A
Unit_A\SAME_SKELETON_FILE

Unit_B\Texture_B
Unit_B\SAME_SKELETON_FILE
...

However, in this way, the runtime will give me error because the SAME_SKELETON_FILE always failed to find some sprites due to the texture file only contain sprites from selected skin. That's why I concluded that the skeleton file need to be precise to only export with skins used for it to load correctly. Thus I turned to toggling the "export" check box of the skin during Spine editor export. Finally I am here requesting to be able to do the same from command line.

So my first question is, it is the expected behavior that using the export all skeleton binary with a partially exported skin texture would cause error in runtime?

By adding such a feature, it will encourage the export of partial skeletons.

What does "partial skeleton" means? A skeleton file only use some of the skins from the project? I don't mind the skeleton to be a full skeleton as long as it can load the only skins I exported with it correctly. All I want is to have files of a unit self contained under it own folder for easy management.

About the decision whether to have an extra -skin command, my comment is, if such configuration can be done from the UI, making it to be possible from command line would not do more harm (but it help me in situation like this). If those skin export check boxes are evil, they should not be exist in the first place but they are doing well in my opinion.

The last thing I want to understand is the work to enable the -skin command. I expected it to be as simple as reading the skin names from command line and flipping a few booleans for the skins before export. Just like how it does from UI. Am I getting it wrong? On the other hand, what you mentioned about writing a tool to parse and extract the json and doing binary conversion, that sounds like a lot of work... 🙁

Nick wrote

the runtime will give me error because the SAME_SKELETON_FILE always failed to find some sprites due to the texture file only contain sprites from selected skin. That's why I concluded that the skeleton file need to be precise to only export with skins used for it to load correctly.

See here:
Loading Skeleton Data - Spine Runtimes Guide: JSON and binary data

SkeletonJson/SkeletonBinary load the skeleton data. When an attachment is encountered, an AttachmentLoader is used to create and partially configure the attachment. It is most common to use AtlasAttachmentLoader, which uses the attachment's path to find a texture region in a texture atlas. If it can't find a region, it throws an error, which is likely what you encountered.

Using AtlasAttachmentLoader is not mandatory. You can provide your own AttachmentLoader when the SkeletonJson/SkeletonBinary is created. This allows you to:

1) Customize how attachments are created. For example, you could subclass an attachment class to store additional data or modify its behavior.

2) Do any preparation necessary to render the attachment later. For example, AtlasAttachmentLoader sets the RegionAttachment rendererObject and MeshAttachment rendererObject to the region from the texture atlas (note it also gives the region to the attachment to set the UVs), then rendering code specific to the game toolkit in use gets the renderer object and knows how to draw it.

Your AttachmentLoader can customize this. For example, you don't have to use a texture atlas, you could prepare SVG or do whatever else you need. Note that you may need to also customize the skeleton rendering to match.

Finally getting around to the point of all this fantastic storytelling, your AttachmentLoader doesn't have to set the renderer object at all. Note that the attachment's skin is passed to the AttachmentLoader. You could copy/paste AtlasAttachmentLoader and modify it to skip looking up atlas regions except for a particular skin. Of course you probably won't be able to render attachments with no renderer object, but that can be OK if you know you don't want to render those anyway, or you could resolve the renderer object later.

Some projects have thousands of attachments and they use an AttachmentLoader that doesn't load any images. Later, they configure all their skeletons with the attachments based on the game state, then they load the images needed only for those attachments (you may want to also consider attachments set by animations). Some projects even pack an atlas at runtime containing only the necessary images (spine-unity has utility functions to do this).

You might notice the AttachmentLoader methods say you can return null to skip loading the attachment completely. This could save you a little bit of memory and load faster, since it won't bother creating and populating attachment objects for attachments you know you won't render. Be wary of doing this for meshes though, if null is returned for a source mesh, all linked meshes for that source mesh should also return null. Since you only get the skin, name, and path of the mesh, there's not actually enough information to know if it's a source mesh. We should improve this.

Nick wrote

What does "partial skeleton" means?

I mean that the data export is a configuration of the skeleton that differs from the project file. Your use case at least has the whole skeleton and the difference is just which skins are included, so it's not as bad as, for example, exporting animations to separate files.

Nick wrote

If those skin export check boxes are evil, they should not be exist in the first place

It's a pretty different scenario to see a skin in the editor and not want it exported for reasons versus exporting the same skeleton many times with different skins.

Nick wrote

what you mentioned about writing a tool to parse and extract the json and doing binary conversion, that sounds like a lot of work

Neither are terribly hard really. The JsonRollback tool almost does what is needed already. It's more about whether setting the skin export flag from the CLI is the right thing to add, as well as us having thousands of not terribly hard things on top of the many hard things we are working on.

Thank you for your detailed response.

Sorry to let you know that, as an indie game developer doing coding and artworks, I am already working 10+ hrs a day 7x24 and I don't really have much time to dig into these custom loader stuff. I think I will stick to custom exporting from the editor UI right now although it is not as convenient as command line, it is simple enough for me. I may have time to explore what you mentioned about the JsonRollback in the future but now is just not the right time.

I certainly understand not having enough time in the day! A custom AttachmentLoader is normal Spine Runtimes usage though. Maybe it sounded more complex than it is, as I wanted to give an understanding of the whole thing. The simplest thing you can do is copy/paste AtlasAttachmentLoader and simply not throw an error if the region is not found in the atlas. That way you can load your skeleton data with an atlas that only has images for one skin. I would recommend this rather than skin export checkboxes or JSON processing.

You mean I just modify the runtime code directly and change

if (region == null) 
    throw new ArgumentException(string.Format("Region not found in atlas: {0} (region attachment: {1})", 
path, name));

to

if (region == null) 
    return null;

in the NewRegionAttachment()? because I don't see I could pass a new AtlasAttachmentLoader to the runtime. It is making its instance internally.

If it is that simple, I will definitely do it this way.

8 mjeseci kasnije

Modifying AtlasAttachmentLoader would work yeah. Or you can pass the attachment loader when you create the SkeletonJson or SkeletonBinary. There is a constructor that takes an AttachmentLoader.


Nick wrote

I don't see I could pass a new AtlasAttachmentLoader to the runtime. It is making its instance internally.

Coming back after some time to make this part clear. If you create a SkeletonJson or SkeletonBinary with the constructor taking a TextureAtlas, then it uses AtlasAttachmentLoader:

var textureAtlas = ...
var loader = new SkeletonJson(textureAtlas); // Uses AtlasAttachmentLoader.

However, you can provide your own AttachmentLoader to the constructor. Eg:

var loader = new SkeletonJson(new MyCustomAttachmentLoader());

That way your AttachmentLoader implementation is in your codebase and you don't need to modify the runtimes. You can pass your AttachmentLoader any number of atlases or whatever is necessary.


Also, I've reading through this thread again. I think the best solution is to pack each atlas and use your own AttachmentLoader. For example, if your images are like this:

images/skin1/head/red.png
images/skin2/head/green.png

Then you might want to run the packer on each skin folder. That would give you multiple atlases and names in each atlas relative to the packed folder, eg head/red.png or head/green.png.

Next, the AtlasAttachmentLoader has this code to find a mesh's atlas region (varies a little per runtime):

public MeshAttachment newMeshAttachment (Skin skin, String name, String path) {
   AtlasRegion region = atlas.findRegion(path);
   if (region == null) throw new RuntimeException("Region not found in atlas: " + path + " (mesh attachment: " + name + ")");
   MeshAttachment attachment = new MeshAttachment(name);
   attachment.setRegion(region);
   return attachment;
}

That won't find any regions because the name parameter will be the name in Spine, which is relative to the images folder, eg skin1/head/red.png while our atlas has the name head/red.png. However, the skin is passed in, so we could easily fix it by changing this line (in a copy of AtlasAttachmentLoader):

AtlasRegion region = atlas.findRegion(skin.getName() + "/" + path);

Next, if needed we can customize how to handle a region not being found. We could throw an error, as the code above does, or we could do something like, like look in additional atlases.

Or, we could return null, which means the skeleton will not have the mesh attachment at all. Note that if the mesh is a source mesh and you need to use one of its linked meshes, it will cause an error to return null.

Or, we could just return the attachment without setting its region. That is the safest, as you can still use its linked meshes, but it means trying to render the regionless attachment will fail. That is probably fine, since if you don't have a region for the attachment, it's probably because you won't be rendering it.

Looks like we are not in sync here and is focusing on different aspects regarding this topic. Let me explain more.

Sometime, people don't want the best (performance? code safe?) solution to the problem. From an end user perspective, we usually prefer the simplest way with minimal effort to solve problems, especially comparing a solution that could just work out of the box with just typing an extra command argument, with another solution that involved extra coding, extra packing commands and strict texture files placement (esp when some sprites are shared among skins), which increase the complexity of the whole workflow.

Don't get me wrong. I do appreciate you to explain and provide detail about how things works. I can see you spend a lot of effort on explaining things and I can feel your excitement to lead people to implement the best way to handle their problems. It just that this solution you suggested here is an overkill and over complicated to a regular user's need. Would you please reconsider and help me keep things simple on my side?

I don't see customizing AttachmentLoader as being a complex solution. It requires changing only a few lines of code and is the way we have designed for the runtimes to be customized for exactly situations like yours. We provide a default solution (for using a single atlas) and customizing AttachmentLoader is the path to take if you want something else. I'm all for making things simpler for you, but there are many ways users might want to customize how attachments and atlas regions are resolved beyond using a single atlas.

Let's explore what it would look like to provide you with something out of the box that solves your problem without customizing AttachmentLoader yourself. I think you'd pack an atlas per skin, as I described above, then you'd use an AtlasSkinAttachmentLoader which 1) prefixes skin + "/" to the name and 2) doesn't set regions on attachments when no region is found. To be clear, your code would look like:

var loader = new SkeletonJson(new AtlasSkinAttachmentLoader(atlas));

We could do this, but I'm not sure how many people could make use of that exact behavior. Also, if you need anything even slightly different, you'd need to customize AttachmentLoader yourself anyway.

Note the above solution does not require a -skin CLI parameter. I am not convinced it is a good idea to export the JSON or binary skeleton data multiple times, with each export only containing a subset of skins. The duplication isn't great and the solution only helps a specific use case. Instead of reducing the data in the skeleton data so AtlasAttachmentLoader can be used, I think it is better to customize AttachmentLoader.

** warning ** I am not a native English speaker. Sometimes people may find my way of expressing my thought a little rude. I want you to know that it is not what I want to achieve in case you get bad feeling reading the post.

I'm all for making things simpler for you, but there are many ways users might want to customize how attachments and atlas regions are resolved beyond using a single atlas.

As I said, we are not in sync. I am talking about keeping things simple while you are talking about functionality and making things flexible from a programmer stand point.

Let's explore what it would look like to provide you with something out of the box...

When I mention "out of the box", I really mean user don't need to do extra works like coding and restricted file placement, etc. I don't expect you to still suggest going the AttachmentLoader route after I explained my concern. My focus is never be how AttachmentLoader can be improved to suit every scenarios but to have the current already working solution, using Editor check box, to also work in command line.

I don't see customizing AttachmentLoader as being a complex solution.

It may not be complex solution solution in your eyes but it is a troublesome solution. As a end user, we really don't expect to do so much for this considering doing the same in UI is just clicking a few checkboxes.

Note the above solution does not require a -skin CLI parameter.

That is the problem! It is not a solution that could be done in a few seconds by just typing an extra -skin "SKIN_NAME". Instead, it require programmer to do extra coding on top of a 3rd party library. It required artist to carefully place their images in the right folders and if there are misplaced image, maintenance work is needed. The texture packing script is more complex because there are shared images that cannot be placed inside a certain skin folder.

I am not convinced it is a good idea to export the JSON or binary skeleton data multiple times, with each export only containing a subset of skins. The duplication isn't great and the solution only helps a specific use case. Instead of reducing the data in the skeleton data so AtlasAttachmentLoader can be used, I think it is better to customize AttachmentLoader.

However, in reality, I am not the only one who try to share single skeleton for different characters and it is a very common, natural way to save time from doing duplicated things this way. Imo, this is not a specific use case but a relatively common use case. It just that people may just do the selective export from the UI and I my suggestion here is just to make it also work from command line. (I like to call it selective export : p )

We all know it is not good to have things duplicated but for productivity, it is a trade off, a good deal and is not a problem at all. Its not worth to sacrifice simplicity to save some bytes of memory for all these trouble. I expect you understand the idea because you also talk about the same idea ( optimization vs effort vs worthness) on other topics in the forum.

Anyway. I gave up. In fact, I didn't continue this persuasion 7 months ago because I had a feeling there is a high chance I am not able to convince you. I have been in this forum for quite sometime. I thing I know your threshold and preference of accepting suggestions. I tried my luck today. I showed you the difference of the solutions from the perspective of an end user but it looks the AttachmentLoader is written so nice so flexible that everyone in this world should give it a try. Up to this point, even if you give me a gun, I don't think I can change your mind. However, if you changed your mind, it would be appreciated. Otherwise, let's end the discussion here and go back to our normal busy life. I will survive clicking the checkbox to keep things simple. :tear: