void Apply() { var skeletonAnimation = GetComponent <SkeletonAnimation>(); var skeleton = skeletonAnimation.Skeleton; // STEP 0: PREPARE SKINS // Let's prepare a new skin to be our custom skin with equips/customizations. //We get a clone so our original skins are unaffected. customSkin = customSkin ?? new Skin("custom skin"); // This requires that all customizations are done with skin placeholders defined in Spine. //customSkin = customSkin ?? skeleton.UnshareSkin(true, false, skeletonAnimation.AnimationState); // use this if you are not customizing on the default skin. var templateSkin = skeleton.Data.FindSkin(templateAttachmentsSkin); // STEP 1: "EQUIP" ITEMS USING SPRITES // STEP 1.1 Find the original/template attachment. // Step 1.2 Get a clone of the original/template attachment. // Step 1.3 Apply the Sprite image to the clone. // Step 1.4 Add the remapped clone to the new custom skin. for (int i = 0; i < AllReplacePairs.Count; i++) { SlotRegionPair Pair = AllReplacePairs[i]; int slotIndex = skeleton.FindSlotIndex(Pair.Slot); // You can access GetAttachment and SetAttachment via string //, but caching the slotIndex is faster. Attachment templateAttachment = templateSkin.GetAttachment(slotIndex, Pair.Key); // STEP 1.1 Attachment newAttachment = templateAttachment.GetRemappedClone (Pair.newSprite, sourceMaterial); // STEP 1.2 - 1.3 if (newAttachment != null) { RegionAttachment ra = newAttachment as RegionAttachment; ra = (RegionAttachment)ra.Copy(); float scale = 1f / Pair.newSprite.pixelsPerUnit; ra.Width = ra.RegionWidth * scale; ra.Height = ra.RegionHeight * scale; customSkin.SetAttachment(slotIndex, Pair.Key, newAttachment);//STEP 1.4 } } if (repack) { var repackedSkin = new Skin("repacked skin"); repackedSkin.AddAttachments(skeleton.Data.DefaultSkin); // Include the "default" skin. (everything outside of skin placeholders) repackedSkin.AddAttachments(customSkin); // Include your new custom skin. repackedSkin = repackedSkin.GetRepackedSkin("repacked skin", sourceMaterial, out runtimeMaterial, out runtimeAtlas); // Pack all the items in the skin. skeleton.SetSkin(repackedSkin); // Assign the repacked skin to your Skeleton. if (bbFollower != null) { bbFollower.Initialize(true); } } else { skeleton.SetSkin(customSkin); // Just use the custom skin directly. } skeleton.SetSlotsToSetupPose(); // Use the pose from setup pose. skeletonAnimation.Update(0); // Use the pose in the currently active animation. Resources.UnloadUnusedAssets(); }
void Apply() { var skeletonAnimation = GetComponent <SkeletonAnimation>(); var skeleton = skeletonAnimation.Skeleton; // STEP 0: PREPARE SKINS // Let's prepare a new skin to be our custom skin with equips/customizations. We get a clone so our original skins are unaffected. customSkin = customSkin ?? new Skin("custom skin"); // This requires that all customizations are done with skin placeholders defined in Spine. //customSkin = customSkin ?? skeleton.UnshareSkin(true, false, skeletonAnimation.AnimationState); // use this if you are not customizing on the default skin and don't plan to remove // Next let's var baseSkin = skeleton.Data.FindSkin(baseSkinName); // STEP 1: "EQUIP" ITEMS USING SPRITES // STEP 1.1 Find the original attachment. // Step 1.2 Get a clone of the original attachment. // Step 1.3 Apply the Sprite image to the clone. // Step 1.4 Add the remapped clone to the new custom skin. // Let's do this for the visor. int visorSlotIndex = skeleton.FindSlotIndex(visorSlot); // You can access GetAttachment and SetAttachment via string, but caching the slotIndex is faster. Attachment baseAttachment = baseSkin.GetAttachment(visorSlotIndex, visorKey); // STEP 1.1 Attachment newAttachment = baseAttachment.GetRemappedClone(visorSprite, sourceMaterial); // STEP 1.2 - 1.3 customSkin.SetAttachment(visorSlotIndex, visorKey, newAttachment); // STEP 1.4 // And now for the gun. int gunSlotIndex = skeleton.FindSlotIndex(gunSlot); Attachment baseGun = baseSkin.GetAttachment(gunSlotIndex, gunKey); // STEP 1.1 Attachment newGun = baseGun.GetRemappedClone(gunSprite, sourceMaterial); // STEP 1.2 - 1.3 if (newGun != null) { customSkin.SetAttachment(gunSlotIndex, gunKey, newGun); // STEP 1.4 } // customSkin.RemoveAttachment(gunSlotIndex, gunKey); // To remove an item. // customSkin.Clear() // Use skin.Clear() To remove all customizations. // Customizations will fall back to the value in the default skin if it was defined there. // To prevent fallback from happening, make sure the key is not defined in the default skin. // STEP 3: APPLY AND CLEAN UP. // Recommended: REPACK THE CUSTOM SKIN TO MINIMIZE DRAW CALLS // Repacking requires that you set all source textures/sprites/atlases to be Read/Write enabled in the inspector. // Combine all the attachment sources into one skin. Usually this means the default skin and the custom skin. // call Skin.GetRepackedSkin to get a cloned skin with cloned attachments that all use one texture. // Under the hood, this relies on if (repack) { var repackedSkin = new Skin("repacked skin"); repackedSkin.Append(skeleton.Data.DefaultSkin); repackedSkin.Append(customSkin); repackedSkin = repackedSkin.GetRepackedSkin("repacked skin", sourceMaterial, out runtimeMaterial, out runtimeAtlas); skeleton.SetSkin(repackedSkin); if (bbFollower != null) { bbFollower.Initialize(true); } } else { skeleton.SetSkin(customSkin); } skeleton.SetSlotsToSetupPose(); skeletonAnimation.Update(0); Resources.UnloadUnusedAssets(); }
public override void OnInspectorGUI() { #if !NEW_PREFAB_SYSTEM bool isInspectingPrefab = (PrefabUtility.GetPrefabType(target) == PrefabType.Prefab); #else bool isInspectingPrefab = false; #endif // Try to auto-assign SkeletonRenderer field. if (skeletonRenderer.objectReferenceValue == null) { var foundSkeletonRenderer = follower.GetComponentInParent <SkeletonRenderer>(); if (foundSkeletonRenderer != null) { Debug.Log("BoundingBoxFollower automatically assigned: " + foundSkeletonRenderer.gameObject.name); } else if (Event.current.type == EventType.Repaint) { Debug.Log("No Spine GameObject detected. Make sure to set this GameObject as a child of the Spine GameObject; or set BoundingBoxFollower's 'Skeleton Renderer' field in the inspector."); } skeletonRenderer.objectReferenceValue = foundSkeletonRenderer; serializedObject.ApplyModifiedProperties(); } var skeletonRendererValue = skeletonRenderer.objectReferenceValue as SkeletonRenderer; if (skeletonRendererValue != null && skeletonRendererValue.gameObject == follower.gameObject) { using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) { EditorGUILayout.HelpBox("It's ideal to add BoundingBoxFollower to a separate child GameObject of the Spine GameObject.", MessageType.Warning); if (GUILayout.Button(new GUIContent("Move BoundingBoxFollower to new GameObject", Icons.boundingBox), GUILayout.Height(50f))) { AddBoundingBoxFollowerChild(skeletonRendererValue, follower); DestroyImmediate(follower); return; } } EditorGUILayout.Space(); } EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(skeletonRenderer); EditorGUILayout.PropertyField(slotName, new GUIContent("Slot")); if (EditorGUI.EndChangeCheck()) { serializedObject.ApplyModifiedProperties(); #if !NEW_PREFAB_SYSTEM if (!isInspectingPrefab) { rebuildRequired = true; } #endif } using (new SpineInspectorUtility.LabelWidthScope(150f)) { EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(isTrigger); bool triggerChanged = EditorGUI.EndChangeCheck(); EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(clearStateOnDisable, new GUIContent(clearStateOnDisable.displayName, "Enable this if you are pooling your Spine GameObject")); bool clearStateChanged = EditorGUI.EndChangeCheck(); if (clearStateChanged || triggerChanged) { serializedObject.ApplyModifiedProperties(); if (triggerChanged) { foreach (var col in follower.colliderTable.Values) { col.isTrigger = isTrigger.boolValue; } } } } if (isInspectingPrefab) { follower.colliderTable.Clear(); follower.nameTable.Clear(); EditorGUILayout.HelpBox("BoundingBoxAttachments cannot be previewed in prefabs.", MessageType.Info); // How do you prevent components from being saved into the prefab? No such HideFlag. DontSaveInEditor | DontSaveInBuild does not work. DestroyImmediate does not work. var collider = follower.GetComponent <PolygonCollider2D>(); if (collider != null) { Debug.LogWarning("Found BoundingBoxFollower collider components in prefab. These are disposed and regenerated at runtime."); } } else { using (new SpineInspectorUtility.BoxScope()) { if (debugIsExpanded = EditorGUILayout.Foldout(debugIsExpanded, "Debug Colliders")) { EditorGUI.indentLevel++; EditorGUILayout.LabelField(string.Format("Attachment Names ({0} PolygonCollider2D)", follower.colliderTable.Count)); EditorGUI.BeginChangeCheck(); foreach (var kp in follower.nameTable) { string attachmentName = kp.Value; var collider = follower.colliderTable[kp.Key]; bool isPlaceholder = attachmentName != kp.Key.Name; collider.enabled = EditorGUILayout.ToggleLeft(new GUIContent(!isPlaceholder ? attachmentName : string.Format("{0} [{1}]", attachmentName, kp.Key.Name), isPlaceholder ? Icons.skinPlaceholder : Icons.boundingBox), collider.enabled); } sceneRepaintRequired |= EditorGUI.EndChangeCheck(); EditorGUI.indentLevel--; } } } bool hasBoneFollower = follower.GetComponent <BoneFollower>() != null; if (!hasBoneFollower) { bool buttonDisabled = follower.Slot == null; using (new EditorGUI.DisabledGroupScope(buttonDisabled)) { addBoneFollower |= SpineInspectorUtility.LargeCenteredButton(AddBoneFollowerLabel, true); EditorGUILayout.Space(); } } if (Event.current.type == EventType.Repaint) { if (addBoneFollower) { var boneFollower = follower.gameObject.AddComponent <BoneFollower>(); boneFollower.skeletonRenderer = skeletonRendererValue; boneFollower.SetBone(follower.Slot.Data.BoneData.Name); addBoneFollower = false; } if (sceneRepaintRequired) { SceneView.RepaintAll(); sceneRepaintRequired = false; } if (rebuildRequired) { follower.Initialize(); rebuildRequired = false; } } }
void Apply() { var skeletonAnimation = GetComponent <SkeletonAnimation>(); var skeleton = skeletonAnimation.Skeleton; // STEP 0: PREPARE SKINS // Let's prepare a new skin to be our custom skin with equips/customizations. We get a clone so our original skins are unaffected. customSkin = customSkin ?? new Skin("custom skin"); // This requires that all customizations are done with skin placeholders defined in Spine. //customSkin = customSkin ?? skeleton.UnshareSkin(true, false, skeletonAnimation.AnimationState); // use this if you are not customizing on the default skin. var templateSkin = skeleton.Data.FindSkin(templateAttachmentsSkin); // STEP 1: "EQUIP" ITEMS USING SPRITES // STEP 1.1 Find the original/template attachment. // Step 1.2 Get a clone of the original/template attachment. // Step 1.3 Apply the Sprite image to the clone. // Step 1.4 Add the remapped clone to the new custom skin. // Let's do this for the visor. int visorSlotIndex = skeleton.FindSlotIndex(visorSlot); // You can access GetAttachment and SetAttachment via string, but caching the slotIndex is faster. Attachment templateAttachment = templateSkin.GetAttachment(visorSlotIndex, visorKey); // STEP 1.1 Attachment newAttachment = templateAttachment.GetRemappedClone(visorSprite, sourceMaterial); // STEP 1.2 - 1.3 customSkin.SetAttachment(visorSlotIndex, visorKey, newAttachment); // STEP 1.4 // And now for the gun. int gunSlotIndex = skeleton.FindSlotIndex(gunSlot); Attachment templateGun = templateSkin.GetAttachment(gunSlotIndex, gunKey); // STEP 1.1 Attachment newGun = templateGun.GetRemappedClone(gunSprite, sourceMaterial); // STEP 1.2 - 1.3 if (newGun != null) { customSkin.SetAttachment(gunSlotIndex, gunKey, newGun); // STEP 1.4 } // customSkin.RemoveAttachment(gunSlotIndex, gunKey); // To remove an item. // customSkin.Clear() // Use skin.Clear() To remove all customizations. // Customizations will fall back to the value in the default skin if it was defined there. // To prevent fallback from happening, make sure the key is not defined in the default skin. // STEP 3: APPLY AND CLEAN UP. // Recommended, preferably at level-load-time: REPACK THE CUSTOM SKIN TO MINIMIZE DRAW CALLS // IMPORTANT NOTE: the GetRepackedSkin() operation is expensive - if multiple characters // need to call it every few seconds the overhead will outweigh the draw call benefits. // // Repacking requires that you set all source textures/sprites/atlases to be Read/Write enabled in the inspector. // Combine all the attachment sources into one skin. Usually this means the default skin and the custom skin. // call Skin.GetRepackedSkin to get a cloned skin with cloned attachments that all use one texture. if (repack) { var repackedSkin = new Skin("repacked skin"); repackedSkin.AddAttachments(skeleton.Data.DefaultSkin); // Include the "default" skin. (everything outside of skin placeholders) repackedSkin.AddAttachments(customSkin); // Include your new custom skin. repackedSkin = repackedSkin.GetRepackedSkin("repacked skin", sourceMaterial, out runtimeMaterial, out runtimeAtlas); // Pack all the items in the skin. skeleton.SetSkin(repackedSkin); // Assign the repacked skin to your Skeleton. if (bbFollower != null) { bbFollower.Initialize(true); } } else { skeleton.SetSkin(customSkin); // Just use the custom skin directly. } skeleton.SetSlotsToSetupPose(); // Use the pose from setup pose. skeletonAnimation.Update(0); // Use the pose in the currently active animation. Resources.UnloadUnusedAssets(); }
void Apply() { var skeletonAnimation = GetComponent <SkeletonAnimation>(); var skeleton = skeletonAnimation.Skeleton; // STEP 0: PREPARE SKINS // Let's prepare a new skin to be our custom skin with equips/customizations. We get a clone so our original skins are unaffected. customSkin = customSkin ?? new Skin("custom skin"); // This requires that all customizations are done with skin placeholders defined in Spine. var templateSkin = skeleton.Data.FindSkin(templateAttachmentsSkin); // STEP 1: "EQUIP" ITEMS USING SPRITES // STEP 1.1 Find the original/template attachment. // Step 1.2 Get a clone of the original/template attachment. // Step 1.3 Apply the Sprite image to the clone. // Step 1.4 Add the remapped clone to the new custom skin. // Let's do this for the visor. int visorSlotIndex = skeleton.Data.FindSlot(visorSlot).Index; // You can access GetAttachment and SetAttachment via string, but caching the slotIndex is faster. Attachment templateAttachment = templateSkin.GetAttachment(visorSlotIndex, visorKey); // STEP 1.1 // Note: Each call to `GetRemappedClone()` with parameter `premultiplyAlpha` set to `true` creates // a cached Texture copy which can be cleared by calling AtlasUtilities.ClearCache() as done in the method below. Attachment newAttachment = templateAttachment.GetRemappedClone(visorSprite, sourceMaterial, pivotShiftsMeshUVCoords: false); // STEP 1.2 - 1.3 customSkin.SetAttachment(visorSlotIndex, visorKey, newAttachment); // STEP 1.4 // And now for the gun. int gunSlotIndex = skeleton.Data.FindSlot(gunSlot).Index; Attachment templateGun = templateSkin.GetAttachment(gunSlotIndex, gunKey); // STEP 1.1 Attachment newGun = templateGun.GetRemappedClone(gunSprite, sourceMaterial, pivotShiftsMeshUVCoords: false); // STEP 1.2 - 1.3 if (newGun != null) { customSkin.SetAttachment(gunSlotIndex, gunKey, newGun); // STEP 1.4 } // customSkin.RemoveAttachment(gunSlotIndex, gunKey); // To remove an item. // customSkin.Clear() // Use skin.Clear() To remove all customizations. // Customizations will fall back to the value in the default skin if it was defined there. // To prevent fallback from happening, make sure the key is not defined in the default skin. // STEP 3: APPLY AND CLEAN UP. // Recommended, preferably at level-load-time: REPACK THE CUSTOM SKIN TO MINIMIZE DRAW CALLS // IMPORTANT NOTE: the GetRepackedSkin() operation is expensive - if multiple characters // need to call it every few seconds the overhead will outweigh the draw call benefits. // // Repacking requires that you set all source textures/sprites/atlases to be Read/Write enabled in the inspector. // Combine all the attachment sources into one skin. Usually this means the default skin and the custom skin. // call Skin.GetRepackedSkin to get a cloned skin with cloned attachments that all use one texture. if (repack) { var repackedSkin = new Skin("repacked skin"); repackedSkin.AddSkin(skeleton.Data.DefaultSkin); // Include the "default" skin. (everything outside of skin placeholders) repackedSkin.AddSkin(customSkin); // Include your new custom skin. // Note: materials and textures returned by GetRepackedSkin() behave like 'new Texture2D()' and need to be destroyed if (runtimeMaterial) { Destroy(runtimeMaterial); } if (runtimeAtlas) { Destroy(runtimeAtlas); } repackedSkin = repackedSkin.GetRepackedSkin("repacked skin", sourceMaterial, out runtimeMaterial, out runtimeAtlas); // Pack all the items in the skin. skeleton.SetSkin(repackedSkin); // Assign the repacked skin to your Skeleton. if (bbFollower != null) { bbFollower.Initialize(true); } } else { skeleton.SetSkin(customSkin); // Just use the custom skin directly. } skeleton.SetSlotsToSetupPose(); // Use the pose from setup pose. skeletonAnimation.Update(0); // Use the pose in the currently active animation. // `GetRepackedSkin()` and each call to `GetRemappedClone()` with parameter `premultiplyAlpha` set to `true` // cache necessarily created Texture copies which can be cleared by calling AtlasUtilities.ClearCache(). // You can optionally clear the textures cache after multiple repack operations. // Just be aware that while this cleanup frees up memory, it is also a costly operation // and will likely cause a spike in the framerate. AtlasUtilities.ClearCache(); Resources.UnloadUnusedAssets(); }