Merge branch '3.8' into 4.0-beta

This commit is contained in:
Nathan Sweet 2020-08-10 23:42:28 +02:00
commit 33e2aea7ff
51 changed files with 1632 additions and 304 deletions

View File

@ -360,6 +360,8 @@
* Added support for **multiple atlas textures at `SkeletonGraphic`**. You can enable this feature by enabling the parameter `Multiple CanvasRenders` in the `Advanced` section of the `SkeletonGraphic` Inspector. This automatically creates the required number of child `CanvasRenderer` GameObjects for each required draw call (submesh).
* Added support for **Render Separator Slots** at `SkeletonGraphic`. Render separation can be enabled directly in the `Advanced` section of the `SkeletonGraphic` Inspector, it does not require any additional components (like `SkeletonRenderSeparator` or `SkeletonPartsRenderer` for `SkeletonRenderer` components). When enabled, additional separator GameObjects will be created automatically for each separation part, and `CanvasRenderer` GameObjects re-parented to them accordingly. The separator GameObjects can be moved around and re-parented in the hierarchy according to your requirements to achieve the desired draw order within your `Canvas`. A usage example can be found in the updated `Spine Examples/Other Examples/SkeletonRenderSeparator` scene.
* Added `SkeletonGraphicCustomMaterials` component, providing functionality to override materials and textures of a `SkeletonGraphic`, similar to `SkeletonRendererCustomMaterials`. Note: overriding materials or textures per slot is not provided due to structural limitations.
* Added **Root Motion support** for `SkeletonAnimation`, `SkeletonMecanim` and `SkeletonGraphic` via new components `SkeletonRootMotion` and `SkeletonMecanimRootMotion`. The `SkeletonAnimation` and `SkeletonGraphic` component Inspector now provides a line `Root Motion` with `Add Component` and `Remove Component` buttons to add/remove the new `SkeletonRootMotion` component to your GameObject. The `SkeletonMecanim` Inspector detects whether root motion is enabled at the `Animator` component and adds a `SkeletonMecanimRootMotion` component automatically.
* `SkeletonMecanim` now provides an additional `Custom MixMode` parameter under `Mecanim Translator`. It is enabled by default in version 3.8 to maintain current behaviour, using the set `Mix Mode` for each Mecanim layer. When disabled, `SkeletonMecanim` will use the recommended `MixMode` according to the layer blend mode. Additional information can be found in the [Mecanim Translator section](http://esotericsoftware.com/spine-unity#Parameters-for-animation-blending-control) on the spine-unity documentation pages.
* **Changes of default values**
* `SkeletonMecanim`'s `Layer Mix Mode` now defaults to `MixMode.MixNext` instead of `MixMode.MixAlways`.

View File

@ -48,6 +48,7 @@ namespace spine {
bool cullRectangle(Renderer* renderer, const Mat4& transform, const cocos2d::Rect& rect);
Color4B ColorToColor4B(const Color& color);
bool slotIsOutRange(Slot& slot, int startSlotIndex, int endSlotIndex);
bool nothingToDraw(Slot& slot, int startSlotIndex, int endSlotIndex);
}
// C Variable length array
@ -301,18 +302,7 @@ namespace spine {
for (int i = 0, n = _skeleton->getSlots().size(); i < n; ++i) {
Slot* slot = _skeleton->getDrawOrder()[i];;
if (slotIsOutRange(*slot, _startSlotIndex, _endSlotIndex)) {
_clipper->clipEnd(*slot);
continue;
}
if (!slot->getAttachment()) {
_clipper->clipEnd(*slot);
continue;
}
// Early exit if slot is invisible
if (slot->getColor().a == 0 || !slot->getBone().isActive()) {
if (nothingToDraw(*slot, _startSlotIndex, _endSlotIndex)) {
_clipper->clipEnd(*slot);
continue;
}
@ -324,12 +314,6 @@ namespace spine {
RegionAttachment* attachment = static_cast<RegionAttachment*>(slot->getAttachment());
attachmentVertices = static_cast<AttachmentVertices*>(attachment->getRendererObject());
// Early exit if attachment is invisible
if (attachment->getColor().a == 0) {
_clipper->clipEnd(*slot);
continue;
}
float* dstTriangleVertices = nullptr;
int dstStride = 0; // in floats
if (hasSingleTint) {
@ -935,21 +919,32 @@ namespace spine {
return startSlotIndex > index || endSlotIndex < index;
}
bool nothingToDraw(Slot& slot, int startSlotIndex, int endSlotIndex) {
Attachment *attachment = slot.getAttachment();
if (!attachment ||
slotIsOutRange(slot, startSlotIndex, endSlotIndex) ||
!slot.getBone().isActive() ||
slot.getColor().a == 0)
return true;
if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) {
if (static_cast<RegionAttachment*>(attachment)->getColor().a == 0)
return true;
}
else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) {
if (static_cast<MeshAttachment*>(attachment)->getColor().a == 0)
return true;
}
return false;
}
int computeTotalCoordCount(Skeleton& skeleton, int startSlotIndex, int endSlotIndex) {
int coordCount = 0;
for (size_t i = 0; i < skeleton.getSlots().size(); ++i) {
Slot& slot = *skeleton.getSlots()[i];
if (nothingToDraw(slot, startSlotIndex, endSlotIndex)) {
continue;
}
Attachment* const attachment = slot.getAttachment();
if (!attachment) {
continue;
}
if (slotIsOutRange(slot, startSlotIndex, endSlotIndex)) {
continue;
}
// Early exit if slot is invisible
if (slot.getColor().a == 0) {
continue;
}
if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) {
coordCount += 8;
}
@ -969,16 +964,10 @@ namespace spine {
#endif
for (size_t i = 0; i < skeleton.getSlots().size(); ++i) {
/*const*/ Slot& slot = *skeleton.getDrawOrder()[i]; // match the draw order of SkeletonRenderer::Draw
if (nothingToDraw(slot, startSlotIndex, endSlotIndex)) {
continue;
}
Attachment* const attachment = slot.getAttachment();
if (!attachment) {
continue;
}
if (slotIsOutRange(slot, startSlotIndex, endSlotIndex)) {
continue;
}
if (slot.getColor().a == 0) {
continue;
}
if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) {
RegionAttachment* const regionAttachment = static_cast<RegionAttachment*>(attachment);
assert(dstPtr + 8 <= dstEnd);

View File

@ -1031,9 +1031,9 @@ void AnimationState::computeHold(TrackEntry *entry) {
} else {
for (TrackEntry *next = to->_mixingTo; next != NULL; next = next->_mixingTo) {
if (next->_animation->hasTimeline(id)) continue;
if (entry->_mixDuration > 0) {
if (next->_mixDuration > 0) {
timelineMode[i] = HoldMix;
timelineHoldMix[i] = entry;
timelineHoldMix[i] = next;
i++;
goto continue_outer; // continue outer;
}

View File

@ -43,9 +43,13 @@ namespace Spine {
public Animation (string name, ExposedList<Timeline> timelines, float duration) {
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
if (timelines == null) throw new ArgumentNullException("timelines", "timelines cannot be null.");
this.timelineIds = new HashSet<int>();
foreach (Timeline timeline in timelines)
timelineIds.Add(timeline.PropertyId);
// Note: avoiding reallocations by adding all hash set entries at
// once (EnsureCapacity() is only available in newer .Net versions).
int[] propertyIDs = new int[timelines.Count];
for (int i = 0; i < timelines.Count; ++i) {
propertyIDs[i] = timelines.Items[i].PropertyId;
}
this.timelineIds = new HashSet<int>(propertyIDs);
this.name = name;
this.timelines = timelines;
this.duration = duration;

View File

@ -85,6 +85,25 @@ namespace Spine {
public delegate void TrackEntryEventDelegate (TrackEntry trackEntry, Event e);
public event TrackEntryEventDelegate Event;
public void AssignEventSubscribersFrom (AnimationState src) {
Event = src.Event;
Start = src.Start;
Interrupt = src.Interrupt;
End = src.End;
Dispose = src.Dispose;
Complete = src.Complete;
}
public void AddEventSubscribersFrom (AnimationState src) {
Event += src.Event;
Start += src.Start;
Interrupt += src.Interrupt;
End += src.End;
Dispose += src.Dispose;
Complete += src.Complete;
}
// end of difference
private readonly EventQueue queue; // Initialized by constructor.
private readonly HashSet<int> propertyIDs = new HashSet<int>();

View File

@ -883,6 +883,7 @@ namespace Spine {
internal class SkeletonInput {
private byte[] chars = new byte[32];
private byte[] bytesBigEndian = new byte[4];
internal ExposedList<String> strings;
Stream input;
@ -905,15 +906,20 @@ namespace Spine {
}
public float ReadFloat () {
chars[3] = (byte)input.ReadByte();
chars[2] = (byte)input.ReadByte();
chars[1] = (byte)input.ReadByte();
chars[0] = (byte)input.ReadByte();
input.Read(bytesBigEndian, 0, 4);
chars[3] = bytesBigEndian[0];
chars[2] = bytesBigEndian[1];
chars[1] = bytesBigEndian[2];
chars[0] = bytesBigEndian[3];
return BitConverter.ToSingle(chars, 0);
}
public int ReadInt () {
return (input.ReadByte() << 24) + (input.ReadByte() << 16) + (input.ReadByte() << 8) + input.ReadByte();
input.Read(bytesBigEndian, 0, 4);
return (bytesBigEndian[0] << 24)
+ (bytesBigEndian[1] << 16)
+ (bytesBigEndian[2] << 8)
+ bytesBigEndian[3];
}
public int ReadInt (bool optimizePositive) {

View File

@ -4,7 +4,6 @@ $(function () {
alert("Error: " + message + "\n" + "URL:" + url + "\nLine: " + lineNo);
}
spineDemos.init();
spineDemos.assetManager = new spine.SharedAssetManager("assets/");

View File

@ -0,0 +1,157 @@
<html>
<script src="../../build/spine-webgl.js"></script>
<style>
* { margin: 0; padding: 0; }
body, html { height: 100% }
canvas { position: absolute; width: 100% ;height: 100%; }
</style>
<body>
<canvas id="canvas"></canvas>
<script>
var canvas;
var gl;
var shader;
var batcher;
var mvp = new spine.webgl.Matrix4();
var assetManager;
var skeletonRenderer;
var lastFrameTime;
var spineboy;
function init () {
// Setup canvas and WebGL context. We pass alpha: false to canvas.getContext() so we don't use premultiplied alpha when
// loading textures. That is handled separately by PolygonBatcher.
canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var config = { alpha: false };
gl = canvas.getContext("webgl", config) || canvas.getContext("experimental-webgl", config);
if (!gl) {
alert('WebGL is unavailable.');
return;
}
// Create a simple shader, mesh, model-view-projection matrix, SkeletonRenderer, and AssetManager.
shader = spine.webgl.Shader.newTwoColoredTextured(gl);
batcher = new spine.webgl.PolygonBatcher(gl);
mvp.ortho2d(0, 0, canvas.width - 1, canvas.height - 1);
skeletonRenderer = new spine.webgl.SkeletonRenderer(gl);
assetManager = new spine.webgl.AssetManager(gl);
// Tell AssetManager to load the resources for each skeleton, including the exported .skel file, the .atlas file and the .png
// file for the atlas. We then wait until all resources are loaded in the load() method.
assetManager.loadBinary("assets/spineboy-pro.skel");
assetManager.loadTextureAtlas("assets/spineboy-pma.atlas");
requestAnimationFrame(load);
}
function load () {
// Wait until the AssetManager has loaded all resources, then load the skeletons.
if (assetManager.isLoadingComplete()) {
spineboy = loadSpineboy("run", true);
lastFrameTime = Date.now() / 1000;
requestAnimationFrame(render); // Loading is done, call render every frame.
} else {
requestAnimationFrame(load);
}
}
function loadSpineboy (initialAnimation, premultipliedAlpha) {
// Load the texture atlas from the AssetManager.
var atlas = assetManager.get("assets/spineboy-pma.atlas");
// Create a AtlasAttachmentLoader that resolves region, mesh, boundingbox and path attachments
var atlasLoader = new spine.AtlasAttachmentLoader(atlas);
// Create a SkeletonBinary instance for parsing the .skel file.
var skeletonBinary = new spine.SkeletonBinary(atlasLoader);
// Set the scale to apply during parsing, parse the file, and create a new skeleton.
skeletonBinary.scale = 1;
var skeletonData = skeletonBinary.readSkeletonData(assetManager.get("assets/spineboy-pro.skel"));
var skeleton = new spine.Skeleton(skeletonData);
var bounds = calculateSetupPoseBounds(skeleton);
// Create an AnimationState, and set the initial animation in looping mode.
var animationStateData = new spine.AnimationStateData(skeleton.data);
var animationState = new spine.AnimationState(animationStateData);
animationState.setAnimation(0, initialAnimation, true);
// Pack everything up and return to caller.
return { skeleton: skeleton, state: animationState, bounds: bounds, premultipliedAlpha: premultipliedAlpha };
}
function calculateSetupPoseBounds (skeleton) {
skeleton.setToSetupPose();
skeleton.updateWorldTransform();
var offset = new spine.Vector2();
var size = new spine.Vector2();
skeleton.getBounds(offset, size, []);
return { offset: offset, size: size };
}
function render () {
var now = Date.now() / 1000;
var delta = now - lastFrameTime;
lastFrameTime = now;
// Update the MVP matrix to adjust for canvas size changes
resize();
gl.clearColor(0.3, 0.3, 0.3, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// Apply the animation state based on the delta time.
var skeleton = spineboy.skeleton;
var state = spineboy.state;
var premultipliedAlpha = spineboy.premultipliedAlpha;
state.update(delta);
state.apply(skeleton);
skeleton.updateWorldTransform();
// Bind the shader and set the texture and model-view-projection matrix.
shader.bind();
shader.setUniformi(spine.webgl.Shader.SAMPLER, 0);
shader.setUniform4x4f(spine.webgl.Shader.MVP_MATRIX, mvp.values);
// Start the batch and tell the SkeletonRenderer to render the active skeleton.
batcher.begin(shader);
skeletonRenderer.premultipliedAlpha = premultipliedAlpha;
skeletonRenderer.draw(batcher, skeleton);
batcher.end();
shader.unbind();
requestAnimationFrame(render);
}
function resize () {
var w = canvas.clientWidth;
var h = canvas.clientHeight;
if (canvas.width != w || canvas.height != h) {
canvas.width = w;
canvas.height = h;
}
// Calculations to center the skeleton in the canvas.
var bounds = spineboy.bounds;
var centerX = bounds.offset.x + bounds.size.x / 2;
var centerY = bounds.offset.y + bounds.size.y / 2;
var scaleX = bounds.size.x / canvas.width;
var scaleY = bounds.size.y / canvas.height;
var scale = Math.max(scaleX, scaleY) * 1.2;
if (scale < 1) scale = 1;
var width = canvas.width * scale;
var height = canvas.height * scale;
mvp.ortho2d(centerX - width / 2, centerY - height / 2, width, height);
gl.viewport(0, 0, canvas.width, canvas.height);
}
init();
</script>
</body>
</html>

View File

@ -2,9 +2,9 @@
<script src="../../build/spine-webgl.js"></script>
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
<style>
* { margin: 0; padding: 0; }
body, html { height: 100% }
canvas { position: absolute; width: 100% ;height: 100%; }
* { margin: 0; padding: 0; }
body, html { height: 100% }
canvas { position: absolute; width: 100% ;height: 100%; }
</style>
<body>
<canvas id="canvas"></canvas>
@ -15,21 +15,22 @@
<span>Skin:</span><select id="skinList"></select>
<span>Vertex Effect:</span><select id="effectList"></select>
<span>Debug:</span><input type="checkbox" id="debug">
<div>
</div>
</center>
</body>
<script>
var lastFrameTime = Date.now() / 1000;
var canvas;
var gl;
var shader;
var batcher;
var gl;
var mvp = new spine.webgl.Matrix4();
var assetManager;
var skeletonRenderer;
var assetManager;
var debugRenderer;
var shapes;
var lastFrameTime;
var skeletons = {};
var activeSkeleton = "spineboy";
var swirlEffect = new spine.SwirlEffect(0);
@ -49,11 +50,14 @@ function init () {
return;
}
// Create a simple shader, mesh, model-view-projection matrix and SkeletonRenderer.
// Create a simple shader, mesh, model-view-projection matrix, SkeletonRenderer, and AssetManager.
shader = spine.webgl.Shader.newTwoColoredTextured(gl);
batcher = new spine.webgl.PolygonBatcher(gl);
mvp.ortho2d(0, 0, canvas.width - 1, canvas.height - 1);
skeletonRenderer = new spine.webgl.SkeletonRenderer(gl);
assetManager = new spine.webgl.AssetManager(gl);
// Create a debug renderer and the ShapeRenderer it needs to render lines.
debugRenderer = new spine.webgl.SkeletonDebugRenderer(gl);
debugRenderer.drawRegionAttachments = true;
debugRenderer.drawBoundingBoxes = true;
@ -62,9 +66,8 @@ function init () {
debugRenderer.drawPaths = true;
debugShader = spine.webgl.Shader.newColored(gl);
shapes = new spine.webgl.ShapeRenderer(gl);
assetManager = new spine.webgl.AssetManager(gl);
// Tell AssetManager to load the resources for each model, including the exported .skel file, the .atlas file and the .png
// Tell AssetManager to load the resources for each skeleton, including the exported .skel file, the .atlas file and the .png
// file for the atlas. We then wait until all resources are loaded in the load() method.
assetManager.loadBinary("assets/spineboy-pro.skel");
assetManager.loadTextureAtlas("assets/spineboy-pma.atlas");
@ -94,7 +97,8 @@ function load () {
skeletons["stretchyman"] = loadSkeleton("stretchyman-pro", "sneak", true);
skeletons["coin"] = loadSkeleton("coin-pro", "animation", true);
setupUI();
requestAnimationFrame(render);
lastFrameTime = Date.now() / 1000;
requestAnimationFrame(render); // Loading is done, call render every frame.
} else {
requestAnimationFrame(load);
}
@ -104,22 +108,23 @@ function loadSkeleton (name, initialAnimation, premultipliedAlpha, skin) {
if (skin === undefined) skin = "default";
// Load the texture atlas using name.atlas from the AssetManager.
atlas = assetManager.get("assets/" + name.replace("-ess", "").replace("-pro", "") + (premultipliedAlpha ? "-pma": "") + ".atlas");
var atlas = assetManager.get("assets/" + name.replace("-ess", "").replace("-pro", "") + (premultipliedAlpha ? "-pma": "") + ".atlas");
// Create a AtlasAttachmentLoader that resolves region, mesh, boundingbox and path attachments
atlasLoader = new spine.AtlasAttachmentLoader(atlas);
var atlasLoader = new spine.AtlasAttachmentLoader(atlas);
// Create a SkeletonBinary instance for parsing the .skel file.
var skeletonBinary = new spine.SkeletonBinary(atlasLoader);
// Set the scale to apply during parsing, parse the file, and create a new skeleton.
skeletonBinary.scale = 1;
var skeletonData = skeletonBinary.readSkeletonData(assetManager.get("assets/" + name + ".skel"));
var skeleton = new spine.Skeleton(skeletonData);
skeleton.setSkinByName(skin);
var bounds = calculateBounds(skeleton);
var bounds = calculateSetupPoseBounds(skeleton);
// Create an AnimationState, and set the initial animation in looping mode.
animationStateData = new spine.AnimationStateData(skeleton.data);
var animationStateData = new spine.AnimationStateData(skeleton.data);
var animationState = new spine.AnimationState(animationStateData);
if (name == "spineboy") {
animationStateData.setMix("walk", "jump", 0.4)
@ -155,7 +160,7 @@ function loadSkeleton (name, initialAnimation, premultipliedAlpha, skin) {
return { skeleton: skeleton, state: animationState, bounds: bounds, premultipliedAlpha: premultipliedAlpha };
}
function calculateBounds(skeleton) {
function calculateSetupPoseBounds(skeleton) {
skeleton.setToSetupPose();
skeleton.updateWorldTransform();
var offset = new spine.Vector2();
@ -236,7 +241,6 @@ function setupUI () {
function render () {
var now = Date.now() / 1000;
var delta = now - lastFrameTime;
delta = 0.016;
lastFrameTime = now;
// Update the MVP matrix to adjust for canvas size changes
@ -246,8 +250,8 @@ function render () {
gl.clear(gl.COLOR_BUFFER_BIT);
// Apply the animation state based on the delta time.
var state = skeletons[activeSkeleton].state;
var skeleton = skeletons[activeSkeleton].skeleton;
var state = skeletons[activeSkeleton].state;
var bounds = skeletons[activeSkeleton].bounds;
var premultipliedAlpha = skeletons[activeSkeleton].premultipliedAlpha;
state.update(delta);
@ -269,10 +273,9 @@ function render () {
swirlTime += delta;
var percent = swirlTime % 2;
if (percent > 1) percent = 1 - (percent -1 );
// swirlEffect.angle = -60 + 120 * (perecent < 0.5 ? Math.pow(percent * 2, 2) / 2 : Math.pow((percent - 1) * 2, 2) / -2 + 1);
swirlEffect.angle = 360 * percent;
swirlEffect.centerX = 200; //bounds.offset.x + bounds.size.x / 2
swirlEffect.centerY = 200; //bounds.offset.y + bounds.size.y / 2
swirlEffect.angle = 120 * percent - 60;
swirlEffect.centerX = bounds.offset.x + bounds.size.x / 2;
swirlEffect.centerY = bounds.offset.y + bounds.size.y / 2;
swirlEffect.radius = Math.sqrt(bounds.size.x * bounds.size.x + bounds.size.y * bounds.size.y);
skeletonRenderer.vertexEffect = swirlEffect;
} else if (effect == "Jitter") {
@ -285,7 +288,7 @@ function render () {
shader.unbind();
// draw debug information
// Draw debug information.
var debug = $('#debug').is(':checked');
if (debug) {
debugShader.bind();
@ -303,13 +306,13 @@ function render () {
function resize () {
var w = canvas.clientWidth;
var h = canvas.clientHeight;
var bounds = skeletons[activeSkeleton].bounds;
if (canvas.width != w || canvas.height != h) {
canvas.width = w;
canvas.height = h;
}
// magic
// Calculations to center the skeleton in the canvas.
var bounds = skeletons[activeSkeleton].bounds;
var centerX = bounds.offset.x + bounds.size.x / 2;
var centerY = bounds.offset.y + bounds.size.y / 2;
var scaleX = bounds.size.x / canvas.width;
@ -323,9 +326,8 @@ function resize () {
gl.viewport(0, 0, canvas.width, canvas.height);
}
(function() {
init();
})();
init();
</script>
</body>
</html>

View File

@ -55,7 +55,7 @@ namespace Spine.Unity.Editor {
}
}
void OnEnable () {
void InitializeEditor () {
skeletonRenderer = serializedObject.FindProperty("skeletonRenderer");
slotName = serializedObject.FindProperty("slotName");
isTrigger = serializedObject.FindProperty("isTrigger");
@ -64,12 +64,17 @@ namespace Spine.Unity.Editor {
}
public override void OnInspectorGUI () {
#if !NEW_PREFAB_SYSTEM
bool isInspectingPrefab = (PrefabUtility.GetPrefabType(target) == PrefabType.Prefab);
#else
bool isInspectingPrefab = false;
#endif
// Note: when calling InitializeEditor() in OnEnable, it throws exception
// "SerializedObjectNotCreatableException: Object at index 0 is null".
InitializeEditor();
// Try to auto-assign SkeletonRenderer field.
if (skeletonRenderer.objectReferenceValue == null) {
var foundSkeletonRenderer = follower.GetComponentInParent<SkeletonRenderer>();
@ -80,6 +85,7 @@ namespace Spine.Unity.Editor {
skeletonRenderer.objectReferenceValue = foundSkeletonRenderer;
serializedObject.ApplyModifiedProperties();
InitializeEditor();
}
var skeletonRendererValue = skeletonRenderer.objectReferenceValue as SkeletonRenderer;
@ -101,6 +107,7 @@ namespace Spine.Unity.Editor {
EditorGUILayout.PropertyField(slotName, new GUIContent("Slot"));
if (EditorGUI.EndChangeCheck()) {
serializedObject.ApplyModifiedProperties();
InitializeEditor();
#if !NEW_PREFAB_SYSTEM
if (!isInspectingPrefab)
rebuildRequired = true;
@ -118,6 +125,7 @@ namespace Spine.Unity.Editor {
if (clearStateChanged || triggerChanged) {
serializedObject.ApplyModifiedProperties();
InitializeEditor();
if (triggerChanged)
foreach (var col in follower.colliderTable.Values)
col.isTrigger = isTrigger.boolValue;
@ -152,6 +160,8 @@ namespace Spine.Unity.Editor {
}
if (follower.Slot == null)
follower.Initialize(false);
bool hasBoneFollower = follower.GetComponent<BoneFollower>() != null;
if (!hasBoneFollower) {
bool buttonDisabled = follower.Slot == null;

View File

@ -72,7 +72,9 @@ namespace Spine.Unity.Editor {
var component = o as SkeletonAnimation;
component.timeScale = Mathf.Max(component.timeScale, 0);
}
EditorGUILayout.Space();
SkeletonRootMotionParameter();
if (!isInspectingPrefab) {
if (requireRepaint) {

View File

@ -33,7 +33,6 @@
using UnityEngine;
using UnityEditor;
using Spine;
namespace Spine.Unity.Editor {
using Icons = SpineEditorUtilities.Icons;
@ -44,6 +43,11 @@ namespace Spine.Unity.Editor {
public class SkeletonGraphicInspector : UnityEditor.Editor {
const string SeparatorSlotNamesFieldName = "separatorSlotNames";
const string ReloadButtonString = "Reload";
protected GUIContent SkeletonDataAssetLabel;
static GUILayoutOption reloadButtonWidth;
static GUILayoutOption ReloadButtonWidth { get { return reloadButtonWidth = reloadButtonWidth ?? GUILayout.Width(GUI.skin.label.CalcSize(new GUIContent(ReloadButtonString)).x + 20); } }
static GUIStyle ReloadButtonStyle { get { return EditorStyles.miniButton; } }
SerializedProperty material, color;
SerializedProperty skeletonDataAsset, initialSkinName;
@ -56,6 +60,7 @@ namespace Spine.Unity.Editor {
SkeletonGraphic thisSkeletonGraphic;
protected bool isInspectingPrefab;
protected bool slotsReapplyRequired = false;
protected bool forceReloadQueued = false;
protected bool TargetIsValid {
get {
@ -80,6 +85,10 @@ namespace Spine.Unity.Editor {
#else
isInspectingPrefab = (PrefabUtility.GetPrefabType(target) == PrefabType.Prefab);
#endif
SpineEditorUtilities.ConfirmInitialization();
// Labels
SkeletonDataAssetLabel = new GUIContent("SkeletonData Asset", Icons.spine);
var so = this.serializedObject;
thisSkeletonGraphic = target as SkeletonGraphic;
@ -115,10 +124,34 @@ namespace Spine.Unity.Editor {
}
public override void OnInspectorGUI () {
if (UnityEngine.Event.current.type == EventType.Layout) {
if (forceReloadQueued) {
forceReloadQueued = false;
foreach (var c in targets) {
SpineEditorUtilities.ReloadSkeletonDataAssetAndComponent(c as SkeletonGraphic);
}
}
else {
foreach (var c in targets) {
var component = c as SkeletonGraphic;
if (!component.IsValid) {
SpineEditorUtilities.ReinitializeComponent(component);
if (!component.IsValid) continue;
}
}
}
}
bool wasChanged = false;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(skeletonDataAsset);
using (new EditorGUILayout.HorizontalScope(EditorStyles.helpBox)) {
SpineInspectorUtility.PropertyFieldFitLabel(skeletonDataAsset, SkeletonDataAssetLabel);
if (GUILayout.Button(ReloadButtonString, ReloadButtonStyle, ReloadButtonWidth))
forceReloadQueued = true;
}
EditorGUILayout.PropertyField(material);
EditorGUILayout.PropertyField(color);
@ -201,6 +234,8 @@ namespace Spine.Unity.Editor {
EditorGUILayout.Space();
EditorGUILayout.PropertyField(freeze);
EditorGUILayout.Space();
SkeletonRendererInspector.SkeletonRootMotionParameter(targets);
EditorGUILayout.Space();
EditorGUILayout.LabelField("UI", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(raycastTarget);

View File

@ -30,21 +30,125 @@
// Contributed by: Mitch Thompson
using UnityEditor;
using UnityEngine;
namespace Spine.Unity.Editor {
[CustomEditor(typeof(SkeletonMecanim))]
[CanEditMultipleObjects]
public class SkeletonMecanimInspector : SkeletonRendererInspector {
protected SerializedProperty mecanimTranslator;
public static bool mecanimSettingsFoldout;
protected SerializedProperty autoReset;
protected SerializedProperty useCustomMixMode;
protected SerializedProperty layerMixModes;
protected SerializedProperty layerBlendModes;
protected override void OnEnable () {
base.OnEnable();
mecanimTranslator = serializedObject.FindProperty("translator");
SerializedProperty mecanimTranslator = serializedObject.FindProperty("translator");
autoReset = mecanimTranslator.FindPropertyRelative("autoReset");
useCustomMixMode = mecanimTranslator.FindPropertyRelative("useCustomMixMode");
layerMixModes = mecanimTranslator.FindPropertyRelative("layerMixModes");
layerBlendModes = mecanimTranslator.FindPropertyRelative("layerBlendModes");
}
protected override void DrawInspectorGUI (bool multi) {
AddRootMotionComponentIfEnabled();
base.DrawInspectorGUI(multi);
EditorGUILayout.PropertyField(mecanimTranslator, true);
using (new SpineInspectorUtility.BoxScope()) {
mecanimSettingsFoldout = EditorGUILayout.Foldout(mecanimSettingsFoldout, "Mecanim Translator");
if (mecanimSettingsFoldout) {
EditorGUILayout.PropertyField(autoReset, new GUIContent("Auto Reset",
"When set to true, the skeleton state is mixed out to setup-" +
"pose when an animation finishes, according to the " +
"animation's keyed items."));
EditorGUILayout.PropertyField(useCustomMixMode, new GUIContent("Custom MixMode",
"When disabled, the recommended MixMode is used according to the layer blend mode. Enable to specify a custom MixMode for each Mecanim layer."));
if (useCustomMixMode.hasMultipleDifferentValues || useCustomMixMode.boolValue == true) {
DrawLayerSettings();
EditorGUILayout.Space();
}
}
}
}
protected void AddRootMotionComponentIfEnabled () {
foreach (var t in targets) {
var component = t as Component;
var animator = component.GetComponent<Animator>();
if (animator != null && animator.applyRootMotion) {
if (component.GetComponent<SkeletonMecanimRootMotion>() == null) {
component.gameObject.AddComponent<SkeletonMecanimRootMotion>();
}
}
}
}
protected void DrawLayerSettings () {
string[] layerNames = GetLayerNames();
float widthLayerColumn = 140;
float widthMixColumn = 84;
using (new GUILayout.HorizontalScope()) {
var rect = GUILayoutUtility.GetRect(EditorGUIUtility.currentViewWidth, EditorGUIUtility.singleLineHeight);
rect.width = widthLayerColumn;
EditorGUI.LabelField(rect, SpineInspectorUtility.TempContent("Mecanim Layer"), EditorStyles.boldLabel);
var savedIndent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
rect.position += new Vector2(rect.width, 0);
rect.width = widthMixColumn;
EditorGUI.LabelField(rect, SpineInspectorUtility.TempContent("Mix Mode"), EditorStyles.boldLabel);
EditorGUI.indentLevel = savedIndent;
}
using (new SpineInspectorUtility.IndentScope()) {
int layerCount = layerMixModes.arraySize;
for (int i = 0; i < layerCount; ++i) {
using (new GUILayout.HorizontalScope()) {
string layerName = i < layerNames.Length ? layerNames[i] : ("Layer " + i);
var rect = GUILayoutUtility.GetRect(EditorGUIUtility.currentViewWidth, EditorGUIUtility.singleLineHeight);
rect.width = widthLayerColumn;
EditorGUI.PrefixLabel(rect, SpineInspectorUtility.TempContent(layerName));
var savedIndent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
var mixMode = layerMixModes.GetArrayElementAtIndex(i);
var blendMode = layerBlendModes.GetArrayElementAtIndex(i);
rect.position += new Vector2(rect.width, 0);
rect.width = widthMixColumn;
EditorGUI.PropertyField(rect, mixMode, GUIContent.none);
EditorGUI.indentLevel = savedIndent;
}
}
}
}
protected string[] GetLayerNames () {
int maxLayerCount = 0;
int maxIndex = 0;
for (int i = 0; i < targets.Length; ++i) {
var skeletonMecanim = ((SkeletonMecanim)targets[i]);
int count = skeletonMecanim.Translator.MecanimLayerCount;
if (count > maxLayerCount) {
maxLayerCount = count;
maxIndex = i;
}
}
if (maxLayerCount == 0)
return new string[0];
var skeletonMecanimMaxLayers = ((SkeletonMecanim)targets[maxIndex]);
return skeletonMecanimMaxLayers.Translator.MecanimLayerNames;
}
}
}

View File

@ -0,0 +1,81 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEditor;
using UnityEngine;
namespace Spine.Unity.Editor {
[CustomEditor(typeof(SkeletonMecanimRootMotion))]
[CanEditMultipleObjects]
public class SkeletonMecanimRootMotionInspector : SkeletonRootMotionBaseInspector {
protected SerializedProperty mecanimLayerFlags;
protected GUIContent mecanimLayersLabel;
protected override void OnEnable () {
base.OnEnable();
mecanimLayerFlags = serializedObject.FindProperty("mecanimLayerFlags");
mecanimLayersLabel = new UnityEngine.GUIContent("Mecanim Layers", "Mecanim layers to apply root motion at. Defaults to the first Mecanim layer.");
}
override public void OnInspectorGUI () {
base.MainPropertyFields();
MecanimLayerMaskPropertyField();
base.OptionalPropertyFields();
serializedObject.ApplyModifiedProperties();
}
protected string[] GetLayerNames () {
int maxLayerCount = 0;
int maxIndex = 0;
for (int i = 0; i < targets.Length; ++i) {
var skeletonMecanim = ((SkeletonMecanimRootMotion)targets[i]).SkeletonMecanim;
int count = skeletonMecanim.Translator.MecanimLayerCount;
if (count > maxLayerCount) {
maxLayerCount = count;
maxIndex = i;
}
}
if (maxLayerCount == 0)
return new string[0];
var skeletonMecanimMaxLayers = ((SkeletonMecanimRootMotion)targets[maxIndex]).SkeletonMecanim;
return skeletonMecanimMaxLayers.Translator.MecanimLayerNames;
}
protected void MecanimLayerMaskPropertyField () {
string[] layerNames = GetLayerNames();
if (layerNames.Length > 0)
mecanimLayerFlags.intValue = EditorGUILayout.MaskField(
mecanimLayersLabel, mecanimLayerFlags.intValue, layerNames);
}
}
}

View File

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

View File

@ -82,7 +82,7 @@ namespace Spine.Unity.Editor {
const string ReloadButtonString = "Reload";
static GUILayoutOption reloadButtonWidth;
static GUILayoutOption ReloadButtonWidth { get { return reloadButtonWidth = reloadButtonWidth ?? GUILayout.Width(GUI.skin.label.CalcSize(new GUIContent(ReloadButtonString)).x + 20); } }
static GUIStyle ReloadButtonStyle { get { return EditorStyles.miniButtonRight; } }
static GUIStyle ReloadButtonStyle { get { return EditorStyles.miniButton; } }
protected bool TargetIsValid {
get {
@ -106,7 +106,6 @@ namespace Spine.Unity.Editor {
#else
isInspectingPrefab = (PrefabUtility.GetPrefabType(target) == PrefabType.Prefab);
#endif
SpineEditorUtilities.ConfirmInitialization();
// Labels
@ -173,11 +172,8 @@ namespace Spine.Unity.Editor {
if (serializedObject.ApplyModifiedProperties() || SpineInspectorUtility.UndoRedoPerformed(Event.current) ||
AreAnyMaskMaterialsMissing()) {
if (!Application.isPlaying) {
if (multi) {
foreach (var o in targets) EditorForceInitializeComponent((SkeletonRenderer)o);
} else {
EditorForceInitializeComponent((SkeletonRenderer)target);
}
foreach (var o in targets)
SpineEditorUtilities.ReinitializeComponent((SkeletonRenderer)o);
SceneView.RepaintAll();
}
}
@ -188,25 +184,16 @@ namespace Spine.Unity.Editor {
if (Event.current.type == EventType.Layout) {
if (forceReloadQueued) {
forceReloadQueued = false;
if (multi) {
foreach (var c in targets)
EditorForceReloadSkeletonDataAssetAndComponent(c as SkeletonRenderer);
} else {
EditorForceReloadSkeletonDataAssetAndComponent(target as SkeletonRenderer);
foreach (var c in targets) {
SpineEditorUtilities.ReloadSkeletonDataAssetAndComponent(c as SkeletonRenderer);
}
} else {
if (multi) {
foreach (var c in targets) {
var component = c as SkeletonRenderer;
if (!component.valid) {
EditorForceInitializeComponent(component);
if (!component.valid) continue;
}
foreach (var c in targets) {
var component = c as SkeletonRenderer;
if (!component.valid) {
SpineEditorUtilities.ReinitializeComponent(component);
if (!component.valid) continue;
}
} else {
var component = (SkeletonRenderer)target;
if (!component.valid)
EditorForceInitializeComponent(component);
}
}
@ -241,15 +228,8 @@ namespace Spine.Unity.Editor {
#if NO_PREFAB_MESH
if (isInspectingPrefab) {
if (multi) {
foreach (var c in targets) {
var component = (SkeletonRenderer)c;
MeshFilter meshFilter = component.GetComponent<MeshFilter>();
if (meshFilter != null && meshFilter.sharedMesh != null)
meshFilter.sharedMesh = null;
}
} else {
var component = (SkeletonRenderer)target;
foreach (var c in targets) {
var component = (SkeletonRenderer)c;
MeshFilter meshFilter = component.GetComponent<MeshFilter>();
if (meshFilter != null && meshFilter.sharedMesh != null)
meshFilter.sharedMesh = null;
@ -286,7 +266,7 @@ namespace Spine.Unity.Editor {
return;
}
if (!SkeletonDataAssetIsValid(component.skeletonDataAsset)) {
if (!SpineEditorUtilities.SkeletonDataAssetIsValid(component.skeletonDataAsset)) {
EditorGUILayout.HelpBox("Skeleton Data Asset error. Please check Skeleton Data Asset.", MessageType.Error);
return;
}
@ -414,6 +394,48 @@ namespace Spine.Unity.Editor {
}
}
protected void SkeletonRootMotionParameter() {
SkeletonRootMotionParameter(targets);
}
public static void SkeletonRootMotionParameter(Object[] targets) {
int rootMotionComponentCount = 0;
foreach (var t in targets) {
var component = t as Component;
if (component.GetComponent<SkeletonRootMotion>() != null) {
++rootMotionComponentCount;
}
}
bool allHaveRootMotion = rootMotionComponentCount == targets.Length;
bool anyHaveRootMotion = rootMotionComponentCount > 0;
using (new GUILayout.HorizontalScope()) {
EditorGUILayout.PrefixLabel("Root Motion");
if (!allHaveRootMotion) {
if (GUILayout.Button(SpineInspectorUtility.TempContent("Add Component", Icons.constraintTransform), GUILayout.MaxWidth(130), GUILayout.Height(18))) {
foreach (var t in targets) {
var component = t as Component;
if (component.GetComponent<SkeletonRootMotion>() == null) {
component.gameObject.AddComponent<SkeletonRootMotion>();
}
}
}
}
if (anyHaveRootMotion) {
if (GUILayout.Button(SpineInspectorUtility.TempContent("Remove Component", Icons.constraintTransform), GUILayout.MaxWidth(140), GUILayout.Height(18))) {
foreach (var t in targets) {
var component = t as Component;
var rootMotionComponent = component.GetComponent<SkeletonRootMotion>();
if (rootMotionComponent != null) {
DestroyImmediate(rootMotionComponent);
}
}
}
}
}
}
public static void SetSeparatorSlotNames (SkeletonRenderer skeletonRenderer, string[] newSlotNames) {
var field = SpineInspectorUtility.GetNonPublicField(typeof(SkeletonRenderer), SeparatorSlotNamesFieldName);
field.SetValue(skeletonRenderer, newSlotNames);
@ -535,38 +557,6 @@ namespace Spine.Unity.Editor {
return false;
}
static void EditorForceReloadSkeletonDataAssetAndComponent (SkeletonRenderer component) {
if (component == null) return;
// Clear all and reload.
if (component.skeletonDataAsset != null) {
foreach (AtlasAssetBase aa in component.skeletonDataAsset.atlasAssets) {
if (aa != null) aa.Clear();
}
component.skeletonDataAsset.Clear();
}
component.skeletonDataAsset.GetSkeletonData(true);
// Reinitialize.
EditorForceInitializeComponent(component);
}
static void EditorForceInitializeComponent (SkeletonRenderer component) {
if (component == null) return;
if (!SkeletonDataAssetIsValid(component.SkeletonDataAsset)) return;
component.Initialize(true);
#if BUILT_IN_SPRITE_MASK_COMPONENT
SpineMaskUtilities.EditorAssignSpriteMaskMaterials(component);
#endif
component.LateUpdate();
}
static bool SkeletonDataAssetIsValid (SkeletonDataAsset asset) {
return asset != null && asset.GetSkeletonData(quiet: true) != null;
}
bool AreAnyMaskMaterialsMissing() {
#if BUILT_IN_SPRITE_MASK_COMPONENT
foreach (var o in targets) {
@ -584,13 +574,13 @@ namespace Spine.Unity.Editor {
static void EditorSetMaskMaterials(SkeletonRenderer component, SpriteMaskInteraction maskType)
{
if (component == null) return;
if (!SkeletonDataAssetIsValid(component.SkeletonDataAsset)) return;
if (!SpineEditorUtilities.SkeletonDataAssetIsValid(component.SkeletonDataAsset)) return;
SpineMaskUtilities.EditorInitMaskMaterials(component, component.maskMaterials, maskType);
}
static void EditorDeleteMaskMaterials(SkeletonRenderer component, SpriteMaskInteraction maskType) {
if (component == null) return;
if (!SkeletonDataAssetIsValid(component.SkeletonDataAsset)) return;
if (!SpineEditorUtilities.SkeletonDataAssetIsValid(component.SkeletonDataAsset)) return;
SpineMaskUtilities.EditorDeleteMaskMaterials(component.maskMaterials, maskType);
}
#endif

View File

@ -0,0 +1,92 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEditor;
using UnityEngine;
namespace Spine.Unity.Editor {
[CustomEditor(typeof(SkeletonRootMotionBase))]
[CanEditMultipleObjects]
public class SkeletonRootMotionBaseInspector : UnityEditor.Editor {
protected SerializedProperty rootMotionBoneName;
protected SerializedProperty transformPositionX;
protected SerializedProperty transformPositionY;
protected SerializedProperty rigidBody2D;
protected SerializedProperty rigidBody;
protected GUIContent rootMotionBoneNameLabel;
protected GUIContent transformPositionXLabel;
protected GUIContent transformPositionYLabel;
protected GUIContent rigidBody2DLabel;
protected GUIContent rigidBodyLabel;
protected virtual void OnEnable () {
rootMotionBoneName = serializedObject.FindProperty("rootMotionBoneName");
transformPositionX = serializedObject.FindProperty("transformPositionX");
transformPositionY = serializedObject.FindProperty("transformPositionY");
rigidBody2D = serializedObject.FindProperty("rigidBody2D");
rigidBody = serializedObject.FindProperty("rigidBody");
rootMotionBoneNameLabel = new UnityEngine.GUIContent("Root Motion Bone", "The bone to take the motion from.");
transformPositionXLabel = new UnityEngine.GUIContent("X", "Root transform position (X)");
transformPositionYLabel = new UnityEngine.GUIContent("Y", "Use the Y-movement of the bone.");
rigidBody2DLabel = new UnityEngine.GUIContent("Rigidbody2D",
"Optional Rigidbody2D: Assign a Rigidbody2D here if you want " +
" to apply the root motion to the rigidbody instead of the Transform." +
"\n\n" +
"Note that animation and physics updates are not always in sync." +
"Some jitter may result at certain framerates.");
rigidBodyLabel = new UnityEngine.GUIContent("Rigidbody",
"Optional Rigidbody: Assign a Rigidbody here if you want " +
" to apply the root motion to the rigidbody instead of the Transform." +
"\n\n" +
"Note that animation and physics updates are not always in sync." +
"Some jitter may result at certain framerates.");
}
public override void OnInspectorGUI () {
MainPropertyFields();
OptionalPropertyFields();
serializedObject.ApplyModifiedProperties();
}
protected virtual void MainPropertyFields () {
EditorGUILayout.PropertyField(rootMotionBoneName, rootMotionBoneNameLabel);
EditorGUILayout.PropertyField(transformPositionX, transformPositionXLabel);
EditorGUILayout.PropertyField(transformPositionY, transformPositionYLabel);
}
protected virtual void OptionalPropertyFields () {
//EditorGUILayout.LabelField("Optional", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(rigidBody2D, rigidBody2DLabel);
EditorGUILayout.PropertyField(rigidBody, rigidBodyLabel);
}
}
}

View File

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

View File

@ -0,0 +1,79 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEditor;
using UnityEngine;
namespace Spine.Unity.Editor {
[CustomEditor(typeof(SkeletonRootMotion))]
[CanEditMultipleObjects]
public class SkeletonRootMotionInspector : SkeletonRootMotionBaseInspector {
protected SerializedProperty animationTrackFlags;
protected GUIContent animationTrackFlagsLabel;
string[] TrackNames;
protected override void OnEnable () {
base.OnEnable();
animationTrackFlags = serializedObject.FindProperty("animationTrackFlags");
animationTrackFlagsLabel = new UnityEngine.GUIContent("Animation Tracks",
"Animation tracks to apply root motion at. Defaults to the first" +
" animation track (index 0).");
}
override public void OnInspectorGUI () {
base.MainPropertyFields();
AnimationTracksPropertyField();
base.OptionalPropertyFields();
serializedObject.ApplyModifiedProperties();
}
protected void AnimationTracksPropertyField () {
if (TrackNames == null) {
InitTrackNames();
}
animationTrackFlags.intValue = EditorGUILayout.MaskField(
animationTrackFlagsLabel, animationTrackFlags.intValue, TrackNames);
}
protected void InitTrackNames () {
int numEntries = 32;
TrackNames = new string[numEntries];
for (int i = 0; i < numEntries; ++i) {
TrackNames[i] = string.Format("Track {0}", i);
}
}
}
}

View File

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

View File

@ -169,7 +169,7 @@ public class SpineSpriteShaderGUI : SpineShaderWithOutlineGUI {
static GUIContent _rimPowerText = new GUIContent("Rim Power");
static GUIContent _specularToggleText = new GUIContent("Specular", "Enable Specular.");
static GUIContent _colorAdjustmentToggleText = new GUIContent("Color Adjustment", "Enable material color adjustment.");
static GUIContent _colorAdjustmentColorText = new GUIContent("Overlay Color");
static GUIContent _colorAdjustmentColorText = new GUIContent("Overlay Color & Alpha");
static GUIContent _colorAdjustmentHueText = new GUIContent("Hue");
static GUIContent _colorAdjustmentSaturationText = new GUIContent("Saturation");
static GUIContent _colorAdjustmentBrightnessText = new GUIContent("Brightness");

View File

@ -46,6 +46,10 @@
#define NEW_PREFERENCES_SETTINGS_PROVIDER
#endif
#if UNITY_2017_1_OR_NEWER
#define BUILT_IN_SPRITE_MASK_COMPONENT
#endif
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
@ -189,6 +193,62 @@ namespace Spine.Unity.Editor {
}
}
public static void ReloadSkeletonDataAssetAndComponent (SkeletonRenderer component) {
if (component == null) return;
ReloadSkeletonDataAsset(component.skeletonDataAsset);
ReinitializeComponent(component);
}
public static void ReloadSkeletonDataAssetAndComponent (SkeletonGraphic component) {
if (component == null) return;
ReloadSkeletonDataAsset(component.skeletonDataAsset);
// Reinitialize.
ReinitializeComponent(component);
}
public static void ReloadSkeletonDataAsset (SkeletonDataAsset skeletonDataAsset) {
if (skeletonDataAsset != null) {
foreach (AtlasAssetBase aa in skeletonDataAsset.atlasAssets) {
if (aa != null) aa.Clear();
}
skeletonDataAsset.Clear();
}
skeletonDataAsset.GetSkeletonData(true);
}
public static void ReinitializeComponent (SkeletonRenderer component) {
if (component == null) return;
if (!SkeletonDataAssetIsValid(component.SkeletonDataAsset)) return;
var stateComponent = component as IAnimationStateComponent;
AnimationState oldAnimationState = null;
if (stateComponent != null) {
oldAnimationState = stateComponent.AnimationState;
}
component.Initialize(true); // implicitly clears any subscribers
if (oldAnimationState != null) {
stateComponent.AnimationState.AssignEventSubscribersFrom(oldAnimationState);
}
#if BUILT_IN_SPRITE_MASK_COMPONENT
SpineMaskUtilities.EditorAssignSpriteMaskMaterials(component);
#endif
component.LateUpdate();
}
public static void ReinitializeComponent (SkeletonGraphic component) {
if (component == null) return;
if (!SkeletonDataAssetIsValid(component.SkeletonDataAsset)) return;
component.Initialize(true);
component.LateUpdate();
}
public static bool SkeletonDataAssetIsValid (SkeletonDataAsset asset) {
return asset != null && asset.GetSkeletonData(quiet: true) != null;
}
public static bool IssueWarningsForUnrecommendedTextureSettings(string texturePath)
{
TextureImporter texImporter = (TextureImporter)TextureImporter.GetAtPath(texturePath);

View File

@ -27,6 +27,10 @@
* 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;
using UnityEditor;
@ -81,7 +85,18 @@ namespace Spine.Unity.Examples {
// Restore mesh part for undo logic after undo of "Add Parts Renderer".
// Triggers regeneration and assignment of the mesh filter's mesh.
if (component.GetComponent<MeshFilter>() && component.GetComponent<MeshFilter>().sharedMesh == null) {
bool isMeshFilterAlwaysNull = false;
#if UNITY_EDITOR && NEW_PREFAB_SYSTEM
// Don't store mesh or material at the prefab, otherwise it will permanently reload
var prefabType = UnityEditor.PrefabUtility.GetPrefabAssetType(component);
if (UnityEditor.PrefabUtility.IsPartOfPrefabAsset(component) &&
(prefabType == UnityEditor.PrefabAssetType.Regular || prefabType == UnityEditor.PrefabAssetType.Variant)) {
isMeshFilterAlwaysNull = true;
}
#endif
if (!isMeshFilterAlwaysNull && component.GetComponent<MeshFilter>() && component.GetComponent<MeshFilter>().sharedMesh == null) {
component.OnDisable();
component.OnEnable();
}

View File

@ -106,7 +106,12 @@ namespace Spine.Unity {
)
return;
DisposeColliders();
slot = null;
currentAttachment = null;
currentAttachmentName = null;
currentCollider = null;
colliderTable.Clear();
nameTable.Clear();
var skeleton = skeletonRenderer.skeleton;
slot = skeleton.FindSlot(slotName);
@ -118,13 +123,16 @@ namespace Spine.Unity {
return;
}
int requiredCollidersCount = 0;
var colliders = GetComponents<PolygonCollider2D>();
if (this.gameObject.activeInHierarchy) {
foreach (var skin in skeleton.Data.Skins)
AddSkin(skin, slotIndex);
AddCollidersForSkin(skin, slotIndex, colliders, ref requiredCollidersCount);
if (skeleton.skin != null)
AddSkin(skeleton.skin, slotIndex);
AddCollidersForSkin(skeleton.skin, slotIndex, colliders, ref requiredCollidersCount);
}
DisposeExcessCollidersAfter(requiredCollidersCount);
if (BoundingBoxFollower.DebugMessages) {
bool valid = colliderTable.Count != 0;
@ -137,7 +145,7 @@ namespace Spine.Unity {
}
}
void AddSkin (Skin skin, int slotIndex) {
void AddCollidersForSkin (Skin skin, int slotIndex, PolygonCollider2D[] previousColliders, ref int collidersCount) {
if (skin == null) return;
var skinEntries = new List<Skin.SkinEntry>();
skin.GetAttachments(slotIndex, skinEntries);
@ -151,8 +159,11 @@ namespace Spine.Unity {
if (boundingBoxAttachment != null) {
if (!colliderTable.ContainsKey(boundingBoxAttachment)) {
var bbCollider = SkeletonUtility.AddBoundingBoxAsComponent(boundingBoxAttachment, slot, gameObject, isTrigger);
var bbCollider = collidersCount < previousColliders.Length ?
previousColliders[collidersCount] : gameObject.AddComponent<PolygonCollider2D>();
++collidersCount;
SkeletonUtility.SetColliderPointsLocal(bbCollider, slot, boundingBoxAttachment);
bbCollider.isTrigger = isTrigger;
bbCollider.enabled = false;
bbCollider.hideFlags = HideFlags.NotEditable;
bbCollider.isTrigger = IsTrigger;
@ -178,33 +189,21 @@ namespace Spine.Unity {
currentCollider = null;
}
void DisposeColliders () {
void DisposeExcessCollidersAfter (int requiredCount) {
var colliders = GetComponents<PolygonCollider2D>();
if (colliders.Length == 0) return;
if (Application.isEditor) {
if (Application.isPlaying) {
foreach (var c in colliders) {
if (c != null)
Destroy(c);
}
} else {
foreach (var c in colliders)
if (c != null)
DestroyImmediate(c);
for (int i = requiredCount; i < colliders.Length; ++i) {
var collider = colliders[i];
if (collider != null) {
#if UNITY_EDITOR
if (Application.isEditor && !Application.isPlaying)
DestroyImmediate(collider);
else
#endif
Destroy(collider);
}
} else {
foreach (PolygonCollider2D c in colliders)
if (c != null)
Destroy(c);
}
slot = null;
currentAttachment = null;
currentAttachmentName = null;
currentCollider = null;
colliderTable.Clear();
nameTable.Clear();
}
void LateUpdate () {

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 2b957aa69dae9f948bacdeec549d28ea
folderAsset: yes
timeCreated: 1593173800
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,100 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEngine;
using System.Collections.Generic;
using Spine.Unity.AnimationTools;
namespace Spine.Unity {
/// <summary>
/// Add this component to a SkeletonMecanim GameObject
/// to turn motion of a selected root bone into Transform or RigidBody motion.
/// Local bone translation movement is used as motion.
/// All top-level bones of the skeleton are moved to compensate the root
/// motion bone location, keeping the distance relationship between bones intact.
/// </summary>
/// <remarks>
/// Only compatible with <c>SkeletonMecanim</c>.
/// For <c>SkeletonAnimation</c> or <c>SkeletonGraphic</c> please use
/// <see cref="SkeletonRootMotion">SkeletonRootMotion</see> instead.
/// </remarks>
public class SkeletonMecanimRootMotion : SkeletonRootMotionBase {
#region Inspector
const int DefaultMecanimLayerFlags = -1;
public int mecanimLayerFlags = DefaultMecanimLayerFlags;
#endregion
protected Vector2 movementDelta;
SkeletonMecanim skeletonMecanim;
public SkeletonMecanim SkeletonMecanim {
get {
return skeletonMecanim ? skeletonMecanim : skeletonMecanim = GetComponent<SkeletonMecanim>();
}
}
protected override void Reset () {
base.Reset();
mecanimLayerFlags = DefaultMecanimLayerFlags;
}
protected override void Start () {
base.Start();
skeletonMecanim = GetComponent<SkeletonMecanim>();
if (skeletonMecanim) {
skeletonMecanim.Translator.OnClipApplied -= OnClipApplied;
skeletonMecanim.Translator.OnClipApplied += OnClipApplied;
}
}
void OnClipApplied(Spine.Animation clip, int layerIndex, float weight,
float time, float lastTime, bool playsBackward) {
if (((mecanimLayerFlags & 1<<layerIndex) == 0) || weight == 0)
return;
var timeline = clip.FindTranslateTimelineForBone(rootMotionBoneIndex);
if (timeline != null) {
if (!playsBackward)
movementDelta += weight * GetTimelineMovementDelta(lastTime, time, timeline, clip);
else
movementDelta -= weight * GetTimelineMovementDelta(time, lastTime, timeline, clip);
}
}
protected override Vector2 CalculateAnimationsMovementDelta () {
// Note: movement delta is not gather after animation but
// in OnClipApplied after every applied animation.
Vector2 result = movementDelta;
movementDelta = Vector2.zero;
return result;
}
}
}

View File

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

View File

@ -0,0 +1,143 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEngine;
using System.Collections.Generic;
using Spine.Unity.AnimationTools;
namespace Spine.Unity {
/// <summary>
/// Add this component to a SkeletonAnimation or SkeletonGraphic GameObject
/// to turn motion of a selected root bone into Transform or RigidBody motion.
/// Local bone translation movement is used as motion.
/// All top-level bones of the skeleton are moved to compensate the root
/// motion bone location, keeping the distance relationship between bones intact.
/// </summary>
/// <remarks>
/// Only compatible with SkeletonAnimation (or other components that implement
/// ISkeletonComponent, ISkeletonAnimation and IAnimationStateComponent).
/// For <c>SkeletonMecanim</c> please use
/// <see cref="SkeletonMecanimRootMotion">SkeletonMecanimRootMotion</see> instead.
/// </remarks>
public class SkeletonRootMotion : SkeletonRootMotionBase {
#region Inspector
const int DefaultAnimationTrackFlags = -1;
public int animationTrackFlags = DefaultAnimationTrackFlags;
#endregion
AnimationState animationState;
Canvas canvas;
protected override float AdditionalScale {
get {
return canvas ? canvas.referencePixelsPerUnit: 1.0f;
}
}
protected override void Reset () {
base.Reset();
animationTrackFlags = DefaultAnimationTrackFlags;
}
protected override void Start () {
base.Start();
var animstateComponent = skeletonComponent as IAnimationStateComponent;
this.animationState = (animstateComponent != null) ? animstateComponent.AnimationState : null;
if (this.GetComponent<CanvasRenderer>() != null) {
canvas = this.GetComponentInParent<Canvas>();
}
}
protected override Vector2 CalculateAnimationsMovementDelta () {
Vector2 localDelta = Vector2.zero;
int trackCount = animationState.Tracks.Count;
for (int trackIndex = 0; trackIndex < trackCount; ++trackIndex) {
// note: animationTrackFlags != -1 below covers trackIndex >= 32,
// with -1 corresponding to entry "everything" of the dropdown list.
if (animationTrackFlags != -1 && (animationTrackFlags & 1 << trackIndex) == 0)
continue;
TrackEntry track = animationState.GetCurrent(trackIndex);
TrackEntry next = null;
while (track != null) {
var animation = track.Animation;
var timeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex);
if (timeline != null) {
var currentDelta = GetTrackMovementDelta(track, timeline, animation, next);
localDelta += currentDelta;
}
// Traverse mixingFrom chain.
next = track;
track = track.mixingFrom;
}
}
return localDelta;
}
Vector2 GetTrackMovementDelta (TrackEntry track, TranslateTimeline timeline,
Animation animation, TrackEntry next) {
float start = track.animationLast;
float end = track.AnimationTime;
Vector2 currentDelta = GetTimelineMovementDelta(start, end, timeline, animation);
ApplyMixAlphaToDelta(ref currentDelta, next, track);
return currentDelta;
}
void ApplyMixAlphaToDelta (ref Vector2 currentDelta, TrackEntry next, TrackEntry track) {
// Apply mix alpha to the delta position (based on AnimationState.cs).
float mix;
if (next != null) {
if (next.mixDuration == 0) { // Single frame mix to undo mixingFrom changes.
mix = 1;
}
else {
mix = next.mixTime / next.mixDuration;
if (mix > 1) mix = 1;
}
float mixAndAlpha = track.alpha * next.interruptAlpha * (1 - mix);
currentDelta *= mixAndAlpha;
}
else {
if (track.mixDuration == 0) {
mix = 1;
}
else {
mix = track.alpha * (track.mixTime / track.mixDuration);
if (mix > 1) mix = 1;
}
currentDelta *= mix;
}
}
}
}

View File

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

View File

@ -0,0 +1,184 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEngine;
using System.Collections.Generic;
using Spine.Unity.AnimationTools;
namespace Spine.Unity {
/// <summary>
/// Base class for skeleton root motion components.
/// </summary>
abstract public class SkeletonRootMotionBase : MonoBehaviour {
#region Inspector
[SpineBone]
[SerializeField]
protected string rootMotionBoneName = "root";
public bool transformPositionX = true;
public bool transformPositionY = true;
[Header("Optional")]
public Rigidbody2D rigidBody2D;
public Rigidbody rigidBody;
public bool UsesRigidbody {
get { return rigidBody != null || rigidBody2D != null; }
}
#endregion
protected ISkeletonComponent skeletonComponent;
protected Bone rootMotionBone;
protected int rootMotionBoneIndex;
protected List<Bone> topLevelBones = new List<Bone>();
protected Vector2 rigidbodyDisplacement;
protected virtual void Reset () {
FindRigidbodyComponent();
}
protected virtual void Start () {
skeletonComponent = GetComponent<ISkeletonComponent>();
GatherTopLevelBones();
SetRootMotionBone(rootMotionBoneName);
var skeletonAnimation = skeletonComponent as ISkeletonAnimation;
if (skeletonAnimation != null)
skeletonAnimation.UpdateLocal += HandleUpdateLocal;
}
abstract protected Vector2 CalculateAnimationsMovementDelta ();
protected virtual float AdditionalScale { get { return 1.0f; } }
protected Vector2 GetTimelineMovementDelta (float startTime, float endTime,
TranslateTimeline timeline, Animation animation) {
Vector2 currentDelta;
if (startTime > endTime) // Looped
currentDelta = (timeline.Evaluate(animation.duration) - timeline.Evaluate(startTime))
+ (timeline.Evaluate(endTime) - timeline.Evaluate(0));
else if (startTime != endTime) // Non-looped
currentDelta = timeline.Evaluate(endTime) - timeline.Evaluate(startTime);
else
currentDelta = Vector2.zero;
return currentDelta;
}
void GatherTopLevelBones () {
topLevelBones.Clear();
var skeleton = skeletonComponent.Skeleton;
foreach (var bone in skeleton.Bones) {
if (bone.Parent == null)
topLevelBones.Add(bone);
}
}
public void SetRootMotionBone (string name) {
var skeleton = skeletonComponent.Skeleton;
int index = skeleton.FindBoneIndex(name);
if (index >= 0) {
this.rootMotionBoneIndex = index;
this.rootMotionBone = skeleton.bones.Items[index];
}
else {
Debug.Log("Bone named \"" + name + "\" could not be found.");
this.rootMotionBoneIndex = 0;
this.rootMotionBone = skeleton.RootBone;
}
}
void HandleUpdateLocal (ISkeletonAnimation animatedSkeletonComponent) {
if (!this.isActiveAndEnabled)
return; // Root motion is only applied when component is enabled.
var movementDelta = CalculateAnimationsMovementDelta();
AdjustMovementDeltaToConfiguration(ref movementDelta, animatedSkeletonComponent.Skeleton);
ApplyRootMotion(movementDelta);
}
void AdjustMovementDeltaToConfiguration (ref Vector2 localDelta, Skeleton skeleton) {
if (skeleton.ScaleX < 0) localDelta.x = -localDelta.x;
if (skeleton.ScaleY < 0) localDelta.y = -localDelta.y;
if (!transformPositionX) localDelta.x = 0f;
if (!transformPositionY) localDelta.y = 0f;
}
void ApplyRootMotion (Vector2 localDelta) {
localDelta *= AdditionalScale;
// Apply root motion to Transform or RigidBody;
if (UsesRigidbody) {
rigidbodyDisplacement += (Vector2)transform.TransformVector(localDelta);
// Accumulated displacement is applied on the next Physics update (FixedUpdate)
}
else {
transform.position += transform.TransformVector(localDelta);
}
// Move top level bones in opposite direction of the root motion bone
foreach (var topLevelBone in topLevelBones) {
if (transformPositionX) topLevelBone.x -= rootMotionBone.x;
if (transformPositionY) topLevelBone.y -= rootMotionBone.y;
}
}
protected virtual void FixedUpdate () {
if (!this.isActiveAndEnabled)
return; // Root motion is only applied when component is enabled.
if(rigidBody2D != null) {
rigidBody2D.MovePosition(new Vector2(transform.position.x, transform.position.y)
+ rigidbodyDisplacement);
}
if (rigidBody != null) {
rigidBody.MovePosition(transform.position
+ new Vector3(rigidbodyDisplacement.x, rigidbodyDisplacement.y, 0));
}
rigidbodyDisplacement = Vector2.zero;
}
protected virtual void OnDisable () {
rigidbodyDisplacement = Vector2.zero;
}
protected void FindRigidbodyComponent () {
rigidBody2D = this.GetComponent<Rigidbody2D>();
if (!rigidBody2D)
rigidBody = this.GetComponent<Rigidbody>();
if (!rigidBody2D && !rigidBody) {
rigidBody2D = this.GetComponentInParent<Rigidbody2D>();
if (!rigidBody2D)
rigidBody = this.GetComponentInParent<Rigidbody>();
}
}
}
}

View File

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

View File

@ -210,6 +210,13 @@ namespace Spine.Unity {
if (allowMultipleCanvasRenderers) canvasRenderer.Clear();
}
protected override void OnDisable () {
base.OnDisable();
foreach (var canvasRenderer in canvasRenderers) {
canvasRenderer.Clear();
}
}
public virtual void Update () {
#if UNITY_EDITOR
if (!Application.isPlaying) {
@ -426,7 +433,7 @@ namespace Spine.Unity {
ScaleY = this.initialFlipY ? -1 : 1
};
meshBuffers = new DoubleBuffered<MeshRendererBuffers.SmartMesh>();
InitMeshBuffers();
baseTexture = skeletonDataAsset.atlasAssets[0].PrimaryMaterial.mainTexture;
canvasRenderer.SetTexture(this.mainTexture); // Needed for overwriting initializations.
@ -478,6 +485,16 @@ namespace Spine.Unity {
}
#endregion
protected void InitMeshBuffers () {
if (meshBuffers != null) {
meshBuffers.GetNext().Clear();
meshBuffers.GetNext().Clear();
}
else {
meshBuffers = new DoubleBuffered<MeshRendererBuffers.SmartMesh>();
}
}
protected void UpdateMeshSingleCanvasRenderer () {
if (canvasRenderers.Count > 0)
DisableUnusedCanvasRenderers(usedCount : 0);

View File

@ -125,10 +125,17 @@ namespace Spine.Unity {
public class MecanimTranslator {
#region Inspector
public bool autoReset = true;
public bool useCustomMixMode = true;
public MixMode[] layerMixModes = new MixMode[0];
public MixBlend[] layerBlendModes = new MixBlend[0];
#endregion
public delegate void OnClipAppliedDelegate (Spine.Animation clip, int layerIndex, float weight,
float time, float lastTime, bool playsBackward);
protected event OnClipAppliedDelegate _OnClipApplied;
public event OnClipAppliedDelegate OnClipApplied { add { _OnClipApplied += value; } remove { _OnClipApplied -= value; } }
public enum MixMode { AlwaysMix, MixNext, Hard }
readonly Dictionary<int, Spine.Animation> animationTable = new Dictionary<int, Spine.Animation>(IntEqualityComparer.Instance);
@ -157,6 +164,26 @@ namespace Spine.Unity {
Animator animator;
public Animator Animator { get { return this.animator; } }
public int MecanimLayerCount {
get {
if (!animator)
return 0;
return animator.layerCount;
}
}
public string[] MecanimLayerNames {
get {
if (!animator)
return new string[0];
string[] layerNames = new string[animator.layerCount];
for (int i = 0; i < animator.layerCount; ++i) {
layerNames[i] = animator.GetLayerName(i);
}
return layerNames;
}
}
public void Initialize(Animator animator, SkeletonDataAsset skeletonDataAsset) {
this.animator = animator;
@ -171,24 +198,87 @@ namespace Spine.Unity {
ClearClipInfosForLayers();
}
public void Apply (Skeleton skeleton) {
if (layerMixModes.Length < animator.layerCount) {
System.Array.Resize<MixMode>(ref layerMixModes, animator.layerCount);
layerMixModes[animator.layerCount-1] = MixMode.MixNext;
}
private bool ApplyAnimation (Skeleton skeleton, AnimatorClipInfo info, AnimatorStateInfo stateInfo,
int layerIndex, float layerWeight, MixBlend layerBlendMode, bool useClipWeight1 = false) {
float weight = info.weight * layerWeight;
if (weight == 0)
return false;
var clip = GetAnimation(info.clip);
if (clip == null)
return false;
var time = AnimationTime(stateInfo.normalizedTime, info.clip.length,
info.clip.isLooping, stateInfo.speed < 0);
weight = useClipWeight1 ? layerWeight : weight;
clip.Apply(skeleton, 0, time, info.clip.isLooping, null,
weight, layerBlendMode, MixDirection.In);
if (_OnClipApplied != null)
OnClipAppliedCallback(clip, stateInfo, layerIndex, time, info.clip.isLooping, weight);
return true;
}
private bool ApplyInterruptionAnimation (Skeleton skeleton,
bool interpolateWeightTo1, AnimatorClipInfo info, AnimatorStateInfo stateInfo,
int layerIndex, float layerWeight, MixBlend layerBlendMode, float interruptingClipTimeAddition,
bool useClipWeight1 = false) {
float clipWeight = interpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight;
float weight = clipWeight * layerWeight;
if (weight == 0)
return false;
var clip = GetAnimation(info.clip);
if (clip == null)
return false;
var time = AnimationTime(stateInfo.normalizedTime + interruptingClipTimeAddition,
info.clip.length, stateInfo.speed < 0);
weight = useClipWeight1 ? layerWeight : weight;
clip.Apply(skeleton, 0, time, info.clip.isLooping, null,
weight, layerBlendMode, MixDirection.In);
if (_OnClipApplied != null) {
OnClipAppliedCallback(clip, stateInfo, layerIndex, time, info.clip.isLooping, weight);
}
return true;
}
private void OnClipAppliedCallback (Spine.Animation clip, AnimatorStateInfo stateInfo,
int layerIndex, float time, bool isLooping, float weight) {
float clipDuration = clip.duration == 0 ? 1 : clip.duration;
float speedFactor = stateInfo.speedMultiplier * stateInfo.speed;
float lastTime = time - (Time.deltaTime * speedFactor);
if (isLooping && clip.duration != 0) {
time %= clip.duration;
lastTime %= clip.duration;
}
_OnClipApplied(clip, layerIndex, weight, time, lastTime, speedFactor < 0);
}
public void Apply (Skeleton skeleton) {
#if UNITY_EDITOR
if (!Application.isPlaying) {
GetLayerBlendModes();
}
#endif
if (layerMixModes.Length < animator.layerCount) {
int oldSize = layerMixModes.Length;
System.Array.Resize<MixMode>(ref layerMixModes, animator.layerCount);
for (int layer = oldSize; layer < animator.layerCount; ++layer) {
bool isAdditiveLayer = false;
if (layer < layerBlendModes.Length)
isAdditiveLayer = layerBlendModes[layer] == MixBlend.Add;
layerMixModes[layer] = isAdditiveLayer ? MixMode.MixNext : MixMode.AlwaysMix;
}
}
InitClipInfosForLayers();
for (int layer = 0, n = animator.layerCount; layer < n; layer++) {
GetStateUpdatesFromAnimator(layer);
}
//skeleton.Update(Time.deltaTime); // Doesn't actually do anything, currently. (Spine 3.6).
// Clear Previous
if (autoReset) {
var previousAnimations = this.previousAnimations;
@ -257,56 +347,41 @@ namespace Spine.Unity {
int clipInfoCount, nextClipInfoCount, interruptingClipInfoCount;
IList<AnimatorClipInfo> clipInfo, nextClipInfo, interruptingClipInfo;
bool shallInterpolateWeightTo1;
bool interpolateWeightTo1;
GetAnimatorClipInfos(layer, out isInterruptionActive, out clipInfoCount, out nextClipInfoCount, out interruptingClipInfoCount,
out clipInfo, out nextClipInfo, out interruptingClipInfo, out shallInterpolateWeightTo1);
out clipInfo, out nextClipInfo, out interruptingClipInfo, out interpolateWeightTo1);
MixMode mode = layerMixModes[layer];
MixBlend layerBlendMode = (layer < layerBlendModes.Length) ? layerBlendModes[layer] : MixBlend.Replace;
MixMode mode = GetMixMode(layer, layerBlendMode);
if (mode == MixMode.AlwaysMix) {
// Always use Mix instead of Applying the first non-zero weighted clip.
for (int c = 0; c < clipInfoCount; c++) {
var info = clipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue;
var clip = GetAnimation(info.clip);
if (clip != null)
clip.Apply(skeleton, 0, AnimationTime(stateInfo.normalizedTime, info.clip.length, info.clip.isLooping, stateInfo.speed < 0), info.clip.isLooping, null, weight, layerBlendMode, MixDirection.In);
ApplyAnimation(skeleton, clipInfo[c], stateInfo, layer, layerWeight, layerBlendMode);
}
if (hasNext) {
for (int c = 0; c < nextClipInfoCount; c++) {
var info = nextClipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue;
var clip = GetAnimation(info.clip);
if (clip != null)
clip.Apply(skeleton, 0, AnimationTime(nextStateInfo.normalizedTime, info.clip.length, nextStateInfo.speed < 0), info.clip.isLooping, null, weight, layerBlendMode, MixDirection.In);
ApplyAnimation(skeleton, nextClipInfo[c], nextStateInfo, layer, layerWeight, layerBlendMode);
}
}
if (isInterruptionActive) {
for (int c = 0; c < interruptingClipInfoCount; c++)
{
var info = interruptingClipInfo[c];
float clipWeight = shallInterpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight;
float weight = clipWeight * layerWeight; if (weight == 0) continue;
var clip = GetAnimation(info.clip);
if (clip != null)
clip.Apply(skeleton, 0, AnimationTime(interruptingStateInfo.normalizedTime + interruptingClipTimeAddition, info.clip.length, interruptingStateInfo.speed < 0),
info.clip.isLooping, null, weight, layerBlendMode, MixDirection.In);
ApplyInterruptionAnimation(skeleton, interpolateWeightTo1,
interruptingClipInfo[c], interruptingStateInfo,
layer, layerWeight, layerBlendMode, interruptingClipTimeAddition);
}
}
} else { // case MixNext || Hard
// Apply first non-zero weighted clip
int c = 0;
for (; c < clipInfoCount; c++) {
var info = clipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue;
var clip = GetAnimation(info.clip);
if (clip != null)
clip.Apply(skeleton, 0, AnimationTime(stateInfo.normalizedTime, info.clip.length, info.clip.isLooping, stateInfo.speed < 0), info.clip.isLooping, null, 1f, layerBlendMode, MixDirection.In);
if (!ApplyAnimation(skeleton, clipInfo[c], stateInfo, layer, layerWeight, layerBlendMode, useClipWeight1:true))
continue;
++c; break;
}
// Mix the rest
for (; c < clipInfoCount; c++) {
var info = clipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue;
var clip = GetAnimation(info.clip);
if (clip != null)
clip.Apply(skeleton, 0, AnimationTime(stateInfo.normalizedTime, info.clip.length, info.clip.isLooping, stateInfo.speed < 0), info.clip.isLooping, null, weight, layerBlendMode, MixDirection.In);
ApplyAnimation(skeleton, clipInfo[c], stateInfo, layer, layerWeight, layerBlendMode);
}
c = 0;
@ -314,19 +389,15 @@ namespace Spine.Unity {
// Apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights)
if (mode == MixMode.Hard) {
for (; c < nextClipInfoCount; c++) {
var info = nextClipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue;
var clip = GetAnimation(info.clip);
if (clip != null)
clip.Apply(skeleton, 0, AnimationTime(nextStateInfo.normalizedTime, info.clip.length, nextStateInfo.speed < 0), info.clip.isLooping, null, 1f, layerBlendMode, MixDirection.In);
if (!ApplyAnimation(skeleton, nextClipInfo[c], nextStateInfo, layer, layerWeight, layerBlendMode, useClipWeight1:true))
continue;
++c; break;
}
}
// Mix the rest
for (; c < nextClipInfoCount; c++) {
var info = nextClipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue;
var clip = GetAnimation(info.clip);
if (clip != null)
clip.Apply(skeleton, 0, AnimationTime(nextStateInfo.normalizedTime, info.clip.length, nextStateInfo.speed < 0), info.clip.isLooping, null, weight, layerBlendMode, MixDirection.In);
if (!ApplyAnimation(skeleton, nextClipInfo[c], nextStateInfo, layer, layerWeight, layerBlendMode))
continue;
}
}
@ -335,23 +406,19 @@ namespace Spine.Unity {
// Apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights)
if (mode == MixMode.Hard) {
for (; c < interruptingClipInfoCount; c++) {
var info = interruptingClipInfo[c];
float clipWeight = shallInterpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight;
float weight = clipWeight * layerWeight; if (weight == 0) continue;
var clip = GetAnimation(info.clip);
if (clip != null)
clip.Apply(skeleton, 0, AnimationTime(interruptingStateInfo.normalizedTime + interruptingClipTimeAddition, info.clip.length, interruptingStateInfo.speed < 0), info.clip.isLooping, null, 1f, layerBlendMode, MixDirection.In);
++c; break;
if (ApplyInterruptionAnimation(skeleton, interpolateWeightTo1,
interruptingClipInfo[c], interruptingStateInfo,
layer, layerWeight, layerBlendMode, interruptingClipTimeAddition, useClipWeight1:true)) {
++c; break;
}
}
}
// Mix the rest
for (; c < interruptingClipInfoCount; c++) {
var info = interruptingClipInfo[c];
float clipWeight = shallInterpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight;
float weight = clipWeight * layerWeight; if (weight == 0) continue;
var clip = GetAnimation(info.clip);
if (clip != null)
clip.Apply(skeleton, 0, AnimationTime(interruptingStateInfo.normalizedTime + interruptingClipTimeAddition, info.clip.length, interruptingStateInfo.speed < 0), info.clip.isLooping, null, weight, layerBlendMode, MixDirection.In);
ApplyInterruptionAnimation(skeleton, interpolateWeightTo1,
interruptingClipInfo[c], interruptingStateInfo,
layer, layerWeight, layerBlendMode, interruptingClipTimeAddition);
}
}
}
@ -359,9 +426,7 @@ namespace Spine.Unity {
}
static float AnimationTime (float normalizedTime, float clipLength, bool loop, bool reversed) {
if (reversed)
normalizedTime = (1 - normalizedTime + (int)normalizedTime) + (int)normalizedTime;
float time = normalizedTime * clipLength;
float time = AnimationTime(normalizedTime, clipLength, reversed);
if (loop) return time;
const float EndSnapEpsilon = 1f / 30f; // Workaround for end-duration keys not being applied.
return (clipLength - time < EndSnapEpsilon) ? clipLength : time; // return a time snapped to clipLength;
@ -398,7 +463,24 @@ namespace Spine.Unity {
}
}
#if UNITY_EDITOR
private MixMode GetMixMode (int layer, MixBlend layerBlendMode) {
if (useCustomMixMode) {
MixMode mode = layerMixModes[layer];
// Note: at additive blending it makes no sense to use constant weight 1 at a fadeout anim add1 as
// with override layers, so we use AlwaysMix instead to use the proper weights.
// AlwaysMix leads to the expected result = lower_layer + lerp(add1, add2, transition_weight).
if (layerBlendMode == MixBlend.Add && mode == MixMode.MixNext) {
mode = MixMode.AlwaysMix;
layerMixModes[layer] = mode;
}
return mode;
}
else {
return layerBlendMode == MixBlend.Add ? MixMode.AlwaysMix : MixMode.MixNext;
}
}
#if UNITY_EDITOR
void GetLayerBlendModes() {
if (layerBlendModes.Length < animator.layerCount) {
System.Array.Resize<MixBlend>(ref layerBlendModes, animator.layerCount);

View File

@ -191,8 +191,10 @@ namespace Spine.Unity {
skeletonRenderer.LateUpdate();
foreach (var s in partsRenderers)
s.ClearMesh();
foreach (var partsRenderer in partsRenderers) {
if (partsRenderer != null)
partsRenderer.ClearMesh();
}
}
MaterialPropertyBlock copiedBlock;
@ -221,6 +223,8 @@ namespace Spine.Unity {
int rendererIndex = 0;
var currentRenderer = partsRenderers[rendererIndex];
for (int si = 0, start = 0; si <= lastSubmeshInstruction; si++) {
if (currentRenderer == null)
continue;
if (submeshInstructionsItems[si].forceSeparate || si == lastSubmeshInstruction) {
// Apply properties
var meshGenerator = currentRenderer.MeshGenerator;
@ -245,7 +249,9 @@ namespace Spine.Unity {
// Clear extra renderers if they exist.
for (; rendererIndex < rendererCount; rendererIndex++) {
partsRenderers[rendererIndex].ClearMesh();
currentRenderer = partsRenderers[rendererIndex];
if (currentRenderer != null)
partsRenderers[rendererIndex].ClearMesh();
}
}

View File

@ -529,7 +529,7 @@ namespace Spine.Unity {
separatorSlots.Add(slot);
}
#if UNITY_EDITOR
else
else if (!string.IsNullOrEmpty(separatorSlotNames[i]))
{
Debug.LogWarning(separatorSlotNames[i] + " is not a slot in " + skeletonDataAsset.skeletonJSON.name);
}

View File

@ -503,7 +503,10 @@ namespace Spine.Unity {
for (int slotIndex = instruction.startSlot; slotIndex < instruction.endSlot; slotIndex++) {
var slot = drawOrderItems[slotIndex];
if (!slot.bone.active) continue;
if (!slot.bone.active) {
clipper.ClipEnd(slot);
continue;
}
var attachment = slot.attachment;
float z = zSpacing * slotIndex;

View File

@ -11,8 +11,6 @@ Shader "Spine/Outline/Sprite/Pixel Lit"
_BumpMap ("Normal Map", 2D) = "bump" {}
[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
[PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {}
[PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0
_EmissionColor("Color", Color) = (0,0,0,0)
_EmissionMap("Emission", 2D) = "white" {}

View File

@ -8,8 +8,6 @@ Shader "Spine/Outline/Sprite/Unlit"
_Color ("Color", Color) = (1,1,1,1)
[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
[PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {}
[PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0
_ZWrite ("Depth Write", Float) = 0.0
_Cutoff ("Depth alpha cutoff", Range(0,1)) = 0.0

View File

@ -11,8 +11,6 @@ Shader "Spine/Outline/Sprite/Vertex Lit"
_BumpMap ("Normal Map", 2D) = "bump" {}
[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
[PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {}
[PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0
_EmissionColor("Color", Color) = (0,0,0,0)
_EmissionMap("Emission", 2D) = "white" {}

View File

@ -1,7 +1,5 @@
// Upgrade NOTE: upgraded instancing buffer 'PerDrawSprite' to new syntax.
// Upgrade NOTE: upgraded instancing buffer 'PerDrawSprite' to new syntax.
#ifndef SHADER_SHARED_INCLUDED
#define SHADER_SHARED_INCLUDED
@ -13,28 +11,6 @@
#include "UnityCG.cginc"
#endif
#ifdef UNITY_INSTANCING_ENABLED
UNITY_INSTANCING_BUFFER_START(PerDrawSprite)
// SpriteRenderer.Color while Non-Batched/Instanced.
fixed4 unity_SpriteRendererColorArray[UNITY_INSTANCED_ARRAY_SIZE];
// this could be smaller but that's how bit each entry is regardless of type
float4 unity_SpriteFlipArray[UNITY_INSTANCED_ARRAY_SIZE];
UNITY_INSTANCING_BUFFER_END(PerDrawSprite)
#define _RendererColor unity_SpriteRendererColorArray[unity_InstanceID]
#define _Flip unity_SpriteFlipArray[unity_InstanceID]
#endif // instancing
CBUFFER_START(UnityPerDrawSprite)
#ifndef UNITY_INSTANCING_ENABLED
fixed4 _RendererColor;
float4 _Flip;
#endif
float _EnableExternalAlpha;
CBUFFER_END
////////////////////////////////////////
// Space functions
//
@ -376,11 +352,6 @@ inline fixed4 applyFog(fixed4 pixel, float fogCoordOrFactorAtLWRP)
uniform sampler2D _MainTex;
#if ETC1_EXTERNAL_ALPHA
//External alpha texture for ETC1 compression
uniform sampler2D _AlphaTex;
#endif //ETC1_EXTERNAL_ALPHA
#if _TEXTURE_BLEND
uniform sampler2D _BlendTex;
uniform float _BlendAmount;
@ -401,11 +372,6 @@ inline fixed4 calculateTexturePixel(float2 texcoord)
pixel = tex2D(_MainTex, texcoord);
#endif // !_TEXTURE_BLEND
#if ETC1_EXTERNAL_ALPHA
fixed4 alpha = tex2D (_AlphaTex, texcoord);
pixel.a = lerp (pixel.a, alpha.r, _EnableExternalAlpha);
#endif
#if defined(_COLOR_ADJUST)
pixel = adjustColor(pixel);
#endif // _COLOR_ADJUST

View File

@ -9,8 +9,6 @@ Shader "Spine/Sprite/Pixel Lit"
_BumpMap ("Normal Map", 2D) = "bump" {}
[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
[PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {}
[PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0
_EmissionColor("Color", Color) = (0,0,0,0)
_EmissionMap("Emission", 2D) = "white" {}

View File

@ -6,8 +6,6 @@ Shader "Spine/Sprite/Unlit"
_Color ("Color", Color) = (1,1,1,1)
[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
[PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {}
[PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0
_ZWrite ("Depth Write", Float) = 0.0
_Cutoff ("Depth alpha cutoff", Range(0,1)) = 0.0

View File

@ -9,8 +9,6 @@ Shader "Spine/Sprite/Vertex Lit"
_BumpMap ("Normal Map", 2D) = "bump" {}
[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
[PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {}
[PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0
_EmissionColor("Color", Color) = (0,0,0,0)
_EmissionMap("Emission", 2D) = "white" {}

View File

@ -27,10 +27,16 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2019_3_OR_NEWER
#define CONFIGURABLE_ENTER_PLAY_MODE
#endif
using UnityEngine;
using System.Collections.Generic;
using System;
namespace Spine.Unity.AttachmentTools {
public static class AtlasUtilities {
@ -41,6 +47,14 @@ namespace Spine.Unity.AttachmentTools {
const int NonrenderingRegion = -1;
#if CONFIGURABLE_ENTER_PLAY_MODE
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
static void Init () {
// handle disabled domain reload
AtlasUtilities.ClearCache();
}
#endif
public static AtlasRegion ToAtlasRegion (this Texture2D t, Material materialPropertySource, float scale = DefaultScale) {
return t.ToAtlasRegion(materialPropertySource.shader, scale, materialPropertySource);
}

View File

@ -442,7 +442,7 @@ namespace Spine {
}
}
public static class SkeletonExtensions {
public static class SpineSkeletonExtensions {
public static bool IsWeighted (this VertexAttachment va) {
return va.bones != null && va.bones.Length > 0;
}

View File

@ -0,0 +1,91 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEngine;
using System.Collections.Generic;
using System.Collections;
namespace Spine.Unity.AnimationTools {
public static class TimelineExtensions {
/// <summary>Evaluates the resulting value of a TranslateTimeline at a given time.
/// SkeletonData can be accessed from Skeleton.Data or from SkeletonDataAsset.GetSkeletonData.
/// If no SkeletonData is given, values are computed relative to setup pose instead of local-absolute.</summary>
public static Vector2 Evaluate (this TranslateTimeline timeline, float time, SkeletonData skeletonData = null) {
const int PREV_TIME = -3, PREV_X = -2, PREV_Y = -1;
const int X = 1, Y = 2;
var frames = timeline.frames;
if (time < frames[0]) return Vector2.zero;
float x, y;
if (time >= frames[frames.Length - TranslateTimeline.ENTRIES]) { // Time is after last frame.
x = frames[frames.Length + PREV_X];
y = frames[frames.Length + PREV_Y];
}
else {
// Interpolate between the previous frame and the current frame.
int frame = Animation.BinarySearch(frames, time, TranslateTimeline.ENTRIES);
x = frames[frame + PREV_X];
y = frames[frame + PREV_Y];
float frameTime = frames[frame];
float percent = timeline.GetCurvePercent(frame / TranslateTimeline.ENTRIES - 1,
1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
x += (frames[frame + X] - x) * percent;
y += (frames[frame + Y] - y) * percent;
}
Vector2 xy = new Vector2(x, y);
if (skeletonData == null) {
return xy;
}
else {
var boneData = skeletonData.bones.Items[timeline.boneIndex];
return xy + new Vector2(boneData.x, boneData.y);
}
}
/// <summary>Gets the translate timeline for a given boneIndex.
/// You can get the boneIndex using SkeletonData.FindBoneIndex.
/// The root bone is always boneIndex 0.
/// This will return null if a TranslateTimeline is not found.</summary>
public static TranslateTimeline FindTranslateTimelineForBone (this Animation a, int boneIndex) {
foreach (var timeline in a.timelines) {
if (timeline.GetType().IsSubclassOf(typeof(TranslateTimeline)))
continue;
var translateTimeline = timeline as TranslateTimeline;
if (translateTimeline != null && translateTimeline.boneIndex == boneIndex)
return translateTimeline;
}
return null;
}
}
}

View File

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

View File

@ -9,8 +9,6 @@ Shader "Lightweight Render Pipeline/Spine/Sprite"
_BumpMap("Normal Map", 2D) = "bump" {}
[MaterialToggle] PixelSnap("Pixel snap", Float) = 0
[PerRendererData] _AlphaTex("External Alpha", 2D) = "white" {}
[PerRendererData] _EnableExternalAlpha("Enable External Alpha", Float) = 0
_EmissionColor("Color", Color) = (0,0,0,0)
_EmissionMap("Emission", 2D) = "white" {}

View File

@ -119,6 +119,7 @@
Tags { "LightMode" = "NormalsRendering"}
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
HLSLPROGRAM
#pragma prefer_hlslcc gles

View File

@ -10,8 +10,6 @@ Shader "Universal Render Pipeline/2D/Spine/Sprite"
_BumpMap("Normal Map", 2D) = "bump" {}
[MaterialToggle] PixelSnap("Pixel snap", Float) = 0
[PerRendererData] _AlphaTex("External Alpha", 2D) = "white" {}
[PerRendererData] _EnableExternalAlpha("Enable External Alpha", Float) = 0
_EmissionColor("Color", Color) = (0,0,0,0)
_EmissionMap("Emission", 2D) = "white" {}
@ -116,6 +114,7 @@ Shader "Universal Render Pipeline/2D/Spine/Sprite"
Blend SrcAlpha OneMinusSrcAlpha
Cull[_Cull]
ZWrite[_ZWrite]
HLSLPROGRAM
#pragma prefer_hlslcc gles

View File

@ -9,8 +9,6 @@ Shader "Universal Render Pipeline/Spine/Sprite"
_BumpMap("Normal Map", 2D) = "bump" {}
[MaterialToggle] PixelSnap("Pixel snap", Float) = 0
[PerRendererData] _AlphaTex("External Alpha", 2D) = "white" {}
[PerRendererData] _EnableExternalAlpha("Enable External Alpha", Float) = 0
_EmissionColor("Color", Color) = (0,0,0,0)
_EmissionMap("Emission", 2D) = "white" {}