Hello,
we're trying to implement a skin system with texture loading on demand (using adressable textures later).
We have alredy read those both threads around that topic :
we have created a script allowing to instantiate the information necessary to the runtime (SpineAtlasAsset, SkeletonDataAsset & SkeletonAnimation) with a temporary empty material during the creation of the SpineAtlasAsset. When we want to display a particular skin, we change the corresponding texture and we update the material.
There is still a problem: An error appears at runtime when calling the method SpineAtlasAsset.CreateRuntimeInstance(...) and more precisely when calling GetAtlas() then new Atlas(...); with the MaterialsTextureLoader.
At the beginning we don't have the textures of the skins yet. Our materials are initialized with a dummy texture of 2x2 pixels with the right page name. Otherwise the MaterialsTextureLoader throws some errors as the given material has a null texture or a wrong texture name.
Would it be possible to pass in argument a custom MaterialsTextureLoader to the GetAtlas method, in order to avoid these errors ? The creation of the dummy textures looks like a workaround : it would be nice to have materials without the final textures in advance, and without throwing errors at that time.
Here is the prototype script that allows us to do this for now:
public TextAsset skeletonJson;
public TextAsset atlasText;
public Texture2D[] textures;
public Material materialPropertySource;
SpineAtlasAsset runtimeAtlasAsset;
SkeletonDataAsset runtimeSkeletonDataAsset;
SkeletonAnimation runtimeSkeletonAnimation;
void CreateRuntimeAssetsAndGameObject () {
runtimeAtlasAsset = CreateRuntimeSpineAtlasAsset(atlasText, materialPropertySource, true);
runtimeSkeletonDataAsset = SkeletonDataAsset.CreateRuntimeInstance(skeletonJson, runtimeAtlasAsset, true);
}
IEnumerator Start () {
CreateRuntimeAssetsAndGameObject();
runtimeSkeletonDataAsset.GetSkeletonData(true); // preload.
yield return new WaitForSeconds(0.5f);
// Create SkeletonAnimation
runtimeSkeletonAnimation = SkeletonAnimation.NewSkeletonAnimationGameObject(runtimeSkeletonDataAsset);
// Extra Stuff
runtimeSkeletonAnimation.Skeleton.SetSkin("common");
runtimeSkeletonAnimation.AnimationState.SetAnimation(0, "idle", true);
yield return new WaitForSeconds(2.0f);
Debug.Log("load first texture on demand (skin common at index 0)");
runtimeAtlasAsset.materials[0].mainTexture = textures[0];
}
private static SpineAtlasAsset CreateRuntimeSpineAtlasAsset (TextAsset atlasText, Material materialPropertySource, bool initialize) {
// Get atlas page names
string atlasString = atlasText.text;
atlasString = atlasString.Replace("\r", "");
string[] atlasLines = atlasString.Split('\n');
var pages = new List<string>();
for (int i = 0; i < atlasLines.Length - 1; i++) {
string line = atlasLines[i].Trim();
if (line.EndsWith(".png"))
pages.Add(line.Replace(".png", ""));
}
Debug.Log($"Pages identified from atlas: {string.Join(", ", pages)}");
var materials = new Material[pages.Count];
for (int i = 0, n = pages.Count; i < n; i++) {
// Create dummy texture with right page name to avoid error with current MaterialsTextureLoader
Texture2D dummy = new Texture2D(2, 2);
dummy.name = pages[i];
Material mat = new Material(materialPropertySource);
mat.mainTexture = dummy;
materials[i] = mat;
}
return SpineAtlasAsset.CreateRuntimeInstance(atlasText, materials, initialize);
}