From 7facdba3966a019c7172e121e60ac26ad1814600 Mon Sep 17 00:00:00 2001 From: Fenrisul Date: Sat, 2 May 2015 00:36:45 -0700 Subject: [PATCH 1/2] [Unity] Updated SkeletonAnimationInterface to include GetSkeleton to avoid additional GetComponents. --- spine-unity/Assets/spine-unity/SkeletonAnimation.cs | 4 +++- spine-unity/Assets/spine-unity/SkeletonAnimationInterface.cs | 2 ++ spine-unity/Assets/spine-unity/SkeletonAnimator.cs | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/spine-unity/Assets/spine-unity/SkeletonAnimation.cs b/spine-unity/Assets/spine-unity/SkeletonAnimation.cs index be8f0299b..cba8bd116 100644 --- a/spine-unity/Assets/spine-unity/SkeletonAnimation.cs +++ b/spine-unity/Assets/spine-unity/SkeletonAnimation.cs @@ -42,7 +42,9 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation { public bool loop; public Spine.AnimationState state; - + public Skeleton GetSkeleton(){ + return this.skeleton; + } public event UpdateBonesDelegate UpdateLocal { add { _UpdateLocal += value; } diff --git a/spine-unity/Assets/spine-unity/SkeletonAnimationInterface.cs b/spine-unity/Assets/spine-unity/SkeletonAnimationInterface.cs index 03d3036ce..2588be2d0 100644 --- a/spine-unity/Assets/spine-unity/SkeletonAnimationInterface.cs +++ b/spine-unity/Assets/spine-unity/SkeletonAnimationInterface.cs @@ -31,6 +31,7 @@ using UnityEngine; using System.Collections; +using Spine; public delegate void UpdateBonesDelegate (SkeletonRenderer skeletonRenderer); public interface ISkeletonAnimation { @@ -39,4 +40,5 @@ public interface ISkeletonAnimation { event UpdateBonesDelegate UpdateComplete; void LateUpdate (); + Skeleton GetSkeleton(); } diff --git a/spine-unity/Assets/spine-unity/SkeletonAnimator.cs b/spine-unity/Assets/spine-unity/SkeletonAnimator.cs index 038ef46ef..3b7865e79 100644 --- a/spine-unity/Assets/spine-unity/SkeletonAnimator.cs +++ b/spine-unity/Assets/spine-unity/SkeletonAnimator.cs @@ -15,6 +15,10 @@ public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation { public enum MixMode { AlwaysMix, MixNext, SpineStyle } public MixMode[] layerMixModes = new MixMode[0]; + public Skeleton GetSkeleton () { + return this.skeleton; + + } public event UpdateBonesDelegate UpdateLocal { add { _UpdateLocal += value; } remove { _UpdateLocal -= value; } From c6ecfb87ce9881e392473a6dafd7b52731ef50ae Mon Sep 17 00:00:00 2001 From: Fenrisul Date: Fri, 15 May 2015 23:14:51 -0700 Subject: [PATCH 2/2] [Unity] Added SkeletonRagdoll, SkeletonRagdoll2D, and Ragdoll example. --- .../Assets/Examples/Scenes/Ragdoll.unity | Bin 0 -> 49612 bytes .../Assets/Examples/Scenes/Ragdoll.unity.meta | 4 + .../Examples/Scripts/RaggedySpineboy.cs | 82 ++++ .../Examples/Scripts/RaggedySpineboy.cs.meta | 8 + .../Examples/Spine/Raggedy Spineboy.meta | 5 + .../Examples/Spine/Raggedy Spineboy.prefab | Bin 0 -> 13928 bytes .../Spine/Raggedy Spineboy.prefab.meta | 4 + .../Raggedy Spineboy.atlas.txt | 34 ++ .../Raggedy Spineboy.atlas.txt.meta | 4 + .../Raggedy Spineboy/Raggedy Spineboy.json | 374 +++++++++++++++++ .../Raggedy Spineboy.json.meta | 4 + .../Raggedy Spineboy/Raggedy Spineboy.png | Bin 0 -> 31903 bytes .../Raggedy Spineboy.png.meta | 47 +++ .../Raggedy Spineboy_Atlas.asset | Bin 0 -> 4192 bytes .../Raggedy Spineboy_Atlas.asset.meta | 4 + .../Raggedy Spineboy_Material.mat | Bin 0 -> 4228 bytes .../Raggedy Spineboy_Material.mat.meta | 4 + .../Raggedy Spineboy_SkeletonData.asset | Bin 0 -> 4228 bytes .../Raggedy Spineboy_SkeletonData.asset.meta | 4 + .../Assets/Examples/Spine/dragon.prefab | Bin 9400 -> 9456 bytes .../spine-unity/Editor/SkeletonBaker.cs.meta | 4 +- .../Editor/SkeletonDataAssetInspector.cs.meta | 2 - .../Editor/SpineEditorUtilities.cs.meta | 2 - spine-unity/Assets/spine-unity/Ragdoll.meta | 5 + .../Assets/spine-unity/Ragdoll/Editor.meta | 5 + .../Editor/SkeletonRagdoll2DInspector.cs | 49 +++ .../Editor/SkeletonRagdoll2DInspector.cs.meta | 12 + .../Editor/SkeletonRagdollInspector.cs | 49 +++ .../Editor/SkeletonRagdollInspector.cs.meta | 12 + .../spine-unity/Ragdoll/SkeletonRagdoll.cs | 379 +++++++++++++++++ .../Ragdoll/SkeletonRagdoll.cs.meta | 12 + .../spine-unity/Ragdoll/SkeletonRagdoll2D.cs | 388 ++++++++++++++++++ .../Ragdoll/SkeletonRagdoll2D.cs.meta | 12 + .../Assets/spine-unity/SkeletonAnimation.cs | 10 +- .../spine-unity/SkeletonAnimationInterface.cs | 2 +- .../Assets/spine-unity/SkeletonAnimator.cs | 6 + .../spine-unity/SkeletonExtensions.cs.meta | 2 - .../SkeletonUtility/SkeletonUtility.cs | 16 + 38 files changed, 1532 insertions(+), 13 deletions(-) create mode 100644 spine-unity/Assets/Examples/Scenes/Ragdoll.unity create mode 100644 spine-unity/Assets/Examples/Scenes/Ragdoll.unity.meta create mode 100644 spine-unity/Assets/Examples/Scripts/RaggedySpineboy.cs create mode 100644 spine-unity/Assets/Examples/Scripts/RaggedySpineboy.cs.meta create mode 100644 spine-unity/Assets/Examples/Spine/Raggedy Spineboy.meta create mode 100644 spine-unity/Assets/Examples/Spine/Raggedy Spineboy.prefab create mode 100644 spine-unity/Assets/Examples/Spine/Raggedy Spineboy.prefab.meta create mode 100644 spine-unity/Assets/Examples/Spine/Raggedy Spineboy/Raggedy Spineboy.atlas.txt create mode 100644 spine-unity/Assets/Examples/Spine/Raggedy Spineboy/Raggedy Spineboy.atlas.txt.meta create mode 100644 spine-unity/Assets/Examples/Spine/Raggedy Spineboy/Raggedy Spineboy.json create mode 100644 spine-unity/Assets/Examples/Spine/Raggedy Spineboy/Raggedy Spineboy.json.meta create mode 100644 spine-unity/Assets/Examples/Spine/Raggedy Spineboy/Raggedy Spineboy.png create mode 100644 spine-unity/Assets/Examples/Spine/Raggedy Spineboy/Raggedy Spineboy.png.meta create mode 100644 spine-unity/Assets/Examples/Spine/Raggedy Spineboy/Raggedy Spineboy_Atlas.asset create mode 100644 spine-unity/Assets/Examples/Spine/Raggedy Spineboy/Raggedy Spineboy_Atlas.asset.meta create mode 100644 spine-unity/Assets/Examples/Spine/Raggedy Spineboy/Raggedy Spineboy_Material.mat create mode 100644 spine-unity/Assets/Examples/Spine/Raggedy Spineboy/Raggedy Spineboy_Material.mat.meta create mode 100644 spine-unity/Assets/Examples/Spine/Raggedy Spineboy/Raggedy Spineboy_SkeletonData.asset create mode 100644 spine-unity/Assets/Examples/Spine/Raggedy Spineboy/Raggedy Spineboy_SkeletonData.asset.meta create mode 100644 spine-unity/Assets/spine-unity/Ragdoll.meta create mode 100644 spine-unity/Assets/spine-unity/Ragdoll/Editor.meta create mode 100644 spine-unity/Assets/spine-unity/Ragdoll/Editor/SkeletonRagdoll2DInspector.cs create mode 100644 spine-unity/Assets/spine-unity/Ragdoll/Editor/SkeletonRagdoll2DInspector.cs.meta create mode 100644 spine-unity/Assets/spine-unity/Ragdoll/Editor/SkeletonRagdollInspector.cs create mode 100644 spine-unity/Assets/spine-unity/Ragdoll/Editor/SkeletonRagdollInspector.cs.meta create mode 100644 spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll.cs create mode 100644 spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll.cs.meta create mode 100644 spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll2D.cs create mode 100644 spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll2D.cs.meta diff --git a/spine-unity/Assets/Examples/Scenes/Ragdoll.unity b/spine-unity/Assets/Examples/Scenes/Ragdoll.unity new file mode 100644 index 0000000000000000000000000000000000000000..b35ed755488172a89ef953b1ed78e1ff9e326423 GIT binary patch literal 49612 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 literal 0 HcmV?d00001 diff --git a/spine-unity/Assets/Examples/Scenes/Ragdoll.unity.meta b/spine-unity/Assets/Examples/Scenes/Ragdoll.unity.meta new file mode 100644 index 000000000..cd725ce04 --- /dev/null +++ b/spine-unity/Assets/Examples/Scenes/Ragdoll.unity.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: 031930e4cebf68345b71c664bfa622cf +DefaultImporter: + userData: diff --git a/spine-unity/Assets/Examples/Scripts/RaggedySpineboy.cs b/spine-unity/Assets/Examples/Scripts/RaggedySpineboy.cs new file mode 100644 index 000000000..eea04f694 --- /dev/null +++ b/spine-unity/Assets/Examples/Scripts/RaggedySpineboy.cs @@ -0,0 +1,82 @@ +using UnityEngine; +using System.Collections; + +public class RaggedySpineboy : MonoBehaviour { + + public LayerMask groundMask; + public float restoreDuration = 0.5f; + public Vector2 launchVelocity = new Vector2(50,100); + + SkeletonRagdoll2D ragdoll; + Collider2D naturalCollider; + + void Start () { + + ragdoll = GetComponent(); + naturalCollider = GetComponent(); + } + + void AddRigidbody () { + var rb = gameObject.AddComponent(); + rb.fixedAngle = true; + naturalCollider.enabled = true; + } + + void RemoveRigidbody () { + Destroy(GetComponent()); + naturalCollider.enabled = false; + } + + void Update () { + + } + + void OnMouseUp () { + if (naturalCollider.enabled) { + Launch(); + } + } + + void Launch () { + RemoveRigidbody(); + ragdoll.Apply(); + ragdoll.RootRigidbody.velocity = new Vector2(Random.Range(-launchVelocity.x, launchVelocity.x), launchVelocity.y); + StartCoroutine(WaitUntilStopped()); + } + + IEnumerator Restore () { + Vector3 estimatedPos = ragdoll.EstimatedSkeletonPosition; + Vector3 rbPosition = ragdoll.RootRigidbody.position; + + Ray ray = new Ray(rbPosition, estimatedPos - rbPosition); + Vector3 skeletonPoint = estimatedPos; + RaycastHit2D hit = Physics2D.Raycast((Vector2)rbPosition, (Vector2)(estimatedPos - rbPosition), Vector3.Distance(estimatedPos, rbPosition), groundMask); + if (hit.collider != null) + skeletonPoint = hit.point; + + + ragdoll.RootRigidbody.isKinematic = true; + ragdoll.SetSkeletonPosition(skeletonPoint); + + yield return ragdoll.SmoothMix(0, restoreDuration); + ragdoll.Remove(); + + AddRigidbody(); + } + + IEnumerator WaitUntilStopped () { + yield return new WaitForSeconds(0.5f); + + float t = 0; + while (t < 0.5f) { + if (ragdoll.RootRigidbody.velocity.magnitude > 0.06f) + t = 0; + else + t += Time.deltaTime; + + yield return null; + } + + StartCoroutine(Restore()); + } +} diff --git a/spine-unity/Assets/Examples/Scripts/RaggedySpineboy.cs.meta b/spine-unity/Assets/Examples/Scripts/RaggedySpineboy.cs.meta new file mode 100644 index 000000000..5c864d277 --- /dev/null +++ b/spine-unity/Assets/Examples/Scripts/RaggedySpineboy.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 849a7739a7df0754882fcb34c09df4c1 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/spine-unity/Assets/Examples/Spine/Raggedy Spineboy.meta b/spine-unity/Assets/Examples/Spine/Raggedy Spineboy.meta new file mode 100644 index 000000000..ff4d0f512 --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/Raggedy Spineboy.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: 31a18437d3dfcde44a2e4b24361a3620 +folderAsset: yes +DefaultImporter: + userData: diff --git a/spine-unity/Assets/Examples/Spine/Raggedy Spineboy.prefab b/spine-unity/Assets/Examples/Spine/Raggedy Spineboy.prefab new file mode 100644 index 0000000000000000000000000000000000000000..618b8cd95ff086851dcd42752a28327710eb70fa GIT binary patch literal 13928 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 bo~rn*7mL zxKR86s`vw>iUUU~6@CN<4*UQ(V*2YIjkRMDt{k?jW~Zh8q;7rcmc~}<$W^6|UsFom zOK*B9bz$z(+{NUa`g?zWe}>wR&~sTkV^cj)%epX%4EsPrZpTll)IIbXWkuT0Os|z@ zP7OD&Y<*$6?xVD4+5_D$)ExxDgb=*F-sw0yzuHS(Zddo5p-J*=fyQ^NN%U4nTa!7N zi@n^&6Zk1+6x!mjoes>}2keK#s{M#30d=(7u0RL~BEblVgAvaMF|1{@{EU{5Ro=;C z4n{@&-_-!2dC33W5m4c5n)PY*pXi}54WAH>vXu9dwR)`N7rQ~5S5rE(sA^7%^ee-B z93cQimMVI8TU*uBPIK>3>6}0?85`TWXDrANjY+sKKtqj^aDM<2b|WMli-2}1Z*p6E z{Md}Y%x$mW#CssXBk?eClMW0HltF^hw@PUXTzWnX)n}Ts~Yk8PWgg$c$tsPYQe@^v|=@jXO5qG1iSYwv9pg zZRlOot2$F7M!g#WyweoZ@2``V@lY7>R zAb1aiMOBz?p7j)Xd?tWG1>;;+1a%{8P*;`8h4pL$?|B_=2B+I|0mw&q=yaKlfUa_3 zollS+T|F@D^7#O$REw)68RXijJw7{LXxPyd`}ZCbUWwQxecOmvviD2~DrK*mIdGORAdbl+jAyw%AYLUS#Ip;1GJEIHMG{VNi|l<( z&@~*xn%Vn$1k;TCT;&ququF~tn8ZgvnZ0jB5W;emx40KC1fWW-PKuR$kXxbpVhAdQ zi)O%=2*bewOPE7czX=104;K0))o-DTgc0R`9u#jg_yCS!O{(9CV49J?OBf${l|2gR6Y*XsY^&fsZ+x=zj z+wW#i^>6(3jr6i<`ZHx<%D|L?DFgpw2JmISuKRt{8@D!g(#&k-izkj;n_hD|`05%7$~OwG{R(@m;t2xDTxfkX~^Fy8dy z6%&`U9=v!l(S(cfs26{Mck#Wd?w*>Nnjc^?MW(7=^;hqETXi*rRCg1ycZCpA4I>PM zw6}D&bfj9z-;Irp2*@yOjJuB8$L8sx=VP-ZPlAgv$q)1Op|m6NUY8&Qf$PPC0Xg(nYFZ z>n8YN!|f6HLs=0HSvDIPkUX0a0WARGT!G?L%=;iT2%8R)#wg~z4I_?V+@ZGQ->No+ zyNvwXia;oq@NYMOB>yd%Vaj8)AumaOi~%;ZItIOwA5tn?079XJzeZs~O(@<4U%P>H z(4liSOUh?EG+ZR6grZ!l^cCiMr@qafOZTv(rW4#+5a^zoN!NC1J8)`rZMOzO_pp_? zwnwc?^8=74xaFjQaRS0Ci&#~?UjWN~NI9oD5|cgFKNdD>kiXu%O`i3T$x_=753vOx zoG0cfCrK)(Ts?qG&~!v-jEY>pO+9ZU36*Z8EXJjE2h}dMZCPzscIB-*q^XVc8mz0; zmH6{Q>kb`E?yp9$rK~a`PQ!J#5 zs^DR%A1i_f`ZBZHaX+qsBK`p>sfiN?kRqO8OG3ICCEf#OGvPX;(~}djCQoX+aKXr5 zuYt<)HyA)E+oghd32LiiI;CMGJ;;7CQrykN94kWwydPycPV3-Rb2=IU6Q%<&f%#!N znsg<8==|c81&n9ljE*TobXFsZ3>%2fX-a|^6;|ST1HyM+xP`7?(4eG@Ma`N;h$(UX zqOPP$3B|5o(lKR-E^9s)j3f0$Vk6^ok8r-d3apMOBD0 z<9IDzD-VH7Ictk?Ye~NS+6_Q;sHU>;GRrWPH<$Ojj^M6sGae<0a4u`Sqew@PsDdNj zseytRmK^ae0|=M!1oZxSBcHB7k44R4l%EOpH*UY2xxO~|_I=yU=e2h}sc(lDPk%32 zk2;2*eYxlU{J7qQMiJuFKl$EVS3S12_Ima4$cJ@>m5(Z2B?6TQR3cD`KqUhIc?9sf z#D?Rq+&h9_&Qmmu?SCl`;J-PlAs+8ZylU}geD!&h)ck1dQBPI> K?dw=HkpBVn@Wk8z literal 0 HcmV?d00001 diff --git a/spine-unity/Assets/Examples/Spine/Raggedy Spineboy/Raggedy Spineboy_SkeletonData.asset.meta b/spine-unity/Assets/Examples/Spine/Raggedy Spineboy/Raggedy Spineboy_SkeletonData.asset.meta new file mode 100644 index 000000000..076aa16cb --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/Raggedy Spineboy/Raggedy Spineboy_SkeletonData.asset.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: 57484171e9b9c7243aa3117bc663e7b9 +NativeFormatImporter: + userData: diff --git a/spine-unity/Assets/Examples/Spine/dragon.prefab b/spine-unity/Assets/Examples/Spine/dragon.prefab index d1177057638657ba89bb87fd0490e261b2b9bf22..f70e632e481dcd058c26641b558a797bdd162bff 100644 GIT binary patch delta 211 zcmdnt`N30?fkCN-fkEX10|Nsmki7s%GnnX^>6uQHG-DLo7;uA?JFO@`uf#1eIWsSP zvLTz~lZt!=}KfF?lVJtqB&XWYn*&1LG&B^lY3XEEl9oQ8awI?UBt263M?qXMB z)CKAio_v5^ict@&P9Ln!0Ibe%vlGWnNkNd+|AC+gNH0kL&Z;n3N2zY}1|>G8$#N=E LlOL#X0I>i7oZ2u( diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonBaker.cs.meta b/spine-unity/Assets/spine-unity/Editor/SkeletonBaker.cs.meta index 1e73fe1df..37b13c997 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonBaker.cs.meta +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonBaker.cs.meta @@ -1,10 +1,8 @@ fileFormatVersion: 2 -guid: 8dc542c228b046648ae29be625ecd1b4 +guid: 687d9be457ea4eb44bf09c35c95ee5cd MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: - assetBundleName: - assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs.meta b/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs.meta index 5a602df97..fba64b6e3 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs.meta +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs.meta @@ -6,5 +6,3 @@ MonoImporter: executionOrder: 0 icon: {instanceID: 0} userData: - assetBundleName: - assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs.meta b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs.meta index 858912afe..544e47769 100644 --- a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs.meta +++ b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs.meta @@ -6,5 +6,3 @@ MonoImporter: executionOrder: 0 icon: {instanceID: 0} userData: - assetBundleName: - assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/Ragdoll.meta b/spine-unity/Assets/spine-unity/Ragdoll.meta new file mode 100644 index 000000000..cb9062592 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Ragdoll.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: 90af663b37d994841b7ac03ae30fe2a9 +folderAsset: yes +DefaultImporter: + userData: diff --git a/spine-unity/Assets/spine-unity/Ragdoll/Editor.meta b/spine-unity/Assets/spine-unity/Ragdoll/Editor.meta new file mode 100644 index 000000000..6cac463d2 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Ragdoll/Editor.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: 7220dc1e8d545e849a2eb63e8633349b +folderAsset: yes +DefaultImporter: + userData: diff --git a/spine-unity/Assets/spine-unity/Ragdoll/Editor/SkeletonRagdoll2DInspector.cs b/spine-unity/Assets/spine-unity/Ragdoll/Editor/SkeletonRagdoll2DInspector.cs new file mode 100644 index 000000000..f059aae31 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Ragdoll/Editor/SkeletonRagdoll2DInspector.cs @@ -0,0 +1,49 @@ +/***************************************************************************** + * SkeletonRagdoll2D added by Mitch Thompson + * Full irrevocable rights and permissions granted to Esoteric Software +*****************************************************************************/ + +using UnityEngine; +using UnityEditor; +using System.Collections; +using System.Collections.Generic; + +[CustomEditor(typeof(SkeletonRagdoll2D))] +public class SkeletonRagdoll2DInspector : Editor { + SerializedProperty startingBoneName, stopBoneNames, applyOnStart, pinStartBone, enableJointCollision, gravityScale, disableIK, defaultThickness, rotationLimit, colliderLayer, mix; + + void OnEnable () { + startingBoneName = serializedObject.FindProperty("startingBoneName"); + stopBoneNames = serializedObject.FindProperty("stopBoneNames"); + applyOnStart = serializedObject.FindProperty("applyOnStart"); + pinStartBone = serializedObject.FindProperty("pinStartBone"); + enableJointCollision = serializedObject.FindProperty("enableJointCollision"); + gravityScale = serializedObject.FindProperty("gravityScale"); + disableIK = serializedObject.FindProperty("disableIK"); + defaultThickness = serializedObject.FindProperty("defaultThickness"); + rotationLimit = serializedObject.FindProperty("rotationLimit"); + colliderLayer = serializedObject.FindProperty("colliderLayer"); + mix = serializedObject.FindProperty("mix"); + } + + public override void OnInspectorGUI () { + EditorGUILayout.PropertyField(startingBoneName); + EditorGUILayout.PropertyField(stopBoneNames, true); + EditorGUILayout.PropertyField(applyOnStart); + EditorGUILayout.PropertyField(pinStartBone); + EditorGUILayout.PropertyField(enableJointCollision); + EditorGUILayout.PropertyField(gravityScale); + EditorGUILayout.PropertyField(disableIK); + EditorGUILayout.PropertyField(defaultThickness); + EditorGUILayout.PropertyField(rotationLimit); + colliderLayer.intValue = EditorGUILayout.LayerField(colliderLayer.displayName, colliderLayer.intValue); + EditorGUILayout.PropertyField(mix); + + serializedObject.ApplyModifiedProperties(); + } + + void Header (string name) { + GUILayout.Space(20); + EditorGUILayout.LabelField(name, EditorStyles.boldLabel); + } +} diff --git a/spine-unity/Assets/spine-unity/Ragdoll/Editor/SkeletonRagdoll2DInspector.cs.meta b/spine-unity/Assets/spine-unity/Ragdoll/Editor/SkeletonRagdoll2DInspector.cs.meta new file mode 100644 index 000000000..08480207a --- /dev/null +++ b/spine-unity/Assets/spine-unity/Ragdoll/Editor/SkeletonRagdoll2DInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: b6dd0b99faf3aeb4d803eb9989cb369c +timeCreated: 1431741936 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/Ragdoll/Editor/SkeletonRagdollInspector.cs b/spine-unity/Assets/spine-unity/Ragdoll/Editor/SkeletonRagdollInspector.cs new file mode 100644 index 000000000..a92f570e2 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Ragdoll/Editor/SkeletonRagdollInspector.cs @@ -0,0 +1,49 @@ +/***************************************************************************** + * SkeletonRagdoll added by Mitch Thompson + * Full irrevocable rights and permissions granted to Esoteric Software +*****************************************************************************/ + +using UnityEngine; +using UnityEditor; +using System.Collections; +using System.Collections.Generic; + +[CustomEditor(typeof(SkeletonRagdoll))] +public class SkeletonRagdollInspector : Editor { + SerializedProperty startingBoneName, stopBoneNames, applyOnStart, pinStartBone, enableJointCollision, useGravity, disableIK, defaultThickness, rotationLimit, colliderLayer, mix; + + void OnEnable () { + startingBoneName = serializedObject.FindProperty("startingBoneName"); + stopBoneNames = serializedObject.FindProperty("stopBoneNames"); + applyOnStart = serializedObject.FindProperty("applyOnStart"); + pinStartBone = serializedObject.FindProperty("pinStartBone"); + enableJointCollision = serializedObject.FindProperty("enableJointCollision"); + useGravity = serializedObject.FindProperty("useGravity"); + disableIK = serializedObject.FindProperty("disableIK"); + defaultThickness = serializedObject.FindProperty("defaultThickness"); + rotationLimit = serializedObject.FindProperty("rotationLimit"); + colliderLayer = serializedObject.FindProperty("colliderLayer"); + mix = serializedObject.FindProperty("mix"); + } + + public override void OnInspectorGUI () { + EditorGUILayout.PropertyField(startingBoneName); + EditorGUILayout.PropertyField(stopBoneNames, true); + EditorGUILayout.PropertyField(applyOnStart); + EditorGUILayout.PropertyField(pinStartBone); + EditorGUILayout.PropertyField(enableJointCollision); + EditorGUILayout.PropertyField(useGravity); + EditorGUILayout.PropertyField(disableIK); + EditorGUILayout.PropertyField(defaultThickness); + EditorGUILayout.PropertyField(rotationLimit); + colliderLayer.intValue = EditorGUILayout.LayerField(colliderLayer.displayName, colliderLayer.intValue); + EditorGUILayout.PropertyField(mix); + + serializedObject.ApplyModifiedProperties(); + } + + void Header (string name) { + GUILayout.Space(20); + EditorGUILayout.LabelField(name, EditorStyles.boldLabel); + } +} diff --git a/spine-unity/Assets/spine-unity/Ragdoll/Editor/SkeletonRagdollInspector.cs.meta b/spine-unity/Assets/spine-unity/Ragdoll/Editor/SkeletonRagdollInspector.cs.meta new file mode 100644 index 000000000..7c478ae50 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Ragdoll/Editor/SkeletonRagdollInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: c95a670c56447c644a0f062e4cdd448e +timeCreated: 1431740230 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll.cs b/spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll.cs new file mode 100644 index 000000000..ac43ad1cd --- /dev/null +++ b/spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll.cs @@ -0,0 +1,379 @@ +/***************************************************************************** + * SkeletonRagdoll added by Mitch Thompson + * Full irrevocable rights and permissions granted to Esoteric Software +*****************************************************************************/ + +using UnityEngine; +using System.Collections; +using System.Collections.Generic; +using Spine; + +[RequireComponent(typeof(SkeletonRenderer))] +public class SkeletonRagdoll : MonoBehaviour { + private static Transform helper; + + [Header("Hierarchy")] + [SpineBone] + public string startingBoneName = ""; + [SpineBone] + public List stopBoneNames = new List(); + + [Header("Parameters")] + public bool applyOnStart; + [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("Warning! You will have to re-enable and tune mix values manually if attempting to remove the ragdoll system.")] + public bool disableIK = 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 defaultThickness = 0.125f; + [Tooltip("Default rotational limit value. Min is negative this value, Max is this value.")] + public float rotationLimit = 20; + [Tooltip("The layer assigned to all of the rigidbody parts.")] + public int colliderLayer = 0; + [Range(0, 1)] + public float mix = 1; + + public Rigidbody RootRigidbody { + get { + return this.rootRigidbody; + } + } + + public Vector3 RootOffset { + get { + return this.rootOffset; + } + } + + public Vector3 EstimatedSkeletonPosition { + get { + return rootRigidbody.position - rootOffset; + } + } + + public bool IsActive { + get { + return this.isActive; + } + } + + private Rigidbody rootRigidbody; + private ISkeletonAnimation skeletonAnim; + private Skeleton skeleton; + private Dictionary boneTable = new Dictionary(); + private Bone startingBone; + private Transform ragdollRoot; + private Vector3 rootOffset; + private bool isActive; + + IEnumerator Start () { + skeletonAnim = (ISkeletonAnimation)GetComponent(); + if (helper == null) { + helper = (Transform)(new GameObject("Helper")).transform; + helper.hideFlags = HideFlags.HideInHierarchy; + } + + if (applyOnStart) { + yield return null; + Apply(); + } + } + + 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) { + mix = Mathf.SmoothStep(startMix, target, (Time.time - startTime) / duration); + yield return null; + } + } + + 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; + } + } + + public void Remove () { + isActive = false; + foreach (var t in boneTable.Values) { + Destroy(t.gameObject); + } + Destroy(ragdollRoot.gameObject); + + boneTable.Clear(); + skeletonAnim.UpdateWorld -= UpdateWorld; + } + + public void Apply () { + isActive = true; + skeleton = skeletonAnim.Skeleton; + mix = 1; + + var ragdollRootBone = skeleton.FindBone(startingBoneName); + startingBone = ragdollRootBone; + RecursivelyCreateBoneProxies(ragdollRootBone); + + rootRigidbody = boneTable[ragdollRootBone].GetComponent(); + rootRigidbody.isKinematic = pinStartBone; + + List boneColliders = new List(); + + foreach (var pair in boneTable) { + var b = pair.Key; + var t = pair.Value; + Bone parentBone = null; + Transform parentTransform = transform; + + boneColliders.Add(t.GetComponent()); + + if (b != startingBone) { + parentBone = b.Parent; + parentTransform = boneTable[parentBone]; + } else { + ragdollRoot = new GameObject("RagdollRoot").transform; + ragdollRoot.parent = transform; + + if (b == skeleton.RootBone) { + ragdollRoot.localPosition = new Vector3(b.WorldX, b.WorldY, 0); + ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetCompensatedRotationIK(b)); + parentTransform = ragdollRoot; + } else { + ragdollRoot.localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0); + ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetCompensatedRotationIK(b.Parent)); + parentTransform = ragdollRoot; + } + + rootOffset = t.position - transform.position; + } + + var rbParent = parentTransform.GetComponent(); + + if (rbParent != null) { + var joint = t.gameObject.AddComponent(); + joint.connectedBody = rbParent; + Vector3 localPos = parentTransform.InverseTransformPoint(t.position); + localPos.x *= 1; + joint.connectedAnchor = localPos; + joint.axis = Vector3.forward; + joint.GetComponent().mass = joint.connectedBody.mass * 0.75f; + JointLimits limits = new JointLimits(); + limits.min = -rotationLimit; + limits.max = rotationLimit; + joint.limits = limits; + joint.useLimits = true; + joint.enableCollision = enableJointCollision; + } + } + + for (int x = 0; x < boneColliders.Count; x++) { + for (int y = 0; y < boneColliders.Count; y++) { + Physics.IgnoreCollision(boneColliders[x], boneColliders[y]); + } + } + + var utilityBones = GetComponentsInChildren(); + if (utilityBones.Length > 0) { + List destroyedUtilityBoneNames = new List(); + foreach (var 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); + } + } + + if (disableIK) { + foreach (IkConstraint ik in skeleton.IkConstraints) { + ik.Mix = 0; + } + } + + skeletonAnim.UpdateWorld += UpdateWorld; + } + + void RecursivelyCreateBoneProxies (Bone b) { + if (stopBoneNames.Contains(b.Data.Name)) + return; + + GameObject go = new GameObject(b.Data.Name); + go.layer = colliderLayer; + Transform t = go.transform; + boneTable.Add(b, t); + + t.parent = transform; + + t.localPosition = new Vector3(b.WorldX, b.WorldY, 0); + t.localRotation = Quaternion.Euler(0, 0, b.WorldFlipX ^ b.WorldFlipY ? -b.WorldRotation : b.WorldRotation); + t.localScale = new Vector3(b.WorldScaleX, b.WorldScaleY, 1); + + float length = b.Data.Length; + + var colliders = AttachBoundingBoxRagdollColliders(b); + + if (length == 0) { + //physics + if (colliders.Count == 0) { + var ball = go.AddComponent(); + ball.radius = defaultThickness / 2f; + } + } else { + //physics + if (colliders.Count == 0) { + var box = go.AddComponent(); + box.size = new Vector3(length, defaultThickness, defaultThickness); + box.center = new Vector3((b.WorldFlipX ? -length : length) / 2, 0); + } + } + + var rb = go.AddComponent(); + rb.constraints = RigidbodyConstraints.FreezePositionZ; + foreach (Bone child in b.Children) { + + RecursivelyCreateBoneProxies(child); + } + } + + List AttachBoundingBoxRagdollColliders (Bone b) { + List colliders = new List(); + + Transform t = boneTable[b]; + GameObject go = t.gameObject; + var skin = skeleton.Skin; + if (skin == null) + skin = skeleton.Data.DefaultSkin; + + bool flipX = b.WorldFlipX; + bool flipY = b.WorldFlipY; + + List attachments = new List(); + foreach (Slot s in skeleton.Slots) { + if (s.Bone == b) { + skin.FindAttachmentsForSlot(skeleton.Slots.IndexOf(s), attachments); + foreach (var a in attachments) { + if (a is BoundingBoxAttachment) { + if (!a.Name.ToLower().Contains("ragdoll")) + continue; + + var collider = go.AddComponent(); + var bounds = SkeletonUtility.GetBoundingBoxBounds((BoundingBoxAttachment)a, defaultThickness); + + collider.center = bounds.center; + collider.size = bounds.size; + + if (flipX || flipY) { + Vector3 center = collider.center; + + if (flipX) + center.x *= -1; + + if (flipY) + center.y *= -1; + + collider.center = center; + } + + colliders.Add(collider); + } + } + } + } + + return colliders; + } + + void UpdateWorld (SkeletonRenderer skeletonRenderer) { + foreach (var pair in boneTable) { + var b = pair.Key; + var t = pair.Value; + bool flip = false; + bool flipX = false; //TODO: deal with negative scale instead of Flip Key for Spine 3.0 + bool flipY = false; //TODO: deal with negative scale instead of Flip Key for Spine 3.0 + Bone parentBone = null; + Transform parentTransform = transform; + + if (b != startingBone) { + parentBone = b.Parent; + parentTransform = boneTable[parentBone]; + flipX = parentBone.WorldFlipX; + flipY = parentBone.WorldFlipY; + + } else { + parentBone = b.Parent; + parentTransform = ragdollRoot; + if (b.Parent != null) { + flipX = b.worldFlipX; + flipY = b.WorldFlipY; + } else { + flipX = b.Skeleton.FlipX; + flipY = b.Skeleton.FlipY; + } + } + + flip = flipX ^ flipY; + + helper.position = parentTransform.position; + helper.rotation = parentTransform.rotation; + helper.localScale = new Vector3(flipX ? -parentTransform.localScale.x : parentTransform.localScale.x, flipY ? -parentTransform.localScale.y : parentTransform.localScale.y, 1); + + + Vector3 pos = t.position; + pos = helper.InverseTransformPoint(pos); + b.X = Mathf.Lerp(b.X, pos.x, mix); + b.Y = Mathf.Lerp(b.Y, pos.y, mix); + + Vector3 right = helper.InverseTransformDirection(t.right); + + float a = Mathf.Atan2(right.y, right.x) * Mathf.Rad2Deg; + + if (b.WorldFlipX ^ b.WorldFlipY) { + a *= -1; + } + + if (parentBone != null) { + if ((b.WorldFlipX ^ b.WorldFlipY) != flip) { + a -= GetCompensatedRotationIK(parentBone) * 2; + } + } + + b.Rotation = Mathf.Lerp(b.Rotation, a, mix); + b.RotationIK = Mathf.Lerp(b.rotationIK, a, mix); + } + } + + float GetCompensatedRotationIK (Bone b) { + Bone parent = b.Parent; + float a = b.RotationIK; + while (parent != null) { + a += parent.RotationIK; + parent = parent.parent; + } + + return a; + } +} diff --git a/spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll.cs.meta b/spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll.cs.meta new file mode 100644 index 000000000..f19d6808f --- /dev/null +++ b/spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 373527d2bf3351348b9fcc499ce9ea23 +timeCreated: 1430552693 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll2D.cs b/spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll2D.cs new file mode 100644 index 000000000..88eba0990 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll2D.cs @@ -0,0 +1,388 @@ +/***************************************************************************** + * SkeletonRagdoll2D added by Mitch Thompson + * Full irrevocable rights and permissions granted to Esoteric Software +*****************************************************************************/ + +using UnityEngine; +using System.Collections; +using System.Collections.Generic; +using Spine; + +[RequireComponent(typeof(SkeletonRenderer))] +public class SkeletonRagdoll2D : MonoBehaviour { + private static Transform helper; + + [Header("Hierarchy")] + [SpineBone] + public string startingBoneName = ""; + [SpineBone] + public List stopBoneNames = new List(); + + [Header("Parameters")] + public bool applyOnStart; + [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 float gravityScale = 1; + [Tooltip("Warning! You will have to re-enable and tune mix values manually if attempting to remove the ragdoll system.")] + public bool disableIK = 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 defaultThickness = 0.125f; + [Tooltip("Default rotational limit value. Min is negative this value, Max is this value.")] + public float rotationLimit = 20; + [Tooltip("The layer assigned to all of the rigidbody parts.")] + public int colliderLayer = 0; + [Range(0, 1)] + public float mix = 1; + + public Rigidbody2D RootRigidbody { + get { + return this.rootRigidbody; + } + } + + public Vector3 RootOffset { + get { + return this.rootOffset; + } + } + + public Vector3 EstimatedSkeletonPosition { + get { + return this.rootRigidbody.position - rootOffset; + } + } + + public bool IsActive { + get { + return this.isActive; + } + } + + private Rigidbody2D rootRigidbody; + private ISkeletonAnimation skeletonAnim; + private Skeleton skeleton; + private Dictionary boneTable = new Dictionary(); + private Bone startingBone; + private Transform ragdollRoot; + private Vector2 rootOffset; + private bool isActive; + + + IEnumerator Start () { + skeletonAnim = (ISkeletonAnimation)GetComponent(); + if (helper == null) { + helper = (Transform)(new GameObject("Helper")).transform; + helper.hideFlags = HideFlags.HideInHierarchy; + } + + if (applyOnStart) { + yield return null; + Apply(); + } + } + + 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) { + mix = Mathf.SmoothStep(startMix, target, (Time.time - startTime) / duration); + yield return null; + } + } + + 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; + } + + UpdateWorld(null); + skeleton.UpdateWorldTransform(); + //skeletonAnim.LateUpdate(); + } + + public void Remove () { + isActive = false; + foreach (var t in boneTable.Values) { + Destroy(t.gameObject); + } + Destroy(ragdollRoot.gameObject); + + boneTable.Clear(); + skeletonAnim.UpdateWorld -= UpdateWorld; + } + + public void Apply () { + isActive = true; + skeleton = skeletonAnim.Skeleton; + mix = 1; + + var ragdollRootBone = skeleton.FindBone(startingBoneName); + startingBone = ragdollRootBone; + RecursivelyCreateBoneProxies(ragdollRootBone); + + rootRigidbody = boneTable[ragdollRootBone].GetComponent(); + rootRigidbody.isKinematic = pinStartBone; + + List boneColliders = new List(); + + foreach (var pair in boneTable) { + var b = pair.Key; + var t = pair.Value; + Bone parentBone = null; + Transform parentTransform = transform; + + boneColliders.Add(t.GetComponent()); + + if (b != startingBone) { + parentBone = b.Parent; + parentTransform = boneTable[parentBone]; + } else { + ragdollRoot = new GameObject("RagdollRoot").transform; + ragdollRoot.parent = transform; + + if (b == skeleton.RootBone) { + ragdollRoot.localPosition = new Vector3(b.WorldX, b.WorldY, 0); + ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetCompensatedRotationIK(b)); + parentTransform = ragdollRoot; + } else { + ragdollRoot.localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0); + ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetCompensatedRotationIK(b.Parent)); + parentTransform = ragdollRoot; + } + + rootOffset = t.position - transform.position; + } + + var rbParent = parentTransform.GetComponent(); + + if (rbParent != null) { + var joint = t.gameObject.AddComponent(); + joint.connectedBody = rbParent; + Vector3 localPos = parentTransform.InverseTransformPoint(t.position); + localPos.x *= 1; + joint.connectedAnchor = localPos; + joint.GetComponent().mass = joint.connectedBody.mass * 0.75f; + JointAngleLimits2D limits = new JointAngleLimits2D(); + limits.min = -rotationLimit; + limits.max = rotationLimit; + joint.limits = limits; + joint.useLimits = enableJointCollision; + } + } + + for (int x = 0; x < boneColliders.Count; x++) { + for (int y = 0; y < boneColliders.Count; y++) { + Physics2D.IgnoreCollision(boneColliders[x], boneColliders[y]); + } + } + + var utilityBones = GetComponentsInChildren(); + if (utilityBones.Length > 0) { + List destroyedUtilityBoneNames = new List(); + foreach (var 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); + } + } + + if (disableIK) { + foreach (IkConstraint ik in skeleton.IkConstraints) { + ik.Mix = 0; + } + } + + skeletonAnim.UpdateWorld += UpdateWorld; + } + + void RecursivelyCreateBoneProxies (Bone b) { + if (stopBoneNames.Contains(b.Data.Name)) + return; + + GameObject go = new GameObject(b.Data.Name); + go.layer = colliderLayer; + Transform t = go.transform; + boneTable.Add(b, t); + + t.parent = transform; + + t.localPosition = new Vector3(b.WorldX, b.WorldY, 0); + //TODO: deal with WorldFlipY + t.localRotation = Quaternion.Euler(0, 0, b.WorldFlipX ? -b.WorldRotation : b.WorldRotation); + t.localScale = new Vector3(b.WorldScaleX, b.WorldScaleY, 0); + + float length = b.Data.Length; + + //TODO proper ragdoll branching + var colliders = AttachBoundingBoxRagdollColliders(b); + + if (length == 0) { + //physics + if (colliders.Count == 0) { + var circle = go.AddComponent(); + circle.radius = defaultThickness / 2f; + } + } else { + //physics + if (colliders.Count == 0) { + var box = go.AddComponent(); + box.size = new Vector2(length, defaultThickness); +#if UNITY_5_0 + box.offset = new Vector2((b.WorldFlipX ? -length : length) / 2, 0); +#else + box.center = new Vector2((b.WorldFlipX ? -length : length) / 2, 0); +#endif + } + } + + var rb = go.AddComponent(); + rb.gravityScale = gravityScale; + + foreach (Bone child in b.Children) { + RecursivelyCreateBoneProxies(child); + } + } + + List AttachBoundingBoxRagdollColliders (Bone b) { + List colliders = new List(); + Transform t = boneTable[b]; + GameObject go = t.gameObject; + var skin = skeleton.Skin; + if (skin == null) + skin = skeleton.Data.DefaultSkin; + + bool flipX = b.WorldFlipX; + bool flipY = b.WorldFlipY; + + List attachments = new List(); + foreach (Slot s in skeleton.Slots) { + if (s.Bone == b) { + skin.FindAttachmentsForSlot(skeleton.Slots.IndexOf(s), attachments); + foreach (var a in attachments) { + if (a is BoundingBoxAttachment) { + if (!a.Name.ToLower().Contains("ragdoll")) + continue; + + var collider = SkeletonUtility.AddBoundingBoxAsComponent((BoundingBoxAttachment)a, go, false); + + if (flipX || flipY) { + Vector2[] points = collider.points; + + for (int i = 0; i < points.Length; i++) { + if (flipX) + points[i].x *= -1; + + if (flipY) + points[i].y *= -1; + } + + collider.points = points; + } + + colliders.Add(collider); + } + } + } + } + + return colliders; + } + + void UpdateWorld (SkeletonRenderer skeletonRenderer) { + foreach (var pair in boneTable) { + var b = pair.Key; + var t = pair.Value; + bool flip = false; + bool flipX = false; //TODO: deal with negative scale instead of Flip Key + bool flipY = false; //TODO: deal with negative scale instead of Flip Key + Bone parentBone = null; + Transform parentTransform = transform; + + if (b != startingBone) { + parentBone = b.Parent; + parentTransform = boneTable[parentBone]; + flipX = parentBone.WorldFlipX; + flipY = parentBone.WorldFlipY; + + } else { + flipX = b.worldFlipX; + flipY = b.WorldFlipY; + } + + flip = flipX ^ flipY; + + helper.position = parentTransform.position; + helper.rotation = parentTransform.rotation; + helper.localScale = new Vector3(flipX ? -parentTransform.localScale.x : parentTransform.localScale.x, flipY ? -parentTransform.localScale.y : parentTransform.localScale.y, 1); + + + Vector3 pos = t.position; + pos = helper.InverseTransformPoint(pos); + b.X = Mathf.Lerp(b.X, pos.x, mix); + b.Y = Mathf.Lerp(b.Y, pos.y, mix); + + Vector3 right = helper.InverseTransformDirection(t.right); + + float a = Mathf.Atan2(right.y, right.x) * Mathf.Rad2Deg; + + if (b.WorldFlipX ^ b.WorldFlipY) { + a *= -1; + } + + if (parentBone != null) { + if ((b.WorldFlipX ^ b.WorldFlipY) != flip) { + a -= GetCompensatedRotationIK(parentBone) * 2; + } + } + + b.Rotation = Mathf.Lerp(b.Rotation, a, mix); + b.RotationIK = Mathf.Lerp(b.rotationIK, a, mix); + } + } + + float GetCompensatedRotationIK (Bone b) { + Bone parent = b.Parent; + float a = b.RotationIK; + while (parent != null) { + a += parent.RotationIK; + parent = parent.parent; + } + + return a; + } + + void OnDrawGizmosSelected () { + if (isActive) { + Gizmos.DrawWireSphere(transform.position, defaultThickness * 1.2f); + Vector3 newTransformPos = rootRigidbody.position - rootOffset; + Gizmos.DrawLine(transform.position, newTransformPos); + Gizmos.DrawWireSphere(newTransformPos, defaultThickness * 1.2f); + } + } + +} diff --git a/spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll2D.cs.meta b/spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll2D.cs.meta new file mode 100644 index 000000000..ed29795fe --- /dev/null +++ b/spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll2D.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: e74a49a26242a214d9084fde00bfe3ab +timeCreated: 1431497383 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/SkeletonAnimation.cs b/spine-unity/Assets/spine-unity/SkeletonAnimation.cs index cba8bd116..0384417bd 100644 --- a/spine-unity/Assets/spine-unity/SkeletonAnimation.cs +++ b/spine-unity/Assets/spine-unity/SkeletonAnimation.cs @@ -42,9 +42,7 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation { public bool loop; public Spine.AnimationState state; - public Skeleton GetSkeleton(){ - return this.skeleton; - } + public event UpdateBonesDelegate UpdateLocal { add { _UpdateLocal += value; } @@ -65,6 +63,12 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation { protected event UpdateBonesDelegate _UpdateWorld; protected event UpdateBonesDelegate _UpdateComplete; + public Skeleton Skeleton { + get { + return this.skeleton; + } + } + [SerializeField] private String _animationName; diff --git a/spine-unity/Assets/spine-unity/SkeletonAnimationInterface.cs b/spine-unity/Assets/spine-unity/SkeletonAnimationInterface.cs index 2588be2d0..520114fd8 100644 --- a/spine-unity/Assets/spine-unity/SkeletonAnimationInterface.cs +++ b/spine-unity/Assets/spine-unity/SkeletonAnimationInterface.cs @@ -40,5 +40,5 @@ public interface ISkeletonAnimation { event UpdateBonesDelegate UpdateComplete; void LateUpdate (); - Skeleton GetSkeleton(); + Skeleton Skeleton { get; } } diff --git a/spine-unity/Assets/spine-unity/SkeletonAnimator.cs b/spine-unity/Assets/spine-unity/SkeletonAnimator.cs index 3b7865e79..4daeda41e 100644 --- a/spine-unity/Assets/spine-unity/SkeletonAnimator.cs +++ b/spine-unity/Assets/spine-unity/SkeletonAnimator.cs @@ -38,6 +38,12 @@ public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation { protected event UpdateBonesDelegate _UpdateWorld; protected event UpdateBonesDelegate _UpdateComplete; + public Skeleton Skeleton { + get { + return this.skeleton; + } + } + Dictionary animationTable = new Dictionary(); Animator animator; diff --git a/spine-unity/Assets/spine-unity/SkeletonExtensions.cs.meta b/spine-unity/Assets/spine-unity/SkeletonExtensions.cs.meta index 0ec145755..427cdd10a 100644 --- a/spine-unity/Assets/spine-unity/SkeletonExtensions.cs.meta +++ b/spine-unity/Assets/spine-unity/SkeletonExtensions.cs.meta @@ -6,5 +6,3 @@ MonoImporter: executionOrder: 0 icon: {instanceID: 0} userData: - assetBundleName: - assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs b/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs index 3252ee5cb..a236c2c78 100644 --- a/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs +++ b/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs @@ -95,6 +95,22 @@ public class SkeletonUtility : MonoBehaviour { return collider; } + public static Bounds GetBoundingBoxBounds (BoundingBoxAttachment boundingBox, float depth = 0) { + float[] floats = boundingBox.Vertices; + int floatCount = floats.Length; + + Bounds bounds = new Bounds(); + + bounds.center = new Vector3(floats[0], floats[1], 0); + for (int i = 2; i < floatCount; i += 2) { + bounds.Encapsulate(new Vector3(floats[i], floats[i + 1], 0)); + } + Vector3 size = bounds.size; + size.z = depth; + bounds.size = size; + + return bounds; + } public delegate void SkeletonUtilityDelegate ();