Initial commit: Client Doc docs Server Tools

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ud18010
2026-07-10 10:24:29 +08:00
co-authored by Cursor
commit 7e35d8da31
3374 changed files with 680813 additions and 0 deletions
@@ -0,0 +1,102 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2026, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
namespace Spine {
/// <summary>
/// An <see cref="AttachmentLoader"/> that configures attachments using texture regions from an <see cref="Atlas"/>.
/// See <a href='http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data'>Loading Skeleton Data</a> in the Spine Runtimes Guide.
/// </summary>
public class AtlasAttachmentLoader : AttachmentLoader {
private Atlas[] atlasArray;
/// <summary>If true, <see cref="FindRegion(string, string)"/> may return null. If false, an error is raised if the texture region is not
/// found. Default is false.</summary>
public bool allowMissingRegions;
public AtlasAttachmentLoader (params Atlas[] atlasArray)
: this(false, atlasArray) {
}
public AtlasAttachmentLoader (bool allowMissingRegions, params Atlas[] atlasArray) {
if (atlasArray == null) throw new ArgumentNullException("atlas", "atlas array cannot be null.");
this.atlasArray = atlasArray;
this.allowMissingRegions = allowMissingRegions;
}
/// <summary>Sets each <see cref="Sequence.Regions"/> by calling <see cref="FindRegion(string, string)"/> for each texture region using
/// <see cref="Sequence.GetPath(string, int)"/>.</summary>
protected void FindRegions (string name, string basePath, Sequence sequence) {
TextureRegion[] regions = sequence.Regions;
for (int i = 0, n = regions.Length; i < n; i++) {
regions[i] = FindRegion(name, sequence.GetPath(basePath, i));
}
}
/// <summary>Looks for the region with the specified path. If not found and <see cref="allowMissingRegions"/> is false, an error is
/// raised.</summary>
protected AtlasRegion FindRegion (string name, string path) {
for (int i = 0; i < atlasArray.Length; i++) {
AtlasRegion region = atlasArray[i].FindRegion(path);
if (region != null)
return region;
}
if (!allowMissingRegions)
throw new ArgumentException(string.Format("Region not found in atlas: {0} (attachment: {1})", path, name));
return null;
}
public RegionAttachment NewRegionAttachment (Skin skin, string placeholder, string name, string path, Sequence sequence) {
FindRegions(name, path, sequence);
return new RegionAttachment(name, sequence);
}
public MeshAttachment NewMeshAttachment (Skin skin, string placeholder, string name, string path, Sequence sequence) {
FindRegions(name, path, sequence);
return new MeshAttachment(name, sequence);
}
public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string placeholder, string name) {
return new BoundingBoxAttachment(name);
}
public PathAttachment NewPathAttachment (Skin skin, string placeholder, string name) {
return new PathAttachment(name);
}
public PointAttachment NewPointAttachment (Skin skin, string placeholder, string name) {
return new PointAttachment(name);
}
public ClippingAttachment NewClippingAttachment (Skin skin, string placeholder, string name) {
return new ClippingAttachment(name);
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3e6ff30e27c28344bad3e67d308c94cd
timeCreated: 1466772712
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,95 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2026, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
namespace Spine {
/// <summary>The base class for all attachments. Multiple <see cref="Skeleton"/> instances, slots, or skins can use the same
/// attachments.</summary>
abstract public class Attachment {
static private readonly int[] Empty = new int[0];
internal Attachment timelineAttachment;
internal int[] timelineSlots = Empty;
/// <summary>The attachment's name.</summary>
public string Name { get; }
/// <summary>Timelines for the timeline attachment are also applied to this attachment.
/// May be null if no attachment-specific timelines should be applied.</summary>
public Attachment TimelineAttachment { get { return timelineAttachment; } set { timelineAttachment = value; } }
/// <summary>
/// Slots that can have attachments whose <see cref="timelineAttachment"/> is this attachment.
/// </summary>
public int[] TimelineSlots { get { return timelineSlots; } set { timelineSlots = value; } }
protected Attachment (string name) {
if (name == null) throw new ArgumentNullException("name", "name cannot be null");
this.Name = name;
timelineAttachment = this;
}
/// <summary>Copy constructor.</summary>
protected Attachment (Attachment other) {
Name = other.Name;
timelineAttachment = other.timelineAttachment;
timelineSlots = other.timelineSlots;
}
/// <summary>
/// Returns true if the <c>slotIndex</c> or any <see cref="timelineSlots"/> have an attachment whose <see cref="timelineAttachment"/> is
/// this attachment.
/// </summary>
/// <param name="slots">The <see cref="Skeleton.Slots"/>.</param>
/// <param name="slotIndex">The timeline's primary slot index.</param>
public bool IsTimelineActive (Slot[] slots, int slotIndex, bool appliedPose) {
Slot slot = slots[slotIndex];
if (slot.Bone.Active) {
Attachment other = (appliedPose ? slot.AppliedPose : slot.Pose).Attachment;
if (other != null && other.timelineAttachment == this) return true;
}
for (int i = 0, n = timelineSlots.Length; i < n; i++) {
slot = slots[timelineSlots[i]];
if (!slot.Bone.Active) continue;
Attachment other = (appliedPose ? slot.AppliedPose : slot.Pose).Attachment;
if (other != null && other.timelineAttachment == this) return true;
}
return false;
}
override public string ToString () {
return Name;
}
/// <summary>Returns a copy of the attachment.</summary>
public abstract Attachment Copy ();
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 05b56321b2ddd8145a888746bc6ab917
timeCreated: 1456265153
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,48 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2026, 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.
*****************************************************************************/
namespace Spine {
public interface AttachmentLoader {
/// <return>May be null to not load any attachment.</return>
RegionAttachment NewRegionAttachment (Skin skin, string placeholder, string name, string path, Sequence sequence);
/// <return>May be null to not load any attachment.</return>
MeshAttachment NewMeshAttachment (Skin skin, string placeholder, string name, string path, Sequence sequence);
/// <return>May be null to not load any attachment.</return>
BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string placeholder, string name);
/// <returns>May be null to not load any attachment</returns>
PathAttachment NewPathAttachment (Skin skin, string placeholder, string name);
PointAttachment NewPointAttachment (Skin skin, string placeholder, string name);
ClippingAttachment NewClippingAttachment (Skin skin, string placeholder, string name);
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 95466a4f5a30dca4aa69e8ee7df8ae85
timeCreated: 1466772712
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,34 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2026, 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.
*****************************************************************************/
namespace Spine {
public enum AttachmentType {
Region, Boundingbox, Mesh, Linkedmesh, Path, Point, Clipping, Sequence
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d6b1941960a9f6f47be3e865554d8695
timeCreated: 1466772712
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,48 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2026, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
namespace Spine {
/// <summary>Attachment that has a polygon for bounds checking.</summary>
public class BoundingBoxAttachment : VertexAttachment {
public BoundingBoxAttachment (string name)
: base(name) {
}
/// <summary>Copy constructor.</summary>
protected BoundingBoxAttachment (BoundingBoxAttachment other)
: base(other) {
}
public override Attachment Copy () {
return new BoundingBoxAttachment(this);
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: cd8ad8fc0f5bce448ba26d096ab32e85
timeCreated: 1466772712
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,69 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2026, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
namespace Spine {
public class ClippingAttachment : VertexAttachment {
internal SlotData endSlot;
internal bool convex, inverse;
/// <summary>Clipping is performed between the clipping attachment's slot and the end slot. If null, clipping is done until the end of
/// the skeleton's rendering.</summary>
public SlotData EndSlot { get { return endSlot; } set { endSlot = value; } }
/// <summary>
/// When true the clipping polygon is treated as convex for more efficient clipping. If the polygon deforms to concave then the
/// convex hull is used.When false the clipping polygon can be concave and if so has an additional CPU cost.Inverse clipping
/// always uses convex.
/// </summary>
public bool Convex { get { return convex; } set { convex = value; } }
/// <summary>
/// When false, everything inside the clipping polygon is visible. When true, everything outside the clipping polygon is
/// visible and clipping is convex.
/// </summary>
public bool Inverse { get { return inverse; } set { inverse = value; } }
public ClippingAttachment (string name) : base(name) {
}
/// <summary>Copy constructor.</summary>
protected ClippingAttachment (ClippingAttachment other)
: base(other) {
endSlot = other.endSlot;
convex = other.convex;
inverse = other.inverse;
}
public override Attachment Copy () {
return new ClippingAttachment(this);
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3380954b107f38b4c85a4cdfeceace42
timeCreated: 1492744746
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,51 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2026, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if (UNITY_5 || UNITY_5_3_OR_NEWER || UNITY_WSA || UNITY_WP8 || UNITY_WP8_1)
#define IS_UNITY
#endif
namespace Spine {
#if IS_UNITY
using Color32F = UnityEngine.Color;
#endif
/// <summary>Interface for an attachment that gets 1 or more texture regions from a <see cref="Sequence"/>.</summary>
public interface IHasSequence {
/// <summary>The base path for the attachment's texture region.</summary>
string Path { get; set; }
/// <summary>The color the attachment is tinted, to be combined with <see cref="SlotPose.Color"/>.</summary>
Color32F GetColor ();
void SetColor (Color32F color);
void SetColor (float r, float g, float b, float a);
/// <summary>The sequence that provides texture regions, UVs, and vertex offsets for rendering this attachment.</summary>
Sequence Sequence { get; }
void UpdateSequence ();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d0e8b0a33cae75d498aa8c328787cafb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,236 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2026, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if (UNITY_5 || UNITY_5_3_OR_NEWER || UNITY_WSA || UNITY_WP8 || UNITY_WP8_1)
#define IS_UNITY
#endif
using System;
namespace Spine {
#if IS_UNITY
using Color32F = UnityEngine.Color;
#endif
/// <summary>Attachment that displays a texture region using a mesh.</summary>
public class MeshAttachment : VertexAttachment, IHasSequence {
internal readonly Sequence sequence;
internal float[] regionUVs;
internal int[] triangles;
internal int hullLength;
internal string path;
// Color is a struct, set to protected to prevent
// Color color = slot.color; color.a = 0.5;
// modifying just a copy of the struct instead of the original
// object as in reference implementation.
protected Color32F color = new Color32F(1, 1, 1, 1);
private MeshAttachment sourceMesh;
public int HullLength { get { return hullLength; } set { hullLength = value; } }
/// <summary>The UV pair for each vertex, normalized within the texture region.</summary>
public float[] RegionUVs { get { return regionUVs; } set { regionUVs = value; } }
/// <summary>Triplets of vertex indices which describe the mesh's triangulation.</summary>
public int[] Triangles { get { return triangles; } set { triangles = value; } }
public Color32F GetColor () {
return color;
}
public void SetColor (Color32F color) {
this.color = color;
}
public void SetColor (float r, float g, float b, float a) {
color = new Color32F(r, g, b, a);
}
public string Path { get { return path; } set { path = value; } }
public Sequence Sequence { get { return sequence; } }
/// <summary>
/// The source mesh if this is a linked mesh, else null. A linked mesh shares the
/// <see cref="VertexAttachment.Bones">Bones</see>, <see cref="VertexAttachment.Vertices">Vertices</see>,
/// <see cref="RegionUVs"/>, <see cref="Triangles"/>, <see cref="HullLength"/>, <see cref="Edges"/>,
/// <see cref="Width"/>, <see cref="Height"/> with the
/// source mesh, but may have a different <see cref="name"/> or <see cref="path"/>, and therefore a different texture region.
/// </summary>
public MeshAttachment SourceMesh {
get { return sourceMesh; }
set {
sourceMesh = value;
if (value != null) {
bones = value.bones;
vertices = value.vertices;
worldVerticesLength = value.worldVerticesLength;
regionUVs = value.regionUVs;
triangles = value.triangles;
HullLength = value.HullLength;
Edges = value.Edges;
Width = value.Width;
Height = value.Height;
}
}
}
// Nonessential.
/// <summary>
/// Vertex index pairs describing edges for controlling triangulation, or null if nonessential data was not exported. Mesh
/// triangles do not cross edges. Triangulation is not performed at runtime.
/// </summary>
public int[] Edges { get; set; }
public float Width { get; set; }
public float Height { get; set; }
public MeshAttachment (string name, Sequence sequence)
: base(name) {
if (sequence == null) throw new ArgumentException("sequence cannot be null.", "sequence");
this.sequence = sequence;
}
/// <summary>Copy constructor. Use <see cref="NewLinkedMesh"/> if the other mesh is a linked mesh.</summary>
protected MeshAttachment (MeshAttachment other)
: base(other) {
if (sourceMesh != null) throw new ArgumentException("Use newLinkedMesh to copy a linked mesh.");
path = other.path;
color = other.color;
regionUVs = new float[other.regionUVs.Length];
Array.Copy(other.regionUVs, 0, regionUVs, 0, regionUVs.Length);
triangles = new int[other.triangles.Length];
Array.Copy(other.triangles, 0, triangles, 0, triangles.Length);
hullLength = other.hullLength;
sequence = new Sequence(other.sequence);
// Nonessential.
if (other.Edges != null) {
Edges = new int[other.Edges.Length];
Array.Copy(other.Edges, 0, Edges, 0, Edges.Length);
}
Width = other.Width;
Height = other.Height;
}
public void UpdateSequence () {
sequence.Update(this);
}
/// <summary>
/// Returns a new mesh with the <see cref="SourceMesh"/> set to this mesh's source mesh, if any, else to this mesh.
/// </summary>
public MeshAttachment NewLinkedMesh () {
var mesh = new MeshAttachment(Name, new Sequence(sequence));
mesh.timelineAttachment = timelineAttachment;
mesh.path = path;
mesh.color = color;
mesh.SourceMesh = sourceMesh != null ? sourceMesh : this;
mesh.UpdateSequence();
return mesh;
}
public override Attachment Copy () {
return sourceMesh != null ? NewLinkedMesh() : new MeshAttachment(this);
}
/// <summary>
/// Computes <see cref="Sequence.GetUVs(int)">UVs</see> for a mesh attachment.
/// </summary>
/// <param name="uvs">Output array for the computed UVs, same length as regionUVs.</param>
internal static void ComputeUVs (TextureRegion region, float[] regionUVs, float[] uvs) {
int n = uvs.Length;
float u, v, width, height;
AtlasRegion r = region as AtlasRegion;
if (r != null) {
u = r.u;
v = r.v;
float textureWidth = region.width / (region.u2 - region.u);
float textureHeight = region.height / (region.v2 - region.v);
switch (r.degrees) {
case 90: {
u -= (r.originalHeight - r.offsetY - r.packedWidth) / textureWidth;
v -= (r.originalWidth - r.offsetX - r.packedHeight) / textureHeight;
width = r.originalHeight / textureWidth;
height = r.originalWidth / textureHeight;
for (int i = 0; i < n; i += 2) {
uvs[i] = u + regionUVs[i + 1] * width;
uvs[i + 1] = v + (1 - regionUVs[i]) * height;
}
return;
}
case 180: {
u -= (r.originalWidth - r.offsetX - r.packedWidth) / textureWidth;
v -= r.offsetY / textureHeight;
width = r.originalWidth / textureWidth;
height = r.originalHeight / textureHeight;
for (int i = 0; i < n; i += 2) {
uvs[i] = u + (1 - regionUVs[i]) * width;
uvs[i + 1] = v + (1 - regionUVs[i + 1]) * height;
}
return;
}
case 270: {
u -= r.offsetY / textureWidth;
v -= r.offsetX / textureHeight;
width = r.originalHeight / textureWidth;
height = r.originalWidth / textureHeight;
for (int i = 0; i < n; i += 2) {
uvs[i] = u + (1 - regionUVs[i + 1]) * width;
uvs[i + 1] = v + regionUVs[i] * height;
}
return;
}
default: {
u -= r.offsetX / textureWidth;
v -= (r.originalHeight - r.offsetY - r.packedHeight) / textureHeight;
width = r.originalWidth / textureWidth;
height = r.originalHeight / textureHeight;
break;
}
}
} else if (region == null) {
u = v = 0;
width = height = 1;
} else {
u = region.u;
v = region.v;
width = region.u2 - u;
height = region.v2 - v;
}
for (int i = 0; i < n; i += 2) {
uvs[i] = u + regionUVs[i] * width;
uvs[i + 1] = v + regionUVs[i + 1] * height;
}
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b7f7514a003143844b6d01ecc93ed4d5
timeCreated: 1466772712
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,65 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2026, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
using System.Collections.Generic;
namespace Spine {
public class PathAttachment : VertexAttachment {
internal float[] lengths;
internal bool closed, constantSpeed;
/// <summary>The length in the setup pose from the start of the path to the end of each curve.</summary>
public float[] Lengths { get { return lengths; } set { lengths = value; } }
/// <summary>If true, the start and end knots are connected.</summary>
public bool Closed { get { return closed; } set { closed = value; } }
/// <summary>If true, additional calculations are performed to make computing positions along the path more accurate so movement along
/// the path has a constant speed.</summary>
public bool ConstantSpeed { get { return constantSpeed; } set { constantSpeed = value; } }
public PathAttachment (String name)
: base(name) {
}
/// <summary>Copy constructor.</summary>
protected PathAttachment (PathAttachment other)
: base(other) {
lengths = new float[other.lengths.Length];
Array.Copy(other.lengths, 0, lengths, 0, lengths.Length);
closed = other.closed;
constantSpeed = other.constantSpeed;
}
public override Attachment Copy () {
return new PathAttachment(this);
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c77d9bf384a1e9f41966464e7e3b4870
timeCreated: 1466772712
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,78 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2026, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
namespace Spine {
/// <summary>
/// An attachment which is a single point and a rotation. This can be used to spawn projectiles, particles, etc. A bone can be
/// used in similar ways, but a PointAttachment is slightly less expensive to compute and can be hidden, shown, and placed in a
/// skin.
/// <p>
/// See <a href="https://esotericsoftware.com/spine-points">Point Attachments</a> in the Spine User Guide.
/// </summary>
public class PointAttachment : Attachment {
internal float x, y, rotation;
/// <summary>The local x position.</summary>
public float X { get { return x; } set { x = value; } }
/// <summary>The local y position.</summary>
public float Y { get { return y; } set { y = value; } }
/// <summary>The local rotation in degrees, counter clockwise.</summary>
public float Rotation { get { return rotation; } set { rotation = value; } }
public PointAttachment (string name)
: base(name) {
}
/// <summary>Copy constructor.</summary>
protected PointAttachment (PointAttachment other)
: base(other) {
x = other.x;
y = other.y;
rotation = other.rotation;
}
/// <summary>Computes the world position from the local position.</summary>
public void ComputeWorldPosition (BonePose bone, out float ox, out float oy) {
bone.LocalToWorld(this.x, this.y, out ox, out oy);
}
/// <summary>Computes the world rotation from the local rotation.</summary>
public float ComputeWorldRotation (BonePose bone) {
float r = rotation * MathUtils.DegRad, cos = (float)Math.Cos(r), sin = (float)Math.Sin(r);
float x = cos * bone.a + sin * bone.b;
float y = cos * bone.c + sin * bone.d;
return MathUtils.Atan2Deg(y, x);
}
public override Attachment Copy () {
return new PointAttachment(this);
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4fdde4cc4df0952468946f4f913dcb36
timeCreated: 1485603478
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,229 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2026, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if (UNITY_5 || UNITY_5_3_OR_NEWER || UNITY_WSA || UNITY_WP8 || UNITY_WP8_1)
#define IS_UNITY
#endif
using System;
namespace Spine {
#if IS_UNITY
using Color32F = UnityEngine.Color;
#endif
/// <summary>Attachment that displays a texture region.</summary>
public class RegionAttachment : Attachment, IHasSequence {
public const int BLX = 0, BLY = 1;
public const int ULX = 2, ULY = 3;
public const int URX = 4, URY = 5;
public const int BRX = 6, BRY = 7;
internal readonly Sequence sequence;
internal float x, y, rotation, scaleX = 1, scaleY = 1, width, height;
// Color is a struct, set to protected to prevent
// Color color = slot.color; color.a = 0.5;
// modifying just a copy of the struct instead of the original
// object as in reference implementation.
protected Color32F color = new Color32F(1, 1, 1, 1);
public float X { get { return x; } set { x = value; } }
public float Y { get { return y; } set { y = value; } }
/// <summary>The local rotation in degrees, counter clockwise.</summary>
public float Rotation { get { return rotation; } set { rotation = value; } }
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
public float Width { get { return width; } set { width = value; } }
public float Height { get { return height; } set { height = value; } }
public Color32F GetColor () {
return color;
}
public void SetColor (Color32F color) {
this.color = color;
}
public void SetColor (float r, float g, float b, float a) {
color = new Color32F(r, g, b, a);
}
public string Path { get; set; }
public Sequence Sequence { get { return sequence; } }
public RegionAttachment (string name, Sequence sequence)
: base(name) {
if (sequence == null) throw new ArgumentException("sequence cannot be null.", "sequence");
this.sequence = sequence;
}
/// <summary>Copy constructor.</summary>
public RegionAttachment (RegionAttachment other)
: base(other) {
Path = other.Path;
x = other.x;
y = other.y;
scaleX = other.scaleX;
scaleY = other.scaleY;
rotation = other.rotation;
width = other.width;
height = other.height;
color = other.color;
sequence = new Sequence(other.sequence);
}
/// <summary><para>
/// Transforms the attachment's four vertices to world coordinates. If the attachment has a <see cref="Sequence"/> the region may
/// be changed.</para>
/// <para>
/// See <see href='https://esotericsoftware.com/spine-runtime-skeletons#World-transforms'>World transforms</a> in the Spine
/// Runtimes Guide.</para></summary>
/// <param name="worldVertices">The output world vertices. Must have a length greater than or equal to offset + 8.</param>
/// <param name="vertexOffsets">The vertex <see cref="Sequence.GetOffsets(int)">offsets</see>.</param>
/// <param name="offset">The worldVertices index to begin writing values.</param>
/// <param name="stride">The number of worldVertices entries between the value pairs written.</param>
public void ComputeWorldVertices (Slot slot, float[] vertexOffsets, float[] worldVertices, int offset, int stride = 2) {
BonePose bone = slot.Bone.AppliedPose;
float bwx = bone.worldX, bwy = bone.worldY;
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
// Vertex order is different from RegionAttachment.java
float offsetX = vertexOffsets[BRX]; // 0
float offsetY = vertexOffsets[BRY]; // 1
worldVertices[offset] = offsetX * a + offsetY * b + bwx; // bl
worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
offset += stride;
offsetX = vertexOffsets[BLX]; // 2
offsetY = vertexOffsets[BLY]; // 3
worldVertices[offset] = offsetX * a + offsetY * b + bwx; // ul
worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
offset += stride;
offsetX = vertexOffsets[ULX]; // 4
offsetY = vertexOffsets[ULY]; // 5
worldVertices[offset] = offsetX * a + offsetY * b + bwx; // ur
worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
offset += stride;
offsetX = vertexOffsets[URX]; // 6
offsetY = vertexOffsets[URY]; // 7
worldVertices[offset] = offsetX * a + offsetY * b + bwx; // br
worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
//offset += stride;
}
/// <summary>
/// Returns the vertex <see cref="Sequence.GetOffsets(int)">offsets</see> for the specified slot pose.
/// </summary>
public float[] GetOffsets (SlotPose pose) {
return sequence.GetOffsets(sequence.ResolveIndex(pose));
}
public void UpdateSequence () {
sequence.Update(this);
}
public override Attachment Copy () {
return new RegionAttachment(this);
}
/// <summary>
/// Computes <see cref="Sequence.GetUVs(int)">UVs</see> and <see cref="Sequence.GetOffsets(int)">offsets</see> for a region attachment.
/// </summary>
/// <param name="uvs">Output array for the computed UVs, length of 8.</param>
/// <param name="offset">Output array for the computed vertex offsets, length of 8.</param>
internal static void ComputeUVs (TextureRegion region, float x, float y, float scaleX, float scaleY, float rotation, float width,
float height, float[] offset, float[] uvs) {
float localX2 = width / 2, localY2 = height / 2;
float localX = -localX2, localY = -localY2;
bool rotated = false;
AtlasRegion r = region as AtlasRegion;
if (r != null) {
localX += r.offsetX / r.originalWidth * width;
localY += r.offsetY / r.originalHeight * height;
if (r.degrees == 90) {
rotated = true;
localX2 -= (r.originalWidth - r.offsetX - r.packedHeight) / r.originalWidth * width;
localY2 -= (r.originalHeight - r.offsetY - r.packedWidth) / r.originalHeight * height;
} else {
localX2 -= (r.originalWidth - r.offsetX - r.packedWidth) / r.originalWidth * width;
localY2 -= (r.originalHeight - r.offsetY - r.packedHeight) / r.originalHeight * height;
}
}
localX *= scaleX;
localY *= scaleY;
localX2 *= scaleX;
localY2 *= scaleY;
float rot = rotation * MathUtils.DegRad, cos = (float)Math.Cos(rot), sin = (float)Math.Sin(rot);
float localXCos = localX * cos + x;
float localXSin = localX * sin;
float localYCos = localY * cos + y;
float localYSin = localY * sin;
float localX2Cos = localX2 * cos + x;
float localX2Sin = localX2 * sin;
float localY2Cos = localY2 * cos + y;
float localY2Sin = localY2 * sin;
offset[BLX] = localXCos - localYSin;
offset[BLY] = localYCos + localXSin;
offset[ULX] = localXCos - localY2Sin;
offset[ULY] = localY2Cos + localXSin;
offset[URX] = localX2Cos - localY2Sin;
offset[URY] = localY2Cos + localX2Sin;
offset[BRX] = localX2Cos - localYSin;
offset[BRY] = localYCos + localX2Sin;
if (region == null) {
uvs[BLX] = 0;
uvs[BLY] = 0;
uvs[ULX] = 0;
uvs[ULY] = 1;
uvs[URX] = 1;
uvs[URY] = 1;
uvs[BRX] = 1;
uvs[BRY] = 0;
} else {
uvs[BLX] = region.u2;
uvs[ULY] = region.v2;
uvs[URX] = region.u;
uvs[BRY] = region.v;
if (rotated) {
uvs[BLY] = region.v;
uvs[ULX] = region.u2;
uvs[URY] = region.v2;
uvs[BRX] = region.u;
} else {
uvs[BLY] = region.v2;
uvs[ULX] = region.u;
uvs[URY] = region.v;
uvs[BRX] = region.u2;
}
}
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 89cefdd024734a941952a05d2b5dff71
timeCreated: 1466772712
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,173 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2026, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
using System.Text;
namespace Spine {
/// <summary>
/// Holds texture regions, UVs, and vertex offsets for rendering a region or mesh attachment. <see cref="Regions"/> must
/// be populated and <see cref="Update(IHasSequence)"/> called before use.
/// </summary>
public class Sequence {
static int nextID = 0;
static readonly Object nextIdLock = new Object();
internal readonly int id;
internal readonly TextureRegion[] regions;
internal readonly bool pathSuffix;
internal float[][] uvs, offsets;
internal int start, digits, setupIndex;
/// <summary>The starting number for the numeric <see cref="GetPath(string, int)">path</see> suffix.</summary>
public int Start { get { return start; } set { start = value; } }
/// <summary>The minimum number of digits in the numeric <see cref="GetPath(string, int)">path</see> suffix, for zero
/// padding. 0 for no zero padding.</summary>
public int Digits { get { return digits; } set { digits = value; } }
/// <summary>The index of the region to show for the setup pose.</summary>
public int SetupIndex { get { return setupIndex; } set { setupIndex = value; } }
/// <summary>The list of texture regions this sequence will display.</summary>
public TextureRegion[] Regions { get { return regions; } }
/// <summary>Returns true if the <see cref="GetPath(string, int)">path</see> has a numeric suffix.</summary>
public bool HasPathSuffix { get { return pathSuffix; } }
/// <summary>Returns a unique ID for this attachment.</summary>
public int Id { get { return id; } }
/// <param name="count">The number of texture regions this sequence will display.</param>
/// <param name="pathSuffix">If true, the <see cref="GetPath(string, int)">path</see> has a numeric suffix. If false, all
/// regions will use the same path, so <c>count</c> should be 1.</param>
public Sequence (int count, bool pathSuffix) {
lock (Sequence.nextIdLock) {
id = Sequence.nextID++;
}
regions = new TextureRegion[count];
this.pathSuffix = pathSuffix;
}
/// <summary>Copy constructor.</summary>
public Sequence (Sequence other) {
lock (Sequence.nextIdLock) {
id = Sequence.nextID++;
}
int regionCount = other.regions.Length;
regions = new TextureRegion[regionCount];
Array.Copy(other.regions, 0, regions, 0, regionCount);
start = other.start;
digits = other.digits;
setupIndex = other.setupIndex;
pathSuffix = other.pathSuffix;
if (other.uvs != null) {
int length = other.uvs[0].Length;
uvs = new float[regionCount][];
for (int i = 0; i < regionCount; i++) {
uvs[i] = new float[length];
Array.Copy(other.uvs[i], 0, uvs[i], 0, length);
}
}
if (other.offsets != null) {
offsets = new float[regionCount][];
for (int i = 0; i < regionCount; i++) {
offsets[i] = new float[8];
Array.Copy(other.offsets[i], 0, offsets[i], 0, 8);
}
}
}
public void Update (IHasSequence attachment) {
int regionCount = regions.Length;
RegionAttachment region = attachment as RegionAttachment;
if (region != null) {
uvs = new float[regionCount][];
offsets = new float[regionCount][];
for (int i = 0; i < regionCount; i++) {
uvs[i] = new float[8];
offsets[i] = new float[8];
RegionAttachment.ComputeUVs(regions[i], region.x, region.y, region.scaleX, region.scaleY, region.rotation,
region.width, region.height, offsets[i], uvs[i]);
}
} else {
MeshAttachment mesh = attachment as MeshAttachment;
if (mesh != null) {
float[] regionUVs = mesh.regionUVs;
uvs = new float[regionCount][];
offsets = null;
for (int i = 0; i < regionCount; i++) {
uvs[i] = new float[regionUVs.Length];
MeshAttachment.ComputeUVs(regions[i], regionUVs, uvs[i]);
}
}
}
}
/// <summary>Returns the <see cref="Regions"/> index for the <see cref="SlotPose.SequenceIndex"/>.</summary>
public int ResolveIndex (SlotPose pose) {
int index = pose.SequenceIndex;
if (index == -1) index = setupIndex;
if (index >= regions.Length) index = regions.Length - 1;
return index;
}
/// <summary>Returns the texture region from <see cref="Regions"/> for the specified index.</summary>
public TextureRegion GetRegion (int index) {
return regions[index];
}
/// <summary>Returns the UVs for the specified index. <see cref="Regions">Regions</see> must be populated and
/// <see cref="Update(IHasSequence)"/> called before calling this method.</summary>
public float[] GetUVs (int index) {
return uvs[index];
}
/// <summary>
/// Returns vertex offsets from the center of a <see cref="RegionAttachment"/>. Invalid to call for a <see cref="MeshAttachment"/>.
/// </summary>
public float[] GetOffsets (int index) {
return offsets[index];
}
/// <summary>Returns the specified base path with an optional numeric suffix for the specified index.</summary>
public string GetPath (string basePath, int index) {
if (!pathSuffix) return basePath;
var buffer = new StringBuilder(basePath.Length + digits);
buffer.Append(basePath);
string frame = (start + index).ToString();
for (int i = digits - frame.Length; i > 0; i--)
buffer.Append('0');
buffer.Append(frame);
return buffer.ToString();
}
}
/// <summary>Controls how <see cref="Sequence.Regions"/> are displayed over time.</summary>
public enum SequenceMode {
Hold, Once, Loop, Pingpong, OnceReverse, LoopReverse, PingpongReverse
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 522632bd4e297fe47acf78100bfd8689
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,159 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2026, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
namespace Spine {
/// <summary>Base class for an attachment with vertices that are transformed by one or more bones and can be deformed by
/// <see cref="SlotPose.Deform"/>.</summary>
public abstract class VertexAttachment : Attachment {
static int nextID = 0;
static readonly Object nextIdLock = new Object();
internal readonly int id;
internal int[] bones;
internal float[] vertices;
internal int worldVerticesLength;
/// <summary>Gets a unique ID for this attachment.</summary>
public int Id { get { return id; } }
/// <summary>The bones that affect the <see cref="Vertices"/>. The entries are, for each vertex, the number of bones affecting the
/// vertex followed by that many bone indices, which is <see cref="Skeleton.Bones"/> index. Null if this attachment has no
/// weights.</summary>
public int[] Bones { get { return bones; } set { bones = value; } }
/// <summary>The vertex positions in the bone's coordinate system. For a non-weighted attachment, the values are <c>x,y</c> pairs
/// for each vertex. For a weighted attachment, the values are <c>x,y,weight</c> triplets for each bone affecting each
/// vertex.</summary>
public float[] Vertices { get { return vertices; } set { vertices = value; } }
public int WorldVerticesLength { get { return worldVerticesLength; } set { worldVerticesLength = value; } }
public VertexAttachment (string name)
: base(name) {
lock (VertexAttachment.nextIdLock) {
id = VertexAttachment.nextID++;
}
}
/// <summary>Copy constructor.</summary>
public VertexAttachment (VertexAttachment other)
: base(other) {
lock (VertexAttachment.nextIdLock) {
id = VertexAttachment.nextID++;
}
timelineAttachment = other.timelineAttachment;
if (other.bones != null) {
bones = new int[other.bones.Length];
Array.Copy(other.bones, 0, bones, 0, bones.Length);
} else
bones = null;
if (other.vertices != null) {
vertices = new float[other.vertices.Length];
Array.Copy(other.vertices, 0, vertices, 0, vertices.Length);
} else
vertices = null;
worldVerticesLength = other.worldVerticesLength;
}
public void ComputeWorldVertices (Skeleton skeleton, Slot slot, float[] worldVertices) {
ComputeWorldVertices(skeleton, slot, 0, worldVerticesLength, worldVertices, 0);
}
/// <summary>
/// Transforms the attachment's local <see cref="Vertices"/> to world coordinates. If <see cref="SlotPose.Deform"/>
/// is not empty, it is used to deform the vertices.
/// <para />
/// See <a href="http://esotericsoftware.com/spine-runtime-skeletons#World-transforms">World transforms</a> in the Spine
/// Runtimes Guide.
/// </summary>
/// <param name="start">The index of the first <see cref="Vertices"/> value to transform. Each vertex has 2 values, x and y.</param>
/// <param name="count">The number of world vertex values to output. Must be less than or equal to <see cref="WorldVerticesLength"/> - start.</param>
/// <param name="worldVertices">The output world vertices. Must have a length greater than or equal to <paramref name="offset"/> + <paramref name="count"/>.</param>
/// <param name="offset">The <paramref name="worldVertices"/> index to begin writing values.</param>
/// <param name="stride">The number of <paramref name="worldVertices"/> entries between the value pairs written.</param>
public virtual void ComputeWorldVertices (Skeleton skeleton, Slot slot, int start, int count, float[] worldVertices, int offset, int stride = 2) {
count = offset + (count >> 1) * stride;
ExposedList<float> deformArray = slot.AppliedPose.deform;
float[] vertices = this.vertices;
int[] bones = this.bones;
if (bones == null) {
if (deformArray.Count > 0) vertices = deformArray.Items;
BonePose bone = slot.bone.AppliedPose;
float x = bone.worldX, y = bone.worldY;
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
for (int vv = start, w = offset; w < count; vv += 2, w += stride) {
float vx = vertices[vv], vy = vertices[vv + 1];
worldVertices[w] = vx * a + vy * b + x;
worldVertices[w + 1] = vx * c + vy * d + y;
}
return;
}
int v = 0, skip = 0;
for (int i = 0; i < start; i += 2) {
int n = bones[v];
v += n + 1;
skip += n;
}
Bone[] skeletonBones = skeleton.bones.Items;
if (deformArray.Count == 0) {
for (int w = offset, b = skip * 3; w < count; w += stride) {
float wx = 0, wy = 0;
int n = bones[v++];
n += v;
for (; v < n; v++, b += 3) {
BonePose bone = skeletonBones[bones[v]].AppliedPose;
float vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2];
wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
}
worldVertices[w] = wx;
worldVertices[w + 1] = wy;
}
} else {
float[] deform = deformArray.Items;
for (int w = offset, b = skip * 3, f = skip << 1; w < count; w += stride) {
float wx = 0, wy = 0;
int n = bones[v++];
n += v;
for (; v < n; v++, b += 3, f += 2) {
BonePose bone = skeletonBones[bones[v]].AppliedPose;
float vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2];
wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
}
worldVertices[w] = wx;
worldVertices[w + 1] = wy;
}
}
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8b40cfb462a8b774891e1604e5360d32
timeCreated: 1466772712
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: