备份CatanBuilding瘦身独立工程
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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 Spine;
|
||||
using Spine.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
public class BoneLocalOverride : MonoBehaviour {
|
||||
[SpineBone]
|
||||
public string boneName;
|
||||
|
||||
[Space]
|
||||
[Range(0, 1)] public float alpha = 1;
|
||||
|
||||
[Space]
|
||||
public bool overridePosition = true;
|
||||
public Vector2 localPosition;
|
||||
|
||||
[Space]
|
||||
public bool overrideRotation = true;
|
||||
[Range(0, 360)] public float rotation = 0;
|
||||
|
||||
ISkeletonAnimation spineComponent;
|
||||
Bone bone;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void OnValidate () {
|
||||
if (Application.isPlaying) return;
|
||||
if (spineComponent == null) spineComponent = GetComponent<ISkeletonAnimation>();
|
||||
if (spineComponent.IsNullOrDestroyed()) return;
|
||||
if (bone != null) bone.SetToSetupPose();
|
||||
OverrideLocal(spineComponent);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Awake () {
|
||||
spineComponent = GetComponent<ISkeletonAnimation>();
|
||||
if (spineComponent == null) { this.enabled = false; return; }
|
||||
spineComponent.UpdateLocal += OverrideLocal;
|
||||
|
||||
if (bone == null) { this.enabled = false; return; }
|
||||
}
|
||||
|
||||
void OverrideLocal (ISkeletonAnimation animated) {
|
||||
if (bone == null || bone.Data.Name != boneName) {
|
||||
if (string.IsNullOrEmpty(boneName)) return;
|
||||
bone = spineComponent.Skeleton.FindBone(boneName);
|
||||
if (bone == null) {
|
||||
Debug.LogFormat("Cannot find bone: '{0}'", boneName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (overridePosition) {
|
||||
bone.X = Mathf.Lerp(bone.X, localPosition.x, alpha);
|
||||
bone.Y = Mathf.Lerp(bone.Y, localPosition.y, alpha);
|
||||
}
|
||||
|
||||
if (overrideRotation)
|
||||
bone.Rotation = Mathf.Lerp(bone.Rotation, rotation, alpha);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 61e6f96a4b02648479575d8b9127f5ed
|
||||
timeCreated: 1492782100
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,62 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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 Spine.Unity.AttachmentTools;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
public class CombinedSkin : MonoBehaviour {
|
||||
[SpineSkin]
|
||||
public List<string> skinsToCombine;
|
||||
|
||||
Skin combinedSkin;
|
||||
|
||||
void Start () {
|
||||
ISkeletonComponent skeletonComponent = GetComponent<ISkeletonComponent>();
|
||||
if (skeletonComponent == null) return;
|
||||
Skeleton skeleton = skeletonComponent.Skeleton;
|
||||
if (skeleton == null) return;
|
||||
|
||||
combinedSkin = combinedSkin ?? new Skin("combined");
|
||||
combinedSkin.Clear();
|
||||
foreach (string skinName in skinsToCombine) {
|
||||
Skin skin = skeleton.Data.FindSkin(skinName);
|
||||
if (skin != null) combinedSkin.AddSkin(skin);
|
||||
}
|
||||
|
||||
skeleton.SetSkin(combinedSkin);
|
||||
skeleton.SetToSetupPose();
|
||||
IAnimationStateComponent animationStateComponent = skeletonComponent as IAnimationStateComponent;
|
||||
if (animationStateComponent != null) animationStateComponent.AnimationState.Apply(skeleton);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e864d2963143ec04eb4f905e6add115e
|
||||
timeCreated: 1495176902
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 13193c9d213765f4c85f4c1faa615711
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
@@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a0cee0de78cef7440ae0b5aac39ae971
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
@@ -0,0 +1,67 @@
|
||||
// - Unlit + no shadow
|
||||
// - Premultiplied Alpha Blending (One OneMinusSrcAlpha)
|
||||
// - Double-sided, no depth
|
||||
|
||||
Shader "Spine/Special/SkeletonGhost" {
|
||||
Properties {
|
||||
_Color ("Main Color", Color) = (1,1,1,1)
|
||||
[NoScaleOffset] _MainTex ("Base (RGB) Alpha (A)", 2D) = "white" {}
|
||||
_TextureFade ("Texture Fade Out", Range(0,1)) = 0
|
||||
[HideInInspector] _StencilRef("Stencil Reference", Float) = 1.0
|
||||
[HideInInspector][Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp("Stencil Comparison", Float) = 8 // Set to Always as default
|
||||
}
|
||||
SubShader {
|
||||
Tags {
|
||||
"Queue"="Transparent"
|
||||
"IgnoreProjector"="False"
|
||||
"RenderType"="Transparent"
|
||||
}
|
||||
Fog { Mode Off }
|
||||
Blend One OneMinusSrcAlpha
|
||||
ZWrite Off
|
||||
Cull Off
|
||||
|
||||
Stencil {
|
||||
Ref[_StencilRef]
|
||||
Comp[_StencilComp]
|
||||
Pass Keep
|
||||
}
|
||||
|
||||
Pass {
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#include "UnityCG.cginc"
|
||||
sampler2D _MainTex;
|
||||
fixed4 _Color;
|
||||
fixed _TextureFade;
|
||||
|
||||
struct VertexInput {
|
||||
float4 vertex : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
float4 color : COLOR;
|
||||
};
|
||||
|
||||
struct VertexOutput {
|
||||
float4 pos : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
float4 color : COLOR;
|
||||
};
|
||||
|
||||
VertexOutput vert (VertexInput v) {
|
||||
VertexOutput o;
|
||||
o.pos = UnityObjectToClipPos(v.vertex);
|
||||
o.uv = v.uv;
|
||||
o.color = v.color;
|
||||
return o;
|
||||
}
|
||||
|
||||
fixed4 frag (VertexOutput i) : SV_Target {
|
||||
fixed4 tc = tex2D(_MainTex, i.uv);
|
||||
tc = fixed4(max(_TextureFade, tc.r), max(_TextureFade, tc.g), max(_TextureFade, tc.b), tc.a);
|
||||
return tc * ((i.color * _Color) * tc.a);
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3873d4699ee8a4b4da8fa6b8c229b94d
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,201 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
// Contributed by: Mitch Thompson
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
|
||||
[RequireComponent(typeof(SkeletonRenderer))]
|
||||
public class SkeletonGhost : MonoBehaviour {
|
||||
// Internal Settings
|
||||
const HideFlags GhostHideFlags = HideFlags.HideInHierarchy;
|
||||
const string GhostingShaderName = "Spine/Special/SkeletonGhost";
|
||||
|
||||
[Header("Animation")]
|
||||
public bool ghostingEnabled = true;
|
||||
[Tooltip("The time between invididual ghost pieces being spawned.")]
|
||||
[UnityEngine.Serialization.FormerlySerializedAs("spawnRate")]
|
||||
public float spawnInterval = 1f / 30f;
|
||||
[Tooltip("Maximum number of ghosts that can exist at a time. If the fade speed is not fast enough, the oldest ghost will immediately disappear to enforce the maximum number.")]
|
||||
public int maximumGhosts = 10;
|
||||
public float fadeSpeed = 10;
|
||||
|
||||
[Header("Rendering")]
|
||||
public Shader ghostShader;
|
||||
public Color32 color = new Color32(0xFF, 0xFF, 0xFF, 0x00); // default for additive.
|
||||
[Tooltip("Remember to set color alpha to 0 if Additive is true")]
|
||||
public bool additive = true;
|
||||
[Tooltip("0 is Color and Alpha, 1 is Alpha only.")]
|
||||
[Range(0, 1)]
|
||||
public float textureFade = 1;
|
||||
|
||||
[Header("Sorting")]
|
||||
public bool sortWithDistanceOnly;
|
||||
public float zOffset = 0f;
|
||||
|
||||
float nextSpawnTime;
|
||||
SkeletonGhostRenderer[] pool;
|
||||
int poolIndex = 0;
|
||||
SkeletonRenderer skeletonRenderer;
|
||||
MeshRenderer meshRenderer;
|
||||
MeshFilter meshFilter;
|
||||
|
||||
readonly Dictionary<Material, Material> materialTable = new Dictionary<Material, Material>();
|
||||
|
||||
void Start () {
|
||||
Initialize(false);
|
||||
}
|
||||
|
||||
public void Initialize (bool overwrite) {
|
||||
if (pool == null || overwrite) {
|
||||
if (ghostShader == null)
|
||||
ghostShader = Shader.Find(GhostingShaderName);
|
||||
|
||||
skeletonRenderer = GetComponent<SkeletonRenderer>();
|
||||
meshFilter = GetComponent<MeshFilter>();
|
||||
meshRenderer = GetComponent<MeshRenderer>();
|
||||
nextSpawnTime = Time.time + spawnInterval;
|
||||
pool = new SkeletonGhostRenderer[maximumGhosts];
|
||||
for (int i = 0; i < maximumGhosts; i++) {
|
||||
GameObject go = new GameObject(gameObject.name + " Ghost", typeof(SkeletonGhostRenderer));
|
||||
pool[i] = go.GetComponent<SkeletonGhostRenderer>();
|
||||
go.SetActive(false);
|
||||
go.hideFlags = GhostHideFlags;
|
||||
}
|
||||
|
||||
IAnimationStateComponent skeletonAnimation = skeletonRenderer as Spine.Unity.IAnimationStateComponent;
|
||||
if (skeletonAnimation != null)
|
||||
skeletonAnimation.AnimationState.Event += OnEvent;
|
||||
}
|
||||
}
|
||||
|
||||
//SkeletonAnimation
|
||||
/*
|
||||
* Int Value: 0 sets ghostingEnabled to false, 1 sets ghostingEnabled to true
|
||||
* Float Value: Values greater than 0 set the spawnRate equal the float value
|
||||
* String Value: Pass RGBA hex color values in to set the color property. IE: "A0FF8BFF"
|
||||
*/
|
||||
void OnEvent (Spine.TrackEntry trackEntry, Spine.Event e) {
|
||||
if (e.Data.Name.Equals("Ghosting", System.StringComparison.Ordinal)) {
|
||||
ghostingEnabled = e.Int > 0;
|
||||
if (e.Float > 0)
|
||||
spawnInterval = e.Float;
|
||||
|
||||
if (!string.IsNullOrEmpty(e.String))
|
||||
this.color = HexToColor(e.String);
|
||||
}
|
||||
}
|
||||
|
||||
//SkeletonAnimator
|
||||
//SkeletonAnimator or Mecanim based animations only support toggling ghostingEnabled. Be sure not to set anything other than the Int param in Spine or String will take priority.
|
||||
void Ghosting (float val) {
|
||||
ghostingEnabled = val > 0;
|
||||
}
|
||||
|
||||
void Update () {
|
||||
if (!ghostingEnabled || poolIndex >= pool.Length)
|
||||
return;
|
||||
|
||||
if (Time.time >= nextSpawnTime) {
|
||||
GameObject go = pool[poolIndex].gameObject;
|
||||
|
||||
Material[] materials = meshRenderer.sharedMaterials;
|
||||
for (int i = 0; i < materials.Length; i++) {
|
||||
Material originalMat = materials[i];
|
||||
Material ghostMat;
|
||||
if (!materialTable.ContainsKey(originalMat)) {
|
||||
ghostMat = new Material(originalMat) {
|
||||
shader = ghostShader,
|
||||
color = Color.white
|
||||
};
|
||||
|
||||
if (ghostMat.HasProperty("_TextureFade"))
|
||||
ghostMat.SetFloat("_TextureFade", textureFade);
|
||||
|
||||
materialTable.Add(originalMat, ghostMat);
|
||||
} else {
|
||||
ghostMat = materialTable[originalMat];
|
||||
}
|
||||
|
||||
materials[i] = ghostMat;
|
||||
}
|
||||
|
||||
Transform goTransform = go.transform;
|
||||
goTransform.parent = transform;
|
||||
|
||||
pool[poolIndex].Initialize(meshFilter.sharedMesh, materials, color, additive, fadeSpeed, meshRenderer.sortingLayerID, (sortWithDistanceOnly) ? meshRenderer.sortingOrder : meshRenderer.sortingOrder - 1);
|
||||
|
||||
goTransform.localPosition = new Vector3(0f, 0f, zOffset);
|
||||
goTransform.localRotation = Quaternion.identity;
|
||||
goTransform.localScale = Vector3.one;
|
||||
|
||||
goTransform.parent = null;
|
||||
|
||||
poolIndex++;
|
||||
|
||||
if (poolIndex == pool.Length)
|
||||
poolIndex = 0;
|
||||
|
||||
nextSpawnTime = Time.time + spawnInterval;
|
||||
}
|
||||
}
|
||||
|
||||
void OnDestroy () {
|
||||
if (pool != null) {
|
||||
for (int i = 0; i < maximumGhosts; i++)
|
||||
if (pool[i] != null) pool[i].Cleanup();
|
||||
}
|
||||
|
||||
foreach (Material mat in materialTable.Values)
|
||||
Destroy(mat);
|
||||
}
|
||||
|
||||
// based on UnifyWiki http://wiki.unity3d.com/index.php?title=HexConverter
|
||||
static Color32 HexToColor (string hex) {
|
||||
const System.Globalization.NumberStyles HexNumber = System.Globalization.NumberStyles.HexNumber;
|
||||
|
||||
if (hex.Length < 6)
|
||||
return Color.magenta;
|
||||
|
||||
hex = hex.Replace("#", "");
|
||||
byte r = byte.Parse(hex.Substring(0, 2), HexNumber);
|
||||
byte g = byte.Parse(hex.Substring(2, 2), HexNumber);
|
||||
byte b = byte.Parse(hex.Substring(4, 2), HexNumber);
|
||||
byte a = 0xFF;
|
||||
if (hex.Length == 8)
|
||||
a = byte.Parse(hex.Substring(6, 2), HexNumber);
|
||||
|
||||
return new Color32(r, g, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 02f2fa991881c6d419500ccc40ad443f
|
||||
timeCreated: 1431858330
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- ghostShader: {fileID: 4800000, guid: 3873d4699ee8a4b4da8fa6b8c229b94d, type: 3}
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,128 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
// Contributed by: Mitch Thompson
|
||||
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
public class SkeletonGhostRenderer : MonoBehaviour {
|
||||
static readonly Color32 TransparentBlack = new Color32(0, 0, 0, 0);
|
||||
const string colorPropertyName = "_Color";
|
||||
|
||||
float fadeSpeed = 10;
|
||||
Color32 startColor;
|
||||
MeshFilter meshFilter;
|
||||
MeshRenderer meshRenderer;
|
||||
|
||||
MaterialPropertyBlock mpb;
|
||||
int colorId;
|
||||
|
||||
void Awake () {
|
||||
meshRenderer = gameObject.AddComponent<MeshRenderer>();
|
||||
meshFilter = gameObject.AddComponent<MeshFilter>();
|
||||
|
||||
colorId = Shader.PropertyToID(colorPropertyName);
|
||||
mpb = new MaterialPropertyBlock();
|
||||
}
|
||||
|
||||
public void Initialize (Mesh mesh, Material[] materials, Color32 color, bool additive, float speed, int sortingLayerID, int sortingOrder) {
|
||||
StopAllCoroutines();
|
||||
|
||||
gameObject.SetActive(true);
|
||||
meshRenderer.sharedMaterials = materials;
|
||||
meshRenderer.sortingLayerID = sortingLayerID;
|
||||
meshRenderer.sortingOrder = sortingOrder;
|
||||
meshFilter.sharedMesh = Instantiate(mesh);
|
||||
startColor = color;
|
||||
mpb.SetColor(colorId, color);
|
||||
meshRenderer.SetPropertyBlock(mpb);
|
||||
|
||||
fadeSpeed = speed;
|
||||
|
||||
if (additive)
|
||||
StartCoroutine(FadeAdditive());
|
||||
else
|
||||
StartCoroutine(Fade());
|
||||
}
|
||||
|
||||
IEnumerator Fade () {
|
||||
Color32 c = startColor;
|
||||
Color32 black = SkeletonGhostRenderer.TransparentBlack;
|
||||
|
||||
float t = 1f;
|
||||
for (float hardTimeLimit = 5f; hardTimeLimit > 0; hardTimeLimit -= Time.deltaTime) {
|
||||
c = Color32.Lerp(black, startColor, t);
|
||||
mpb.SetColor(colorId, c);
|
||||
meshRenderer.SetPropertyBlock(mpb);
|
||||
|
||||
t = Mathf.Lerp(t, 0, Time.deltaTime * fadeSpeed);
|
||||
if (t <= 0)
|
||||
break;
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
Destroy(meshFilter.sharedMesh);
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
IEnumerator FadeAdditive () {
|
||||
Color32 c = startColor;
|
||||
Color32 black = SkeletonGhostRenderer.TransparentBlack;
|
||||
|
||||
float t = 1f;
|
||||
|
||||
for (float hardTimeLimit = 5f; hardTimeLimit > 0; hardTimeLimit -= Time.deltaTime) {
|
||||
c = Color32.Lerp(black, startColor, t);
|
||||
mpb.SetColor(colorId, c);
|
||||
meshRenderer.SetPropertyBlock(mpb);
|
||||
|
||||
t = Mathf.Lerp(t, 0, Time.deltaTime * fadeSpeed);
|
||||
if (t <= 0)
|
||||
break;
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
Destroy(meshFilter.sharedMesh);
|
||||
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
public void Cleanup () {
|
||||
if (meshFilter != null && meshFilter.sharedMesh != null)
|
||||
Destroy(meshFilter.sharedMesh);
|
||||
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 58e3a9b80754b7545a1dff4d8475b51f
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8033179a12443e4eaef33b35fed074c
|
||||
folderAsset: yes
|
||||
timeCreated: 1495179724
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,81 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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 Spine;
|
||||
using Spine.Unity.AttachmentTools;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
/// <summary>
|
||||
/// Example code for a component that replaces the default attachment of a slot with an image from a Spine atlas.</summary>
|
||||
public class AtlasRegionAttacher : MonoBehaviour {
|
||||
|
||||
[System.Serializable]
|
||||
public class SlotRegionPair {
|
||||
[SpineSlot] public string slot;
|
||||
[SpineAtlasRegion] public string region;
|
||||
}
|
||||
|
||||
[SerializeField] protected SpineAtlasAsset atlasAsset;
|
||||
[SerializeField] protected bool inheritProperties = true;
|
||||
[SerializeField] protected List<SlotRegionPair> attachments = new List<SlotRegionPair>();
|
||||
|
||||
Atlas atlas;
|
||||
|
||||
void Awake () {
|
||||
SkeletonRenderer skeletonRenderer = GetComponent<SkeletonRenderer>();
|
||||
skeletonRenderer.OnRebuild += Apply;
|
||||
if (skeletonRenderer.valid) Apply(skeletonRenderer);
|
||||
}
|
||||
|
||||
void Apply (SkeletonRenderer skeletonRenderer) {
|
||||
if (!this.enabled) return;
|
||||
|
||||
atlas = atlasAsset.GetAtlas();
|
||||
if (atlas == null) return;
|
||||
float scale = skeletonRenderer.skeletonDataAsset.scale;
|
||||
|
||||
foreach (SlotRegionPair entry in attachments) {
|
||||
Slot slot = skeletonRenderer.Skeleton.FindSlot(entry.slot);
|
||||
Attachment originalAttachment = slot.Attachment;
|
||||
AtlasRegion region = atlas.FindRegion(entry.region);
|
||||
|
||||
if (region == null) {
|
||||
slot.Attachment = null;
|
||||
} else if (inheritProperties && originalAttachment != null) {
|
||||
slot.Attachment = originalAttachment.GetRemappedClone(region, true, true, scale);
|
||||
} else {
|
||||
RegionAttachment newRegionAttachment = region.ToRegionAttachment(region.name, scale);
|
||||
slot.Attachment = newRegionAttachment;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: afde57cc4fd39bb4dbd61b73403ae6a8
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,184 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
// Original Contribution by: Mitch Thompson
|
||||
|
||||
using Spine.Unity.AttachmentTools;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
public class SpriteAttacher : MonoBehaviour {
|
||||
public const string DefaultPMAShader = "Spine/Skeleton";
|
||||
public const string DefaultStraightAlphaShader = "Sprites/Default";
|
||||
|
||||
#region Inspector
|
||||
public bool attachOnStart = true;
|
||||
public bool overrideAnimation = true;
|
||||
public Sprite sprite;
|
||||
[SpineSlot] public string slot;
|
||||
#endregion
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void OnValidate () {
|
||||
ISkeletonComponent skeletonComponent = GetComponent<ISkeletonComponent>();
|
||||
SkeletonRenderer skeletonRenderer = skeletonComponent as SkeletonRenderer;
|
||||
bool applyPMA;
|
||||
|
||||
if (skeletonRenderer != null) {
|
||||
applyPMA = skeletonRenderer.pmaVertexColors;
|
||||
} else {
|
||||
SkeletonGraphic skeletonGraphic = skeletonComponent as SkeletonGraphic;
|
||||
applyPMA = skeletonGraphic != null && skeletonGraphic.MeshGenerator.settings.pmaVertexColors;
|
||||
}
|
||||
|
||||
if (applyPMA) {
|
||||
try {
|
||||
if (sprite == null)
|
||||
return;
|
||||
sprite.texture.GetPixel(0, 0);
|
||||
} catch (UnityException e) {
|
||||
Debug.LogFormat("Texture of {0} ({1}) is not read/write enabled. SpriteAttacher requires this in order to work with a SkeletonRenderer that renders premultiplied alpha. Please check the texture settings.", sprite.name, sprite.texture.name);
|
||||
UnityEditor.EditorGUIUtility.PingObject(sprite.texture);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
RegionAttachment attachment;
|
||||
Slot spineSlot;
|
||||
bool applyPMA;
|
||||
|
||||
static Dictionary<Texture, AtlasPage> atlasPageCache;
|
||||
static AtlasPage GetPageFor (Texture texture, Shader shader) {
|
||||
if (atlasPageCache == null) atlasPageCache = new Dictionary<Texture, AtlasPage>();
|
||||
AtlasPage atlasPage;
|
||||
atlasPageCache.TryGetValue(texture, out atlasPage);
|
||||
if (atlasPage == null) {
|
||||
Material newMaterial = new Material(shader);
|
||||
atlasPage = newMaterial.ToSpineAtlasPage();
|
||||
atlasPageCache[texture] = atlasPage;
|
||||
}
|
||||
return atlasPage;
|
||||
}
|
||||
|
||||
void Start () {
|
||||
// Initialize slot and attachment references.
|
||||
Initialize(false);
|
||||
|
||||
if (attachOnStart)
|
||||
Attach();
|
||||
}
|
||||
|
||||
void AnimationOverrideSpriteAttach (ISkeletonAnimation animated) {
|
||||
if (overrideAnimation && isActiveAndEnabled)
|
||||
Attach();
|
||||
}
|
||||
|
||||
public void Initialize (bool overwrite = true) {
|
||||
if (overwrite || attachment == null) {
|
||||
// Get the applyPMA value.
|
||||
ISkeletonComponent skeletonComponent = GetComponent<ISkeletonComponent>();
|
||||
SkeletonRenderer skeletonRenderer = skeletonComponent as SkeletonRenderer;
|
||||
if (skeletonRenderer != null)
|
||||
this.applyPMA = skeletonRenderer.pmaVertexColors;
|
||||
else {
|
||||
SkeletonGraphic skeletonGraphic = skeletonComponent as SkeletonGraphic;
|
||||
if (skeletonGraphic != null)
|
||||
this.applyPMA = skeletonGraphic.MeshGenerator.settings.pmaVertexColors;
|
||||
}
|
||||
|
||||
// Subscribe to UpdateComplete to override animation keys.
|
||||
if (overrideAnimation) {
|
||||
ISkeletonAnimation animatedSkeleton = skeletonComponent as ISkeletonAnimation;
|
||||
if (animatedSkeleton != null) {
|
||||
animatedSkeleton.UpdateComplete -= AnimationOverrideSpriteAttach;
|
||||
animatedSkeleton.UpdateComplete += AnimationOverrideSpriteAttach;
|
||||
}
|
||||
}
|
||||
|
||||
spineSlot = spineSlot ?? skeletonComponent.Skeleton.FindSlot(slot);
|
||||
Shader attachmentShader = applyPMA ? Shader.Find(DefaultPMAShader) : Shader.Find(DefaultStraightAlphaShader);
|
||||
if (sprite == null)
|
||||
attachment = null;
|
||||
else
|
||||
attachment = applyPMA ? sprite.ToRegionAttachmentPMAClone(attachmentShader) : sprite.ToRegionAttachment(SpriteAttacher.GetPageFor(sprite.texture, attachmentShader));
|
||||
}
|
||||
}
|
||||
|
||||
void OnDestroy () {
|
||||
ISkeletonAnimation animatedSkeleton = GetComponent<ISkeletonAnimation>();
|
||||
if (animatedSkeleton != null)
|
||||
animatedSkeleton.UpdateComplete -= AnimationOverrideSpriteAttach;
|
||||
}
|
||||
|
||||
/// <summary>Update the slot's attachment to the Attachment generated from the sprite.</summary>
|
||||
public void Attach () {
|
||||
if (spineSlot != null)
|
||||
spineSlot.Attachment = attachment;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static class SpriteAttachmentExtensions {
|
||||
[System.Obsolete]
|
||||
public static RegionAttachment AttachUnitySprite (this Skeleton skeleton, string slotName, Sprite sprite, string shaderName = SpriteAttacher.DefaultPMAShader, bool applyPMA = true, float rotation = 0f) {
|
||||
return skeleton.AttachUnitySprite(slotName, sprite, Shader.Find(shaderName), applyPMA, rotation: rotation);
|
||||
}
|
||||
|
||||
[System.Obsolete]
|
||||
public static RegionAttachment AddUnitySprite (this SkeletonData skeletonData, string slotName, Sprite sprite, string skinName = "", string shaderName = SpriteAttacher.DefaultPMAShader, bool applyPMA = true, float rotation = 0f) {
|
||||
return skeletonData.AddUnitySprite(slotName, sprite, skinName, Shader.Find(shaderName), applyPMA, rotation: rotation);
|
||||
}
|
||||
|
||||
[System.Obsolete]
|
||||
public static RegionAttachment AttachUnitySprite (this Skeleton skeleton, string slotName, Sprite sprite, Shader shader, bool applyPMA, float rotation = 0f) {
|
||||
RegionAttachment att = applyPMA ? sprite.ToRegionAttachmentPMAClone(shader, rotation: rotation) : sprite.ToRegionAttachment(new Material(shader), rotation: rotation);
|
||||
skeleton.FindSlot(slotName).Attachment = att;
|
||||
return att;
|
||||
}
|
||||
|
||||
[System.Obsolete]
|
||||
public static RegionAttachment AddUnitySprite (this SkeletonData skeletonData, string slotName, Sprite sprite, string skinName, Shader shader, bool applyPMA, float rotation = 0f) {
|
||||
RegionAttachment att = applyPMA ? sprite.ToRegionAttachmentPMAClone(shader, rotation: rotation) : sprite.ToRegionAttachment(new Material(shader), rotation);
|
||||
|
||||
int slotIndex = skeletonData.FindSlot(slotName).Index;
|
||||
Skin skin = skeletonData.DefaultSkin;
|
||||
if (skinName != "")
|
||||
skin = skeletonData.FindSkin(skinName);
|
||||
|
||||
if (skin != null)
|
||||
skin.SetAttachment(slotIndex, att.Name, att);
|
||||
|
||||
return att;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ee7b5e36685e2445a0097de42940987
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,69 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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 Spine.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
|
||||
public class OutlineSkeletonGraphic : MonoBehaviour {
|
||||
|
||||
public SkeletonGraphic skeletonGraphic;
|
||||
public Material materialWithoutOutline;
|
||||
public Material materialWithOutline;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void Reset () {
|
||||
skeletonGraphic = GetComponent<SkeletonGraphic>();
|
||||
|
||||
// Add normal material as default
|
||||
if (skeletonGraphic != null && skeletonGraphic.skeletonDataAsset != null) {
|
||||
AtlasAssetBase[] atlasAssets = skeletonGraphic.skeletonDataAsset.atlasAssets;
|
||||
|
||||
if (atlasAssets.Length > 0 && atlasAssets[0].PrimaryMaterial) {
|
||||
materialWithoutOutline = atlasAssets[0].PrimaryMaterial;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void OnEnable () {
|
||||
if (skeletonGraphic == null)
|
||||
skeletonGraphic = GetComponent<SkeletonGraphic>();
|
||||
}
|
||||
|
||||
public void EnableOutlineRendering () {
|
||||
skeletonGraphic.material = materialWithOutline;
|
||||
}
|
||||
|
||||
public void DisableOutlineRendering () {
|
||||
skeletonGraphic.material = materialWithoutOutline;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 78ceaa27e3b3c27498dcdd7729ebad83
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,268 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
|
||||
#define NEW_PREFAB_SYSTEM
|
||||
#endif
|
||||
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
#define SET_VERTICES_HAS_LENGTH_PARAMETER
|
||||
#endif
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
|
||||
#if NEW_PREFAB_SYSTEM
|
||||
[ExecuteAlways]
|
||||
#else
|
||||
[ExecuteInEditMode]
|
||||
#endif
|
||||
public class RenderCombinedMesh : MonoBehaviour {
|
||||
public SkeletonRenderer skeletonRenderer;
|
||||
public SkeletonRenderSeparator renderSeparator;
|
||||
public MeshRenderer[] referenceRenderers;
|
||||
|
||||
bool updateViaSkeletonCallback = false;
|
||||
MeshFilter[] referenceMeshFilters;
|
||||
MeshRenderer ownRenderer;
|
||||
MeshFilter ownMeshFilter;
|
||||
|
||||
protected DoubleBuffered<Mesh> doubleBufferedMesh;
|
||||
protected ExposedList<Vector3> positionBuffer;
|
||||
protected ExposedList<Color32> colorBuffer;
|
||||
protected ExposedList<Vector2> uvBuffer;
|
||||
protected ExposedList<int> indexBuffer;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void Reset () {
|
||||
if (skeletonRenderer == null)
|
||||
skeletonRenderer = this.GetComponentInParent<SkeletonRenderer>();
|
||||
GatherRenderers();
|
||||
|
||||
Awake();
|
||||
if (referenceRenderers.Length > 0)
|
||||
ownRenderer.sharedMaterial = referenceRenderers[0].sharedMaterial;
|
||||
|
||||
LateUpdate();
|
||||
}
|
||||
#endif
|
||||
protected void GatherRenderers () {
|
||||
referenceRenderers = this.GetComponentsInChildren<MeshRenderer>();
|
||||
if (referenceRenderers.Length == 0 ||
|
||||
(referenceRenderers.Length == 1 && referenceRenderers[0].gameObject == this.gameObject)) {
|
||||
Transform parent = this.transform.parent;
|
||||
if (parent)
|
||||
referenceRenderers = parent.GetComponentsInChildren<MeshRenderer>();
|
||||
}
|
||||
referenceRenderers = referenceRenderers.Where(
|
||||
(val, idx) => val.gameObject != this.gameObject && val.enabled).ToArray();
|
||||
}
|
||||
|
||||
void Awake () {
|
||||
if (skeletonRenderer == null)
|
||||
skeletonRenderer = this.GetComponentInParent<SkeletonRenderer>();
|
||||
if (referenceRenderers == null || referenceRenderers.Length == 0) {
|
||||
GatherRenderers();
|
||||
}
|
||||
|
||||
if (renderSeparator == null) {
|
||||
if (skeletonRenderer)
|
||||
renderSeparator = skeletonRenderer.GetComponent<SkeletonRenderSeparator>();
|
||||
else
|
||||
renderSeparator = this.GetComponentInParent<SkeletonRenderSeparator>();
|
||||
}
|
||||
|
||||
int count = referenceRenderers.Length;
|
||||
referenceMeshFilters = new MeshFilter[count];
|
||||
for (int i = 0; i < count; ++i) {
|
||||
referenceMeshFilters[i] = referenceRenderers[i].GetComponent<MeshFilter>();
|
||||
}
|
||||
|
||||
ownRenderer = this.GetComponent<MeshRenderer>();
|
||||
if (ownRenderer == null)
|
||||
ownRenderer = this.gameObject.AddComponent<MeshRenderer>();
|
||||
ownMeshFilter = this.GetComponent<MeshFilter>();
|
||||
if (ownMeshFilter == null)
|
||||
ownMeshFilter = this.gameObject.AddComponent<MeshFilter>();
|
||||
}
|
||||
|
||||
void OnEnable () {
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isPlaying)
|
||||
Awake();
|
||||
#endif
|
||||
if (skeletonRenderer) {
|
||||
skeletonRenderer.OnMeshAndMaterialsUpdated -= UpdateOnCallback;
|
||||
skeletonRenderer.OnMeshAndMaterialsUpdated += UpdateOnCallback;
|
||||
updateViaSkeletonCallback = true;
|
||||
}
|
||||
if (renderSeparator) {
|
||||
renderSeparator.OnMeshAndMaterialsUpdated -= UpdateOnCallback;
|
||||
renderSeparator.OnMeshAndMaterialsUpdated += UpdateOnCallback;
|
||||
updateViaSkeletonCallback = true;
|
||||
}
|
||||
}
|
||||
|
||||
void OnDisable () {
|
||||
if (skeletonRenderer)
|
||||
skeletonRenderer.OnMeshAndMaterialsUpdated -= UpdateOnCallback;
|
||||
if (renderSeparator)
|
||||
renderSeparator.OnMeshAndMaterialsUpdated -= UpdateOnCallback;
|
||||
}
|
||||
|
||||
void OnDestroy () {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
Mesh mesh = doubleBufferedMesh.GetNext();
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isEditor && !Application.isPlaying)
|
||||
UnityEngine.Object.DestroyImmediate(mesh);
|
||||
else
|
||||
UnityEngine.Object.Destroy(mesh);
|
||||
#else
|
||||
UnityEngine.Object.Destroy(mesh);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void LateUpdate () {
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying) {
|
||||
UpdateMesh();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (updateViaSkeletonCallback)
|
||||
return;
|
||||
UpdateMesh();
|
||||
}
|
||||
|
||||
void UpdateOnCallback (SkeletonRenderer r) {
|
||||
UpdateMesh();
|
||||
}
|
||||
|
||||
protected void EnsureBufferSizes (int combinedVertexCount, int combinedIndexCount) {
|
||||
if (positionBuffer == null) {
|
||||
positionBuffer = new ExposedList<Vector3>(combinedVertexCount);
|
||||
uvBuffer = new ExposedList<Vector2>(combinedVertexCount);
|
||||
colorBuffer = new ExposedList<Color32>(combinedVertexCount);
|
||||
indexBuffer = new ExposedList<int>(combinedIndexCount);
|
||||
}
|
||||
|
||||
if (positionBuffer.Count != combinedVertexCount) {
|
||||
positionBuffer.Resize(combinedVertexCount);
|
||||
uvBuffer.Resize(combinedVertexCount);
|
||||
colorBuffer.Resize(combinedVertexCount);
|
||||
}
|
||||
if (indexBuffer.Count != combinedIndexCount) {
|
||||
indexBuffer.Resize(combinedIndexCount);
|
||||
}
|
||||
}
|
||||
|
||||
void InitMesh () {
|
||||
if (doubleBufferedMesh == null) {
|
||||
doubleBufferedMesh = new DoubleBuffered<Mesh>();
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
Mesh combinedMesh = doubleBufferedMesh.GetNext();
|
||||
combinedMesh.MarkDynamic();
|
||||
combinedMesh.name = "RenderCombinedMesh" + i;
|
||||
combinedMesh.subMeshCount = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateMesh () {
|
||||
InitMesh();
|
||||
int combinedVertexCount = 0;
|
||||
int combinedIndexCount = 0;
|
||||
GetCombinedMeshInfo(ref combinedVertexCount, ref combinedIndexCount);
|
||||
|
||||
EnsureBufferSizes(combinedVertexCount, combinedIndexCount);
|
||||
|
||||
int combinedV = 0;
|
||||
int combinedI = 0;
|
||||
for (int r = 0, rendererCount = referenceMeshFilters.Length; r < rendererCount; ++r) {
|
||||
MeshFilter meshFilter = referenceMeshFilters[r];
|
||||
Mesh mesh = meshFilter.sharedMesh;
|
||||
if (mesh == null) continue;
|
||||
|
||||
int vertexCount = mesh.vertexCount;
|
||||
Vector3[] positions = mesh.vertices;
|
||||
Vector2[] uvs = mesh.uv;
|
||||
Color32[] colors = mesh.colors32;
|
||||
|
||||
System.Array.Copy(positions, 0, this.positionBuffer.Items, combinedV, vertexCount);
|
||||
System.Array.Copy(uvs, 0, this.uvBuffer.Items, combinedV, vertexCount);
|
||||
System.Array.Copy(colors, 0, this.colorBuffer.Items, combinedV, vertexCount);
|
||||
|
||||
for (int s = 0, submeshCount = mesh.subMeshCount; s < submeshCount; ++s) {
|
||||
int submeshIndexCount = (int)mesh.GetIndexCount(s);
|
||||
int[] submeshIndices = mesh.GetIndices(s);
|
||||
int[] dstIndices = this.indexBuffer.Items;
|
||||
for (int i = 0; i < submeshIndexCount; ++i)
|
||||
dstIndices[i + combinedI] = submeshIndices[i] + combinedV;
|
||||
combinedI += submeshIndexCount;
|
||||
}
|
||||
combinedV += vertexCount;
|
||||
}
|
||||
|
||||
Mesh combinedMesh = doubleBufferedMesh.GetNext();
|
||||
combinedMesh.Clear();
|
||||
#if SET_VERTICES_HAS_LENGTH_PARAMETER
|
||||
combinedMesh.SetVertices(this.positionBuffer.Items, 0, this.positionBuffer.Count);
|
||||
combinedMesh.SetUVs(0, this.uvBuffer.Items, 0, this.uvBuffer.Count);
|
||||
combinedMesh.SetColors(this.colorBuffer.Items, 0, this.colorBuffer.Count);
|
||||
combinedMesh.SetTriangles(this.indexBuffer.Items, 0, this.indexBuffer.Count, 0);
|
||||
#else
|
||||
// Note: excess already contains zero positions and indices after ExposedList.Resize().
|
||||
combinedMesh.vertices = this.positionBuffer.Items;
|
||||
combinedMesh.uv = this.uvBuffer.Items;
|
||||
combinedMesh.colors32 = this.colorBuffer.Items;
|
||||
combinedMesh.triangles = this.indexBuffer.Items;
|
||||
#endif
|
||||
ownMeshFilter.sharedMesh = combinedMesh;
|
||||
}
|
||||
|
||||
void GetCombinedMeshInfo (ref int vertexCount, ref int indexCount) {
|
||||
for (int r = 0, rendererCount = referenceMeshFilters.Length; r < rendererCount; ++r) {
|
||||
MeshFilter meshFilter = referenceMeshFilters[r];
|
||||
Mesh mesh = meshFilter.sharedMesh;
|
||||
if (mesh == null) continue;
|
||||
|
||||
vertexCount += mesh.vertexCount;
|
||||
for (int s = 0, submeshCount = mesh.subMeshCount; s < submeshCount; ++s) {
|
||||
indexCount += (int)mesh.GetIndexCount(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 05709c69e8e14304b9781652ad05daef
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,157 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
|
||||
#define NEW_PREFAB_SYSTEM
|
||||
#endif
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
|
||||
#if NEW_PREFAB_SYSTEM
|
||||
[ExecuteAlways]
|
||||
#else
|
||||
[ExecuteInEditMode]
|
||||
#endif
|
||||
[RequireComponent(typeof(MeshRenderer)), RequireComponent(typeof(MeshFilter))]
|
||||
public class RenderExistingMesh : MonoBehaviour {
|
||||
public MeshRenderer referenceRenderer;
|
||||
|
||||
bool updateViaSkeletonCallback = false;
|
||||
MeshFilter referenceMeshFilter;
|
||||
MeshRenderer ownRenderer;
|
||||
MeshFilter ownMeshFilter;
|
||||
|
||||
[System.Serializable]
|
||||
public struct MaterialReplacement {
|
||||
public Material originalMaterial;
|
||||
public Material replacementMaterial;
|
||||
}
|
||||
public MaterialReplacement[] replacementMaterials = new MaterialReplacement[0];
|
||||
|
||||
private Dictionary<Material, Material> replacementMaterialDict = new Dictionary<Material, Material>();
|
||||
private Material[] sharedMaterials = new Material[0];
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void Reset () {
|
||||
if (referenceRenderer == null) {
|
||||
referenceRenderer = this.transform.parent.GetComponentInParent<MeshRenderer>();
|
||||
if (!referenceRenderer)
|
||||
return;
|
||||
}
|
||||
|
||||
Material[] parentMaterials = referenceRenderer.sharedMaterials;
|
||||
if (replacementMaterials.Length != parentMaterials.Length) {
|
||||
replacementMaterials = new MaterialReplacement[parentMaterials.Length];
|
||||
}
|
||||
for (int i = 0; i < parentMaterials.Length; ++i) {
|
||||
replacementMaterials[i].originalMaterial = parentMaterials[i];
|
||||
replacementMaterials[i].replacementMaterial = parentMaterials[i];
|
||||
}
|
||||
Awake();
|
||||
LateUpdate();
|
||||
}
|
||||
#endif
|
||||
|
||||
void Awake () {
|
||||
if (referenceRenderer == null) {
|
||||
referenceRenderer = this.transform.parent.GetComponentInParent<MeshRenderer>();
|
||||
}
|
||||
|
||||
// subscribe to OnMeshAndMaterialsUpdated
|
||||
SkeletonAnimation skeletonRenderer = referenceRenderer.GetComponent<SkeletonAnimation>();
|
||||
if (skeletonRenderer) {
|
||||
skeletonRenderer.OnMeshAndMaterialsUpdated -= UpdateOnCallback;
|
||||
skeletonRenderer.OnMeshAndMaterialsUpdated += UpdateOnCallback;
|
||||
updateViaSkeletonCallback = true;
|
||||
}
|
||||
referenceMeshFilter = referenceRenderer.GetComponent<MeshFilter>();
|
||||
ownRenderer = this.GetComponent<MeshRenderer>();
|
||||
ownMeshFilter = this.GetComponent<MeshFilter>();
|
||||
|
||||
InitializeDict();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// handle disabled scene reload
|
||||
private void OnEnable () {
|
||||
if (Application.isPlaying)
|
||||
Awake();
|
||||
}
|
||||
|
||||
private void Update () {
|
||||
if (!Application.isPlaying)
|
||||
InitializeDict();
|
||||
}
|
||||
#endif
|
||||
|
||||
void LateUpdate () {
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying) {
|
||||
UpdateMaterials();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (updateViaSkeletonCallback)
|
||||
return;
|
||||
UpdateMaterials();
|
||||
}
|
||||
|
||||
void UpdateOnCallback (SkeletonRenderer r) {
|
||||
UpdateMaterials();
|
||||
}
|
||||
|
||||
void UpdateMaterials () {
|
||||
ownMeshFilter.sharedMesh = referenceMeshFilter.sharedMesh;
|
||||
|
||||
Material[] parentMaterials = referenceRenderer.sharedMaterials;
|
||||
if (sharedMaterials.Length != parentMaterials.Length) {
|
||||
sharedMaterials = new Material[parentMaterials.Length];
|
||||
}
|
||||
for (int i = 0; i < parentMaterials.Length; ++i) {
|
||||
Material parentMaterial = parentMaterials[i];
|
||||
if (replacementMaterialDict.ContainsKey(parentMaterial)) {
|
||||
sharedMaterials[i] = replacementMaterialDict[parentMaterial];
|
||||
}
|
||||
}
|
||||
ownRenderer.sharedMaterials = sharedMaterials;
|
||||
}
|
||||
|
||||
void InitializeDict () {
|
||||
replacementMaterialDict.Clear();
|
||||
for (int i = 0; i < replacementMaterials.Length; ++i) {
|
||||
MaterialReplacement entry = replacementMaterials[i];
|
||||
replacementMaterialDict[entry.originalMaterial] = entry.replacementMaterial;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bbb58643468d3dc479e20aff7c8c611e
|
||||
timeCreated: 1585240369
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,77 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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 Spine.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
|
||||
public class RootMotionDeltaCompensation : MonoBehaviour {
|
||||
|
||||
[SerializeField] protected SkeletonRootMotionBase rootMotion;
|
||||
public Transform targetPosition;
|
||||
public int trackIndex = 0;
|
||||
public bool adjustX = true;
|
||||
public bool adjustY = true;
|
||||
public float minScaleX = -999;
|
||||
public float minScaleY = -999;
|
||||
public float maxScaleX = 999;
|
||||
public float maxScaleY = 999;
|
||||
|
||||
public bool allowXTranslation = false;
|
||||
public bool allowYTranslation = true;
|
||||
|
||||
void Start () {
|
||||
if (rootMotion == null)
|
||||
rootMotion = this.GetComponent<SkeletonRootMotionBase>();
|
||||
}
|
||||
|
||||
void Update () {
|
||||
AdjustDelta();
|
||||
}
|
||||
|
||||
void OnDisable () {
|
||||
if (adjustX)
|
||||
rootMotion.rootMotionScaleX = 1;
|
||||
if (adjustY)
|
||||
rootMotion.rootMotionScaleY = 1;
|
||||
if (allowXTranslation)
|
||||
rootMotion.rootMotionTranslateXPerY = 0;
|
||||
if (allowYTranslation)
|
||||
rootMotion.rootMotionTranslateYPerX = 0;
|
||||
}
|
||||
|
||||
void AdjustDelta () {
|
||||
Vector3 toTarget = targetPosition.position - this.transform.position;
|
||||
rootMotion.AdjustRootMotionToDistance(toTarget, trackIndex, adjustX, adjustY,
|
||||
minScaleX, maxScaleX, minScaleY, maxScaleY,
|
||||
allowXTranslation, allowYTranslation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e002a2e05cd8a441801b685e426f6c8
|
||||
timeCreated: 1599066046
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1a4a1f889c97e84db5e1ef512f77f3e
|
||||
folderAsset: yes
|
||||
timeCreated: 1498053541
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,84 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
|
||||
// This is a sample component for C# vertex effects for Spine rendering components.
|
||||
// Using shaders and materials to control vertex properties is still more performant
|
||||
// than using this API, but in cases where your vertex effect logic cannot be
|
||||
// expressed as shader code, these vertex effects can be useful.
|
||||
public class JitterEffectExample : MonoBehaviour {
|
||||
|
||||
[Range(0f, 0.8f)]
|
||||
public float jitterMagnitude = 0.2f;
|
||||
|
||||
SkeletonRenderer skeletonRenderer;
|
||||
|
||||
void OnEnable () {
|
||||
skeletonRenderer = GetComponent<SkeletonRenderer>();
|
||||
if (skeletonRenderer == null) return;
|
||||
|
||||
// Use the OnPostProcessVertices callback to modify the vertices at the correct time.
|
||||
skeletonRenderer.OnPostProcessVertices -= ProcessVertices;
|
||||
skeletonRenderer.OnPostProcessVertices += ProcessVertices;
|
||||
|
||||
Debug.Log("Jitter Effect Enabled.");
|
||||
}
|
||||
|
||||
void ProcessVertices (MeshGeneratorBuffers buffers) {
|
||||
if (!this.enabled) return;
|
||||
|
||||
// For efficiency, limit your effect to the actual mesh vertex count using vertexCount
|
||||
int vertexCount = buffers.vertexCount;
|
||||
|
||||
// Modify vertex positions by accessing Vector3[] vertexBuffer
|
||||
Vector3[] vertices = buffers.vertexBuffer;
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
vertices[i] += (Vector3)(Random.insideUnitCircle * jitterMagnitude);
|
||||
|
||||
// You can also modify uvs and colors.
|
||||
//Vector2[] uvs = buffers.uvBuffer;
|
||||
//Color32[] colors = buffers.colorBuffer;
|
||||
|
||||
//
|
||||
}
|
||||
|
||||
void OnDisable () {
|
||||
if (skeletonRenderer == null) return;
|
||||
skeletonRenderer.OnPostProcessVertices -= ProcessVertices;
|
||||
|
||||
Debug.Log("Jitter Effect Disabled.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b9ca76eac8062f42b99bbf78e777ee1
|
||||
timeCreated: 1498053868
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,105 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
|
||||
// This is a sample component for C# vertex effects for Spine rendering components.
|
||||
// Using shaders and materials to control vertex properties is still more performant
|
||||
// than using this API, but in cases where your vertex effect logic cannot be
|
||||
// expressed as shader code, these vertex effects can be useful.
|
||||
public class TwoByTwoTransformEffectExample : MonoBehaviour {
|
||||
|
||||
public Vector2 xAxis = new Vector2(1, 0);
|
||||
public Vector2 yAxis = new Vector2(0, 1);
|
||||
|
||||
SkeletonRenderer skeletonRenderer;
|
||||
|
||||
void OnEnable () {
|
||||
skeletonRenderer = GetComponent<SkeletonRenderer>();
|
||||
if (skeletonRenderer == null) return;
|
||||
|
||||
// Use the OnPostProcessVertices callback to modify the vertices at the correct time.
|
||||
skeletonRenderer.OnPostProcessVertices -= ProcessVertices;
|
||||
skeletonRenderer.OnPostProcessVertices += ProcessVertices;
|
||||
|
||||
Debug.Log("2x2 Transform Effect Enabled.");
|
||||
}
|
||||
|
||||
void ProcessVertices (MeshGeneratorBuffers buffers) {
|
||||
if (!this.enabled)
|
||||
return;
|
||||
|
||||
int vertexCount = buffers.vertexCount; // For efficiency, limit your effect to the actual mesh vertex count using vertexCount
|
||||
|
||||
// Modify vertex positions by accessing Vector3[] vertexBuffer
|
||||
Vector3[] vertices = buffers.vertexBuffer;
|
||||
Vector3 transformedPos = default(Vector3);
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
Vector3 originalPos = vertices[i];
|
||||
transformedPos.x = (xAxis.x * originalPos.x) + (yAxis.x * originalPos.y);
|
||||
transformedPos.y = (xAxis.y * originalPos.x) + (yAxis.y * originalPos.y);
|
||||
vertices[i] = transformedPos;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void OnDisable () {
|
||||
if (skeletonRenderer == null) return;
|
||||
skeletonRenderer.OnPostProcessVertices -= ProcessVertices;
|
||||
Debug.Log("2x2 Transform Effect Disabled.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.CustomEditor(typeof(Spine.Unity.Examples.TwoByTwoTransformEffectExample))]
|
||||
public class TwoByTwoTransformEffectExampleEditor : UnityEditor.Editor {
|
||||
|
||||
Spine.Unity.Examples.TwoByTwoTransformEffectExample Target { get { return target as Spine.Unity.Examples.TwoByTwoTransformEffectExample; } }
|
||||
|
||||
void OnSceneGUI () {
|
||||
Transform transform = Target.transform;
|
||||
LocalVectorHandle(ref Target.xAxis, transform, Color.red);
|
||||
LocalVectorHandle(ref Target.yAxis, transform, Color.green);
|
||||
}
|
||||
|
||||
static void LocalVectorHandle (ref Vector2 v, Transform transform, Color color) {
|
||||
Color originalColor = UnityEditor.Handles.color;
|
||||
UnityEditor.Handles.color = color;
|
||||
UnityEditor.Handles.DrawLine(transform.position, transform.TransformPoint(v));
|
||||
v = transform.InverseTransformPoint(UnityEditor.Handles.FreeMoveHandle(transform.TransformPoint(v), Quaternion.identity, 0.3f, Vector3.zero, UnityEditor.Handles.CubeHandleCap));
|
||||
UnityEditor.Handles.color = originalColor;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8afb2340fbd3fe14f9f4e07cba073e17
|
||||
timeCreated: 1523229765
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,53 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
|
||||
/// <summary>
|
||||
/// This component is intended to increase the physics solver iteration count
|
||||
/// for Rigidbody Joint setups which would otherwise be too unstable.
|
||||
///
|
||||
/// To use this example component, add it to a GameObject which is parent of
|
||||
/// one or more Rigidbody instances. The physics setting "solver iteration count"
|
||||
/// will be overwritten by the provided value.
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public class SetRigidbodySolverIterations : MonoBehaviour {
|
||||
public int solverIterations = 30;
|
||||
|
||||
void Awake () {
|
||||
Rigidbody[] rigidbodies = this.GetComponentsInChildren<Rigidbody>();
|
||||
foreach (Rigidbody rigidbody in rigidbodies) {
|
||||
rigidbody.solverIterations = solverIterations;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0710ab226a06cfa4689c784fbd9d1753
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,96 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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;
|
||||
|
||||
namespace Spine.Unity {
|
||||
|
||||
// To use this example component, add it to your SkeletonAnimation Spine GameObject.
|
||||
// This component will disable that SkeletonAnimation component to prevent it from calling its own Update and LateUpdate methods.
|
||||
|
||||
[DisallowMultipleComponent]
|
||||
public sealed class SkeletonAnimationFixedTimestep : MonoBehaviour {
|
||||
#region Inspector
|
||||
public SkeletonAnimation skeletonAnimation;
|
||||
|
||||
[Tooltip("The duration of each frame in seconds. For 12 fps: enter '1/12' in the Unity inspector.")]
|
||||
public float frameDeltaTime = 1 / 15f;
|
||||
|
||||
[Header("Advanced")]
|
||||
[Tooltip("The maximum number of fixed timesteps. If the game framerate drops below the If the framerate is consistently faster than the limited frames, this does nothing.")]
|
||||
public int maxFrameSkip = 4;
|
||||
|
||||
[Tooltip("If enabled, the Skeleton mesh will be updated only on the same frame when the animation and skeleton are updated. Disable this or call SkeletonAnimation.LateUpdate yourself if you are modifying the Skeleton using other components that don't run in the same fixed timestep.")]
|
||||
public bool frameskipMeshUpdate = true;
|
||||
|
||||
[Tooltip("This is the amount the internal accumulator starts with. Set it to some fraction of your frame delta time if you want to stagger updates between multiple skeletons.")]
|
||||
public float timeOffset;
|
||||
#endregion
|
||||
|
||||
float accumulatedTime = 0;
|
||||
bool requiresNewMesh;
|
||||
|
||||
void OnValidate () {
|
||||
skeletonAnimation = GetComponent<SkeletonAnimation>();
|
||||
if (frameDeltaTime <= 0) frameDeltaTime = 1 / 60f;
|
||||
if (maxFrameSkip < 1) maxFrameSkip = 1;
|
||||
}
|
||||
|
||||
void Awake () {
|
||||
requiresNewMesh = true;
|
||||
accumulatedTime = timeOffset;
|
||||
}
|
||||
|
||||
void Update () {
|
||||
if (skeletonAnimation.enabled)
|
||||
skeletonAnimation.enabled = false;
|
||||
|
||||
accumulatedTime += Time.deltaTime;
|
||||
|
||||
float frames = 0;
|
||||
while (accumulatedTime >= frameDeltaTime) {
|
||||
frames++;
|
||||
if (frames > maxFrameSkip) break;
|
||||
accumulatedTime -= frameDeltaTime;
|
||||
}
|
||||
|
||||
if (frames > 0) {
|
||||
skeletonAnimation.Update(frames * frameDeltaTime);
|
||||
requiresNewMesh = true;
|
||||
}
|
||||
}
|
||||
|
||||
void LateUpdate () {
|
||||
if (frameskipMeshUpdate && !requiresNewMesh) return;
|
||||
|
||||
skeletonAnimation.LateUpdate();
|
||||
requiresNewMesh = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e1670ee04b19c794db301d734c71bdd6
|
||||
timeCreated: 1545031871
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1258037ca4297c7428842419a266f7a4
|
||||
folderAsset: yes
|
||||
timeCreated: 1502103133
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,168 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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 Spine;
|
||||
using Spine.Unity;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity {
|
||||
|
||||
using Animation = Spine.Animation;
|
||||
using AnimationState = Spine.AnimationState;
|
||||
|
||||
public class SkeletonAnimationMulti : MonoBehaviour {
|
||||
const int MainTrackIndex = 0;
|
||||
|
||||
public bool initialFlipX, initialFlipY;
|
||||
public string initialAnimation;
|
||||
public bool initialLoop;
|
||||
[Space]
|
||||
public List<SkeletonDataAsset> skeletonDataAssets = new List<SkeletonDataAsset>();
|
||||
[Header("Settings")]
|
||||
public MeshGenerator.Settings meshGeneratorSettings = MeshGenerator.Settings.Default;
|
||||
|
||||
readonly List<SkeletonAnimation> skeletonAnimations = new List<SkeletonAnimation>();
|
||||
readonly Dictionary<string, Animation> animationNameTable = new Dictionary<string, Animation>();
|
||||
readonly Dictionary<Animation, SkeletonAnimation> animationSkeletonTable = new Dictionary<Animation, SkeletonAnimation>();
|
||||
//Stateful
|
||||
SkeletonAnimation currentSkeletonAnimation;
|
||||
|
||||
void Clear () {
|
||||
foreach (SkeletonAnimation skeletonAnimation in skeletonAnimations)
|
||||
Destroy(skeletonAnimation.gameObject);
|
||||
|
||||
skeletonAnimations.Clear();
|
||||
animationNameTable.Clear();
|
||||
animationSkeletonTable.Clear();
|
||||
}
|
||||
|
||||
void SetActiveSkeleton (int index) {
|
||||
if (index < 0 || index >= skeletonAnimations.Count)
|
||||
SetActiveSkeleton(null);
|
||||
else
|
||||
SetActiveSkeleton(skeletonAnimations[index]);
|
||||
}
|
||||
|
||||
void SetActiveSkeleton (SkeletonAnimation skeletonAnimation) {
|
||||
foreach (SkeletonAnimation iter in skeletonAnimations)
|
||||
iter.gameObject.SetActive(iter == skeletonAnimation);
|
||||
|
||||
currentSkeletonAnimation = skeletonAnimation;
|
||||
}
|
||||
|
||||
#region Lifecycle
|
||||
void Awake () {
|
||||
Initialize(false);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region API
|
||||
public Dictionary<Animation, SkeletonAnimation> AnimationSkeletonTable { get { return this.animationSkeletonTable; } }
|
||||
public Dictionary<string, Animation> AnimationNameTable { get { return this.animationNameTable; } }
|
||||
public SkeletonAnimation CurrentSkeletonAnimation { get { return this.currentSkeletonAnimation; } }
|
||||
public List<SkeletonAnimation> SkeletonAnimations { get { return skeletonAnimations; } }
|
||||
|
||||
public void Initialize (bool overwrite) {
|
||||
if (skeletonAnimations.Count != 0 && !overwrite) return;
|
||||
|
||||
Clear();
|
||||
|
||||
MeshGenerator.Settings settings = this.meshGeneratorSettings;
|
||||
Transform thisTransform = this.transform;
|
||||
foreach (SkeletonDataAsset dataAsset in skeletonDataAssets) {
|
||||
SkeletonAnimation newSkeletonAnimation = SkeletonAnimation.NewSkeletonAnimationGameObject(dataAsset);
|
||||
newSkeletonAnimation.transform.SetParent(thisTransform, false);
|
||||
|
||||
newSkeletonAnimation.SetMeshSettings(settings);
|
||||
newSkeletonAnimation.initialFlipX = this.initialFlipX;
|
||||
newSkeletonAnimation.initialFlipY = this.initialFlipY;
|
||||
Skeleton skeleton = newSkeletonAnimation.skeleton;
|
||||
skeleton.ScaleX = this.initialFlipX ? -1 : 1;
|
||||
skeleton.ScaleY = this.initialFlipY ? -1 : 1;
|
||||
|
||||
newSkeletonAnimation.Initialize(false);
|
||||
skeletonAnimations.Add(newSkeletonAnimation);
|
||||
}
|
||||
|
||||
// Build cache
|
||||
Dictionary<string, Animation> animationNameTable = this.animationNameTable;
|
||||
Dictionary<Animation, SkeletonAnimation> animationSkeletonTable = this.animationSkeletonTable;
|
||||
foreach (SkeletonAnimation skeletonAnimation in skeletonAnimations) {
|
||||
foreach (Animation animationObject in skeletonAnimation.Skeleton.Data.Animations) {
|
||||
animationNameTable[animationObject.Name] = animationObject;
|
||||
animationSkeletonTable[animationObject] = skeletonAnimation;
|
||||
}
|
||||
}
|
||||
|
||||
SetActiveSkeleton(skeletonAnimations[0]);
|
||||
SetAnimation(initialAnimation, initialLoop);
|
||||
}
|
||||
|
||||
public Animation FindAnimation (string animationName) {
|
||||
Animation animation;
|
||||
animationNameTable.TryGetValue(animationName, out animation);
|
||||
return animation;
|
||||
}
|
||||
|
||||
public TrackEntry SetAnimation (string animationName, bool loop) {
|
||||
return SetAnimation(FindAnimation(animationName), loop);
|
||||
}
|
||||
|
||||
public TrackEntry SetAnimation (Animation animation, bool loop) {
|
||||
if (animation == null) return null;
|
||||
|
||||
SkeletonAnimation skeletonAnimation;
|
||||
animationSkeletonTable.TryGetValue(animation, out skeletonAnimation);
|
||||
|
||||
if (skeletonAnimation != null) {
|
||||
SetActiveSkeleton(skeletonAnimation);
|
||||
skeletonAnimation.skeleton.SetToSetupPose();
|
||||
TrackEntry trackEntry = skeletonAnimation.state.SetAnimation(MainTrackIndex, animation, loop);
|
||||
skeletonAnimation.Update(0);
|
||||
return trackEntry;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SetEmptyAnimation (float mixDuration) {
|
||||
currentSkeletonAnimation.state.SetEmptyAnimation(MainTrackIndex, mixDuration);
|
||||
}
|
||||
|
||||
public void ClearAnimation () {
|
||||
currentSkeletonAnimation.state.ClearTrack(MainTrackIndex);
|
||||
}
|
||||
|
||||
public TrackEntry GetCurrent () {
|
||||
return currentSkeletonAnimation.state.GetCurrent(MainTrackIndex);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a5ea35d82fb1b5d4583e89b4343976b6
|
||||
timeCreated: 1502103123
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,83 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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 Spine;
|
||||
using Spine.Unity;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Prototyping {
|
||||
/// <summary>
|
||||
/// Stores and serializes initial settings for a Spine Skeleton component. The settings only get applied on Start at runtime.</summary>
|
||||
public class SkeletonColorInitialize : MonoBehaviour {
|
||||
public Color skeletonColor = Color.white;
|
||||
public List<SlotSettings> slotSettings = new List<SlotSettings>();
|
||||
|
||||
[System.Serializable]
|
||||
public class SlotSettings {
|
||||
[SpineSlot]
|
||||
public string slot = string.Empty;
|
||||
public Color color = Color.white;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void OnValidate () {
|
||||
ISkeletonComponent skeletonComponent = GetComponent<ISkeletonComponent>();
|
||||
if (skeletonComponent != null) {
|
||||
skeletonComponent.Skeleton.SetSlotsToSetupPose();
|
||||
IAnimationStateComponent animationStateComponent = GetComponent<IAnimationStateComponent>();
|
||||
if (animationStateComponent != null && animationStateComponent.AnimationState != null) {
|
||||
animationStateComponent.AnimationState.Apply(skeletonComponent.Skeleton);
|
||||
}
|
||||
}
|
||||
ApplySettings();
|
||||
}
|
||||
#endif
|
||||
|
||||
void Start () {
|
||||
ApplySettings();
|
||||
}
|
||||
|
||||
void ApplySettings () {
|
||||
ISkeletonComponent skeletonComponent = GetComponent<ISkeletonComponent>();
|
||||
if (skeletonComponent != null) {
|
||||
Skeleton skeleton = skeletonComponent.Skeleton;
|
||||
skeleton.SetColor(skeletonColor);
|
||||
|
||||
foreach (SlotSettings s in slotSettings) {
|
||||
Slot slot = skeleton.FindSlot(s.slot);
|
||||
if (slot != null) slot.SetColor(s.color);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7e3501002f468384b80d5853d04e19ca
|
||||
timeCreated: 1494113429
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,104 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
public class SkeletonGraphicMirror : MonoBehaviour {
|
||||
|
||||
public SkeletonRenderer source;
|
||||
public bool mirrorOnStart = true;
|
||||
public bool restoreOnDisable = true;
|
||||
SkeletonGraphic skeletonGraphic;
|
||||
|
||||
Skeleton originalSkeleton;
|
||||
bool originalFreeze;
|
||||
Texture2D overrideTexture;
|
||||
|
||||
private void Awake () {
|
||||
skeletonGraphic = GetComponent<SkeletonGraphic>();
|
||||
}
|
||||
|
||||
void Start () {
|
||||
if (mirrorOnStart)
|
||||
StartMirroring();
|
||||
}
|
||||
|
||||
void LateUpdate () {
|
||||
skeletonGraphic.UpdateMesh();
|
||||
}
|
||||
|
||||
void OnDisable () {
|
||||
if (restoreOnDisable)
|
||||
RestoreIndependentSkeleton();
|
||||
}
|
||||
|
||||
/// <summary>Freeze the SkeletonGraphic on this GameObject, and use the source as the Skeleton to be rendered by the SkeletonGraphic.</summary>
|
||||
public void StartMirroring () {
|
||||
if (source == null)
|
||||
return;
|
||||
if (skeletonGraphic == null)
|
||||
return;
|
||||
|
||||
skeletonGraphic.startingAnimation = string.Empty;
|
||||
|
||||
if (originalSkeleton == null) {
|
||||
originalSkeleton = skeletonGraphic.Skeleton;
|
||||
originalFreeze = skeletonGraphic.freeze;
|
||||
}
|
||||
|
||||
skeletonGraphic.Skeleton = source.skeleton;
|
||||
skeletonGraphic.freeze = true;
|
||||
if (overrideTexture != null)
|
||||
skeletonGraphic.OverrideTexture = overrideTexture;
|
||||
}
|
||||
|
||||
/// <summary>Use a new texture for the SkeletonGraphic. Use this if your source skeleton uses a repacked atlas. </summary>
|
||||
public void UpdateTexture (Texture2D newOverrideTexture) {
|
||||
overrideTexture = newOverrideTexture;
|
||||
if (newOverrideTexture != null)
|
||||
skeletonGraphic.OverrideTexture = overrideTexture;
|
||||
}
|
||||
|
||||
/// <summary>Stops mirroring the source SkeletonRenderer and allows the SkeletonGraphic to become an independent Skeleton component again.</summary>
|
||||
public void RestoreIndependentSkeleton () {
|
||||
if (originalSkeleton == null)
|
||||
return;
|
||||
|
||||
skeletonGraphic.Skeleton = originalSkeleton;
|
||||
skeletonGraphic.freeze = originalFreeze;
|
||||
skeletonGraphic.OverrideTexture = null;
|
||||
|
||||
originalSkeleton = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dbeb0b9949e46754eb0e0b61021b4f1c
|
||||
timeCreated: 1532024358
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,52 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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 Spine.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
public class SkeletonGraphicPlayAnimationAtEvent : MonoBehaviour {
|
||||
|
||||
public SkeletonGraphic skeletonGraphic;
|
||||
public int trackIndex = 0;
|
||||
public float playbackSpeed = 1.0f;
|
||||
|
||||
public void PlayAnimationLooping (string animation) {
|
||||
Spine.TrackEntry entry = skeletonGraphic.AnimationState.SetAnimation(trackIndex, animation, true);
|
||||
entry.TimeScale = playbackSpeed;
|
||||
}
|
||||
|
||||
public void PlayAnimationOnce (string animation) {
|
||||
Spine.TrackEntry entry = skeletonGraphic.AnimationState.SetAnimation(trackIndex, animation, false);
|
||||
entry.TimeScale = playbackSpeed;
|
||||
}
|
||||
|
||||
public void ClearTrack () {
|
||||
skeletonGraphic.AnimationState.ClearTrack(trackIndex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 243a062ac84ddf2468989143c0500a95
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b5dc12395d030642b857afc9dff2ae2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,90 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: RenderQuadMaterial
|
||||
m_Shader: {fileID: 4800000, guid: 1e0cc951f440af74dacaf86ac4ae2602, type: 3}
|
||||
m_ShaderKeywords: _ALPHAPREMULTIPLY_ON _USE8NEIGHBOURHOOD_ON
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: -1
|
||||
stringTagMap: {}
|
||||
disabledShaderPasses: []
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Floats:
|
||||
- _BumpScale: 1
|
||||
- _Cutoff: 0.5
|
||||
- _DarkColorAlphaAdditive: 0
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 10
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.5
|
||||
- _GlossyReflections: 1
|
||||
- _Metallic: 0
|
||||
- _Mode: 3
|
||||
- _OcclusionStrength: 1
|
||||
- _OutlineMipLevel: 0
|
||||
- _OutlineReferenceTexWidth: 1024
|
||||
- _OutlineSmoothness: 1
|
||||
- _OutlineWidth: 3
|
||||
- _Parallax: 0.02
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _StencilComp: 8
|
||||
- _StencilRef: 1
|
||||
- _StraightAlphaInput: 0
|
||||
- _ThresholdEnd: 0.25
|
||||
- _UVSec: 0
|
||||
- _Use8Neighbourhood: 1
|
||||
- _ZWrite: 0
|
||||
m_Colors:
|
||||
- _Black: {r: 0, g: 0, b: 0, a: 0}
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _OutlineColor: {r: 1, g: 1, b: 0, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4c507f887c6274a44a603d96e0eabf2a
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,104 @@
|
||||
// Simple shader for e.g. a Quad that renders a RenderTexture.
|
||||
// Texture color is multiplied by a color property, mostly for alpha fadeout.
|
||||
Shader "Spine/RenderQuad" {
|
||||
Properties{
|
||||
_Color("Color", Color) = (1,1,1,1)
|
||||
[NoScaleOffset] _MainTex("MainTex", 2D) = "white" {}
|
||||
_Cutoff("Shadow alpha cutoff", Range(0,1)) = 0.1
|
||||
[HideInInspector] _StencilRef("Stencil Reference", Float) = 1.0
|
||||
[HideInInspector][Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp("Stencil Comparison", Float) = 8 // Set to Always as default
|
||||
}
|
||||
SubShader{
|
||||
Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" "PreviewType" = "Plane" }
|
||||
Blend One OneMinusSrcAlpha
|
||||
Cull Off
|
||||
ZWrite Off
|
||||
Lighting Off
|
||||
|
||||
Stencil {
|
||||
Ref[_StencilRef]
|
||||
Comp[_StencilComp]
|
||||
Pass Keep
|
||||
}
|
||||
|
||||
Pass {
|
||||
Name "Normal"
|
||||
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#include "UnityCG.cginc"
|
||||
sampler2D _MainTex;
|
||||
float4 _Color;
|
||||
|
||||
struct VertexInput {
|
||||
float4 vertex : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
float4 vertexColor : COLOR;
|
||||
};
|
||||
|
||||
struct VertexOutput {
|
||||
float4 pos : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
float4 vertexColor : COLOR;
|
||||
};
|
||||
|
||||
VertexOutput vert(VertexInput v) {
|
||||
VertexOutput o = (VertexOutput)0;
|
||||
o.uv = v.uv;
|
||||
o.vertexColor = v.vertexColor;
|
||||
o.pos = UnityObjectToClipPos(v.vertex);
|
||||
return o;
|
||||
}
|
||||
|
||||
float4 frag(VertexOutput i) : SV_Target {
|
||||
float4 texColor = tex2D(_MainTex,i.uv);
|
||||
_Color.rgb *= _Color.a;
|
||||
return texColor * _Color;
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
|
||||
Pass {
|
||||
Name "Caster"
|
||||
Tags { "LightMode" = "ShadowCaster" }
|
||||
Offset 1, 1
|
||||
ZWrite On
|
||||
ZTest LEqual
|
||||
|
||||
Fog { Mode Off }
|
||||
Cull Off
|
||||
Lighting Off
|
||||
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#pragma multi_compile_shadowcaster
|
||||
#pragma fragmentoption ARB_precision_hint_fastest
|
||||
#include "UnityCG.cginc"
|
||||
sampler2D _MainTex;
|
||||
fixed _Cutoff;
|
||||
|
||||
struct VertexOutput {
|
||||
V2F_SHADOW_CASTER;
|
||||
float4 uvAndAlpha : TEXCOORD1;
|
||||
};
|
||||
|
||||
VertexOutput vert(appdata_base v, float4 vertexColor : COLOR) {
|
||||
VertexOutput o;
|
||||
o.uvAndAlpha = v.texcoord;
|
||||
o.uvAndAlpha.a = vertexColor.a;
|
||||
TRANSFER_SHADOW_CASTER(o)
|
||||
return o;
|
||||
}
|
||||
|
||||
float4 frag(VertexOutput i) : SV_Target {
|
||||
fixed4 texcol = tex2D(_MainTex, i.uvAndAlpha.xy);
|
||||
clip(texcol.a* i.uvAndAlpha.a - _Cutoff);
|
||||
SHADOW_CASTER_FRAGMENT(i)
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
FallBack "Diffuse"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e0cc951f440af74dacaf86ac4ae2602
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
preprocessorOverride: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,260 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#if UNITY_2017_2_OR_NEWER
|
||||
#define HAS_VECTOR2INT
|
||||
#endif
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
|
||||
/// <summary>
|
||||
/// When enabled, this component renders a skeleton to a RenderTexture and
|
||||
/// then draws this RenderTexture at a UI RawImage quad of the same size.
|
||||
/// This allows changing transparency at a single quad, which produces a more
|
||||
/// natural fadeout effect.
|
||||
/// Note: It is recommended to keep this component disabled as much as possible
|
||||
/// because of the additional rendering overhead. Only enable it when alpha blending is required.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(SkeletonGraphic))]
|
||||
public class SkeletonGraphicRenderTexture : SkeletonRenderTextureBase {
|
||||
#if HAS_VECTOR2INT
|
||||
[System.Serializable]
|
||||
public struct TextureMaterialPair {
|
||||
public Texture texture;
|
||||
public Material material;
|
||||
|
||||
public TextureMaterialPair (Texture texture, Material material) {
|
||||
this.texture = texture;
|
||||
this.material = material;
|
||||
}
|
||||
}
|
||||
|
||||
public RectTransform customRenderRect;
|
||||
protected SkeletonGraphic skeletonGraphic;
|
||||
public List<TextureMaterialPair> meshRendererMaterialForTexture = new List<TextureMaterialPair>();
|
||||
protected CanvasRenderer quadCanvasRenderer;
|
||||
protected RawImage quadRawImage;
|
||||
protected readonly Vector3[] worldCorners = new Vector3[4];
|
||||
|
||||
protected override void Awake () {
|
||||
base.Awake();
|
||||
skeletonGraphic = this.GetComponent<SkeletonGraphic>();
|
||||
if (targetCamera == null) {
|
||||
targetCamera = skeletonGraphic.canvas.worldCamera;
|
||||
if (targetCamera == null)
|
||||
targetCamera = Camera.main;
|
||||
}
|
||||
CreateQuadChild();
|
||||
}
|
||||
|
||||
void CreateQuadChild () {
|
||||
quad = new GameObject(this.name + " RenderTexture", typeof(CanvasRenderer), typeof(RawImage));
|
||||
quad.transform.SetParent(this.transform.parent, false);
|
||||
quadCanvasRenderer = quad.GetComponent<CanvasRenderer>();
|
||||
quadRawImage = quad.GetComponent<RawImage>();
|
||||
|
||||
quadMesh = new Mesh();
|
||||
quadMesh.MarkDynamic();
|
||||
quadMesh.name = "RenderTexture Quad";
|
||||
quadMesh.hideFlags = HideFlags.DontSaveInBuild | HideFlags.DontSaveInEditor;
|
||||
}
|
||||
|
||||
void Reset () {
|
||||
skeletonGraphic = this.GetComponent<SkeletonGraphic>();
|
||||
AtlasAssetBase[] atlasAssets = skeletonGraphic.SkeletonDataAsset.atlasAssets;
|
||||
for (int i = 0; i < atlasAssets.Length; ++i) {
|
||||
foreach (Material material in atlasAssets[i].Materials) {
|
||||
if (material.mainTexture != null) {
|
||||
meshRendererMaterialForTexture.Add(
|
||||
new TextureMaterialPair(material.mainTexture, material));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnEnable () {
|
||||
skeletonGraphic.OnInstructionsPrepared += PrepareQuad;
|
||||
skeletonGraphic.AssignMeshOverrideSingleRenderer += RenderSingleMeshToRenderTexture;
|
||||
skeletonGraphic.AssignMeshOverrideMultipleRenderers += RenderMultipleMeshesToRenderTexture;
|
||||
skeletonGraphic.disableMeshAssignmentOnOverride = true;
|
||||
skeletonGraphic.OnMeshAndMaterialsUpdated += RenderOntoQuad;
|
||||
List<CanvasRenderer> canvasRenderers = skeletonGraphic.canvasRenderers;
|
||||
for (int i = 0; i < canvasRenderers.Count; ++i)
|
||||
canvasRenderers[i].cull = true;
|
||||
|
||||
if (quadCanvasRenderer)
|
||||
quadCanvasRenderer.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
void OnDisable () {
|
||||
skeletonGraphic.OnInstructionsPrepared -= PrepareQuad;
|
||||
skeletonGraphic.AssignMeshOverrideSingleRenderer -= RenderSingleMeshToRenderTexture;
|
||||
skeletonGraphic.AssignMeshOverrideMultipleRenderers -= RenderMultipleMeshesToRenderTexture;
|
||||
skeletonGraphic.disableMeshAssignmentOnOverride = false;
|
||||
skeletonGraphic.OnMeshAndMaterialsUpdated -= RenderOntoQuad;
|
||||
List<CanvasRenderer> canvasRenderers = skeletonGraphic.canvasRenderers;
|
||||
for (int i = 0; i < canvasRenderers.Count; ++i)
|
||||
canvasRenderers[i].cull = false;
|
||||
|
||||
if (quadCanvasRenderer)
|
||||
quadCanvasRenderer.gameObject.SetActive(false);
|
||||
if (renderTexture)
|
||||
RenderTexture.ReleaseTemporary(renderTexture);
|
||||
allocatedRenderTextureSize = Vector2Int.zero;
|
||||
}
|
||||
|
||||
void PrepareQuad (SkeletonRendererInstruction instruction) {
|
||||
PrepareForMesh();
|
||||
SetupQuad();
|
||||
}
|
||||
|
||||
void RenderOntoQuad (SkeletonGraphic skeletonRenderer) {
|
||||
AssignAtQuad();
|
||||
}
|
||||
|
||||
protected void PrepareForMesh () {
|
||||
// We need to get the min/max of all four corners, rotation of the skeleton
|
||||
// in combination with perspective projection otherwise might lead to incorrect
|
||||
// screen space min/max.
|
||||
RectTransform rectTransform = customRenderRect ? customRenderRect : skeletonGraphic.rectTransform;
|
||||
rectTransform.GetWorldCorners(worldCorners);
|
||||
|
||||
RenderMode canvasRenderMode = skeletonGraphic.canvas.renderMode;
|
||||
Vector3 screenCorner0, screenCorner1, screenCorner2, screenCorner3;
|
||||
// note: world corners are ordered bottom left, top left, top right, bottom right.
|
||||
// This corresponds to 0, 3, 1, 2 in our desired order.
|
||||
if (canvasRenderMode == RenderMode.ScreenSpaceOverlay) {
|
||||
screenCorner0 = worldCorners[0];
|
||||
screenCorner1 = worldCorners[3];
|
||||
screenCorner2 = worldCorners[1];
|
||||
screenCorner3 = worldCorners[2];
|
||||
} else {
|
||||
screenCorner0 = targetCamera.WorldToScreenPoint(worldCorners[0]);
|
||||
screenCorner1 = targetCamera.WorldToScreenPoint(worldCorners[3]);
|
||||
screenCorner2 = targetCamera.WorldToScreenPoint(worldCorners[1]);
|
||||
screenCorner3 = targetCamera.WorldToScreenPoint(worldCorners[2]);
|
||||
}
|
||||
|
||||
// To avoid perspective distortion when rotated, we project all vertices
|
||||
// onto a plane parallel to the view frustum near plane.
|
||||
// Avoids the requirement of 'noperspective' vertex attribute interpolation modifier in shaders.
|
||||
float averageScreenDepth = (screenCorner0.z + screenCorner1.z + screenCorner2.z + screenCorner3.z) / 4.0f;
|
||||
screenCorner0.z = screenCorner1.z = screenCorner2.z = screenCorner3.z = averageScreenDepth;
|
||||
|
||||
if (canvasRenderMode == RenderMode.ScreenSpaceOverlay) {
|
||||
worldCornerNoDistortion0 = screenCorner0;
|
||||
worldCornerNoDistortion1 = screenCorner1;
|
||||
worldCornerNoDistortion2 = screenCorner2;
|
||||
worldCornerNoDistortion3 = screenCorner3;
|
||||
} else {
|
||||
worldCornerNoDistortion0 = targetCamera.ScreenToWorldPoint(screenCorner0);
|
||||
worldCornerNoDistortion1 = targetCamera.ScreenToWorldPoint(screenCorner1);
|
||||
worldCornerNoDistortion2 = targetCamera.ScreenToWorldPoint(screenCorner2);
|
||||
worldCornerNoDistortion3 = targetCamera.ScreenToWorldPoint(screenCorner3);
|
||||
}
|
||||
Vector3 screenSpaceMin, screenSpaceMax;
|
||||
PrepareTextureMapping(out screenSpaceMin, out screenSpaceMax,
|
||||
screenCorner0, screenCorner1, screenCorner2, screenCorner3);
|
||||
PrepareCommandBuffer(targetCamera, screenSpaceMin, screenSpaceMax);
|
||||
}
|
||||
|
||||
protected Material MeshRendererMaterialForTexture (Texture texture) {
|
||||
return meshRendererMaterialForTexture.Find(x => x.texture == texture).material;
|
||||
}
|
||||
|
||||
protected void RenderSingleMeshToRenderTexture (Mesh mesh, Material graphicMaterial, Texture texture) {
|
||||
Material meshRendererMaterial = MeshRendererMaterialForTexture(texture);
|
||||
commandBuffer.DrawMesh(mesh, transform.localToWorldMatrix, meshRendererMaterial, 0, -1);
|
||||
Graphics.ExecuteCommandBuffer(commandBuffer);
|
||||
}
|
||||
|
||||
protected void RenderMultipleMeshesToRenderTexture (int meshCount,
|
||||
Mesh[] meshes, Material[] graphicMaterials, Texture[] textures) {
|
||||
|
||||
for (int i = 0; i < meshCount; ++i) {
|
||||
Material meshRendererMaterial = MeshRendererMaterialForTexture(textures[i]);
|
||||
commandBuffer.DrawMesh(meshes[i], transform.localToWorldMatrix, meshRendererMaterial, 0, -1);
|
||||
}
|
||||
Graphics.ExecuteCommandBuffer(commandBuffer);
|
||||
}
|
||||
|
||||
protected void SetupQuad () {
|
||||
quadRawImage.texture = this.renderTexture;
|
||||
quadRawImage.color = color;
|
||||
quadCanvasRenderer.SetColor(color);
|
||||
|
||||
RectTransform srcRectTransform = skeletonGraphic.rectTransform;
|
||||
RectTransform dstRectTransform = quadRawImage.rectTransform;
|
||||
|
||||
dstRectTransform.anchorMin = srcRectTransform.anchorMin;
|
||||
dstRectTransform.anchorMax = srcRectTransform.anchorMax;
|
||||
dstRectTransform.anchoredPosition = srcRectTransform.anchoredPosition;
|
||||
dstRectTransform.pivot = srcRectTransform.pivot;
|
||||
dstRectTransform.localScale = srcRectTransform.localScale;
|
||||
dstRectTransform.sizeDelta = srcRectTransform.sizeDelta;
|
||||
dstRectTransform.rotation = srcRectTransform.rotation;
|
||||
}
|
||||
|
||||
protected void PrepareCommandBuffer (Camera targetCamera, Vector3 screenSpaceMin, Vector3 screenSpaceMax) {
|
||||
commandBuffer.Clear();
|
||||
commandBuffer.SetRenderTarget(renderTexture);
|
||||
commandBuffer.ClearRenderTarget(true, true, Color.clear);
|
||||
|
||||
Rect canvasRect = skeletonGraphic.canvas.pixelRect;
|
||||
|
||||
Matrix4x4 projectionMatrix = Matrix4x4.Ortho(
|
||||
canvasRect.x, canvasRect.x + canvasRect.width,
|
||||
canvasRect.y, canvasRect.y + canvasRect.height,
|
||||
float.MinValue, float.MaxValue);
|
||||
|
||||
RenderMode canvasRenderMode = skeletonGraphic.canvas.renderMode;
|
||||
if (canvasRenderMode == RenderMode.ScreenSpaceOverlay) {
|
||||
commandBuffer.SetViewMatrix(Matrix4x4.identity);
|
||||
commandBuffer.SetProjectionMatrix(projectionMatrix);
|
||||
} else {
|
||||
commandBuffer.SetViewMatrix(targetCamera.worldToCameraMatrix);
|
||||
commandBuffer.SetProjectionMatrix(targetCamera.projectionMatrix);
|
||||
}
|
||||
|
||||
Vector2 targetCameraViewportSize = targetCamera.pixelRect.size;
|
||||
Rect viewportRect = new Rect(-screenSpaceMin * downScaleFactor, targetCameraViewportSize * downScaleFactor);
|
||||
commandBuffer.SetViewport(viewportRect);
|
||||
}
|
||||
|
||||
protected override void AssignMeshAtRenderer () {
|
||||
quadCanvasRenderer.SetMesh(quadMesh);
|
||||
}
|
||||
#endif // HAS_VECTOR2INT
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6cbe1f11426513d49ad8e21e9d6643f7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,202 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
#define HAS_FORCE_RENDER_OFF
|
||||
#endif
|
||||
|
||||
#if UNITY_2018_2_OR_NEWER
|
||||
#define HAS_GET_SHARED_MATERIALS
|
||||
#endif
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
|
||||
/// <summary>
|
||||
/// When enabled, this component renders a skeleton to a RenderTexture and
|
||||
/// then draws this RenderTexture at a quad of the same size.
|
||||
/// This allows changing transparency at a single quad, which produces a more
|
||||
/// natural fadeout effect.
|
||||
/// Note: It is recommended to keep this component disabled as much as possible
|
||||
/// because of the additional rendering overhead. Only enable it when alpha blending is required.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(SkeletonRenderer))]
|
||||
public class SkeletonRenderTexture : SkeletonRenderTextureBase {
|
||||
#if HAS_GET_SHARED_MATERIALS
|
||||
public Material quadMaterial;
|
||||
protected SkeletonRenderer skeletonRenderer;
|
||||
protected MeshRenderer meshRenderer;
|
||||
protected MeshFilter meshFilter;
|
||||
protected MeshRenderer quadMeshRenderer;
|
||||
protected MeshFilter quadMeshFilter;
|
||||
|
||||
private MaterialPropertyBlock propertyBlock;
|
||||
private readonly List<Material> materials = new List<Material>();
|
||||
protected override void Awake () {
|
||||
base.Awake();
|
||||
meshRenderer = this.GetComponent<MeshRenderer>();
|
||||
meshFilter = this.GetComponent<MeshFilter>();
|
||||
skeletonRenderer = this.GetComponent<SkeletonRenderer>();
|
||||
if (targetCamera == null)
|
||||
targetCamera = Camera.main;
|
||||
|
||||
propertyBlock = new MaterialPropertyBlock();
|
||||
CreateQuadChild();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected void Reset () {
|
||||
string[] folders = { "Assets", "Packages" };
|
||||
string[] assets = UnityEditor.AssetDatabase.FindAssets("t:material RenderQuadMaterial", folders);
|
||||
if (assets.Length > 0) {
|
||||
string materialPath = UnityEditor.AssetDatabase.GUIDToAssetPath(assets[0]);
|
||||
quadMaterial = UnityEditor.AssetDatabase.LoadAssetAtPath<Material>(materialPath);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void CreateQuadChild () {
|
||||
quad = new GameObject(this.name + " RenderTexture", typeof(MeshRenderer), typeof(MeshFilter));
|
||||
quad.transform.SetParent(this.transform.parent, false);
|
||||
quadMeshRenderer = quad.GetComponent<MeshRenderer>();
|
||||
quadMeshFilter = quad.GetComponent<MeshFilter>();
|
||||
|
||||
quadMeshRenderer.sortingOrder = meshRenderer.sortingOrder;
|
||||
quadMeshRenderer.sortingLayerID = meshRenderer.sortingLayerID;
|
||||
|
||||
quadMesh = new Mesh();
|
||||
quadMesh.MarkDynamic();
|
||||
quadMesh.name = "RenderTexture Quad";
|
||||
quadMesh.hideFlags = HideFlags.DontSaveInBuild | HideFlags.DontSaveInEditor;
|
||||
|
||||
if (quadMaterial != null)
|
||||
quadMeshRenderer.material = new Material(quadMaterial);
|
||||
else
|
||||
quadMeshRenderer.material = new Material(Shader.Find("Spine/RenderQuad"));
|
||||
}
|
||||
|
||||
void OnEnable () {
|
||||
skeletonRenderer.OnMeshAndMaterialsUpdated += RenderOntoQuad;
|
||||
#if HAS_FORCE_RENDER_OFF
|
||||
meshRenderer.forceRenderingOff = true;
|
||||
#else
|
||||
Debug.LogError("This component requires Unity 2019.3 or newer for meshRenderer.forceRenderingOff. " +
|
||||
"Otherwise you will see the mesh rendered twice.");
|
||||
#endif
|
||||
if (quadMeshRenderer)
|
||||
quadMeshRenderer.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
void OnDisable () {
|
||||
skeletonRenderer.OnMeshAndMaterialsUpdated -= RenderOntoQuad;
|
||||
#if HAS_FORCE_RENDER_OFF
|
||||
meshRenderer.forceRenderingOff = false;
|
||||
#endif
|
||||
if (quadMeshRenderer)
|
||||
quadMeshRenderer.gameObject.SetActive(false);
|
||||
if (renderTexture)
|
||||
RenderTexture.ReleaseTemporary(renderTexture);
|
||||
allocatedRenderTextureSize = Vector2Int.zero;
|
||||
}
|
||||
|
||||
void RenderOntoQuad (SkeletonRenderer skeletonRenderer) {
|
||||
PrepareForMesh();
|
||||
RenderToRenderTexture();
|
||||
AssignAtQuad();
|
||||
}
|
||||
|
||||
protected void PrepareForMesh () {
|
||||
// We need to get the min/max of all four corners, rotation of the skeleton
|
||||
// in combination with perspective projection otherwise might lead to incorrect
|
||||
// screen space min/max.
|
||||
Bounds boundsLocalSpace = meshFilter.sharedMesh.bounds;
|
||||
Vector3 localCorner0 = boundsLocalSpace.min;
|
||||
Vector3 localCorner3 = boundsLocalSpace.max;
|
||||
Vector3 localCorner1 = new Vector3(localCorner0.x, localCorner3.y, localCorner0.z);
|
||||
Vector3 localCorner2 = new Vector3(localCorner3.x, localCorner0.y, localCorner3.z);
|
||||
|
||||
Vector3 worldCorner0 = transform.TransformPoint(localCorner0);
|
||||
Vector3 worldCorner1 = transform.TransformPoint(localCorner1);
|
||||
Vector3 worldCorner2 = transform.TransformPoint(localCorner2);
|
||||
Vector3 worldCorner3 = transform.TransformPoint(localCorner3);
|
||||
|
||||
Vector3 screenCorner0 = targetCamera.WorldToScreenPoint(worldCorner0);
|
||||
Vector3 screenCorner1 = targetCamera.WorldToScreenPoint(worldCorner1);
|
||||
Vector3 screenCorner2 = targetCamera.WorldToScreenPoint(worldCorner2);
|
||||
Vector3 screenCorner3 = targetCamera.WorldToScreenPoint(worldCorner3);
|
||||
|
||||
// To avoid perspective distortion when rotated, we project all vertices
|
||||
// onto a plane parallel to the view frustum near plane.
|
||||
// Avoids the requirement of 'noperspective' vertex attribute interpolation modifier in shaders.
|
||||
float averageScreenDepth = (screenCorner0.z + screenCorner1.z + screenCorner2.z + screenCorner3.z) / 4.0f;
|
||||
screenCorner0.z = screenCorner1.z = screenCorner2.z = screenCorner3.z = averageScreenDepth;
|
||||
worldCornerNoDistortion0 = targetCamera.ScreenToWorldPoint(screenCorner0);
|
||||
worldCornerNoDistortion1 = targetCamera.ScreenToWorldPoint(screenCorner1);
|
||||
worldCornerNoDistortion2 = targetCamera.ScreenToWorldPoint(screenCorner2);
|
||||
worldCornerNoDistortion3 = targetCamera.ScreenToWorldPoint(screenCorner3);
|
||||
|
||||
Vector3 screenSpaceMin, screenSpaceMax;
|
||||
PrepareTextureMapping(out screenSpaceMin, out screenSpaceMax,
|
||||
screenCorner0, screenCorner1, screenCorner2, screenCorner3);
|
||||
PrepareCommandBuffer(targetCamera, screenSpaceMin, screenSpaceMax);
|
||||
}
|
||||
|
||||
protected void PrepareCommandBuffer (Camera targetCamera, Vector3 screenSpaceMin, Vector3 screenSpaceMax) {
|
||||
commandBuffer.Clear();
|
||||
commandBuffer.SetRenderTarget(renderTexture);
|
||||
commandBuffer.ClearRenderTarget(true, true, Color.clear);
|
||||
|
||||
commandBuffer.SetProjectionMatrix(targetCamera.projectionMatrix);
|
||||
commandBuffer.SetViewMatrix(targetCamera.worldToCameraMatrix);
|
||||
Vector2 targetCameraViewportSize = targetCamera.pixelRect.size;
|
||||
Rect viewportRect = new Rect(-screenSpaceMin * downScaleFactor, targetCameraViewportSize * downScaleFactor);
|
||||
commandBuffer.SetViewport(viewportRect);
|
||||
}
|
||||
|
||||
protected void RenderToRenderTexture () {
|
||||
meshRenderer.GetPropertyBlock(propertyBlock);
|
||||
meshRenderer.GetSharedMaterials(materials);
|
||||
|
||||
for (int i = 0; i < materials.Count; i++)
|
||||
commandBuffer.DrawMesh(meshFilter.sharedMesh, transform.localToWorldMatrix,
|
||||
materials[i], meshRenderer.subMeshStartIndex + i, -1, propertyBlock);
|
||||
Graphics.ExecuteCommandBuffer(commandBuffer);
|
||||
}
|
||||
|
||||
protected override void AssignMeshAtRenderer () {
|
||||
quadMeshFilter.mesh = quadMesh;
|
||||
quadMeshRenderer.sharedMaterial.mainTexture = this.renderTexture;
|
||||
quadMeshRenderer.sharedMaterial.color = color;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25e6ceb271c9af848ae53f2af1073d0d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,164 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#if UNITY_2017_2_OR_NEWER
|
||||
#define HAS_VECTOR2INT
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
|
||||
public abstract class SkeletonRenderTextureBase : MonoBehaviour {
|
||||
#if HAS_VECTOR2INT
|
||||
public Color color = Color.white;
|
||||
public int maxRenderTextureSize = 1024;
|
||||
public GameObject quad;
|
||||
protected Mesh quadMesh;
|
||||
public RenderTexture renderTexture;
|
||||
public Camera targetCamera;
|
||||
|
||||
protected CommandBuffer commandBuffer;
|
||||
protected Vector2Int screenSize;
|
||||
protected Vector2Int usedRenderTextureSize;
|
||||
protected Vector2Int allocatedRenderTextureSize;
|
||||
protected Vector2 downScaleFactor = Vector2.one;
|
||||
|
||||
protected Vector3 worldCornerNoDistortion0;
|
||||
protected Vector3 worldCornerNoDistortion1;
|
||||
protected Vector3 worldCornerNoDistortion2;
|
||||
protected Vector3 worldCornerNoDistortion3;
|
||||
protected Vector2 uvCorner0;
|
||||
protected Vector2 uvCorner1;
|
||||
protected Vector2 uvCorner2;
|
||||
protected Vector2 uvCorner3;
|
||||
|
||||
protected virtual void Awake () {
|
||||
commandBuffer = new CommandBuffer();
|
||||
}
|
||||
|
||||
void OnDestroy () {
|
||||
if (renderTexture)
|
||||
RenderTexture.ReleaseTemporary(renderTexture);
|
||||
}
|
||||
|
||||
protected void PrepareTextureMapping (out Vector3 screenSpaceMin, out Vector3 screenSpaceMax,
|
||||
Vector3 screenCorner0, Vector3 screenCorner1, Vector3 screenCorner2, Vector3 screenCorner3) {
|
||||
|
||||
screenSpaceMin =
|
||||
Vector3.Min(screenCorner0, Vector3.Min(screenCorner1,
|
||||
Vector3.Min(screenCorner2, screenCorner3)));
|
||||
screenSpaceMax =
|
||||
Vector3.Max(screenCorner0, Vector3.Max(screenCorner1,
|
||||
Vector3.Max(screenCorner2, screenCorner3)));
|
||||
// ensure we are on whole pixel borders
|
||||
screenSpaceMin.x = Mathf.Floor(screenSpaceMin.x);
|
||||
screenSpaceMin.y = Mathf.Floor(screenSpaceMin.y);
|
||||
screenSpaceMax.x = Mathf.Ceil(screenSpaceMax.x);
|
||||
screenSpaceMax.y = Mathf.Ceil(screenSpaceMax.y);
|
||||
|
||||
// inverse-map screenCornerN to screenSpaceMin/screenSpaceMax area to get UV coordinates
|
||||
uvCorner0 = MathUtilities.InverseLerp(screenSpaceMin, screenSpaceMax, screenCorner0);
|
||||
uvCorner1 = MathUtilities.InverseLerp(screenSpaceMin, screenSpaceMax, screenCorner1);
|
||||
uvCorner2 = MathUtilities.InverseLerp(screenSpaceMin, screenSpaceMax, screenCorner2);
|
||||
uvCorner3 = MathUtilities.InverseLerp(screenSpaceMin, screenSpaceMax, screenCorner3);
|
||||
|
||||
screenSize = new Vector2Int(Math.Abs((int)screenSpaceMax.x - (int)screenSpaceMin.x),
|
||||
Math.Abs((int)screenSpaceMax.y - (int)screenSpaceMin.y));
|
||||
usedRenderTextureSize = new Vector2Int(
|
||||
Math.Min(maxRenderTextureSize, screenSize.x),
|
||||
Math.Min(maxRenderTextureSize, screenSize.y));
|
||||
downScaleFactor = new Vector2(
|
||||
(float)usedRenderTextureSize.x / (float)screenSize.x,
|
||||
(float)usedRenderTextureSize.y / (float)screenSize.y);
|
||||
|
||||
PrepareRenderTexture();
|
||||
}
|
||||
|
||||
protected void PrepareRenderTexture () {
|
||||
Vector2Int textureSize = new Vector2Int(
|
||||
Mathf.NextPowerOfTwo(usedRenderTextureSize.x),
|
||||
Mathf.NextPowerOfTwo(usedRenderTextureSize.y));
|
||||
|
||||
if (textureSize != allocatedRenderTextureSize) {
|
||||
if (renderTexture)
|
||||
RenderTexture.ReleaseTemporary(renderTexture);
|
||||
renderTexture = RenderTexture.GetTemporary(textureSize.x, textureSize.y);
|
||||
renderTexture.filterMode = FilterMode.Point;
|
||||
allocatedRenderTextureSize = textureSize;
|
||||
}
|
||||
}
|
||||
|
||||
protected void AssignAtQuad () {
|
||||
Transform quadTransform = quad.transform;
|
||||
quadTransform.position = this.transform.position;
|
||||
quadTransform.rotation = this.transform.rotation;
|
||||
quadTransform.localScale = this.transform.localScale;
|
||||
|
||||
Vector3 v0 = quadTransform.InverseTransformPoint(worldCornerNoDistortion0);
|
||||
Vector3 v1 = quadTransform.InverseTransformPoint(worldCornerNoDistortion1);
|
||||
Vector3 v2 = quadTransform.InverseTransformPoint(worldCornerNoDistortion2);
|
||||
Vector3 v3 = quadTransform.InverseTransformPoint(worldCornerNoDistortion3);
|
||||
Vector3[] vertices = new Vector3[4] { v0, v1, v2, v3 };
|
||||
|
||||
quadMesh.vertices = vertices;
|
||||
|
||||
int[] indices = new int[6] { 0, 1, 2, 2, 1, 3 };
|
||||
quadMesh.triangles = indices;
|
||||
|
||||
Vector3[] normals = new Vector3[4] {
|
||||
-Vector3.forward,
|
||||
-Vector3.forward,
|
||||
-Vector3.forward,
|
||||
-Vector3.forward
|
||||
};
|
||||
quadMesh.normals = normals;
|
||||
|
||||
float maxU = (float)usedRenderTextureSize.x / (float)allocatedRenderTextureSize.x;
|
||||
float maxV = (float)usedRenderTextureSize.y / (float)allocatedRenderTextureSize.y;
|
||||
if (downScaleFactor.x < 1 || downScaleFactor.y < 1) {
|
||||
maxU = downScaleFactor.x * (float)screenSize.x / (float)allocatedRenderTextureSize.x;
|
||||
maxV = downScaleFactor.y * (float)screenSize.y / (float)allocatedRenderTextureSize.y;
|
||||
}
|
||||
Vector2[] uv = new Vector2[4] {
|
||||
new Vector2(uvCorner0.x * maxU, uvCorner0.y * maxV),
|
||||
new Vector2(uvCorner1.x * maxU, uvCorner1.y * maxV),
|
||||
new Vector2(uvCorner2.x * maxU, uvCorner2.y * maxV),
|
||||
new Vector2(uvCorner3.x * maxU, uvCorner3.y * maxV),
|
||||
};
|
||||
quadMesh.uv = uv;
|
||||
AssignMeshAtRenderer();
|
||||
}
|
||||
|
||||
protected abstract void AssignMeshAtRenderer ();
|
||||
#endif // HAS_VECTOR2INT
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ccf9b5e5034b0ea45962f9cf32168dd9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,88 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
#define HAS_FORCE_RENDER_OFF
|
||||
#endif
|
||||
|
||||
#if UNITY_2017_2_OR_NEWER
|
||||
#define HAS_VECTOR_INT
|
||||
#endif
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
|
||||
/// <summary>
|
||||
/// A simple fadeout component that uses a <see cref="SkeletonRenderTexture"/> for transparency fadeout.
|
||||
/// Attach a <see cref="SkeletonRenderTexture"/> and this component to a skeleton GameObject and disable both
|
||||
/// components initially and keep them disabled during normal gameplay. When you need to start fadeout,
|
||||
/// enable this component.
|
||||
/// At the end of the fadeout, the event delegate <c>OnFadeoutComplete</c> is called, to which you can bind e.g.
|
||||
/// a method that disables or destroys the entire GameObject.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(SkeletonRenderTextureBase))]
|
||||
public class SkeletonRenderTextureFadeout : MonoBehaviour {
|
||||
SkeletonRenderTextureBase skeletonRenderTexture;
|
||||
|
||||
public float fadeoutSeconds = 2.0f;
|
||||
protected float fadeoutSecondsRemaining;
|
||||
|
||||
public delegate void FadeoutCallback (SkeletonRenderTextureFadeout skeleton);
|
||||
public event FadeoutCallback OnFadeoutComplete;
|
||||
|
||||
protected void Awake () {
|
||||
skeletonRenderTexture = this.GetComponent<SkeletonRenderTextureBase>();
|
||||
}
|
||||
|
||||
protected void OnEnable () {
|
||||
fadeoutSecondsRemaining = fadeoutSeconds;
|
||||
skeletonRenderTexture.enabled = true;
|
||||
}
|
||||
|
||||
protected void Update () {
|
||||
if (fadeoutSecondsRemaining == 0)
|
||||
return;
|
||||
|
||||
fadeoutSecondsRemaining -= Time.deltaTime;
|
||||
if (fadeoutSecondsRemaining <= 0) {
|
||||
fadeoutSecondsRemaining = 0;
|
||||
if (OnFadeoutComplete != null)
|
||||
OnFadeoutComplete(this);
|
||||
return;
|
||||
}
|
||||
float fadeoutAlpha = fadeoutSecondsRemaining / fadeoutSeconds;
|
||||
#if HAS_VECTOR_INT
|
||||
skeletonRenderTexture.color.a = fadeoutAlpha;
|
||||
#else
|
||||
Debug.LogError("The SkeletonRenderTexture component requires Unity 2017.2 or newer.");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5fc94f89310427643babb41e000a8462
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d81fbd54cb5cab844900eaa11c48a907
|
||||
folderAsset: yes
|
||||
timeCreated: 1455489575
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2b000c4fc21b39418c2b27dd66e237f
|
||||
folderAsset: yes
|
||||
timeCreated: 1563304075
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,37 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
// Contributed by: Mitch Thompson
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
public class SkeletonRagdoll2DInspector { }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b6dd0b99faf3aeb4d803eb9989cb369c
|
||||
timeCreated: 1431741936
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,46 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
// Contributed by: Mitch Thompson
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
|
||||
public class SkeletonRagdollInspector : UnityEditor.Editor {
|
||||
[CustomPropertyDrawer(typeof(SkeletonRagdoll.LayerFieldAttribute))]
|
||||
public class LayerFieldPropertyDrawer : PropertyDrawer {
|
||||
public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
|
||||
property.intValue = EditorGUI.LayerField(position, label, property.intValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c95a670c56447c644a0f062e4cdd448e
|
||||
timeCreated: 1431740230
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "spine-unity-examples-editor",
|
||||
"references": [
|
||||
"spine-unity",
|
||||
"spine-unity-examples"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 39599136c72c0b64b925d3ff2885aecb
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,443 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
// Contributed by: Mitch Thompson
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
[RequireComponent(typeof(SkeletonRenderer))]
|
||||
public class SkeletonRagdoll : MonoBehaviour {
|
||||
static Transform parentSpaceHelper;
|
||||
|
||||
#region Inspector
|
||||
[Header("Hierarchy")]
|
||||
[SpineBone]
|
||||
public string startingBoneName = "";
|
||||
[SpineBone]
|
||||
public List<string> stopBoneNames = new List<string>();
|
||||
|
||||
[Header("Parameters")]
|
||||
public bool applyOnStart;
|
||||
[Tooltip("Warning! You will have to re-enable and tune mix values manually if attempting to remove the ragdoll system.")]
|
||||
public bool disableIK = true;
|
||||
public bool disableOtherConstraints = false;
|
||||
[Space(18)]
|
||||
[Tooltip("Set RootRigidbody IsKinematic to true when Apply is called.")]
|
||||
public bool pinStartBone;
|
||||
[Tooltip("Enable Collision between adjacent ragdoll elements (IE: Neck and Head)")]
|
||||
public bool enableJointCollision;
|
||||
public bool useGravity = true;
|
||||
[Tooltip("If no BoundingBox Attachment is attached to a bone, this becomes the default Width or Radius of a Bone's ragdoll Rigidbody")]
|
||||
public float thickness = 0.125f;
|
||||
[Tooltip("Default rotational limit value. Min is negative this value, Max is this value.")]
|
||||
public float rotationLimit = 20;
|
||||
public float rootMass = 20;
|
||||
[Tooltip("If your ragdoll seems unstable or uneffected by limits, try lowering this value.")]
|
||||
[Range(0.01f, 1f)]
|
||||
public float massFalloffFactor = 0.4f;
|
||||
[Tooltip("The layer assigned to all of the rigidbody parts.")]
|
||||
public int colliderLayer = 0;
|
||||
[Range(0, 1)]
|
||||
public float mix = 1;
|
||||
public bool oldRagdollBehaviour = false;
|
||||
#endregion
|
||||
|
||||
ISkeletonAnimation targetSkeletonComponent;
|
||||
Skeleton skeleton;
|
||||
struct BoneFlipEntry {
|
||||
public BoneFlipEntry (bool flipX, bool flipY) {
|
||||
this.flipX = flipX;
|
||||
this.flipY = flipY;
|
||||
}
|
||||
|
||||
public bool flipX;
|
||||
public bool flipY;
|
||||
}
|
||||
Dictionary<Bone, Transform> boneTable = new Dictionary<Bone, Transform>();
|
||||
Dictionary<Bone, BoneFlipEntry> boneFlipTable = new Dictionary<Bone, BoneFlipEntry>();
|
||||
Transform ragdollRoot;
|
||||
public Rigidbody RootRigidbody { get; private set; }
|
||||
public Bone StartingBone { get; private set; }
|
||||
Vector3 rootOffset;
|
||||
public Vector3 RootOffset { get { return this.rootOffset; } }
|
||||
bool isActive;
|
||||
public bool IsActive { get { return this.isActive; } }
|
||||
|
||||
IEnumerator Start () {
|
||||
if (parentSpaceHelper == null) {
|
||||
parentSpaceHelper = (new GameObject("Parent Space Helper")).transform;
|
||||
parentSpaceHelper.hideFlags = HideFlags.HideInHierarchy;
|
||||
}
|
||||
|
||||
targetSkeletonComponent = GetComponent<SkeletonRenderer>() as ISkeletonAnimation;
|
||||
if (targetSkeletonComponent == null) Debug.LogError("Attached Spine component does not implement ISkeletonAnimation. This script is not compatible.");
|
||||
skeleton = targetSkeletonComponent.Skeleton;
|
||||
|
||||
if (applyOnStart) {
|
||||
yield return null;
|
||||
Apply();
|
||||
}
|
||||
}
|
||||
|
||||
#region API
|
||||
public Rigidbody[] RigidbodyArray {
|
||||
get {
|
||||
if (!isActive)
|
||||
return new Rigidbody[0];
|
||||
|
||||
Rigidbody[] rigidBodies = new Rigidbody[boneTable.Count];
|
||||
int i = 0;
|
||||
foreach (Transform t in boneTable.Values) {
|
||||
rigidBodies[i] = t.GetComponent<Rigidbody>();
|
||||
i++;
|
||||
}
|
||||
|
||||
return rigidBodies;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 EstimatedSkeletonPosition {
|
||||
get { return RootRigidbody.position - rootOffset; }
|
||||
}
|
||||
|
||||
/// <summary>Instantiates the ragdoll simulation and applies its transforms to the skeleton.</summary>
|
||||
public void Apply () {
|
||||
isActive = true;
|
||||
mix = 1;
|
||||
|
||||
StartingBone = skeleton.FindBone(startingBoneName);
|
||||
RecursivelyCreateBoneProxies(StartingBone);
|
||||
|
||||
RootRigidbody = boneTable[StartingBone].GetComponent<Rigidbody>();
|
||||
RootRigidbody.isKinematic = pinStartBone;
|
||||
RootRigidbody.mass = rootMass;
|
||||
List<Collider> boneColliders = new List<Collider>();
|
||||
foreach (KeyValuePair<Bone, Transform> pair in boneTable) {
|
||||
Bone b = pair.Key;
|
||||
Transform t = pair.Value;
|
||||
Transform parentTransform;
|
||||
boneColliders.Add(t.GetComponent<Collider>());
|
||||
if (b == StartingBone) {
|
||||
ragdollRoot = new GameObject("RagdollRoot").transform;
|
||||
ragdollRoot.SetParent(transform, false);
|
||||
if (b == skeleton.RootBone) { // RagdollRoot is skeleton root's parent, thus the skeleton's scale and position.
|
||||
ragdollRoot.localPosition = new Vector3(skeleton.X, skeleton.Y, 0);
|
||||
ragdollRoot.localRotation = (skeleton.ScaleX < 0) ? Quaternion.Euler(0, 0, 180.0f) : Quaternion.identity;
|
||||
} else {
|
||||
ragdollRoot.localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0);
|
||||
ragdollRoot.localRotation = Quaternion.Euler(0, 0, b.Parent.WorldRotationX - b.Parent.ShearX);
|
||||
}
|
||||
parentTransform = ragdollRoot;
|
||||
rootOffset = t.position - transform.position;
|
||||
} else {
|
||||
parentTransform = boneTable[b.Parent];
|
||||
}
|
||||
|
||||
// Add joint and attach to parent.
|
||||
Rigidbody rbParent = parentTransform.GetComponent<Rigidbody>();
|
||||
if (rbParent != null) {
|
||||
HingeJoint joint = t.gameObject.AddComponent<HingeJoint>();
|
||||
joint.connectedBody = rbParent;
|
||||
Vector3 localPos = parentTransform.InverseTransformPoint(t.position);
|
||||
localPos.x *= 1;
|
||||
joint.connectedAnchor = localPos;
|
||||
joint.axis = Vector3.forward;
|
||||
|
||||
joint.GetComponent<Rigidbody>().mass = joint.connectedBody.mass * massFalloffFactor;
|
||||
joint.limits = new JointLimits {
|
||||
min = -rotationLimit,
|
||||
max = rotationLimit,
|
||||
};
|
||||
joint.useLimits = true;
|
||||
joint.enableCollision = enableJointCollision;
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore collisions among bones.
|
||||
for (int x = 0; x < boneColliders.Count; x++) {
|
||||
for (int y = 0; y < boneColliders.Count; y++) {
|
||||
if (x == y) continue;
|
||||
Physics.IgnoreCollision(boneColliders[x], boneColliders[y]);
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy existing override-mode SkeletonUtilityBones.
|
||||
SkeletonUtilityBone[] utilityBones = GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
if (utilityBones.Length > 0) {
|
||||
List<string> destroyedUtilityBoneNames = new List<string>();
|
||||
foreach (SkeletonUtilityBone ub in utilityBones) {
|
||||
if (ub.mode == SkeletonUtilityBone.Mode.Override) {
|
||||
destroyedUtilityBoneNames.Add(ub.gameObject.name);
|
||||
Destroy(ub.gameObject);
|
||||
}
|
||||
}
|
||||
if (destroyedUtilityBoneNames.Count > 0) {
|
||||
string msg = "Destroyed Utility Bones: ";
|
||||
for (int i = 0; i < destroyedUtilityBoneNames.Count; i++) {
|
||||
msg += destroyedUtilityBoneNames[i];
|
||||
if (i != destroyedUtilityBoneNames.Count - 1) {
|
||||
msg += ",";
|
||||
}
|
||||
}
|
||||
Debug.LogWarning(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Disable skeleton constraints.
|
||||
if (disableIK) {
|
||||
ExposedList<IkConstraint> ikConstraints = skeleton.IkConstraints;
|
||||
for (int i = 0, n = ikConstraints.Count; i < n; i++)
|
||||
ikConstraints.Items[i].Mix = 0;
|
||||
}
|
||||
|
||||
if (disableOtherConstraints) {
|
||||
ExposedList<TransformConstraint> transformConstraints = skeleton.TransformConstraints;
|
||||
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
|
||||
transformConstraints.Items[i].MixRotate = 0;
|
||||
transformConstraints.Items[i].MixScaleX = 0;
|
||||
transformConstraints.Items[i].MixScaleY = 0;
|
||||
transformConstraints.Items[i].MixShearY = 0;
|
||||
transformConstraints.Items[i].MixX = 0;
|
||||
transformConstraints.Items[i].MixY = 0;
|
||||
}
|
||||
|
||||
ExposedList<PathConstraint> pathConstraints = skeleton.PathConstraints;
|
||||
for (int i = 0, n = pathConstraints.Count; i < n; i++) {
|
||||
pathConstraints.Items[i].MixRotate = 0;
|
||||
pathConstraints.Items[i].MixX = 0;
|
||||
pathConstraints.Items[i].MixY = 0;
|
||||
}
|
||||
}
|
||||
|
||||
targetSkeletonComponent.UpdateWorld += UpdateSpineSkeleton;
|
||||
}
|
||||
|
||||
/// <summary>Transitions the mix value from the current value to a target value.</summary>
|
||||
public Coroutine SmoothMix (float target, float duration) {
|
||||
return StartCoroutine(SmoothMixCoroutine(target, duration));
|
||||
}
|
||||
|
||||
IEnumerator SmoothMixCoroutine (float target, float duration) {
|
||||
float startTime = Time.time;
|
||||
float startMix = mix;
|
||||
while (mix > 0) {
|
||||
skeleton.SetBonesToSetupPose();
|
||||
mix = Mathf.SmoothStep(startMix, target, (Time.time - startTime) / duration);
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Set the transform world position while preserving the ragdoll parts world position.</summary>
|
||||
public void SetSkeletonPosition (Vector3 worldPosition) {
|
||||
if (!isActive) {
|
||||
Debug.LogWarning("Can't call SetSkeletonPosition while Ragdoll is not active!");
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 offset = worldPosition - transform.position;
|
||||
transform.position = worldPosition;
|
||||
foreach (Transform t in boneTable.Values)
|
||||
t.position -= offset;
|
||||
|
||||
UpdateSpineSkeleton(null);
|
||||
skeleton.UpdateWorldTransform();
|
||||
}
|
||||
|
||||
/// <summary>Removes the ragdoll instance and effect from the animated skeleton.</summary>
|
||||
public void Remove () {
|
||||
isActive = false;
|
||||
foreach (Transform t in boneTable.Values)
|
||||
Destroy(t.gameObject);
|
||||
|
||||
Destroy(ragdollRoot.gameObject);
|
||||
|
||||
boneTable.Clear();
|
||||
targetSkeletonComponent.UpdateWorld -= UpdateSpineSkeleton;
|
||||
}
|
||||
|
||||
public Rigidbody GetRigidbody (string boneName) {
|
||||
Bone bone = skeleton.FindBone(boneName);
|
||||
return (bone != null && boneTable.ContainsKey(bone)) ? boneTable[bone].GetComponent<Rigidbody>() : null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
void RecursivelyCreateBoneProxies (Bone b) {
|
||||
string boneName = b.Data.Name;
|
||||
if (stopBoneNames.Contains(boneName))
|
||||
return;
|
||||
|
||||
GameObject boneGameObject = new GameObject(boneName);
|
||||
boneGameObject.layer = colliderLayer;
|
||||
Transform t = boneGameObject.transform;
|
||||
boneTable.Add(b, t);
|
||||
|
||||
t.parent = transform;
|
||||
t.localPosition = new Vector3(b.WorldX, b.WorldY, 0);
|
||||
t.localRotation = Quaternion.Euler(0, 0, b.WorldRotationX - b.ShearX);
|
||||
t.localScale = new Vector3(b.WorldScaleX, b.WorldScaleY, 1);
|
||||
|
||||
List<Collider> colliders = AttachBoundingBoxRagdollColliders(b);
|
||||
if (colliders.Count == 0) {
|
||||
float length = b.Data.Length;
|
||||
if (length == 0) {
|
||||
SphereCollider ball = boneGameObject.AddComponent<SphereCollider>();
|
||||
ball.radius = thickness * 0.5f;
|
||||
} else {
|
||||
BoxCollider box = boneGameObject.AddComponent<BoxCollider>();
|
||||
box.size = new Vector3(length, thickness, thickness);
|
||||
box.center = new Vector3(length * 0.5f, 0);
|
||||
}
|
||||
}
|
||||
Rigidbody rb = boneGameObject.AddComponent<Rigidbody>();
|
||||
rb.constraints = RigidbodyConstraints.FreezePositionZ;
|
||||
|
||||
foreach (Bone child in b.Children)
|
||||
RecursivelyCreateBoneProxies(child);
|
||||
}
|
||||
|
||||
void UpdateSpineSkeleton (ISkeletonAnimation skeletonRenderer) {
|
||||
bool parentFlipX;
|
||||
bool parentFlipY;
|
||||
GetStartBoneParentFlipState(out parentFlipX, out parentFlipY);
|
||||
|
||||
foreach (KeyValuePair<Bone, Transform> pair in boneTable) {
|
||||
Bone b = pair.Key;
|
||||
Transform t = pair.Value;
|
||||
bool isStartingBone = b == StartingBone;
|
||||
Bone parentBone = b.Parent;
|
||||
Transform parentTransform = isStartingBone ? ragdollRoot : boneTable[parentBone];
|
||||
if (!isStartingBone) {
|
||||
BoneFlipEntry parentBoneFlip = boneFlipTable[parentBone];
|
||||
parentFlipX = parentBoneFlip.flipX;
|
||||
parentFlipY = parentBoneFlip.flipY;
|
||||
}
|
||||
bool flipX = parentFlipX ^ (b.ScaleX < 0);
|
||||
bool flipY = parentFlipY ^ (b.ScaleY < 0);
|
||||
|
||||
BoneFlipEntry boneFlip;
|
||||
boneFlipTable.TryGetValue(b, out boneFlip);
|
||||
boneFlip.flipX = flipX;
|
||||
boneFlip.flipY = flipY;
|
||||
boneFlipTable[b] = boneFlip;
|
||||
|
||||
bool flipXOR = flipX ^ flipY;
|
||||
bool parentFlipXOR = parentFlipX ^ parentFlipY;
|
||||
|
||||
if (!oldRagdollBehaviour && isStartingBone) {
|
||||
if (b != skeleton.RootBone) { // RagdollRoot is not skeleton root.
|
||||
ragdollRoot.localPosition = new Vector3(parentBone.WorldX, parentBone.WorldY, 0);
|
||||
ragdollRoot.localRotation = Quaternion.Euler(0, 0, parentBone.WorldRotationX - parentBone.ShearX);
|
||||
ragdollRoot.localScale = new Vector3(parentBone.WorldScaleX, parentBone.WorldScaleY, 1);
|
||||
}
|
||||
}
|
||||
Vector3 parentTransformWorldPosition = parentTransform.position;
|
||||
Quaternion parentTransformWorldRotation = parentTransform.rotation;
|
||||
|
||||
parentSpaceHelper.position = parentTransformWorldPosition;
|
||||
parentSpaceHelper.rotation = parentTransformWorldRotation;
|
||||
parentSpaceHelper.localScale = parentTransform.lossyScale;
|
||||
|
||||
if (oldRagdollBehaviour) {
|
||||
if (isStartingBone && b != skeleton.RootBone) {
|
||||
Vector3 localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0);
|
||||
parentSpaceHelper.position = ragdollRoot.TransformPoint(localPosition);
|
||||
parentSpaceHelper.localRotation = Quaternion.Euler(0, 0, parentBone.WorldRotationX - parentBone.ShearX);
|
||||
parentSpaceHelper.localScale = new Vector3(parentBone.WorldScaleX, parentBone.WorldScaleY, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 boneWorldPosition = t.position;
|
||||
Vector3 right = parentSpaceHelper.InverseTransformDirection(t.right);
|
||||
|
||||
Vector3 boneLocalPosition = parentSpaceHelper.InverseTransformPoint(boneWorldPosition);
|
||||
float boneLocalRotation = Mathf.Atan2(right.y, right.x) * Mathf.Rad2Deg;
|
||||
|
||||
if (flipXOR) boneLocalPosition.y *= -1f;
|
||||
if (parentFlipXOR != flipXOR) boneLocalPosition.y *= -1f;
|
||||
|
||||
if (parentFlipXOR) boneLocalRotation *= -1f;
|
||||
if (parentFlipX != flipX) boneLocalRotation += 180;
|
||||
|
||||
b.X = Mathf.Lerp(b.X, boneLocalPosition.x, mix);
|
||||
b.Y = Mathf.Lerp(b.Y, boneLocalPosition.y, mix);
|
||||
b.Rotation = Mathf.Lerp(b.Rotation, boneLocalRotation, mix);
|
||||
//b.AppliedRotation = Mathf.Lerp(b.AppliedRotation, boneLocalRotation, mix);
|
||||
}
|
||||
}
|
||||
|
||||
void GetStartBoneParentFlipState (out bool parentFlipX, out bool parentFlipY) {
|
||||
parentFlipX = skeleton.ScaleX < 0;
|
||||
parentFlipY = skeleton.ScaleY < 0;
|
||||
Bone parent = this.StartingBone == null ? null : this.StartingBone.Parent;
|
||||
while (parent != null) {
|
||||
parentFlipX ^= parent.ScaleX < 0;
|
||||
parentFlipY ^= parent.ScaleY < 0;
|
||||
parent = parent.Parent;
|
||||
}
|
||||
}
|
||||
|
||||
List<Collider> AttachBoundingBoxRagdollColliders (Bone b) {
|
||||
const string AttachmentNameMarker = "ragdoll";
|
||||
List<Collider> colliders = new List<Collider>();
|
||||
|
||||
Transform t = boneTable[b];
|
||||
GameObject go = t.gameObject;
|
||||
Skin skin = skeleton.Skin ?? skeleton.Data.DefaultSkin;
|
||||
|
||||
List<Skin.SkinEntry> skinEntries = new List<Skin.SkinEntry>();
|
||||
foreach (Slot s in skeleton.Slots) {
|
||||
if (s.Bone == b) {
|
||||
skin.GetAttachments(skeleton.Slots.IndexOf(s), skinEntries);
|
||||
|
||||
foreach (Skin.SkinEntry entry in skinEntries) {
|
||||
BoundingBoxAttachment bbAttachment = entry.Attachment as BoundingBoxAttachment;
|
||||
if (bbAttachment != null) {
|
||||
if (!entry.Name.ToLower().Contains(AttachmentNameMarker))
|
||||
continue;
|
||||
|
||||
BoxCollider bbCollider = go.AddComponent<BoxCollider>();
|
||||
Bounds bounds = SkeletonUtility.GetBoundingBoxBounds(bbAttachment, thickness);
|
||||
bbCollider.center = bounds.center;
|
||||
bbCollider.size = bounds.size;
|
||||
colliders.Add(bbCollider);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return colliders;
|
||||
}
|
||||
|
||||
public class LayerFieldAttribute : PropertyAttribute { }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 373527d2bf3351348b9fcc499ce9ea23
|
||||
timeCreated: 1430552693
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,472 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
// Contributed by: Mitch Thompson
|
||||
|
||||
#if UNITY_2019_2 || UNITY_2019_3 || UNITY_2019_4 || UNITY_2020_1 || UNITY_2020_2 // note: 2020.3+ uses old bahavior again
|
||||
#define HINGE_JOINT_2019_BEHAVIOUR
|
||||
#endif
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
[RequireComponent(typeof(SkeletonRenderer))]
|
||||
public class SkeletonRagdoll2D : MonoBehaviour {
|
||||
static Transform parentSpaceHelper;
|
||||
|
||||
#region Inspector
|
||||
[Header("Hierarchy")]
|
||||
[SpineBone]
|
||||
public string startingBoneName = "";
|
||||
[SpineBone]
|
||||
public List<string> stopBoneNames = new List<string>();
|
||||
|
||||
[Header("Parameters")]
|
||||
public bool applyOnStart;
|
||||
[Tooltip("Warning! You will have to re-enable and tune mix values manually if attempting to remove the ragdoll system.")]
|
||||
public bool disableIK = true;
|
||||
public bool disableOtherConstraints = false;
|
||||
[Space]
|
||||
[Tooltip("Set RootRigidbody IsKinematic to true when Apply is called.")]
|
||||
public bool pinStartBone;
|
||||
public float gravityScale = 1;
|
||||
[Tooltip("If no BoundingBox Attachment is attached to a bone, this becomes the default Width or Radius of a Bone's ragdoll Rigidbody")]
|
||||
public float thickness = 0.125f;
|
||||
[Tooltip("Default rotational limit value. Min is negative this value, Max is this value.")]
|
||||
public float rotationLimit = 20;
|
||||
public float rootMass = 20;
|
||||
[Tooltip("If your ragdoll seems unstable or uneffected by limits, try lowering this value.")]
|
||||
[Range(0.01f, 1f)]
|
||||
public float massFalloffFactor = 0.4f;
|
||||
[Tooltip("The layer assigned to all of the rigidbody parts.")]
|
||||
[SkeletonRagdoll.LayerField]
|
||||
public int colliderLayer = 0;
|
||||
[Range(0, 1)]
|
||||
public float mix = 1;
|
||||
public bool oldRagdollBehaviour = false;
|
||||
#endregion
|
||||
|
||||
ISkeletonAnimation targetSkeletonComponent;
|
||||
Skeleton skeleton;
|
||||
struct BoneFlipEntry {
|
||||
public BoneFlipEntry (bool flipX, bool flipY) {
|
||||
this.flipX = flipX;
|
||||
this.flipY = flipY;
|
||||
}
|
||||
|
||||
public bool flipX;
|
||||
public bool flipY;
|
||||
}
|
||||
Dictionary<Bone, Transform> boneTable = new Dictionary<Bone, Transform>();
|
||||
Dictionary<Bone, BoneFlipEntry> boneFlipTable = new Dictionary<Bone, BoneFlipEntry>();
|
||||
Transform ragdollRoot;
|
||||
public Rigidbody2D RootRigidbody { get; private set; }
|
||||
public Bone StartingBone { get; private set; }
|
||||
Vector2 rootOffset;
|
||||
public Vector3 RootOffset { get { return this.rootOffset; } }
|
||||
bool isActive;
|
||||
public bool IsActive { get { return this.isActive; } }
|
||||
|
||||
IEnumerator Start () {
|
||||
if (parentSpaceHelper == null) {
|
||||
parentSpaceHelper = (new GameObject("Parent Space Helper")).transform;
|
||||
}
|
||||
|
||||
targetSkeletonComponent = GetComponent<SkeletonRenderer>() as ISkeletonAnimation;
|
||||
if (targetSkeletonComponent == null) Debug.LogError("Attached Spine component does not implement ISkeletonAnimation. This script is not compatible.");
|
||||
skeleton = targetSkeletonComponent.Skeleton;
|
||||
|
||||
if (applyOnStart) {
|
||||
yield return null;
|
||||
Apply();
|
||||
}
|
||||
}
|
||||
|
||||
#region API
|
||||
public Rigidbody2D[] RigidbodyArray {
|
||||
get {
|
||||
if (!isActive)
|
||||
return new Rigidbody2D[0];
|
||||
|
||||
Rigidbody2D[] rigidBodies = new Rigidbody2D[boneTable.Count];
|
||||
int i = 0;
|
||||
foreach (Transform t in boneTable.Values) {
|
||||
rigidBodies[i] = t.GetComponent<Rigidbody2D>();
|
||||
i++;
|
||||
}
|
||||
|
||||
return rigidBodies;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 EstimatedSkeletonPosition {
|
||||
get { return this.RootRigidbody.position - rootOffset; }
|
||||
}
|
||||
|
||||
/// <summary>Instantiates the ragdoll simulation and applies its transforms to the skeleton.</summary>
|
||||
public void Apply () {
|
||||
isActive = true;
|
||||
mix = 1;
|
||||
|
||||
Bone startingBone = this.StartingBone = skeleton.FindBone(startingBoneName);
|
||||
RecursivelyCreateBoneProxies(startingBone);
|
||||
|
||||
RootRigidbody = boneTable[startingBone].GetComponent<Rigidbody2D>();
|
||||
RootRigidbody.isKinematic = pinStartBone;
|
||||
RootRigidbody.mass = rootMass;
|
||||
List<Collider2D> boneColliders = new List<Collider2D>();
|
||||
foreach (KeyValuePair<Bone, Transform> pair in boneTable) {
|
||||
Bone b = pair.Key;
|
||||
Transform t = pair.Value;
|
||||
Transform parentTransform;
|
||||
boneColliders.Add(t.GetComponent<Collider2D>());
|
||||
if (b == startingBone) {
|
||||
ragdollRoot = new GameObject("RagdollRoot").transform;
|
||||
ragdollRoot.SetParent(transform, false);
|
||||
if (b == skeleton.RootBone) { // RagdollRoot is skeleton root's parent, thus the skeleton's scale and position.
|
||||
ragdollRoot.localPosition = new Vector3(skeleton.X, skeleton.Y, 0);
|
||||
ragdollRoot.localRotation = (skeleton.ScaleX < 0) ? Quaternion.Euler(0, 0, 180.0f) : Quaternion.identity;
|
||||
} else {
|
||||
ragdollRoot.localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0);
|
||||
ragdollRoot.localRotation = Quaternion.Euler(0, 0, b.Parent.WorldRotationX - b.Parent.ShearX);
|
||||
}
|
||||
parentTransform = ragdollRoot;
|
||||
rootOffset = t.position - transform.position;
|
||||
} else {
|
||||
parentTransform = boneTable[b.Parent];
|
||||
}
|
||||
|
||||
// Add joint and attach to parent.
|
||||
Rigidbody2D rbParent = parentTransform.GetComponent<Rigidbody2D>();
|
||||
if (rbParent != null) {
|
||||
HingeJoint2D joint = t.gameObject.AddComponent<HingeJoint2D>();
|
||||
joint.connectedBody = rbParent;
|
||||
Vector3 localPos = parentTransform.InverseTransformPoint(t.position);
|
||||
joint.connectedAnchor = localPos;
|
||||
|
||||
joint.GetComponent<Rigidbody2D>().mass = joint.connectedBody.mass * massFalloffFactor;
|
||||
|
||||
#if HINGE_JOINT_2019_BEHAVIOUR
|
||||
float referenceAngle = (rbParent.transform.eulerAngles.z - t.eulerAngles.z + 360f) % 360f;
|
||||
float minAngle = referenceAngle - rotationLimit;
|
||||
float maxAngle = referenceAngle + rotationLimit;
|
||||
if (maxAngle > 180f) {
|
||||
minAngle -= 360f;
|
||||
maxAngle -= 360f;
|
||||
}
|
||||
#else
|
||||
float minAngle = -rotationLimit;
|
||||
float maxAngle = rotationLimit;
|
||||
#endif
|
||||
joint.limits = new JointAngleLimits2D {
|
||||
min = minAngle,
|
||||
max = maxAngle
|
||||
};
|
||||
joint.useLimits = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore collisions among bones.
|
||||
for (int x = 0; x < boneColliders.Count; x++) {
|
||||
for (int y = 0; y < boneColliders.Count; y++) {
|
||||
if (x == y) continue;
|
||||
Physics2D.IgnoreCollision(boneColliders[x], boneColliders[y]);
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy existing override-mode SkeletonUtility bones.
|
||||
SkeletonUtilityBone[] utilityBones = GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
if (utilityBones.Length > 0) {
|
||||
List<string> destroyedUtilityBoneNames = new List<string>();
|
||||
foreach (SkeletonUtilityBone ub in utilityBones) {
|
||||
if (ub.mode == SkeletonUtilityBone.Mode.Override) {
|
||||
destroyedUtilityBoneNames.Add(ub.gameObject.name);
|
||||
Destroy(ub.gameObject);
|
||||
}
|
||||
}
|
||||
if (destroyedUtilityBoneNames.Count > 0) {
|
||||
string msg = "Destroyed Utility Bones: ";
|
||||
for (int i = 0; i < destroyedUtilityBoneNames.Count; i++) {
|
||||
msg += destroyedUtilityBoneNames[i];
|
||||
if (i != destroyedUtilityBoneNames.Count - 1) {
|
||||
msg += ",";
|
||||
}
|
||||
}
|
||||
Debug.LogWarning(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Disable skeleton constraints.
|
||||
if (disableIK) {
|
||||
ExposedList<IkConstraint> ikConstraints = skeleton.IkConstraints;
|
||||
for (int i = 0, n = ikConstraints.Count; i < n; i++)
|
||||
ikConstraints.Items[i].Mix = 0;
|
||||
}
|
||||
|
||||
if (disableOtherConstraints) {
|
||||
ExposedList<TransformConstraint> transformConstraints = skeleton.TransformConstraints;
|
||||
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
|
||||
transformConstraints.Items[i].MixRotate = 0;
|
||||
transformConstraints.Items[i].MixScaleX = 0;
|
||||
transformConstraints.Items[i].MixScaleY = 0;
|
||||
transformConstraints.Items[i].MixShearY = 0;
|
||||
transformConstraints.Items[i].MixX = 0;
|
||||
transformConstraints.Items[i].MixY = 0;
|
||||
}
|
||||
|
||||
ExposedList<PathConstraint> pathConstraints = skeleton.PathConstraints;
|
||||
for (int i = 0, n = pathConstraints.Count; i < n; i++) {
|
||||
pathConstraints.Items[i].MixRotate = 0;
|
||||
pathConstraints.Items[i].MixX = 0;
|
||||
pathConstraints.Items[i].MixY = 0;
|
||||
}
|
||||
}
|
||||
|
||||
targetSkeletonComponent.UpdateWorld += UpdateSpineSkeleton;
|
||||
}
|
||||
|
||||
/// <summary>Transitions the mix value from the current value to a target value.</summary>
|
||||
public Coroutine SmoothMix (float target, float duration) {
|
||||
return StartCoroutine(SmoothMixCoroutine(target, duration));
|
||||
}
|
||||
|
||||
IEnumerator SmoothMixCoroutine (float target, float duration) {
|
||||
float startTime = Time.time;
|
||||
float startMix = mix;
|
||||
while (mix > 0) {
|
||||
skeleton.SetBonesToSetupPose();
|
||||
mix = Mathf.SmoothStep(startMix, target, (Time.time - startTime) / duration);
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Set the transform world position while preserving the ragdoll parts world position.</summary>
|
||||
public void SetSkeletonPosition (Vector3 worldPosition) {
|
||||
if (!isActive) {
|
||||
Debug.LogWarning("Can't call SetSkeletonPosition while Ragdoll is not active!");
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 offset = worldPosition - transform.position;
|
||||
transform.position = worldPosition;
|
||||
foreach (Transform t in boneTable.Values)
|
||||
t.position -= offset;
|
||||
|
||||
UpdateSpineSkeleton(null);
|
||||
skeleton.UpdateWorldTransform();
|
||||
}
|
||||
|
||||
/// <summary>Removes the ragdoll instance and effect from the animated skeleton.</summary>
|
||||
public void Remove () {
|
||||
isActive = false;
|
||||
foreach (Transform t in boneTable.Values)
|
||||
Destroy(t.gameObject);
|
||||
|
||||
Destroy(ragdollRoot.gameObject);
|
||||
boneTable.Clear();
|
||||
targetSkeletonComponent.UpdateWorld -= UpdateSpineSkeleton;
|
||||
}
|
||||
|
||||
public Rigidbody2D GetRigidbody (string boneName) {
|
||||
Bone bone = skeleton.FindBone(boneName);
|
||||
return (bone != null && boneTable.ContainsKey(bone)) ? boneTable[bone].GetComponent<Rigidbody2D>() : null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>Generates the ragdoll simulation's Transform and joint setup.</summary>
|
||||
void RecursivelyCreateBoneProxies (Bone b) {
|
||||
string boneName = b.Data.Name;
|
||||
if (stopBoneNames.Contains(boneName))
|
||||
return;
|
||||
|
||||
GameObject boneGameObject = new GameObject(boneName);
|
||||
boneGameObject.layer = this.colliderLayer;
|
||||
Transform t = boneGameObject.transform;
|
||||
boneTable.Add(b, t);
|
||||
|
||||
t.parent = transform;
|
||||
t.localPosition = new Vector3(b.WorldX, b.WorldY, 0);
|
||||
t.localRotation = Quaternion.Euler(0, 0, b.WorldRotationX - b.ShearX);
|
||||
t.localScale = new Vector3(b.WorldScaleX, b.WorldScaleY, 1);
|
||||
|
||||
List<Collider2D> colliders = AttachBoundingBoxRagdollColliders(b, boneGameObject, skeleton, this.gravityScale);
|
||||
if (colliders.Count == 0) {
|
||||
float length = b.Data.Length;
|
||||
if (length == 0) {
|
||||
CircleCollider2D circle = boneGameObject.AddComponent<CircleCollider2D>();
|
||||
circle.radius = thickness * 0.5f;
|
||||
} else {
|
||||
BoxCollider2D box = boneGameObject.AddComponent<BoxCollider2D>();
|
||||
box.size = new Vector2(length, thickness);
|
||||
box.offset = new Vector2(length * 0.5f, 0); // box.center in UNITY_4
|
||||
}
|
||||
}
|
||||
|
||||
Rigidbody2D rb = boneGameObject.GetComponent<Rigidbody2D>();
|
||||
if (rb == null) rb = boneGameObject.AddComponent<Rigidbody2D>();
|
||||
rb.gravityScale = this.gravityScale;
|
||||
|
||||
foreach (Bone child in b.Children)
|
||||
RecursivelyCreateBoneProxies(child);
|
||||
}
|
||||
|
||||
/// <summary>Performed every skeleton animation update to translate Unity Transforms positions into Spine bone transforms.</summary>
|
||||
void UpdateSpineSkeleton (ISkeletonAnimation animatedSkeleton) {
|
||||
bool parentFlipX;
|
||||
bool parentFlipY;
|
||||
Bone startingBone = this.StartingBone;
|
||||
GetStartBoneParentFlipState(out parentFlipX, out parentFlipY);
|
||||
|
||||
foreach (KeyValuePair<Bone, Transform> pair in boneTable) {
|
||||
Bone b = pair.Key;
|
||||
Transform t = pair.Value;
|
||||
bool isStartingBone = (b == startingBone);
|
||||
Bone parentBone = b.Parent;
|
||||
Transform parentTransform = isStartingBone ? ragdollRoot : boneTable[parentBone];
|
||||
if (!isStartingBone) {
|
||||
BoneFlipEntry parentBoneFlip = boneFlipTable[parentBone];
|
||||
parentFlipX = parentBoneFlip.flipX;
|
||||
parentFlipY = parentBoneFlip.flipY;
|
||||
}
|
||||
bool flipX = parentFlipX ^ (b.ScaleX < 0);
|
||||
bool flipY = parentFlipY ^ (b.ScaleY < 0);
|
||||
|
||||
BoneFlipEntry boneFlip;
|
||||
boneFlipTable.TryGetValue(b, out boneFlip);
|
||||
boneFlip.flipX = flipX;
|
||||
boneFlip.flipY = flipY;
|
||||
boneFlipTable[b] = boneFlip;
|
||||
|
||||
bool flipXOR = flipX ^ flipY;
|
||||
bool parentFlipXOR = parentFlipX ^ parentFlipY;
|
||||
|
||||
if (!oldRagdollBehaviour && isStartingBone) {
|
||||
if (b != skeleton.RootBone) { // RagdollRoot is not skeleton root.
|
||||
ragdollRoot.localPosition = new Vector3(parentBone.WorldX, parentBone.WorldY, 0);
|
||||
ragdollRoot.localRotation = Quaternion.Euler(0, 0, parentBone.WorldRotationX - parentBone.ShearX);
|
||||
ragdollRoot.localScale = new Vector3(parentBone.WorldScaleX, parentBone.WorldScaleY, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 parentTransformWorldPosition = parentTransform.position;
|
||||
Quaternion parentTransformWorldRotation = parentTransform.rotation;
|
||||
|
||||
parentSpaceHelper.position = parentTransformWorldPosition;
|
||||
parentSpaceHelper.rotation = parentTransformWorldRotation;
|
||||
parentSpaceHelper.localScale = parentTransform.lossyScale;
|
||||
|
||||
if (oldRagdollBehaviour) {
|
||||
if (isStartingBone && b != skeleton.RootBone) {
|
||||
Vector3 localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0);
|
||||
parentSpaceHelper.position = ragdollRoot.TransformPoint(localPosition);
|
||||
parentSpaceHelper.localRotation = Quaternion.Euler(0, 0, parentBone.WorldRotationX - parentBone.ShearX);
|
||||
parentSpaceHelper.localScale = new Vector3(parentBone.WorldScaleX, parentBone.WorldScaleY, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 boneWorldPosition = t.position;
|
||||
Vector3 right = parentSpaceHelper.InverseTransformDirection(t.right);
|
||||
|
||||
Vector3 boneLocalPosition = parentSpaceHelper.InverseTransformPoint(boneWorldPosition);
|
||||
float boneLocalRotation = Mathf.Atan2(right.y, right.x) * Mathf.Rad2Deg;
|
||||
|
||||
if (flipXOR) boneLocalPosition.y *= -1f;
|
||||
if (parentFlipXOR != flipXOR) boneLocalPosition.y *= -1f;
|
||||
|
||||
if (parentFlipXOR) boneLocalRotation *= -1f;
|
||||
if (parentFlipX != flipX) boneLocalRotation += 180;
|
||||
|
||||
b.X = Mathf.Lerp(b.X, boneLocalPosition.x, mix);
|
||||
b.Y = Mathf.Lerp(b.Y, boneLocalPosition.y, mix);
|
||||
b.Rotation = Mathf.Lerp(b.Rotation, boneLocalRotation, mix);
|
||||
//b.AppliedRotation = Mathf.Lerp(b.AppliedRotation, boneLocalRotation, mix);
|
||||
}
|
||||
}
|
||||
|
||||
void GetStartBoneParentFlipState (out bool parentFlipX, out bool parentFlipY) {
|
||||
parentFlipX = skeleton.ScaleX < 0;
|
||||
parentFlipY = skeleton.ScaleY < 0;
|
||||
Bone parent = this.StartingBone == null ? null : this.StartingBone.Parent;
|
||||
while (parent != null) {
|
||||
parentFlipX ^= parent.ScaleX < 0;
|
||||
parentFlipY ^= parent.ScaleY < 0;
|
||||
parent = parent.Parent;
|
||||
}
|
||||
}
|
||||
|
||||
static List<Collider2D> AttachBoundingBoxRagdollColliders (Bone b, GameObject go, Skeleton skeleton, float gravityScale) {
|
||||
const string AttachmentNameMarker = "ragdoll";
|
||||
List<Collider2D> colliders = new List<Collider2D>();
|
||||
Skin skin = skeleton.Skin ?? skeleton.Data.DefaultSkin;
|
||||
|
||||
List<Skin.SkinEntry> skinEntries = new List<Skin.SkinEntry>();
|
||||
foreach (Slot slot in skeleton.Slots) {
|
||||
if (slot.Bone == b) {
|
||||
skin.GetAttachments(skeleton.Slots.IndexOf(slot), skinEntries);
|
||||
|
||||
bool bbAttachmentAdded = false;
|
||||
foreach (Skin.SkinEntry entry in skinEntries) {
|
||||
BoundingBoxAttachment bbAttachment = entry.Attachment as BoundingBoxAttachment;
|
||||
if (bbAttachment != null) {
|
||||
if (!entry.Name.ToLower().Contains(AttachmentNameMarker))
|
||||
continue;
|
||||
|
||||
bbAttachmentAdded = true;
|
||||
PolygonCollider2D bbCollider = SkeletonUtility.AddBoundingBoxAsComponent(bbAttachment, slot, go, isTrigger: false);
|
||||
colliders.Add(bbCollider);
|
||||
}
|
||||
}
|
||||
|
||||
if (bbAttachmentAdded)
|
||||
SkeletonUtility.AddBoneRigidbody2D(go, isKinematic: false, gravityScale: gravityScale);
|
||||
}
|
||||
}
|
||||
|
||||
return colliders;
|
||||
}
|
||||
|
||||
static Vector3 FlipScale (bool flipX, bool flipY) {
|
||||
return new Vector3(flipX ? -1f : 1f, flipY ? -1f : 1f, 1f);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void OnDrawGizmosSelected () {
|
||||
if (isActive) {
|
||||
Gizmos.DrawWireSphere(transform.position, thickness * 1.2f);
|
||||
Vector3 newTransformPos = RootRigidbody.position - rootOffset;
|
||||
Gizmos.DrawLine(transform.position, newTransformPos);
|
||||
Gizmos.DrawWireSphere(newTransformPos, thickness * 1.2f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e74a49a26242a214d9084fde00bfe3ab
|
||||
timeCreated: 1431497383
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,84 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
public class SkeletonUtilityEyeConstraint : SkeletonUtilityConstraint {
|
||||
public Transform[] eyes;
|
||||
public float radius = 0.5f;
|
||||
public Transform target;
|
||||
public Vector3 targetPosition;
|
||||
public float speed = 10;
|
||||
Vector3[] origins;
|
||||
Vector3 centerPoint;
|
||||
|
||||
protected override void OnEnable () {
|
||||
if (!Application.isPlaying) return;
|
||||
base.OnEnable();
|
||||
|
||||
Bounds centerBounds = new Bounds(eyes[0].localPosition, Vector3.zero);
|
||||
origins = new Vector3[eyes.Length];
|
||||
for (int i = 0; i < eyes.Length; i++) {
|
||||
origins[i] = eyes[i].localPosition;
|
||||
centerBounds.Encapsulate(origins[i]);
|
||||
}
|
||||
|
||||
centerPoint = centerBounds.center;
|
||||
}
|
||||
|
||||
protected override void OnDisable () {
|
||||
if (!Application.isPlaying) return;
|
||||
|
||||
for (int i = 0; i < eyes.Length; i++) {
|
||||
eyes[i].localPosition = origins[i];
|
||||
}
|
||||
base.OnDisable();
|
||||
}
|
||||
|
||||
public override void DoUpdate () {
|
||||
if (target != null) targetPosition = target.position;
|
||||
|
||||
Vector3 goal = targetPosition;
|
||||
Vector3 center = transform.TransformPoint(centerPoint);
|
||||
Vector3 dir = goal - center;
|
||||
|
||||
if (dir.magnitude > 1)
|
||||
dir.Normalize();
|
||||
|
||||
for (int i = 0; i < eyes.Length; i++) {
|
||||
center = transform.TransformPoint(origins[i]);
|
||||
eyes[i].position = Vector3.MoveTowards(eyes[i].position, center + (dir * radius * hierarchy.PositionScale),
|
||||
speed * hierarchy.PositionScale * Time.deltaTime);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d994c65b6daec64f80ae2ae04e9d999
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,140 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
|
||||
#define NEW_PREFAB_SYSTEM
|
||||
#endif
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
|
||||
#if NEW_PREFAB_SYSTEM
|
||||
[ExecuteAlways]
|
||||
#else
|
||||
[ExecuteInEditMode]
|
||||
#endif
|
||||
[RequireComponent(typeof(SkeletonUtilityBone))]
|
||||
public class SkeletonUtilityGroundConstraint : SkeletonUtilityConstraint {
|
||||
|
||||
[Tooltip("LayerMask for what objects to raycast against")]
|
||||
public LayerMask groundMask;
|
||||
[Tooltip("Use 2D")]
|
||||
public bool use2D = false;
|
||||
[Tooltip("Uses SphereCast for 3D mode and CircleCast for 2D mode")]
|
||||
public bool useRadius = false;
|
||||
[Tooltip("The Radius")]
|
||||
public float castRadius = 0.1f;
|
||||
[Tooltip("How high above the target bone to begin casting from")]
|
||||
public float castDistance = 5f;
|
||||
[Tooltip("X-Axis adjustment")]
|
||||
public float castOffset = 0;
|
||||
[Tooltip("Y-Axis adjustment")]
|
||||
public float groundOffset = 0;
|
||||
[Tooltip("How fast the target IK position adjusts to the ground. Use smaller values to prevent snapping")]
|
||||
public float adjustSpeed = 5;
|
||||
|
||||
Vector3 rayOrigin;
|
||||
Vector3 rayDir = new Vector3(0, -1, 0);
|
||||
float hitY;
|
||||
float lastHitY;
|
||||
|
||||
protected override void OnEnable () {
|
||||
base.OnEnable();
|
||||
lastHitY = transform.position.y;
|
||||
}
|
||||
|
||||
public override void DoUpdate () {
|
||||
rayOrigin = transform.position + new Vector3(castOffset, castDistance, 0);
|
||||
|
||||
float positionScale = hierarchy.PositionScale;
|
||||
float adjustDistanceThisFrame = adjustSpeed * positionScale * Time.deltaTime;
|
||||
hitY = float.MinValue;
|
||||
if (use2D) {
|
||||
RaycastHit2D hit;
|
||||
|
||||
if (useRadius)
|
||||
hit = Physics2D.CircleCast(rayOrigin, castRadius, rayDir, castDistance + groundOffset, groundMask);
|
||||
else
|
||||
hit = Physics2D.Raycast(rayOrigin, rayDir, castDistance + groundOffset, groundMask);
|
||||
|
||||
if (hit.collider != null) {
|
||||
hitY = hit.point.y + groundOffset;
|
||||
if (Application.isPlaying)
|
||||
hitY = Mathf.MoveTowards(lastHitY, hitY, adjustDistanceThisFrame);
|
||||
} else {
|
||||
if (Application.isPlaying)
|
||||
hitY = Mathf.MoveTowards(lastHitY, transform.position.y, adjustDistanceThisFrame);
|
||||
}
|
||||
} else {
|
||||
RaycastHit hit;
|
||||
bool validHit = false;
|
||||
|
||||
if (useRadius)
|
||||
validHit = Physics.SphereCast(rayOrigin, castRadius, rayDir, out hit, castDistance + groundOffset, groundMask);
|
||||
else
|
||||
validHit = Physics.Raycast(rayOrigin, rayDir, out hit, castDistance + groundOffset, groundMask);
|
||||
|
||||
if (validHit) {
|
||||
hitY = hit.point.y + groundOffset;
|
||||
if (Application.isPlaying)
|
||||
hitY = Mathf.MoveTowards(lastHitY, hitY, adjustDistanceThisFrame);
|
||||
|
||||
} else {
|
||||
if (Application.isPlaying)
|
||||
hitY = Mathf.MoveTowards(lastHitY, transform.position.y, adjustDistanceThisFrame);
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 v = transform.position;
|
||||
v.y = Mathf.Clamp(v.y, Mathf.Min(lastHitY, hitY), float.MaxValue);
|
||||
transform.position = v;
|
||||
|
||||
bone.bone.X = transform.localPosition.x / hierarchy.PositionScale;
|
||||
bone.bone.Y = transform.localPosition.y / hierarchy.PositionScale;
|
||||
|
||||
lastHitY = hitY;
|
||||
}
|
||||
|
||||
void OnDrawGizmos () {
|
||||
Vector3 hitEnd = rayOrigin + (rayDir * Mathf.Min(castDistance, rayOrigin.y - hitY));
|
||||
Vector3 clearEnd = rayOrigin + (rayDir * castDistance);
|
||||
Gizmos.DrawLine(rayOrigin, hitEnd);
|
||||
|
||||
if (useRadius) {
|
||||
Gizmos.DrawLine(new Vector3(hitEnd.x - castRadius, hitEnd.y - groundOffset, hitEnd.z), new Vector3(hitEnd.x + castRadius, hitEnd.y - groundOffset, hitEnd.z));
|
||||
Gizmos.DrawLine(new Vector3(clearEnd.x - castRadius, clearEnd.y, clearEnd.z), new Vector3(clearEnd.x + castRadius, clearEnd.y, clearEnd.z));
|
||||
}
|
||||
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawLine(hitEnd, clearEnd);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3662334b99de5fe4396ab24e30c4fd12
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,137 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
|
||||
// SkeletonUtilityKinematicShadow allows hinge chains to inherit a velocity interpreted from changes in parent transform position or from unrelated rigidbodies.
|
||||
// Note: Uncheck "useRootTransformIfNull
|
||||
public class SkeletonUtilityKinematicShadow : MonoBehaviour {
|
||||
#region Inspector
|
||||
[Tooltip("If checked, the hinge chain can inherit your root transform's velocity or position/rotation changes.")]
|
||||
public bool detachedShadow = false;
|
||||
public Transform parent;
|
||||
public bool hideShadow = true;
|
||||
public PhysicsSystem physicsSystem = PhysicsSystem.Physics3D;
|
||||
#endregion
|
||||
|
||||
GameObject shadowRoot;
|
||||
readonly List<TransformPair> shadowTable = new List<TransformPair>();
|
||||
struct TransformPair {
|
||||
public Transform dest, src;
|
||||
}
|
||||
|
||||
public enum PhysicsSystem {
|
||||
Physics2D,
|
||||
Physics3D
|
||||
};
|
||||
|
||||
void Start () {
|
||||
// Duplicate this gameObject as the "shadow" with a different parent.
|
||||
shadowRoot = Instantiate<GameObject>(this.gameObject);
|
||||
Destroy(shadowRoot.GetComponent<SkeletonUtilityKinematicShadow>());
|
||||
|
||||
// Prepare shadow gameObject's properties.
|
||||
Transform shadowRootTransform = shadowRoot.transform;
|
||||
shadowRootTransform.position = transform.position;
|
||||
shadowRootTransform.rotation = transform.rotation;
|
||||
|
||||
Vector3 scaleRef = transform.TransformPoint(Vector3.right);
|
||||
float scale = Vector3.Distance(transform.position, scaleRef);
|
||||
shadowRootTransform.localScale = Vector3.one;
|
||||
|
||||
if (!detachedShadow) {
|
||||
// Do not change to null coalescing operator (??). Unity overloads null checks for UnityEngine.Objects but not the ?? operator.
|
||||
if (parent == null)
|
||||
shadowRootTransform.parent = transform.root;
|
||||
else
|
||||
shadowRootTransform.parent = parent;
|
||||
}
|
||||
|
||||
if (hideShadow)
|
||||
shadowRoot.hideFlags = HideFlags.HideInHierarchy;
|
||||
|
||||
Joint[] shadowJoints = shadowRoot.GetComponentsInChildren<Joint>();
|
||||
foreach (Joint j in shadowJoints)
|
||||
j.connectedAnchor *= scale;
|
||||
|
||||
// Build list of bone pairs (matches shadow transforms with bone transforms)
|
||||
SkeletonUtilityBone[] bones = GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
SkeletonUtilityBone[] shadowBones = shadowRoot.GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
foreach (SkeletonUtilityBone b in bones) {
|
||||
if (b.gameObject == this.gameObject)
|
||||
continue;
|
||||
|
||||
System.Type checkType = (physicsSystem == PhysicsSystem.Physics2D) ? typeof(Rigidbody2D) : typeof(Rigidbody);
|
||||
foreach (SkeletonUtilityBone sb in shadowBones) {
|
||||
if (sb.GetComponent(checkType) != null && sb.boneName == b.boneName) {
|
||||
shadowTable.Add(new TransformPair {
|
||||
dest = b.transform,
|
||||
src = sb.transform
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Destroy conflicting and unneeded components
|
||||
DestroyComponents(shadowBones);
|
||||
|
||||
DestroyComponents(GetComponentsInChildren<Joint>());
|
||||
DestroyComponents(GetComponentsInChildren<Rigidbody>());
|
||||
DestroyComponents(GetComponentsInChildren<Collider>());
|
||||
}
|
||||
|
||||
static void DestroyComponents (Component[] components) {
|
||||
for (int i = 0, n = components.Length; i < n; i++)
|
||||
Destroy(components[i]);
|
||||
}
|
||||
|
||||
void FixedUpdate () {
|
||||
if (physicsSystem == PhysicsSystem.Physics2D) {
|
||||
Rigidbody2D shadowRootRigidbody = shadowRoot.GetComponent<Rigidbody2D>();
|
||||
shadowRootRigidbody.MovePosition(transform.position);
|
||||
shadowRootRigidbody.MoveRotation(transform.rotation.eulerAngles.z);
|
||||
} else {
|
||||
Rigidbody shadowRootRigidbody = shadowRoot.GetComponent<Rigidbody>();
|
||||
shadowRootRigidbody.MovePosition(transform.position);
|
||||
shadowRootRigidbody.MoveRotation(transform.rotation);
|
||||
}
|
||||
|
||||
for (int i = 0, n = shadowTable.Count; i < n; i++) {
|
||||
TransformPair pair = shadowTable[i];
|
||||
pair.dest.localPosition = pair.src.localPosition;
|
||||
pair.dest.localRotation = pair.src.localRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cfeac06b8a6aa1645813700e3e4c0863
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,88 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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 Spine.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
|
||||
/// <summary>
|
||||
/// Add this component to a Spine GameObject to apply a specific slot's Colors as MaterialProperties.
|
||||
/// This allows you to apply the two color tint to the whole skeleton and not require the overhead of an extra vertex stream on the mesh.
|
||||
/// </summary>
|
||||
public class SlotTintBlackFollower : MonoBehaviour {
|
||||
#region Inspector
|
||||
/// <summary>
|
||||
/// Serialized name of the slot loaded at runtime. Change the slot field instead of this if you want to change the followed slot at runtime.</summary>
|
||||
[SpineSlot]
|
||||
[SerializeField]
|
||||
protected string slotName;
|
||||
|
||||
[SerializeField]
|
||||
protected string colorPropertyName = "_Color";
|
||||
[SerializeField]
|
||||
protected string blackPropertyName = "_Black";
|
||||
#endregion
|
||||
|
||||
public Slot slot;
|
||||
MeshRenderer mr;
|
||||
MaterialPropertyBlock mb;
|
||||
int colorPropertyId, blackPropertyId;
|
||||
|
||||
void Start () {
|
||||
Initialize(false);
|
||||
}
|
||||
|
||||
public void Initialize (bool overwrite) {
|
||||
if (overwrite || mb == null) {
|
||||
mb = new MaterialPropertyBlock();
|
||||
mr = GetComponent<MeshRenderer>();
|
||||
slot = GetComponent<ISkeletonComponent>().Skeleton.FindSlot(slotName);
|
||||
|
||||
colorPropertyId = Shader.PropertyToID(colorPropertyName);
|
||||
blackPropertyId = Shader.PropertyToID(blackPropertyName);
|
||||
}
|
||||
}
|
||||
|
||||
public void Update () {
|
||||
Slot s = slot;
|
||||
if (s == null) return;
|
||||
|
||||
mb.SetColor(colorPropertyId, s.GetColor());
|
||||
mb.SetColor(blackPropertyId, s.GetColorTintBlack());
|
||||
|
||||
mr.SetPropertyBlock(mb);
|
||||
}
|
||||
|
||||
void OnDisable () {
|
||||
mb.Clear();
|
||||
mr.SetPropertyBlock(mb);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 49a62759c814e7a458b9026d504e0898
|
||||
timeCreated: 1489227143
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,82 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated July 28, 2023. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2023, 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 UnityEngine.Events;
|
||||
|
||||
namespace Spine.Unity.Prototyping {
|
||||
|
||||
public class SpineEventUnityHandler : MonoBehaviour {
|
||||
|
||||
[System.Serializable]
|
||||
public class EventPair {
|
||||
[SpineEvent] public string spineEvent;
|
||||
public UnityEvent unityHandler;
|
||||
public AnimationState.TrackEntryEventDelegate eventDelegate;
|
||||
}
|
||||
|
||||
public List<EventPair> events = new List<EventPair>();
|
||||
|
||||
ISkeletonComponent skeletonComponent;
|
||||
IAnimationStateComponent animationStateComponent;
|
||||
|
||||
void Start () {
|
||||
if (skeletonComponent == null)
|
||||
skeletonComponent = GetComponent<ISkeletonComponent>();
|
||||
if (skeletonComponent == null) return;
|
||||
if (animationStateComponent == null)
|
||||
animationStateComponent = skeletonComponent as IAnimationStateComponent;
|
||||
if (animationStateComponent == null) return;
|
||||
Skeleton skeleton = skeletonComponent.Skeleton;
|
||||
if (skeleton == null) return;
|
||||
|
||||
|
||||
SkeletonData skeletonData = skeleton.Data;
|
||||
AnimationState state = animationStateComponent.AnimationState;
|
||||
foreach (EventPair ep in events) {
|
||||
EventData eventData = skeletonData.FindEvent(ep.spineEvent);
|
||||
ep.eventDelegate = ep.eventDelegate ?? delegate (TrackEntry trackEntry, Event e) { if (e.Data == eventData) ep.unityHandler.Invoke(); };
|
||||
state.Event += ep.eventDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
void OnDestroy () {
|
||||
if (animationStateComponent == null) animationStateComponent = GetComponent<IAnimationStateComponent>();
|
||||
if (animationStateComponent.IsNullOrDestroyed()) return;
|
||||
|
||||
AnimationState state = animationStateComponent.AnimationState;
|
||||
foreach (EventPair ep in events) {
|
||||
if (ep.eventDelegate != null) state.Event -= ep.eventDelegate;
|
||||
ep.eventDelegate = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90293750f472d3340b452cec6fea2606
|
||||
timeCreated: 1495263964
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user