mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 14:24:53 +08:00
Merge branch '3.8' into 4.0-beta
This commit is contained in:
commit
33e2aea7ff
@ -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`.
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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>();
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -4,7 +4,6 @@ $(function () {
|
||||
alert("Error: " + message + "\n" + "URL:" + url + "\nLine: " + lineNo);
|
||||
}
|
||||
|
||||
|
||||
spineDemos.init();
|
||||
spineDemos.assetManager = new spine.SharedAssetManager("assets/");
|
||||
|
||||
|
||||
157
spine-ts/webgl/example/barebones.html
Normal file
157
spine-ts/webgl/example/barebones.html
Normal 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>
|
||||
@ -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>
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4613924c50d66cf458f0db803776dd2f
|
||||
timeCreated: 1593175106
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f2cba83baf6afdf44a996e40017c6325
|
||||
timeCreated: 1593175106
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4836100aed984c4a9af11d39c63cb6b
|
||||
timeCreated: 1593183609
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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");
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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 () {
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2b957aa69dae9f948bacdeec549d28ea
|
||||
folderAsset: yes
|
||||
timeCreated: 1593173800
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95813afe390494344a6ce2cbc8bfb7d1
|
||||
timeCreated: 1592849332
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f21c9538588898a45a3da22bf4779ab3
|
||||
timeCreated: 1591121072
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fc23a4f220b20024ab0592719f92587d
|
||||
timeCreated: 1592849332
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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" {}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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" {}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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" {}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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" {}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 07807fefbff25484ba41b1d16911fb0e
|
||||
timeCreated: 1591974498
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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" {}
|
||||
|
||||
@ -119,6 +119,7 @@
|
||||
Tags { "LightMode" = "NormalsRendering"}
|
||||
|
||||
Blend SrcAlpha OneMinusSrcAlpha
|
||||
ZWrite Off
|
||||
|
||||
HLSLPROGRAM
|
||||
#pragma prefer_hlslcc gles
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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" {}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user