Merge branch '3.8' into 3.9-beta

This commit is contained in:
badlogic 2019-10-31 17:56:29 +01:00
commit f68f8c401b
23 changed files with 580 additions and 57 deletions

View File

@ -287,6 +287,7 @@
You can leave this parameter disabled when everything is drawn correctly to save the additional performance cost.
* **Additional Timeline features.** SpineAnimationStateClip now provides a `Speed Multiplier`, a start time offset parameter `Clip In`, support for blending successive animations by overlapping tracks. An additional `Use Blend Duration` parameter *(defaults to true)* allows for automatic synchronisation of MixDuration with the current overlap blend duration. An additional Spine preferences parameter `Use Blend Duration` has been added which can be disabled to default to the previous behaviour before this update.
* Additional `SpriteMask and RectMask2D` example scene added for demonstration of mask setup and interaction.
* `Real physics hinge chains` for both 2D and 3D physics. The [SkeletonUtilityBone](http://esotericsoftware.com/spine-unity#SkeletonUtilityBone) Inspector provides an interface to create 2D and 3D hinge chains. Previously created chains have only been respecting gravity, but not momentum of the skeleton or parent bones. The new physics rig created when pressing `Create 3D Hinge Chain` and `Create 2D Hinge Chain` creates a more complex setup that also works when flipping the skeleton. Note that the chain root node is no longer parented to bones of the skeleton. This is a requirement in Unity to have momentum applied properly - do not reparent the chain root to bones of your skeleton, or you will loose any momentum applied by the skeleton's movement.
* **Changes of default values**
* `SkeletonMecanim`'s `Layer Mix Mode` now defaults to `MixMode.MixNext` instead of `MixMode.MixAlways`.

View File

@ -1392,6 +1392,7 @@ declare module spine.webgl {
private texture;
private boundUnit;
private useMipMaps;
static DISABLE_UNPACK_PREMULTIPLIED_ALPHA_WEBGL: boolean;
constructor(context: ManagedWebGLRenderingContext | WebGLRenderingContext, image: HTMLImageElement, useMipMaps?: boolean);
setFilters(minFilter: TextureFilter, magFilter: TextureFilter): void;
static validateMagFilter(magFilter: TextureFilter): TextureFilter.Nearest | TextureFilter.Linear | TextureFilter.Linear;

View File

@ -8521,6 +8521,8 @@ var spine;
this.texture = this.context.gl.createTexture();
}
this.bind();
if (GLTexture.DISABLE_UNPACK_PREMULTIPLIED_ALPHA_WEBGL)
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, useMipMaps ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR);
@ -8550,6 +8552,7 @@ var spine;
var gl = this.context.gl;
gl.deleteTexture(this.texture);
};
GLTexture.DISABLE_UNPACK_PREMULTIPLIED_ALPHA_WEBGL = false;
return GLTexture;
}(spine.Texture));
webgl.GLTexture = GLTexture;

File diff suppressed because one or more lines are too long

View File

@ -1361,6 +1361,7 @@ declare module spine.webgl {
private texture;
private boundUnit;
private useMipMaps;
static DISABLE_UNPACK_PREMULTIPLIED_ALPHA_WEBGL: boolean;
constructor(context: ManagedWebGLRenderingContext | WebGLRenderingContext, image: HTMLImageElement, useMipMaps?: boolean);
setFilters(minFilter: TextureFilter, magFilter: TextureFilter): void;
static validateMagFilter(magFilter: TextureFilter): TextureFilter.Nearest | TextureFilter.Linear | TextureFilter.Linear;

View File

@ -8253,6 +8253,8 @@ var spine;
this.texture = this.context.gl.createTexture();
}
this.bind();
if (GLTexture.DISABLE_UNPACK_PREMULTIPLIED_ALPHA_WEBGL)
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, useMipMaps ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR);
@ -8282,6 +8284,7 @@ var spine;
var gl = this.context.gl;
gl.deleteTexture(this.texture);
};
GLTexture.DISABLE_UNPACK_PREMULTIPLIED_ALPHA_WEBGL = false;
return GLTexture;
}(spine.Texture));
webgl.GLTexture = GLTexture;

File diff suppressed because one or more lines are too long

View File

@ -1361,6 +1361,7 @@ declare module spine.webgl {
private texture;
private boundUnit;
private useMipMaps;
static DISABLE_UNPACK_PREMULTIPLIED_ALPHA_WEBGL: boolean;
constructor(context: ManagedWebGLRenderingContext | WebGLRenderingContext, image: HTMLImageElement, useMipMaps?: boolean);
setFilters(minFilter: TextureFilter, magFilter: TextureFilter): void;
static validateMagFilter(magFilter: TextureFilter): TextureFilter.Nearest | TextureFilter.Linear | TextureFilter.Linear;

View File

@ -8253,6 +8253,8 @@ var spine;
this.texture = this.context.gl.createTexture();
}
this.bind();
if (GLTexture.DISABLE_UNPACK_PREMULTIPLIED_ALPHA_WEBGL)
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, useMipMaps ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR);
@ -8282,6 +8284,7 @@ var spine;
var gl = this.context.gl;
gl.deleteTexture(this.texture);
};
GLTexture.DISABLE_UNPACK_PREMULTIPLIED_ALPHA_WEBGL = false;
return GLTexture;
}(spine.Texture));
webgl.GLTexture = GLTexture;

File diff suppressed because one or more lines are too long

View File

@ -34,6 +34,8 @@ module spine.webgl {
private boundUnit = 0;
private useMipMaps = false;
public static DISABLE_UNPACK_PREMULTIPLIED_ALPHA_WEBGL = false;
constructor (context: ManagedWebGLRenderingContext | WebGLRenderingContext, image: HTMLImageElement, useMipMaps: boolean = false) {
super(image);
this.context = context instanceof ManagedWebGLRenderingContext? context : new ManagedWebGLRenderingContext(context);
@ -75,6 +77,7 @@ module spine.webgl {
this.texture = this.context.gl.createTexture();
}
this.bind();
if (GLTexture.DISABLE_UNPACK_PREMULTIPLIED_ALPHA_WEBGL) gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, useMipMaps ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR);

View File

@ -29,11 +29,10 @@
#pragma once
#include "SpinePluginPrivatePCH.h"
#include "Runtime/UMG/Public/UMG.h"
#include "Runtime/UMG/Public/UMGStyle.h"
#include "SpineSkeletonDataAsset.h"
#include "spine/spine.h"
#include "SpineWidget.generated.h"
class SSpineWidget;

View File

@ -29,6 +29,10 @@
// Contributed by: Mitch Thompson
#if UNITY_2019_2_OR_NEWER
#define HINGE_JOINT_NEW_BEHAVIOUR
#endif
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
@ -39,7 +43,8 @@ namespace Spine.Unity.Editor {
[CustomEditor(typeof(SkeletonUtilityBone)), CanEditMultipleObjects]
public class SkeletonUtilityBoneInspector : UnityEditor.Editor {
SerializedProperty mode, boneName, zPosition, position, rotation, scale, overrideAlpha, parentReference;
SerializedProperty mode, boneName, zPosition, position, rotation, scale, overrideAlpha, hierarchy, parentReference;
GUIContent hierarchyLabel;
//multi selected flags
bool containsFollows, containsOverrides, multiObject;
@ -59,7 +64,12 @@ namespace Spine.Unity.Editor {
rotation = this.serializedObject.FindProperty("rotation");
scale = this.serializedObject.FindProperty("scale");
overrideAlpha = this.serializedObject.FindProperty("overrideAlpha");
hierarchy = this.serializedObject.FindProperty("hierarchy");
hierarchyLabel = new GUIContent("Skeleton Utility Parent");
parentReference = this.serializedObject.FindProperty("parentReference");
utilityBone = (SkeletonUtilityBone)target;
skeletonUtility = utilityBone.hierarchy;
EvaluateFlags();
if (!utilityBone.valid && skeletonUtility != null && skeletonUtility.skeletonRenderer != null)
@ -99,9 +109,6 @@ namespace Spine.Unity.Editor {
}
void EvaluateFlags () {
utilityBone = (SkeletonUtilityBone)target;
skeletonUtility = utilityBone.hierarchy;
if (Selection.objects.Length == 1) {
containsFollows = utilityBone.mode == SkeletonUtilityBone.Mode.Follow;
containsOverrides = utilityBone.mode == SkeletonUtilityBone.Mode.Override;
@ -156,6 +163,7 @@ namespace Spine.Unity.Editor {
using (new EditorGUI.DisabledGroupScope(containsFollows)) {
EditorGUILayout.PropertyField(overrideAlpha);
EditorGUILayout.PropertyField(parentReference);
EditorGUILayout.PropertyField(hierarchy, hierarchyLabel);
}
EditorGUILayout.Space();
@ -292,85 +300,238 @@ namespace Spine.Unity.Editor {
}
void CreateHingeChain2D () {
var utilBoneArr = utilityBone.GetComponentsInChildren<SkeletonUtilityBone>();
foreach (var utilBone in utilBoneArr) {
if (utilBone.GetComponent<Collider2D>() == null) {
if (utilBone.bone.Data.Length == 0) {
var sphere = utilBone.gameObject.AddComponent<CircleCollider2D>();
sphere.radius = 0.1f;
} else {
float length = utilBone.bone.Data.Length;
var box = utilBone.gameObject.AddComponent<BoxCollider2D>();
box.size = new Vector3(length, length / 3f, 0.2f);
box.offset = new Vector3(length / 2f, 0, 0);
}
}
utilBone.gameObject.AddComponent<Rigidbody2D>();
var kinematicParentUtilityBone = utilityBone.transform.parent.GetComponent<SkeletonUtilityBone>();
if (kinematicParentUtilityBone == null) {
UnityEditor.EditorUtility.DisplayDialog("No parent SkeletonUtilityBone found!", "Please select the first physically moving chain node, having a parent GameObject with a SkeletonUtilityBone component attached.", "OK");
return;
}
utilityBone.GetComponent<Rigidbody2D>().isKinematic = true;
float mass = 10;
const float rotationLimit = 20.0f;
foreach (var utilBone in utilBoneArr) {
if (utilBone == utilityBone)
continue;
SetSkeletonUtilityToFlipByRotation();
utilBone.mode = SkeletonUtilityBone.Mode.Override;
kinematicParentUtilityBone.mode = SkeletonUtilityBone.Mode.Follow;
kinematicParentUtilityBone.position = kinematicParentUtilityBone.rotation = kinematicParentUtilityBone.scale = kinematicParentUtilityBone.zPosition = true;
var joint = utilBone.gameObject.AddComponent<HingeJoint2D>();
joint.connectedBody = utilBone.transform.parent.GetComponent<Rigidbody2D>();
GameObject commonParentObject = new GameObject(skeletonUtility.name + " HingeChain Parent " + utilityBone.name);
var commonParentActivateOnFlip = commonParentObject.AddComponent<ActivateBasedOnFlipDirection>();
commonParentActivateOnFlip.skeletonRenderer = skeletonUtility.skeletonRenderer;
// HingeChain Parent
// Needs to be on top hierarchy level (not attached to the moving skeleton at least) for physics to apply proper momentum.
GameObject normalChainParentObject = new GameObject("HingeChain");
normalChainParentObject.transform.SetParent(commonParentObject.transform);
commonParentActivateOnFlip.activeOnNormalX = normalChainParentObject;
//var followRotationComponent = normalChainParentObject.AddComponent<FollowSkeletonUtilityRootRotation>();
//followRotationComponent.reference = skeletonUtility.boneRoot;
// Follower Kinematic Rigidbody
GameObject followerKinematicObject = new GameObject(kinematicParentUtilityBone.name + " Follower");
followerKinematicObject.transform.parent = normalChainParentObject.transform;
var followerRigidbody = followerKinematicObject.AddComponent<Rigidbody2D>();
followerRigidbody.mass = mass;
followerRigidbody.isKinematic = true;
followerKinematicObject.AddComponent<FollowLocationRigidbody2D>().reference = kinematicParentUtilityBone.transform;
followerKinematicObject.transform.position = kinematicParentUtilityBone.transform.position;
followerKinematicObject.transform.rotation = kinematicParentUtilityBone.transform.rotation;
// Child Bones
var utilityBones = utilityBone.GetComponentsInChildren<SkeletonUtilityBone>();
var childBoneParentReference = followerKinematicObject.transform;
for (int i = 0; i < utilityBones.Length; ++i) {
var childBone = utilityBones[i];
mass *= 0.75f;
childBone.parentReference = (i == 0) ? kinematicParentUtilityBone.transform : childBoneParentReference;
childBone.transform.SetParent(normalChainParentObject.transform, true); // we need a flat hierarchy of all Joint objects in Unity.
AttachRigidbodyAndCollider2D(childBone);
childBone.mode = SkeletonUtilityBone.Mode.Override;
childBone.scale = childBone.position = childBone.zPosition = false;
HingeJoint2D joint = childBone.gameObject.AddComponent<HingeJoint2D>();
joint.connectedBody = childBoneParentReference.GetComponent<Rigidbody2D>();
joint.useLimits = true;
joint.limits = new JointAngleLimits2D {
min = -20,
max = 20
};
utilBone.GetComponent<Rigidbody2D>().mass = utilBone.transform.parent.GetComponent<Rigidbody2D>().mass * 0.75f;
ApplyJoint2DAngleLimits(joint, rotationLimit, childBoneParentReference, childBone.transform);
childBone.GetComponent<Rigidbody2D>().mass = mass;
childBoneParentReference = childBone.transform;
}
Duplicate2DHierarchyForFlippedChains(normalChainParentObject, commonParentActivateOnFlip, skeletonUtility.transform, rotationLimit);
UnityEditor.Selection.activeGameObject = commonParentObject;
}
void ApplyJoint2DAngleLimits (HingeJoint2D joint, float rotationLimit, Transform parentBone, Transform bone) {
#if HINGE_JOINT_NEW_BEHAVIOUR
float referenceAngle = (parentBone.eulerAngles.z - bone.eulerAngles.z + 360f) % 360f;
float minAngle = referenceAngle - rotationLimit;
float maxAngle = referenceAngle + rotationLimit;
if (maxAngle > 270f) {
minAngle -= 360f;
maxAngle -= 360f;
}
if (minAngle < -90f) {
minAngle += 360f;
maxAngle += 360f;
}
#else
float minAngle = - rotationLimit;
float maxAngle = rotationLimit;
#endif
joint.limits = new JointAngleLimits2D {
min = minAngle,
max = maxAngle
};
}
void Duplicate2DHierarchyForFlippedChains (GameObject normalChainParentObject, ActivateBasedOnFlipDirection commonParentActivateOnFlip,
Transform skeletonUtilityRoot, float rotationLimit) {
GameObject mirroredChain = GameObject.Instantiate(normalChainParentObject, normalChainParentObject.transform.position,
normalChainParentObject.transform.rotation, commonParentActivateOnFlip.transform);
mirroredChain.name = normalChainParentObject.name + " FlippedX";
commonParentActivateOnFlip.activeOnFlippedX = mirroredChain;
var followerKinematicObject = mirroredChain.GetComponentInChildren<FollowLocationRigidbody2D>();
followerKinematicObject.followFlippedX = true;
FlipBone2DHorizontal(followerKinematicObject.transform, skeletonUtilityRoot);
var childBoneJoints = mirroredChain.GetComponentsInChildren<HingeJoint2D>();
Transform prevRotatedChild = null;
Transform parentTransformForAngles = followerKinematicObject.transform;
for (int i = 0; i < childBoneJoints.Length; ++i) {
var joint = childBoneJoints[i];
FlipBone2DHorizontal(joint.transform, skeletonUtilityRoot);
ApplyJoint2DAngleLimits(joint, rotationLimit, parentTransformForAngles, joint.transform);
GameObject rotatedChild = GameObject.Instantiate(joint.gameObject, joint.transform, true);
rotatedChild.name = joint.name + " rotated";
var rotationEulerAngles = rotatedChild.transform.localEulerAngles;
rotationEulerAngles.x = 180;
rotatedChild.transform.localEulerAngles = rotationEulerAngles;
DestroyImmediate(rotatedChild.GetComponent<HingeJoint2D>());
DestroyImmediate(rotatedChild.GetComponent<BoxCollider2D>());
DestroyImmediate(rotatedChild.GetComponent<Rigidbody2D>());
DestroyImmediate(joint.gameObject.GetComponent<SkeletonUtilityBone>());
if (i > 0) {
var utilityBone = rotatedChild.GetComponent<SkeletonUtilityBone>();
utilityBone.parentReference = prevRotatedChild;
}
prevRotatedChild = rotatedChild.transform;
parentTransformForAngles = joint.transform;
}
mirroredChain.SetActive(false);
}
void FlipBone2DHorizontal(Transform bone, Transform mirrorPosition) {
Vector3 position = bone.position;
position.x = 2 * mirrorPosition.position.x - position.x; // = mirrorPosition + (mirrorPosition - bone.position)
bone.position = position;
Vector3 boneZ = bone.forward;
Vector3 boneX = bone.right;
boneX.x *= -1;
bone.rotation = Quaternion.LookRotation(boneZ, Vector3.Cross(boneZ, boneX));
}
void CreateHingeChain () {
var utilBoneArr = utilityBone.GetComponentsInChildren<SkeletonUtilityBone>();
foreach (var utilBone in utilBoneArr) {
AttachRigidbody(utilBone);
var kinematicParentUtilityBone = utilityBone.transform.parent.GetComponent<SkeletonUtilityBone>();
if (kinematicParentUtilityBone == null) {
UnityEditor.EditorUtility.DisplayDialog("No parent SkeletonUtilityBone found!", "Please select the first physically moving chain node, having a parent GameObject with a SkeletonUtilityBone component attached.", "OK");
return;
}
SetSkeletonUtilityToFlipByRotation();
kinematicParentUtilityBone.mode = SkeletonUtilityBone.Mode.Follow;
kinematicParentUtilityBone.position = kinematicParentUtilityBone.rotation = kinematicParentUtilityBone.scale = kinematicParentUtilityBone.zPosition = true;
utilityBone.GetComponent<Rigidbody>().isKinematic = true;
// HingeChain Parent
// Needs to be on top hierarchy level (not attached to the moving skeleton at least) for physics to apply proper momentum.
GameObject chainParentObject = new GameObject(skeletonUtility.name + " HingeChain Parent " + utilityBone.name);
var followRotationComponent = chainParentObject.AddComponent<FollowSkeletonUtilityRootRotation>();
followRotationComponent.reference = skeletonUtility.boneRoot;
foreach (var utilBone in utilBoneArr) {
if (utilBone == utilityBone)
continue;
// Follower Kinematic Rigidbody
GameObject followerKinematicObject = new GameObject(kinematicParentUtilityBone.name + " Follower");
followerKinematicObject.transform.parent = chainParentObject.transform;
var followerRigidbody = followerKinematicObject.AddComponent<Rigidbody>();
followerRigidbody.mass = 10;
followerRigidbody.isKinematic = true;
followerKinematicObject.AddComponent<FollowLocationRigidbody>().reference = kinematicParentUtilityBone.transform;
followerKinematicObject.transform.position = kinematicParentUtilityBone.transform.position;
followerKinematicObject.transform.rotation = kinematicParentUtilityBone.transform.rotation;
utilBone.mode = SkeletonUtilityBone.Mode.Override;
HingeJoint joint = utilBone.gameObject.AddComponent<HingeJoint>();
// Child Bones
var utilityBones = utilityBone.GetComponentsInChildren<SkeletonUtilityBone>();
var childBoneParentReference = followerKinematicObject.transform;
foreach (var childBone in utilityBones) {
childBone.parentReference = childBoneParentReference;
childBone.transform.SetParent(chainParentObject.transform, true); // we need a flat hierarchy of all Joint objects in Unity.
AttachRigidbodyAndCollider(childBone);
childBone.mode = SkeletonUtilityBone.Mode.Override;
HingeJoint joint = childBone.gameObject.AddComponent<HingeJoint>();
joint.axis = Vector3.forward;
joint.connectedBody = utilBone.transform.parent.GetComponent<Rigidbody>();
joint.connectedBody = childBoneParentReference.GetComponent<Rigidbody>();
joint.useLimits = true;
joint.limits = new JointLimits {
min = -20,
max = 20
};
utilBone.GetComponent<Rigidbody>().mass = utilBone.transform.parent.GetComponent<Rigidbody>().mass * 0.75f;
childBone.GetComponent<Rigidbody>().mass = childBoneParentReference.transform.GetComponent<Rigidbody>().mass * 0.75f;
childBoneParentReference = childBone.transform;
}
UnityEditor.Selection.activeGameObject = chainParentObject;
}
void SetSkeletonUtilityToFlipByRotation () {
if (!skeletonUtility.flipBy180DegreeRotation) {
skeletonUtility.flipBy180DegreeRotation = true;
Debug.Log("Set SkeletonUtility " + skeletonUtility.name + " to flip by rotation instead of negative scale (required).", skeletonUtility);
}
}
static void AttachRigidbody (SkeletonUtilityBone utilBone) {
static void AttachRigidbodyAndCollider (SkeletonUtilityBone utilBone, bool enableCollider = false) {
if (utilBone.GetComponent<Collider>() == null) {
if (utilBone.bone.Data.Length == 0) {
SphereCollider sphere = utilBone.gameObject.AddComponent<SphereCollider>();
sphere.radius = 0.1f;
sphere.enabled = enableCollider;
} else {
float length = utilBone.bone.Data.Length;
BoxCollider box = utilBone.gameObject.AddComponent<BoxCollider>();
box.size = new Vector3(length, length / 3f, 0.2f);
box.center = new Vector3(length / 2f, 0, 0);
box.enabled = enableCollider;
}
}
utilBone.gameObject.AddComponent<Rigidbody>();
}
}
static void AttachRigidbodyAndCollider2D(SkeletonUtilityBone utilBone, bool enableCollider = false) {
if (utilBone.GetComponent<Collider2D>() == null) {
if (utilBone.bone.Data.Length == 0) {
var sphere = utilBone.gameObject.AddComponent<CircleCollider2D>();
sphere.radius = 0.1f;
sphere.enabled = enableCollider;
}
else {
float length = utilBone.bone.Data.Length;
var box = utilBone.gameObject.AddComponent<BoxCollider2D>();
box.size = new Vector3(length, length / 3f, 0.2f);
box.offset = new Vector3(length / 2f, 0, 0);
box.enabled = enableCollider;
}
}
utilBone.gameObject.AddComponent<Rigidbody2D>();
}
}
}

View File

@ -73,6 +73,7 @@ namespace Spine.Unity.Editor {
}
public override void OnInspectorGUI () {
#if !NEW_PREFAB_SYSTEM
if (isPrefab) {
GUILayout.Label(new GUIContent("Cannot edit Prefabs", Icons.warning));
@ -80,12 +81,21 @@ namespace Spine.Unity.Editor {
}
#endif
serializedObject.Update();
if (!skeletonRenderer.valid) {
GUILayout.Label(new GUIContent("Spine Component invalid. Check Skeleton Data Asset.", Icons.warning));
return;
}
EditorGUILayout.PropertyField(serializedObject.FindProperty("boneRoot"), SpineInspectorUtility.TempContent("Skeleton Root"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("flipBy180DegreeRotation"), SpineInspectorUtility.TempContent("Flip by Rotation", null,
"If true, Skeleton.ScaleX and Skeleton.ScaleY are followed " +
"by 180 degree rotation. If false, negative Transform scale is used. " +
"Note that using negative scale is consistent with previous behaviour (hence the default), " +
"however causes serious problems with rigidbodies and physics. Therefore, it is recommended to " +
"enable this parameter where possible. When creating hinge chains for a chain of skeleton bones " +
"via SkeletonUtilityBone, it is mandatory to have this parameter enabled."));
bool hasRootBone = skeletonUtility.boneRoot != null;
@ -104,6 +114,8 @@ namespace Spine.Unity.Editor {
skeletonUtility.boneRoot = null;
}
}
serializedObject.ApplyModifiedProperties();
}
void SpawnHierarchyContextMenu () {

View File

@ -0,0 +1,89 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Utility component to support flipping of 2D hinge chains (chains of HingeJoint2D objects) along
/// with the parent skeleton by activating the respective mirrored versions of the hinge chain.
/// Note: This component is automatically attached when calling "Create Hinge Chain 2D" at <see cref="SkeletonUtilityBone"/>,
/// do not attempt to use this component for other purposes.
/// </summary>
public class ActivateBasedOnFlipDirection : MonoBehaviour {
public SkeletonRenderer skeletonRenderer;
public GameObject activeOnNormalX;
public GameObject activeOnFlippedX;
HingeJoint2D[] jointsNormalX;
HingeJoint2D[] jointsFlippedX;
bool wasFlippedXBefore = false;
private void Start () {
jointsNormalX = activeOnNormalX.GetComponentsInChildren<HingeJoint2D>();
jointsFlippedX = activeOnFlippedX.GetComponentsInChildren<HingeJoint2D>();
}
private void FixedUpdate () {
bool isFlippedX = (skeletonRenderer.Skeleton.ScaleX < 0);
if (isFlippedX != wasFlippedXBefore) {
HandleFlip(isFlippedX);
}
wasFlippedXBefore = isFlippedX;
}
void HandleFlip (bool isFlippedX) {
GameObject gameObjectToActivate = isFlippedX ? activeOnFlippedX : activeOnNormalX;
GameObject gameObjectToDeactivate = isFlippedX ? activeOnNormalX : activeOnFlippedX;
gameObjectToActivate.SetActive(true);
gameObjectToDeactivate.SetActive(false);
ResetJointPositions(isFlippedX ? jointsFlippedX : jointsNormalX);
ResetJointPositions(isFlippedX ? jointsNormalX : jointsFlippedX);
CompensateMovementAfterFlipX(gameObjectToActivate.transform, gameObjectToDeactivate.transform);
}
void ResetJointPositions (HingeJoint2D[] joints) {
for (int i = 0; i < joints.Length; ++i) {
var joint = joints[i];
var parent = joint.connectedBody.transform;
joint.transform.position = parent.TransformPoint(joint.connectedAnchor);
}
}
void CompensateMovementAfterFlipX (Transform toActivate, Transform toDeactivate) {
Transform targetLocation = toDeactivate.GetChild(0);
Transform currentLocation = toActivate.GetChild(0);
toActivate.position += targetLocation.position - currentLocation.position;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 70ae96e4f2feb654681a2f16e4effeec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,54 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Utility component to support flipping of hinge chains (chains of HingeJoint objects) along with the parent skeleton.
///
/// Note: This component is automatically attached when calling "Create Hinge Chain" at <see cref="SkeletonUtilityBone"/>.
/// </summary>
[RequireComponent(typeof(Rigidbody))]
public class FollowLocationRigidbody : MonoBehaviour {
public Transform reference;
Rigidbody ownRigidbody;
private void Awake () {
ownRigidbody = this.GetComponent<Rigidbody>();
}
void FixedUpdate () {
ownRigidbody.rotation = reference.rotation;
ownRigidbody.position = reference.position;
}
}
}

View File

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

View File

@ -0,0 +1,59 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Utility component to support flipping of hinge chains (chains of HingeJoint objects) along with the parent skeleton.
///
/// Note: This component is automatically attached when calling "Create Hinge Chain" at <see cref="SkeletonUtilityBone"/>.
/// </summary>
[RequireComponent(typeof(Rigidbody2D))]
public class FollowLocationRigidbody2D : MonoBehaviour {
public Transform reference;
public bool followFlippedX;
Rigidbody2D ownRigidbody;
private void Awake () {
ownRigidbody = this.GetComponent<Rigidbody2D>();
}
void FixedUpdate () {
if (followFlippedX) {
ownRigidbody.rotation = ((-reference.rotation.eulerAngles.z + 270f) % 360f) - 90f;
}
else
ownRigidbody.rotation = reference.rotation.eulerAngles.z;
ownRigidbody.position = reference.position;
}
}
}

View File

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

View File

@ -0,0 +1,86 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Utility component to support flipping of hinge chains (chains of HingeJoint objects) along with the parent skeleton.
/// Note that flipping needs to be performed by 180 degree rotation at <see cref="SkeletonUtility"/>,
/// by setting <see cref="SkeletonUtility.flipBy180DegreeRotation"/> to true, not via negative scale.
///
/// Note: This component is automatically attached when calling "Create Hinge Chain" at <see cref="SkeletonUtilityBone"/>,
/// do not attempt to use this component for other purposes.
/// </summary>
public class FollowSkeletonUtilityRootRotation : MonoBehaviour {
const float FLIP_ANGLE_THRESHOLD = 100.0f;
public Transform reference;
Vector3 prevLocalEulerAngles;
private void Start () {
prevLocalEulerAngles = this.transform.localEulerAngles;
}
void FixedUpdate () {
this.transform.rotation = reference.rotation;
bool wasFlippedAroundY = Mathf.Abs(this.transform.localEulerAngles.y - prevLocalEulerAngles.y) > FLIP_ANGLE_THRESHOLD;
bool wasFlippedAroundX = Mathf.Abs(this.transform.localEulerAngles.x - prevLocalEulerAngles.x) > FLIP_ANGLE_THRESHOLD;
if (wasFlippedAroundY)
CompensatePositionToYRotation();
if (wasFlippedAroundX)
CompensatePositionToXRotation();
prevLocalEulerAngles = this.transform.localEulerAngles;
}
/// <summary>
/// Compensates the position so that a child at the reference position remains in the same place,
/// to counter any movement that occurred by rotation.
/// </summary>
void CompensatePositionToYRotation () {
Vector3 newPosition = reference.position + (reference.position - this.transform.position);
newPosition.y = this.transform.position.y;
this.transform.position = newPosition;
}
/// <summary>
/// Compensates the position so that a child at the reference position remains in the same place,
/// to counter any movement that occurred by rotation.
/// </summary>
void CompensatePositionToXRotation () {
Vector3 newPosition = reference.position + (reference.position - this.transform.position);
newPosition.x = this.transform.position.x;
this.transform.position = newPosition;
}
}
}

View File

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

View File

@ -63,7 +63,7 @@ namespace Spine.Unity {
public float overrideAlpha = 1;
#endregion
[System.NonSerialized] public SkeletonUtility hierarchy;
public SkeletonUtility hierarchy;
[System.NonSerialized] public Bone bone;
[System.NonSerialized] public bool transformLerpComplete;
[System.NonSerialized] public bool valid;
@ -85,7 +85,7 @@ namespace Spine.Unity {
}
void OnEnable () {
hierarchy = transform.GetComponentInParent<SkeletonUtility>();
if (hierarchy == null) hierarchy = transform.GetComponentInParent<SkeletonUtility>();
if (hierarchy == null) return;
hierarchy.RegisterBone(this);
@ -230,7 +230,7 @@ namespace Spine.Unity {
SkeletonUtility.AddBoundingBoxGameObject(bone.skeleton, skinName, slotName, attachmentName, transform);
}
#if UNITY_EDITOR
#if UNITY_EDITOR
void OnDrawGizmos () {
if (IncompatibleTransformMode)
Gizmos.DrawIcon(transform.position + new Vector3(0, 0.128f, 0), "icon-warning");