• Runtimes
  • Using the same assets in Pixi and Spine

That's a cool example! Thank you!

How about I will have multiple skins for my game... but each skin should be its own game instance and the textures for each skin should be packed separately as I don't want to pollute the atlas for skin1 with assets for skin2 as this would increase the size of the entire game.

Would this be a case where it would be better to do all the spine animations with just slots so Pixi can manage this and only load the necessary assets? Or is there a way to do this in spine without having to create a duplicate of the spine project?

  • Davide ответили на это сообщение.
    Related Discussions
    ...

    michalvadak

    I'm not entirely sure I understand how your game and game instances work.
    From what I gather, each game instance uses only one skin. Because of this, you want to pack the skin images into separate atlases. Please correct me if I'm wrong.

    Keep in mind that you don’t need to duplicate your projects to create separate atlas pages.
    If you organize the skin images into different folders, the Spine Texture Packer is smart enough to pack them into separate atlas pages, as explained in this user guide section.

    However, even if you use multiple atlas pages, once you load your atlas file (txt), all atlas pages (pngs) are loaded into memory.
    If you want to load only specific pages into memory, you'll need to modify our atlas loader and implement a deferred loading strategy.
    If in your game instance you never need of other skins, you could also just remove the atlas page info not needed from the atlas text file.

    Additionally, if you have multiple skin variations, you don’t need to add all of them directly into Spine. Instead, create a single dummy version in Spine, and generate the variations externally. Then, you can use the texture packer separately to create different atlas variations. I recommend reading the full Spine Texture Packer user guide for a deeper understanding.

    You can also use the CLI to pack your textures, creating a streamlined pipeline for generating skin variations.

    As a last resort, you could use an external Texture Packer to provide assets to the Spine skeleton.
    If you use addSlotObjects, it will generate a large number of Pixi elements. Alternatively, you can create a custom AttachmentLoader to feed images directly to the skeleton.

    • michalvadak ответили на это сообщение.

      Let me give you some more context.

      Let's say I have 3 pixi games (same game code, just different assets). All of them will use a single Spine project as the game is the same, just with a different skin.

      If I leave all the assets in Spine it will pack them into atlas pages (png) together with info about all in the atlas file (txt). I don't want this as it will increase the size of the games with each skin.

      What I need is to have atlas pages (png) with assets only for game1 and atlas file (txt) with information about assets only for game1 so I can load only that specific game skin.

      Same for game2, game3 etc.

      Davide Additionally, if you have multiple skin variations, you don’t need to add all of them directly into Spine. Instead, create a single dummy version in Spine, and generate the variations externally. Then, you can use the texture packer separately to create different atlas variations. I recommend reading the full Spine Texture Packer user guide for a deeper understanding.

      That would mean I would have a single Spine project with the "base" skin and that would provide me with just the JSON files and I would be able to generate atlas pages (png) and atlas file (txt) for each skin separately using the CLI?

      • Davide ответили на это сообщение.
        • Изменено

        michalvadak

        The easiest approach in your case is to export the skeleton three times, enabling the export flag only for the skin you want to export each time. This way, at runtime, your skeleton will require only the regions for the selected skin.

        Next, use the texture packer separately, providing as input a folder containing only the attachments of that skin. For example, to create the atlas for skin1, if your image folder is structured as follows:

        • images/
          • skin1/
          • skin2/
          • skin3/

        You can create a temporary folder containing only skin1:

        • temp/
          • skin1/

        Then, provide this temp/ folder to the standalone texture packer.

        If there are shared elements across skins stored in a separate common/ folder:

        • images/
          • common/
          • skin1/
          • skin2/
          • skin3/

        Copy the common/ folder as well:

        • temp/
          • common/
          • skin1/

        Make sure to select Combine subdirectories so the texture packer doesn’t generate a separate atlas page for each folder.

        Note that, as stated in the export flag documentation:

        If a mesh attachment is not exported, none of its linked meshes will be exported either.

        This means that if you export skin1, and skin1 contains a linked mesh that references a mesh in skin2, you must also export skin2 to preserve the dependency.
        Alternatively, to overcome this limitation, you can export a JSON containing all the skins (or the skins for which there are dependencies). However, you’ll need to create a custom AttachmentLoader that doesn’t throw an error when a region is missing from the atlas.
        In this case, the custom AttachmentLoader would function similarly to the AtlasAttachmentLoader, but with the throw declarations removed.
        If you take this approach, ensure that at runtime, you only set skins that are present in your atlas to avoid errors.
        You probably want to use CLI in this case to automatize this process.

        • michalvadak ответили на это сообщение.

          Davide The easiest approach in your case is to export the skeleton three times, enabling the export flag only for the skin you want to export each time. This way, at runtime, your skeleton will require only the regions for the selected skin.

          Thanks this seems to work!

          Davide Next, use the texture packer separately, providing as input a folder containing only the attachments of that skin. For example, to create the atlas for skin1, if your image folder is structured as follows:...

          I'm not sure why I need this? Doesn't the first part cover all my needs? Is this a different approach or an extension?

          • Davide ответили на это сообщение.

            Glad it worked!

            michalvadak I'm not sure why I need this. Doesn't the first part cover all my needs? Is this a different approach or an extension?

            If your skin has no dependencies, the first step should be enough.
            If there are dependencies, as explained below, you also need to export the skin you depend on. In that case, its images will be included in your atlas. To avoid that you need to export the skeleton and the atlas separately.

            Okay, thanks!

            5 дней спустя

            Davide // load the atlas and automatically the textures
            const atlas = await PIXI.Assets.load("https://esotericsoftware.com/files/examples/latest/spineboy/export/spineboy-pma.atlas");

            // util to get a new sprite, given an attachment name
            const getSprite = attachmentName => {
            // find the region with the given attachment name
            const region = atlas.regions.find(element => element.name === attachmentName);

            // get the respective page texture
            const originalTexture = region.texture.texture;
            
            // determine the position of the desired attachment, considering rotation
            const frame = {
                x: region.x,
                y: region.y,
                width: region.rotate === undefined ? region.width : region.height,
                height: region.rotate === undefined ? region.height : region.width,
            };
            
            // creating the new sub texture
                const texture = new PIXI.Texture({
                source: originalTexture.baseTexture,
                crop: new PIXI.Rectangle(frame.x, frame.y, frame.width, frame.height),
                frame,
            });
            
            // creating a new sprite
            return new PIXI.Sprite(texture);

            }

            const miniSpineBoy = new PIXI.Container();

            const footR = getSprite("front-foot");
            footR.x = 85;
            footR.y = 135;
            footR.scale.set(.70)
            miniSpineBoy.addChild(footR);

            const head = getSprite("head");
            miniSpineBoy.addChild(head);

            const eyeL = getSprite("eye-surprised");
            eyeL.x = 45;
            eyeL.y = 75;
            eyeL.scale.set(.75)
            miniSpineBoy.addChild(eyeL);

            const eyeR = getSprite("eye-indifferent");
            eyeR.x = 70;
            eyeR.y = 80;
            eyeR.scale.set(.75)
            miniSpineBoy.addChild(eyeR);

            const mouth = getSprite("mouth-oooo");
            mouth.x = 50;
            mouth.y = 120;
            miniSpineBoy.addChild(mouth);

            const footL = getSprite("front-foot");
            footL.x = 25;
            footL.y = 135;
            footL.scale.set(.70)
            miniSpineBoy.addChild(footL);

            miniSpineBoy.x = window.innerWidth / 2 - miniSpineBoy.width / 2;
            miniSpineBoy.y = window.innerHeight / 2 - miniSpineBoy.height / 2;;
            app.stage.addChild(miniSpineBoy);

            The only thing is I can't get the script to work correctly in Spine 7 with rotated assets and white strip on :/

            • Davide ответили на это сообщение.

              michalvadak The only thing is I can't get the script to work correctly in Spine 7 with rotated assets and white strip on :/

              The script above was just a quick experiment. I did not test any special use cases.

              But what's the problem? In my example, I also had rotated textures and white strips.
              Just knowing that it doesn't work for you won't help me in helping you 🙂
              Could you describe your issue better?

              Got it! We made it to work now... but we have calculate the offsets and rotate the Texture at the end which I'm not sure is the correct approach.

              export const createTexturesFromSpineAtlas = (
                atlas: string,
                assets: string[]
              ) => {
                const regions = Assets.get(atlas)['regions'];
              
                regions
                  .filter((region: TextureAtlasRegion) => assets.includes(region.name))
                  .forEach((region: TextureAtlasRegion) => {
                    const frame = new Rectangle(
                      region.x,
                      region.y,
                      region.degrees !== 0 ? region.height : region.width,
                      region.degrees !== 0 ? region.width : region.height
                    );
              
                    const orig = new Rectangle(
                      0,
                      0,
                      region.originalWidth,
                      region.originalHeight
                    );
              
                    const offsetY = region.originalHeight - region.height - region.offsetY; // spine stores data from the bottom-left corner, and we need to adjust for that
              
                    const trim = new Rectangle(
                      region.offsetX,
                      offsetY,
                      region.width,
                      region.height
                    );
              
                    const texture = new Texture(
                      region.texture.texture,
                      frame,
                      orig,
                      trim,
                      Pixi.groupD8.N
                    );
              
                    Texture.addToCache(texture, region.name);
                  });
              };
              • Davide ответили на это сообщение.

                michalvadak

                Here you can find all the properties parsed from the texture atlas file. If something doesn't work in the future, you can double-check there.

                You may also want to check the API documentation for the AtlasRegion where all relevant properties are documented.