Some background info - I'm updating a game that was originally written in Phaser 3.18.1 using Spine runtime 3.7.94. The only real update that I'm doing to it is to bring it up to Phaser 3.55.2 and Spine runtime 3.8.99. Within this game there is a customizable character that is also a Spine animation. The customization is just changing the body color of the character and also a pattern can be applied if desired (there are 12 different colors that can be used for the body color and for the pattern color). There are approximately 672 different combinations. I've attached two customizations to give an idea of what is possible.
In the original code, this is how this customization was done:
this.character = this.add.spine(0, 0, "fuzzbug", "Fall", true); // this creates the spine object; this is a Phaser.Scene
// the color canvas creates the color that the character will be (plus the pattern and color)
let colorCanvas = new ACanvas(this, "colorCanvas", 258, 259); // this creates a Canvas (ACanvas is a wrapper class that we use to encompass some functionality)
if(this.stencil != "")
{
colorCanvas.addSquare(this.stencilColor); // makes the entire canvas the color provided
colorCanvas.setComposition(ACanvas.compositions.DESTINATION_OUT); // changes the composition of the canvas
colorCanvas.addImage(this.stencilData[this.stencil].x, this.stencilData[this.stencil].y, this.stencil); // draws the stencil/pattern to the canvas
colorCanvas.setComposition(ACanvas.compositions.DESTINATION_OVER);
}
colorCanvas.addSquare(this.color); // makes the entire canvas the color provided
// the spine canvas is the canvas that contains the body of the character. the color canvas is then applied to it to paint the character body the color
let spineCanvas = new ACanvas(this, "spineCanvas", this.atlasSize.width, this.atlasSize.height); // atlas size is 1024x1024
spineCanvas.addImage(0, 0, this.textureKey); // texture key is character body image key
spineCanvas.setComposition(ACanvas.compositions.MULTIPLY);
spineCanvas.context.drawImage(colorCanvas.context.canvas, this.bodyData.x, this.bodyData.y); //
spineCanvas.setComposition(ACanvas.compositions.DESTINATION_IN);
spineCanvas.addImage(0, 0, this.textureKey);
// create a new WebGLTexture
let gl = this.game.context; // this is a Phaser.Scene
let texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, spineCanvas.context.getImageData(0, 0, this.atlasSize.width, this.atlasSize.height));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// set the texture into the skin of the spin/skeleton
this.character.skeleton.skin.attachments[7]["images/Base"].region.texture.texture = texture;
And this worked. It was probably a bit hacky but it did the job (note that I'm not the original developer of this game, I'm simply porting it).
The issue I'm running into now (after solving a few other issues), is that when I set the texture
, it now applies the texture to every instance of the Spine object that was created:
this.characterA = this.add.spine(0, 0, "fuzzbug", "Fall", true);
// do the customization from above for characterA
this.characterB = this.add.spine(0, 0, "fuzzbug", "Fall", true);
// do a different customization for characterB
// both characterA and characterB have the same body instead of being different
I've tried the following:
this.character = this.add.spine(0, 0, "fuzzbug", "Fall", true);
let baseAttachment = this.character.skeleton.getAttachment(7, "images/Base");
let skin = new spine.Skin(myUniqueSkinName); // create a new skin for this spin
skin.copySkin(this.character.skeletonData.defaultSkin);
skin.setAttachment(7, "images/Base", a.copy()); // I've tried including this and also not including this...
this.character.setSkin(skin); // set the new skin
// do the customization
But this doesn't seem to work. After a bit of testing and reading through documentation, the Skin gets completely copied, so it is unique between the two instances, however the Regions (which are TextureAtlasRegions) are still the same instance, so when I do this.character.spine.skeleton.getAttachment(7, "images/Base").region.texture.texture = texture;
it still changes every Skin that is using it, because even though the Skins are different instances, the regions they have are references to the same region, so both get changed. I tried hacking my way down and creating a new TextureAtlasRegion but I haven't been able to get it to work and I feel like there has to be a better way to do this.
I do realize that "Skins" I think are meant to be a solution for this, but considering there are 672 or so combinations of colors/patterns, it doesn't seem like a great way to do it, because setting all of those up in Spine will be a huge pain I would think. If there were only a dozen options maybe. But there has to be some way to say "take this body part and change what texture it is using to render" or something similar? And then make sure that each instance of the same Spine animation can use a different one.
Any help would be great appreciated. Thanks!