- Изменено
[iOS] add attachment from different atlas file
Hi,
I am running spine-cocos2d-objc, and want to add some items from different atlasses into my skeleton. Is that possible? If so, how to do it? Thank you.
It is possible, but it's not straight forward. As an alternative, may I suggest looking into skins? Skins - Spine User Guide
badlogic написалIt is possible, but it's not straight forward. As an alternative, may I suggest looking into skins? Skins - Spine User Guide
Yes, but how to load different atlas and png file that will be used?
You need to load the atlas yourself, then you can load multiple SkeletonDatas with it. For how to do that see:
spine-runtimes/SkeletonRenderer.m at 3.8
After that you can use SkeletonAnimation skeletonWithData
to create a SkeletonAnimation with a SkeletonData.
Nate написалYou need to load the atlas yourself, then you can load multiple SkeletonDatas with it. For how to do that see:
spine-runtimes/SkeletonRenderer.m at 3.8
After that you can use SkeletonAnimationskeletonWithData
to create a SkeletonAnimation with a SkeletonData.
I create atlas from my other asset like this
spAtlas* atlasFromAnimal = spAtlas_createFromFile("myOtherAnimal.atlas", 0);
spSkeletonJson* jsonFromAnimal = spSkeletonJson_create(atlasFromAnimal);
jsonFromAnimal->scale = 1;
Then how to assign that into my skeletonData? I already assign my skeletonData with my main atlas like this
skeletonData = spSkeletonJson_readSkeletonDataFile(json, [skeletonDataFile UTF8String]);
The atlas is used by the spSkeletonJson
:
spine-runtimes/SkeletonRenderer.m at 3.8
Nate написалThe atlas is used by the
spSkeletonJson
:
spine-runtimes/SkeletonRenderer.m at 3.8
Yes, I created spSkeletonJson from the new atlas.
spSkeletonJson* jsonFromAnimal = spSkeletonJson_create(atlasFromAnimal);
But my question is, how to create skeletonData from 2 or more different spSkeletonJson object as I need skeletonData for create my SkeletonAnimation?
@synchronized(self.class) {
skeletonData = spSkeletonJson_readSkeletonDataFile(json, [skeletonDataFile UTF8String]);
}
Or how to create SkeletonAnimation from 2 or more skeletonData if I couldn't create skeletonData from more than one spSkeletonJson? Thank you
Sorry, I misread and thought you wanted to create multiple SkeletonData with the same atlas.
To load the regions for some attachments from one atlas and other attachments from a different atlas, you will need to implement your own AttachmentLoader:
Loading Skeleton Data - Spine Runtimes Guide: AttachmentLoader
By default, when an AttachmentLoader is not specified, an AtlasAttachmentLoader is used. You will need to create your own AttachmentLoader, but you can use AtlasAttachmentLoader as a guide. In spine-c it looks like:
spine-runtimes/AtlasAttachmentLoader.c at 3.8
In your AttachmentLoader you will do the same, except change the spAtlas_findRegion
calls to look in the correct atlas. Maybe you know which attachment names map to which atlas, or maybe you just look in each atlas and use the first one to return a region.
Nate написалSorry, I misread and thought you wanted to create multiple SkeletonData with the same atlas.
To load the regions for some attachments from one atlas and other attachments from a different atlas, you will need to implement your own AttachmentLoader:
Loading Skeleton Data - Spine Runtimes Guide: AttachmentLoader
By default, when an AttachmentLoader is not specified, an AtlasAttachmentLoader is used. You will need to create your own AttachmentLoader, but you can use AtlasAttachmentLoader as a guide. In spine-c it looks like:
spine-runtimes/AtlasAttachmentLoader.c at 3.8
In your AttachmentLoader you will do the same, except change thespAtlas_findRegion
calls to look in the correct atlas. Maybe you know which attachment names map to which atlas, or maybe you just look in each atlas and use the first one to return a region.
After load the atlas, then I have no clue how to create skin item spSkin
from that.
_atlas = spAtlas_createFromFile("myOtherAnimal.atlas", 0);
spAtlasAttachmentLoader_create(_atlas);
spSkin_addSkin(skin, spSkeletonData_findSkin(skeletonData, "weapon_m")); // in this line, it couldn't fine the "weapon_m" skin
Any ideas? Thank you.
An atlas cannot be just turned into a skin. An atlas is a list of regions (not region attachments), each with a name and UVs for the 4 corners. A skin is a name -> attachment map. An attachment, such as a region attachment, has many properties, such as x, y, rotation, scale, which texture atlas region to use (stored in rendererObject
), etc. See:
Spine Runtimes Guide
Изображение удалено из-за отсутствия поддержки HTTPS. | Показать
The skeleton data has attachment data inside it. SkeletonJson loads the skeleton data and uses an AttachmentLoader to create the attachments and assign them regions from an atlas.
If you have multiple atlases, one thing you can do is use an AttachmentLoader that looks in each one until it finds a region for the attachment, then it assigns the region to the attachment. Copy AtlasAttachmentLoader and hack it up to do that. You would create a SkeletonJson, give it your AttachmentLoader, then give it your JSON and it will give you a SkeletonData that has all the attachments with regions assigned from any number of atlases.
Another way to do it is to get an attachment from your SkeletonData, make a copy of it, then change the copy attachment's region (rendererObject
) to that of whatever atlas you like. You can then add the attachment to a skin, or set it directly on a skeleton by getting a slot and calling Slot setAttachment
.
Yet another way to do it is to write code to create an attachment entirely from scratch. However, this is difficult because it is hard to visualize where to place the attachment, at what rotation, etc. This is why it's more common to setup an attachment in the Spine editor, then make a copy at runtime and change the region/rendererObject. Again, once you have the attachment you can add it to a skin or call Slot setAttachment
.
All of these setups require understanding how things work and write a fair amount of code. That is why badlogic said it is not easy. You'll need to read through the code to understand how SkeletonJson loads attachments and what the AttachmentLoader does.
Thank you for the explanation. After reading that, I try to follow your first suggestion.
If you have multiple atlases, one thing you can do is use an AttachmentLoader that looks in each one until it finds a region for the attachment, then it assigns the region to the attachment. Copy AtlasAttachmentLoader and hack it up to do that. You would create a SkeletonJson, give it your AttachmentLoader, then give it your JSON and it will give you a SkeletonData that has all the attachments with regions assigned from any number of atlases.
I try to understand, but still there are questions in my mind :think:
Currently I have my asset (main.json
, main.atlas
, and main.png
). Then, I have addition asset to load (addition.atlas
and addition.png
).
In my main
skeleton, I use the
- (id) initWithFile:(NSString*)skeletonDataFile atlasFile:(NSString*)atlasFile scale:(float)scale
There, spAtlas
is created with atlasFile
string, then it created spSkeletonJson
with those atlas. After that, it created spSkeletonData
with
spSkeletonJson_readSkeletonDataFile(json, [skeletonDataFile UTF8String]);
From that block, I couldn't figure it out how to create AttachmentLoader correctly. I try to create the AtlasAttachmentLoader like this
spAtlas *myAtlas = spAtlas_createFromFile("myAnotherAtlas.atlas", 0);
spAtlasAttachmentLoader *atAtlas = spAtlasAttachmentLoader_create(myAtlas);
But after that, I have no clue how to use my spAtlasAttachmentLoader
.
From the example, I couldn't find the example how to load another asset (.atlas and .png files). Would you show me the example of that, please? Thank you.
OutputDataPort написалIn my
main
skeleton, I use the- (id) initWithFile:(NSString*)skeletonDataFile atlasFile:(NSString*)atlasFile scale:(float)scale
That function loads the skeleton and atlas using AtlasAttachmentLoader. You can't use that function. You need to load the skeleton data yourself and use:
+ (id) skeletonWithData:(spSkeletonData*)skeletonData ownsSkeletonData:(bool)ownsSkeletonData
OutputDataPort написалFrom that block, I couldn't figure it out how to create AttachmentLoader correctly. I try to create the AtlasAttachmentLoader like this
AtlasAttachmentLoader creates and configures attachments using regions from a single atlas. You can't use AtlasAttachmentLoader. You need to copy/paste AtlasAttachmentLoader.h and AtlasAttachmentLoader.c, rename the functions to something else, and add the logic to load from multiple atlases. You use your loader like this (untested):
spAtlas* atlas1 = spAtlas_createFromFile([atlasFile1 UTF8String], 0);
spAtlas* atlas2 = spAtlas_createFromFile([atlasFile2 UTF8String], 0);
YourAttachmentLoader* loader = ...
spSkeletonJson* json = spSkeletonJson_createWithLoader(loader);
json->scale = 1;
spSkeletonData* skeletonData = spSkeletonJson_readSkeletonDataFile(json, [skeletonDataFile UTF8String]);
spSkeletonJson_dispose(json);
SkeletonAnimation* skeletonAnimation = [SkeletonAnimation skeletonWithData:skeletonData ownsSkeletonData:true];
In your loader, instead of:
spAtlasRegion* region = spAtlas_findRegion(self->atlas, path);
You'd do something like:
spAtlasRegion* region = spAtlas_findRegion(self->atlas1, path);
if (!region) region = spAtlas_findRegion(self->atlas2, path);
You can also get fancier and use an array of atlases.
I wonder why you do all this though? Did you know you can pack a single atlas that contains all your regions? You don't need to pack two separate atlases and then go through the above. Put all your images in a folder and run the texture packer on them (run the texture packer separately, not as part of JSON/binary data export). See:
Texture Packing - Spine User Guide
Nate написалOutputDataPort написалIn my
main
skeleton, I use the- (id) initWithFile:(NSString*)skeletonDataFile atlasFile:(NSString*)atlasFile scale:(float)scale
That function loads the skeleton and atlas using AtlasAttachmentLoader. You can't use that function. You need to load the skeleton data yourself and use:
+ (id) skeletonWithData:(spSkeletonData*)skeletonData ownsSkeletonData:(bool)ownsSkeletonData
OutputDataPort написалFrom that block, I couldn't figure it out how to create AttachmentLoader correctly. I try to create the AtlasAttachmentLoader like this
AtlasAttachmentLoader creates and configures attachments using regions from a single atlas. You can't use AtlasAttachmentLoader. You need to copy/paste AtlasAttachmentLoader.h and AtlasAttachmentLoader.c, rename the functions to something else, and add the logic to load from multiple atlases. You use your loader like this (untested):
spAtlas* atlas1 = spAtlas_createFromFile([atlasFile1 UTF8String], 0); spAtlas* atlas2 = spAtlas_createFromFile([atlasFile2 UTF8String], 0); YourAttachmentLoader* loader = ... spSkeletonJson* json = spSkeletonJson_createWithLoader(loader); json->scale = 1; spSkeletonData* skeletonData = spSkeletonJson_readSkeletonDataFile(json, [skeletonDataFile UTF8String]); spSkeletonJson_dispose(json); SkeletonAnimation* skeletonAnimation = [SkeletonAnimation skeletonWithData:skeletonData ownsSkeletonData:true];
In your loader, instead of:
spAtlasRegion* region = spAtlas_findRegion(self->atlas, path);
You'd do something like:
spAtlasRegion* region = spAtlas_findRegion(self->atlas1, path); if (!region) region = spAtlas_findRegion(self->atlas2, path);
You can also get fancier and use an array of atlases.
I wonder why you do all this though? Did you know you can pack a single atlas that contains all your regions? You don't need to pack two separate atlases and then go through the above. Put all your images in a folder and run the texture packer on them (run the texture packer separately, not as part of JSON/binary data export). See:
Texture Packing - Spine User Guide
Tbh, what I'm doing right now is trying to implement some feature that already implemented in our android app. Our server give me all the assets separately.
Anyway, I already create my custom atlasAttachmentLoader that accept array of atlas, then will iterate on each atlas
spRGAtlasAttachmentLoader* spRGAtlasAttachmentLoader_create (spAtlas* atlas[], int size) {
spRGAtlasAttachmentLoader* self = NEW(spRGAtlasAttachmentLoader);
_spAttachmentLoader_init(SUPER(self), _spAttachmentLoader_deinit, _spRGAtlasAttachmentLoader_createAttachment, 0, 0);
for (int i=0; i<size; i++) {
self->multipleAtlases[i] = atlas[i];
}
return self;
}
Then, in _spRGAtlasAttachmentLoader_createAttachment
,
case SP_ATTACHMENT_REGION: {
spRegionAttachment* attachment;
spAtlasRegion* region = spAtlas_findRegion(self->multipleAtlases[0], path);
for (int i=0; i < sizeof(self->multipleAtlases); i++) {
if (!region) {
region = spAtlas_findRegion(self->multipleAtlases[i], path);
}
}
if (!region) {
_spAttachmentLoader_setError(loader, "Region not found: ", path);
return 0;
}
...
The problem is, when I try to create spSkeletonJson
object. Its spSkeletonJson_createWithLoader
input is spAtlasAttachmentLoader
. So should I create another function that accept my custom loader or is there any other idea?
Thank you so much.
Hmm spSkeletonJson_createWithLoader
should take an spAttachmentLoader* attachmentLoader
:
spine-runtimes/SkeletonJson.h at 3.8
spine-runtimes/SkeletonJson.c at 3.8
You'd call it using the SUPER
macro, like:
spRGAtlasAttachmentLoader* attachmentLoader = spRGAtlasAttachmentLoader_create(atlases, size);
spSkeletonJson* json = spSkeletonJson_createWithLoader(SUPER(attachmentLoader));