[unity] Added an example scene "Mix and Match Skins" demmonstrating a character customization wardrobe system using the new Skins API, using the mix-and-match ("Spine dudes") asset. See #1628.

This commit is contained in:
Harald Csaszar 2020-09-30 16:55:38 +02:00
parent 5c1faa85af
commit 899a109a13
18 changed files with 13111 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: af3bca1819847fb46b46f8196526c285
timeCreated: 1480087951
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,50 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEngine;
using Spine.Unity;
using UnityEngine.UI;
namespace Spine.Unity.Examples {
public class MixAndMatchSkinsButtonExample : MonoBehaviour {
public SkeletonDataAsset skeletonDataAsset;
public MixAndMatchSkinsExample skinsSystem;
[SpineSkin(dataField:"skeletonDataAsset")] public string itemSkin;
public MixAndMatchSkinsExample.ItemType itemType;
void Start () {
var button = GetComponent<Button>();
button.onClick.AddListener(
delegate { skinsSystem.Equip(itemSkin, itemType); }
);
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c32a308f5ae4c534991805c82c575058
timeCreated: 1522744049
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,190 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System.Collections.Generic;
using UnityEngine;
using Spine.Unity.AttachmentTools;
namespace Spine.Unity.Examples {
public class MixAndMatchSkinsExample : MonoBehaviour {
// character skins
[SpineSkin] public string baseSkin = "skin-base";
[SpineSkin] public string eyelidsSkin = "eyelids/girly";
// here we use arrays of strings to be able to cycle between them easily.
[SpineSkin] public string[] hairSkins = { "hair/brown", "hair/blue", "hair/pink", "hair/short-red", "hair/long-blue-with-scarf" };
public int activeHairIndex = 0;
[SpineSkin] public string[] eyesSkins = { "eyes/violet", "eyes/green", "eyes/yellow" };
public int activeEyesIndex = 0;
[SpineSkin] public string[] noseSkins = { "nose/short", "nose/long" };
public int activeNoseIndex = 0;
// equipment skins
public enum ItemType {
Cloth,
Pants,
Bag,
Hat
}
[SpineSkin] public string clothesSkin = "clothes/hoodie-orange";
[SpineSkin] public string pantsSkin = "legs/pants-jeans";
[SpineSkin] public string bagSkin = "";
[SpineSkin] public string hatSkin = "accessories/hat-red-yellow";
SkeletonAnimation skeletonAnimation;
// This "naked body" skin will likely change only once upon character creation,
// so we store this combined set of non-equipment Skins for later re-use.
Skin characterSkin;
// for repacking the skin to a new atlas texture
public Material runtimeMaterial;
public Texture2D runtimeAtlas;
void Awake () {
skeletonAnimation = this.GetComponent<SkeletonAnimation>();
}
void Start () {
UpdateCharacterSkin();
UpdateCombinedSkin();
}
public void NextHairSkin() {
activeHairIndex = (activeHairIndex + 1) % hairSkins.Length;
UpdateCharacterSkin();
UpdateCombinedSkin();
}
public void PrevHairSkin () {
activeHairIndex = (activeHairIndex + hairSkins.Length - 1) % hairSkins.Length;
UpdateCharacterSkin();
UpdateCombinedSkin();
}
public void NextEyesSkin () {
activeEyesIndex = (activeEyesIndex + 1) % eyesSkins.Length;
UpdateCharacterSkin();
UpdateCombinedSkin();
}
public void PrevEyesSkin () {
activeEyesIndex = (activeEyesIndex + eyesSkins.Length - 1) % eyesSkins.Length;
UpdateCharacterSkin();
UpdateCombinedSkin();
}
public void NextNoseSkin () {
activeNoseIndex = (activeNoseIndex + 1) % noseSkins.Length;
UpdateCharacterSkin();
UpdateCombinedSkin();
}
public void PrevNoseSkin () {
activeNoseIndex = (activeNoseIndex + noseSkins.Length - 1) % noseSkins.Length;
UpdateCharacterSkin();
UpdateCombinedSkin();
}
public void Equip(string itemSkin, ItemType itemType) {
switch (itemType) {
case ItemType.Cloth:
clothesSkin = itemSkin;
break;
case ItemType.Pants:
pantsSkin = itemSkin;
break;
case ItemType.Bag:
bagSkin = itemSkin;
break;
case ItemType.Hat:
hatSkin = itemSkin;
break;
default:
break;
}
UpdateCombinedSkin();
}
public void OptimizeSkin () {
// Create a repacked skin.
var previousSkin = skeletonAnimation.Skeleton.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);
Skin repackedSkin = previousSkin.GetRepackedSkin("Repacked skin", skeletonAnimation.SkeletonDataAsset.atlasAssets[0].PrimaryMaterial, out runtimeMaterial, out runtimeAtlas);
previousSkin.Clear();
// Use the repacked skin.
skeletonAnimation.Skeleton.Skin = repackedSkin;
skeletonAnimation.Skeleton.SetSlotsToSetupPose();
skeletonAnimation.AnimationState.Apply(skeletonAnimation.Skeleton);
// You can optionally clear the cache after multiple repack operations.
AtlasUtilities.ClearCache();
}
void UpdateCharacterSkin () {
var skeleton = skeletonAnimation.Skeleton;
var skeletonData = skeleton.data;
characterSkin = new Skin("character-base");
// Note that the result Skin returned by calls to skeletonData.FindSkin()
// could be cached once in Start() instead of searching for the same skin
// every time. For demonstration purposes we keep it simple here.
characterSkin.AddSkin(skeletonData.FindSkin(baseSkin));
characterSkin.AddSkin(skeletonData.FindSkin(noseSkins[activeNoseIndex]));
characterSkin.AddSkin(skeletonData.FindSkin(eyelidsSkin));
characterSkin.AddSkin(skeletonData.FindSkin(eyesSkins[activeEyesIndex]));
characterSkin.AddSkin(skeletonData.FindSkin(hairSkins[activeHairIndex]));
}
void AddEquipmentSkinsTo (Skin combinedSkin) {
var skeleton = skeletonAnimation.Skeleton;
var skeletonData = skeleton.data;
combinedSkin.AddSkin(skeletonData.FindSkin(clothesSkin));
combinedSkin.AddSkin(skeletonData.FindSkin(pantsSkin));
if (!string.IsNullOrEmpty(bagSkin)) combinedSkin.AddSkin(skeletonData.FindSkin(bagSkin));
if (!string.IsNullOrEmpty(hatSkin)) combinedSkin.AddSkin(skeletonData.FindSkin(hatSkin));
}
void UpdateCombinedSkin () {
var skeleton = skeletonAnimation.Skeleton;
var resultCombinedSkin = new Skin("character-combined");
resultCombinedSkin.AddSkin(characterSkin);
AddEquipmentSkinsTo(resultCombinedSkin);
skeleton.SetSkin(resultCombinedSkin);
skeleton.SetSlotsToSetupPose();
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b5a66492fdefc494b8399943a0f9b250
timeCreated: 1601458489
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9b5072c1342a08b429b2bbfa38e83bab
timeCreated: 1601458174
licenseType: Pro
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

View File

@ -0,0 +1,82 @@
fileFormatVersion: 2
guid: a837e662e711d1a4ba0b4a9aeb141878
timeCreated: 1601458176
licenseType: Pro
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 4
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 1
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -1
wrapU: -1
wrapV: -1
wrapW: -1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
- buildTarget: Standalone
maxTextureSize: 2048
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,16 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a6b194f808b1af6499c93410e504af42, type: 3}
m_Name: mix-and-match-pma_Atlas
m_EditorClassIdentifier:
atlasFile: {fileID: 4900000, guid: 9b5072c1342a08b429b2bbfa38e83bab, type: 3}
materials:
- {fileID: 2100000, guid: f68af5332bca5ef49a0bb6a25be1a8e5, type: 2}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 071eeff112dbeca4e961d78214f41c38
timeCreated: 1601458175
licenseType: Pro
NativeFormatImporter:
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,37 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 6
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_Name: mix-and-match-pma_Material
m_Shader: {fileID: 4800000, guid: 1e8a610c9e01c3648bac42585e5fc676, type: 3}
m_ShaderKeywords: _USE8NEIGHBOURHOOD_ON
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _MainTex:
m_Texture: {fileID: 2800000, guid: a837e662e711d1a4ba0b4a9aeb141878, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Floats:
- _Cutoff: 0.1
- _OutlineMipLevel: 0
- _OutlineReferenceTexWidth: 1024
- _OutlineSmoothness: 1
- _OutlineWidth: 3
- _StencilComp: 8
- _StencilRef: 1
- _StraightAlphaInput: 0
- _ThresholdEnd: 0.25
- _Use8Neighbourhood: 1
m_Colors:
- _OutlineColor: {r: 1, g: 1, b: 0, a: 1}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: f68af5332bca5ef49a0bb6a25be1a8e5
timeCreated: 1601458174
licenseType: Pro
NativeFormatImporter:
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 05c17836f4f4fe748b11ad96e9436421
timeCreated: 1601458174
licenseType: Pro
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,23 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f1b3b4b945939a54ea0b23d3396115fb, type: 3}
m_Name: mix-and-match-pro_SkeletonData
m_EditorClassIdentifier:
atlasAssets:
- {fileID: 11400000, guid: 071eeff112dbeca4e961d78214f41c38, type: 2}
scale: 0.01
skeletonJSON: {fileID: 4900000, guid: 05c17836f4f4fe748b11ad96e9436421, type: 3}
skeletonDataModifiers: []
fromAnimation: []
toAnimation: []
duration: []
defaultMix: 0.2
controller: {fileID: 0}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 91a60be3ea058c245b89efb440a49a58
timeCreated: 1601458175
licenseType: Pro
NativeFormatImporter:
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant: