Generic C# runtime & XNA runtime.

This commit is contained in:
NathanSweet 2013-04-11 10:25:12 +02:00
parent d5d5839a15
commit 4a354f4565
37 changed files with 4860 additions and 0 deletions

9
.gitignore vendored
View File

@ -20,3 +20,12 @@ spine-cocos2d-iphone/libs/*
!spine-cocos2d-iphone/libs/cocos2d/Place cocos2d here.txt
!spine-cocos2d-iphone/libs/CocosDenshion/Place CocosDenshion here.txt
!spine-cocos2d-iphone/libs/kazmath/Place kazmath here.txt
*.user
forums
spine-csharp/bin
spine-csharp/obj
spine-xna/bin
spine-xna/obj
spine-xna/example/bin
spine-xna/example/obj

View File

@ -0,0 +1,34 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("spine-csharp")]
[assembly: AssemblyProduct("spine-csharp")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type. Only Windows
// assemblies support COM.
[assembly: ComVisible(false)]
// On Windows, the following GUID is for the ID of the typelib if this
// project is exposed to COM. On other platforms, it unique identifies the
// title storage container when deploying this assembly to the device.
[assembly: Guid("3ac8567e-9ae8-4624-87b9-a84f0101c629")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
[assembly: AssemblyVersion("1.0.0.0")]

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}</ProjectGuid>
<ProjectTypeGuids>{6D335F3A-9D43-41b4-9D22-F6F17C4BE596};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Spine</RootNamespace>
<AssemblyName>spine-csharp</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<XnaFrameworkVersion>v4.0</XnaFrameworkVersion>
<XnaPlatform>Windows</XnaPlatform>
<XnaProfile>HiDef</XnaProfile>
<XnaCrossPlatformGroupID>99dfd52d-8beb-4e5c-a68b-365be39e8064</XnaCrossPlatformGroupID>
<XnaOutputType>Library</XnaOutputType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\x86\Debug</OutputPath>
<DefineConstants>DEBUG;TRACE;WINDOWS</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<NoStdLib>true</NoStdLib>
<UseVSHostingProcess>false</UseVSHostingProcess>
<PlatformTarget>x86</PlatformTarget>
<XnaCompressContent>false</XnaCompressContent>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\x86\Release</OutputPath>
<DefineConstants>TRACE;WINDOWS</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<NoStdLib>true</NoStdLib>
<UseVSHostingProcess>false</UseVSHostingProcess>
<PlatformTarget>x86</PlatformTarget>
<XnaCompressContent>true</XnaCompressContent>
</PropertyGroup>
<ItemGroup>
<Reference Include="mscorlib">
<Private>False</Private>
</Reference>
<Reference Include="System">
<Private>False</Private>
</Reference>
<Reference Include="System.Core">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
<Private>False</Private>
</Reference>
<Reference Include="System.Net">
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="src\Animation.cs" />
<Compile Include="src\Attachments\AttachmentLoader.cs" />
<Compile Include="src\Attachments\AtlasAttachmentLoader.cs" />
<Compile Include="src\Attachments\Attachment.cs" />
<Compile Include="src\Attachments\AttachmentType.cs" />
<Compile Include="src\Attachments\RegionAttachment.cs" />
<Compile Include="src\BaseAtlas.cs" />
<Compile Include="src\Bone.cs" />
<Compile Include="src\BoneData.cs" />
<Compile Include="src\Json.cs" />
<Compile Include="src\Skeleton.cs" />
<Compile Include="src\SkeletonData.cs" />
<Compile Include="src\SkeletonJson.cs" />
<Compile Include="src\Skin.cs" />
<Compile Include="src\Slot.cs" />
<Compile Include="src\SlotData.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\Microsoft.Xna.GameStudio.targets" />
<!--
To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,390 @@
using System;
using System.Collections.Generic;
namespace Spine {
public class Animation {
public String Name { get; private set; }
public List<Timeline> Timelines { get; set; }
public float Duration { get; set; }
public Animation (String name, List<Timeline> timelines, float duration) {
if (name == null) throw new ArgumentNullException("name cannot be null.");
if (timelines == null) throw new ArgumentNullException("timelines cannot be null.");
Name = name;
Timelines = timelines;
Duration = duration;
}
/** Poses the skeleton at the specified time for this animation. */
public void Apply (Skeleton skeleton, float time, bool loop) {
if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
if (loop && Duration != 0) time %= Duration;
List<Timeline> timelines = Timelines;
for (int i = 0, n = timelines.Count; i < n; i++)
timelines[i].Apply(skeleton, time, 1);
}
/** Poses the skeleton at the specified time for this animation mixed with the current pose.
* @param alpha The amount of this animation that affects the current pose. */
public void Mix (Skeleton skeleton, float time, bool loop, float alpha) {
if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
if (loop && Duration != 0) time %= Duration;
List<Timeline> timelines = Timelines;
for (int i = 0, n = timelines.Count; i < n; i++)
timelines[i].Apply(skeleton, time, alpha);
}
/** @param target After the first and before the last entry. */
internal static int binarySearch (float[] values, float target, int step) {
int low = 0;
int high = values.Length / step - 2;
if (high == 0) return step;
int current = (int)((uint)high >> 1);
while (true) {
if (values[(current + 1) * step] <= target)
low = current + 1;
else
high = current;
if (low == high) return (low + 1) * step;
current = (int)((uint)(low + high) >> 1);
}
}
internal static int linearSearch (float[] values, float target, int step) {
for (int i = 0, last = values.Length - step; i <= last; i += step)
if (values[i] > target) return i;
return -1;
}
}
public interface Timeline {
/** Sets the value(s) for the specified time. */
void Apply (Skeleton skeleton, float time, float alpha);
}
/** Base class for frames that use an interpolation bezier curve. */
abstract public class CurveTimeline : Timeline {
static protected float LINEAR = 0;
static protected float STEPPED = -1;
static protected int BEZIER_SEGMENTS = 10;
private float[] curves; // dfx, dfy, ddfx, ddfy, dddfx, dddfy, ...
public int FrameCount {
get {
return curves.Length / 6 + 1;
}
}
public CurveTimeline (int frameCount) {
curves = new float[(frameCount - 1) * 6];
}
abstract public void Apply (Skeleton skeleton, float time, float alpha);
public void SetLinear (int frameIndex) {
curves[frameIndex * 6] = LINEAR;
}
public void SetStepped (int frameIndex) {
curves[frameIndex * 6] = STEPPED;
}
/** Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next.
* cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of
* the difference between the keyframe's values. */
public void SetCurve (int frameIndex, float cx1, float cy1, float cx2, float cy2) {
float subdiv_step = 1f / BEZIER_SEGMENTS;
float subdiv_step2 = subdiv_step * subdiv_step;
float subdiv_step3 = subdiv_step2 * subdiv_step;
float pre1 = 3 * subdiv_step;
float pre2 = 3 * subdiv_step2;
float pre4 = 6 * subdiv_step2;
float pre5 = 6 * subdiv_step3;
float tmp1x = -cx1 * 2 + cx2;
float tmp1y = -cy1 * 2 + cy2;
float tmp2x = (cx1 - cx2) * 3 + 1;
float tmp2y = (cy1 - cy2) * 3 + 1;
int i = frameIndex * 6;
float[] curves = this.curves;
curves[i] = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv_step3;
curves[i + 1] = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv_step3;
curves[i + 2] = tmp1x * pre4 + tmp2x * pre5;
curves[i + 3] = tmp1y * pre4 + tmp2y * pre5;
curves[i + 4] = tmp2x * pre5;
curves[i + 5] = tmp2y * pre5;
}
public float GetCurvePercent (int frameIndex, float percent) {
int curveIndex = frameIndex * 6;
float[] curves = this.curves;
float dfx = curves[curveIndex];
if (dfx == LINEAR) return percent;
if (dfx == STEPPED) return 0;
float dfy = curves[curveIndex + 1];
float ddfx = curves[curveIndex + 2];
float ddfy = curves[curveIndex + 3];
float dddfx = curves[curveIndex + 4];
float dddfy = curves[curveIndex + 5];
float x = dfx, y = dfy;
int i = BEZIER_SEGMENTS - 2;
while (true) {
if (x >= percent) {
float lastX = x - dfx;
float lastY = y - dfy;
return lastY + (y - lastY) * (percent - lastX) / (x - lastX);
}
if (i == 0) break;
i--;
dfx += ddfx;
dfy += ddfy;
ddfx += dddfx;
ddfy += dddfy;
x += dfx;
y += dfy;
}
return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1.
}
}
public class RotateTimeline : CurveTimeline {
static protected int LAST_FRAME_TIME = -2;
static protected int FRAME_VALUE = 1;
public int BoneIndex { get; set; }
public float[] Frames { get; private set; } // time, value, ...
public RotateTimeline (int frameCount)
: base(frameCount) {
Frames = new float[frameCount * 2];
}
/** Sets the time and value of the specified keyframe. */
public void SetFrame (int frameIndex, float time, float angle) {
frameIndex *= 2;
Frames[frameIndex] = time;
Frames[frameIndex + 1] = angle;
}
override public void Apply (Skeleton skeleton, float time, float alpha) {
float[] frames = Frames;
if (time < frames[0]) return; // Time is before first frame.
Bone bone = skeleton.Bones[BoneIndex];
float amount;
if (time >= frames[frames.Length - 2]) { // Time is after last frame.
amount = bone.Data.Rotation + frames[frames.Length - 1] - bone.Rotation;
while (amount > 180)
amount -= 360;
while (amount < -180)
amount += 360;
bone.Rotation += amount * alpha;
return;
}
// Interpolate between the last frame and the current frame.
int frameIndex = Animation.binarySearch(frames, time, 2);
float lastFrameValue = frames[frameIndex - 1];
float frameTime = frames[frameIndex];
float percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime);
percent = GetCurvePercent(frameIndex / 2 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
amount = frames[frameIndex + FRAME_VALUE] - lastFrameValue;
while (amount > 180)
amount -= 360;
while (amount < -180)
amount += 360;
amount = bone.Data.Rotation + (lastFrameValue + amount * percent) - bone.Rotation;
while (amount > 180)
amount -= 360;
while (amount < -180)
amount += 360;
bone.Rotation += amount * alpha;
}
}
public class TranslateTimeline : CurveTimeline {
static protected int LAST_FRAME_TIME = -3;
static protected int FRAME_X = 1;
static protected int FRAME_Y = 2;
public int BoneIndex { get; set; }
public float[] Frames { get; private set; } // time, value, value, ...
public TranslateTimeline (int frameCount)
: base(frameCount) {
Frames = new float[frameCount * 3];
}
/** Sets the time and value of the specified keyframe. */
public void SetFrame (int frameIndex, float time, float x, float y) {
frameIndex *= 3;
Frames[frameIndex] = time;
Frames[frameIndex + 1] = x;
Frames[frameIndex + 2] = y;
}
override public void Apply (Skeleton skeleton, float time, float alpha) {
float[] frames = Frames;
if (time < frames[0]) return; // Time is before first frame.
Bone bone = skeleton.Bones[BoneIndex];
if (time >= frames[frames.Length - 3]) { // Time is after last frame.
bone.X += (bone.Data.X + frames[frames.Length - 2] - bone.X) * alpha;
bone.Y += (bone.Data.Y + frames[frames.Length - 1] - bone.Y) * alpha;
return;
}
// Interpolate between the last frame and the current frame.
int frameIndex = Animation.binarySearch(frames, time, 3);
float lastFrameX = frames[frameIndex - 2];
float lastFrameY = frames[frameIndex - 1];
float frameTime = frames[frameIndex];
float percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime);
percent = GetCurvePercent(frameIndex / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
bone.X += (bone.Data.X + lastFrameX + (frames[frameIndex + FRAME_X] - lastFrameX) * percent - bone.X) * alpha;
bone.Y += (bone.Data.Y + lastFrameY + (frames[frameIndex + FRAME_Y] - lastFrameY) * percent - bone.Y) * alpha;
}
}
public class ScaleTimeline : TranslateTimeline {
public ScaleTimeline (int frameCount)
: base(frameCount) {
}
override public void Apply (Skeleton skeleton, float time, float alpha) {
float[] frames = Frames;
if (time < frames[0]) return; // Time is before first frame.
Bone bone = skeleton.Bones[BoneIndex];
if (time >= frames[frames.Length - 3]) { // Time is after last frame.
bone.ScaleX += (bone.Data.ScaleX - 1 + frames[frames.Length - 2] - bone.ScaleX) * alpha;
bone.ScaleY += (bone.Data.ScaleY - 1 + frames[frames.Length - 1] - bone.ScaleY) * alpha;
return;
}
// Interpolate between the last frame and the current frame.
int frameIndex = Animation.binarySearch(frames, time, 3);
float lastFrameX = frames[frameIndex - 2];
float lastFrameY = frames[frameIndex - 1];
float frameTime = frames[frameIndex];
float percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime);
percent = GetCurvePercent(frameIndex / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
bone.ScaleX += (bone.Data.ScaleX - 1 + lastFrameX + (frames[frameIndex + FRAME_X] - lastFrameX) * percent - bone.ScaleX) * alpha;
bone.ScaleY += (bone.Data.ScaleY - 1 + lastFrameY + (frames[frameIndex + FRAME_Y] - lastFrameY) * percent - bone.ScaleY) * alpha;
}
}
public class ColorTimeline : CurveTimeline {
static protected int LAST_FRAME_TIME = -5;
static protected int FRAME_R = 1;
static protected int FRAME_G = 2;
static protected int FRAME_B = 3;
static protected int FRAME_A = 4;
public int SlotIndex { get; set; }
public float[] Frames { get; private set; } // time, r, g, b, a, ...
public ColorTimeline (int frameCount)
: base(frameCount) {
Frames = new float[frameCount * 5];
}
/** Sets the time and value of the specified keyframe. */
public void setFrame (int frameIndex, float time, float r, float g, float b, float a) {
frameIndex *= 5;
Frames[frameIndex] = time;
Frames[frameIndex + 1] = r;
Frames[frameIndex + 2] = g;
Frames[frameIndex + 3] = b;
Frames[frameIndex + 4] = a;
}
override public void Apply (Skeleton skeleton, float time, float alpha) {
float[] frames = Frames;
if (time < frames[0]) return; // Time is before first frame.
Slot slot = skeleton.Slots[SlotIndex];
if (time >= frames[frames.Length - 5]) { // Time is after last frame.
int i = frames.Length - 1;
slot.R = frames[i - 3];
slot.G = frames[i - 2];
slot.B = frames[i - 1];
slot.A = frames[i];
return;
}
// Interpolate between the last frame and the current frame.
int frameIndex = Animation.binarySearch(frames, time, 5);
float lastFrameR = frames[frameIndex - 4];
float lastFrameG = frames[frameIndex - 3];
float lastFrameB = frames[frameIndex - 2];
float lastFrameA = frames[frameIndex - 1];
float frameTime = frames[frameIndex];
float percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime);
percent = GetCurvePercent(frameIndex / 5 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
float r = lastFrameR + (frames[frameIndex + FRAME_R] - lastFrameR) * percent;
float g = lastFrameG + (frames[frameIndex + FRAME_G] - lastFrameG) * percent;
float b = lastFrameB + (frames[frameIndex + FRAME_B] - lastFrameB) * percent;
float a = lastFrameA + (frames[frameIndex + FRAME_A] - lastFrameA) * percent;
if (alpha < 1) {
slot.R += (r - slot.R) * alpha;
slot.G += (g - slot.G) * alpha;
slot.B += (b - slot.B) * alpha;
slot.A += (a - slot.A) * alpha;
} else {
slot.R = r;
slot.G = g;
slot.B = b;
slot.A = a;
}
}
}
public class AttachmentTimeline : Timeline {
public int SlotIndex { get; set; }
public float[] Frames { get; private set; } // time, ...
public String[] AttachmentNames { get; private set; }
public int FrameCount {
get {
return Frames.Length;
}
}
public AttachmentTimeline (int frameCount) {
Frames = new float[frameCount];
AttachmentNames = new String[frameCount];
}
/** Sets the time and value of the specified keyframe. */
public void setFrame (int frameIndex, float time, String attachmentName) {
Frames[frameIndex] = time;
AttachmentNames[frameIndex] = attachmentName;
}
public void Apply (Skeleton skeleton, float time, float alpha) {
float[] frames = Frames;
if (time < frames[0]) return; // Time is before first frame.
int frameIndex;
if (time >= frames[frames.Length - 1]) // Time is after last frame.
frameIndex = frames.Length - 1;
else
frameIndex = Animation.binarySearch(frames, time, 1) - 1;
String attachmentName = AttachmentNames[frameIndex];
skeleton.Slots[SlotIndex].Attachment =
attachmentName == null ? null : skeleton.GetAttachment(SlotIndex, attachmentName);
}
}
}

View File

@ -0,0 +1,24 @@
using System;
namespace Spine {
public class AtlasAttachmentLoader : AttachmentLoader {
private BaseAtlas atlas;
public AtlasAttachmentLoader (BaseAtlas atlas) {
if (atlas == null) throw new ArgumentNullException("atlas cannot be null.");
this.atlas = atlas;
}
public Attachment NewAttachment (AttachmentType type, String name) {
switch (type) {
case AttachmentType.region:
AtlasRegion region = atlas.FindRegion(name);
if (region == null) throw new Exception("Region not found in atlas: " + name + " (" + type + ")");
RegionAttachment attachment = new RegionAttachment(name);
attachment.Region = region;
return attachment;
}
throw new Exception("Unknown attachment type: " + type);
}
}
}

View File

@ -0,0 +1,16 @@
using System;
namespace Spine {
abstract public class Attachment {
public String Name { get; private set; }
public Attachment (String name) {
if (name == null) throw new ArgumentNullException("name cannot be null.");
Name = name;
}
override public String ToString () {
return Name;
}
}
}

View File

@ -0,0 +1,8 @@
using System;
namespace Spine {
public interface AttachmentLoader {
/** @return May be null to not load any attachment. */
Attachment NewAttachment (AttachmentType type, String name);
}
}

View File

@ -0,0 +1,6 @@
namespace Spine {
public enum AttachmentType {
region, regionSequence
}
}

View File

@ -0,0 +1,134 @@
using System;
namespace Spine {
/** Attachment that displays a texture region. */
public class RegionAttachment : Attachment {
public const int X1 = 0;
public const int Y1 = 1;
public const int X2 = 2;
public const int Y2 = 3;
public const int X3 = 4;
public const int Y3 = 5;
public const int X4 = 6;
public const int Y4 = 7;
public float X { get; set; }
public float Y { get; set; }
public float ScaleX { get; set; }
public float ScaleY { get; set; }
public float Rotation { get; set; }
public float Width { get; set; }
public float Height { get; set; }
public float[] Offset { get; private set; }
public float[] Vertices { get; private set; }
public float[] UVs { get; private set; }
private AtlasRegion region;
public AtlasRegion Region {
get {
return region;
}
set {
region = value;
float[] uvs = UVs;
if (value.Rotate) {
uvs[X2] = value.U;
uvs[Y2] = value.V2;
uvs[X3] = value.U;
uvs[Y3] = value.V;
uvs[X4] = value.U2;
uvs[Y4] = value.V;
uvs[X1] = value.U2;
uvs[Y1] = value.V2;
} else {
uvs[X1] = value.U;
uvs[Y1] = value.V2;
uvs[X2] = value.U;
uvs[Y2] = value.V;
uvs[X3] = value.U2;
uvs[Y3] = value.V;
uvs[X4] = value.U2;
uvs[Y4] = value.V2;
}
}
}
public RegionAttachment (string name)
: base(name) {
Offset = new float[8];
Vertices = new float[8];
UVs = new float[8];
ScaleX = 1;
ScaleY = 1;
}
public void UpdateOffset () {
float width = Width;
float height = Height;
float localX2 = width / 2;
float localY2 = height / 2;
float localX = -localX2;
float localY = -localY2;
AtlasRegion region = Region;
if (region.Rotate) {
localX += region.OffsetX / region.OriginalWidth * height;
localY += region.OffsetY / region.OriginalHeight * width;
localX2 -= (region.OriginalWidth - region.OffsetX - region.Height) / region.OriginalWidth * width;
localY2 -= (region.OriginalHeight - region.OffsetY - region.Width) / region.OriginalHeight * height;
} else {
localX += region.OffsetX / region.OriginalWidth * width;
localY += region.OffsetY / region.OriginalHeight * height;
localX2 -= (region.OriginalWidth - region.OffsetX - region.Width) / region.OriginalWidth * width;
localY2 -= (region.OriginalHeight - region.OffsetY - region.Height) / region.OriginalHeight * height;
}
float scaleX = ScaleX;
float scaleY = ScaleY;
localX *= scaleX;
localY *= scaleY;
localX2 *= scaleX;
localY2 *= scaleY;
float radians = Rotation * (float)Math.PI / 180;
float cos = (float)Math.Cos(radians);
float sin = (float)Math.Sin(radians);
float x = X;
float y = Y;
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;
float[] offset = Offset;
offset[X1] = localXCos - localYSin;
offset[Y1] = localYCos + localXSin;
offset[X2] = localXCos - localY2Sin;
offset[Y2] = localY2Cos + localXSin;
offset[X3] = localX2Cos - localY2Sin;
offset[Y3] = localY2Cos + localX2Sin;
offset[X4] = localX2Cos - localYSin;
offset[Y4] = localYCos + localX2Sin;
}
public void UpdateVertices (Bone bone) {
float x = bone.WorldX;
float y = bone.WorldY;
float m00 = bone.M00;
float m01 = bone.M01;
float m10 = bone.M10;
float m11 = bone.M11;
float[] vertices = Vertices;
float[] offset = Offset;
vertices[X1] = offset[X1] * m00 + offset[Y1] * m01 + x;
vertices[Y1] = offset[X1] * m10 + offset[Y1] * m11 + y;
vertices[X2] = offset[X2] * m00 + offset[Y2] * m01 + x;
vertices[Y2] = offset[X2] * m10 + offset[Y2] * m11 + y;
vertices[X3] = offset[X3] * m00 + offset[Y3] * m01 + x;
vertices[Y3] = offset[X3] * m10 + offset[Y3] * m11 + y;
vertices[X4] = offset[X4] * m00 + offset[Y4] * m01 + x;
vertices[Y4] = offset[X4] * m10 + offset[Y4] * m11 + y;
}
}
}

View File

@ -0,0 +1,177 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace Spine {
abstract public class BaseAtlas {
List<AtlasPage> pages = new List<AtlasPage>();
List<AtlasRegion> regions = new List<AtlasRegion>();
abstract protected AtlasPage NewAtlasPage (String path);
public void load (StreamReader reader, String imagesDir) {
String[] tuple = new String[4];
AtlasPage page = null;
while (true) {
String line = reader.ReadLine();
if (line == null) break;
if (line.Trim().Length == 0)
page = null;
else if (page == null) {
page = NewAtlasPage(Path.Combine(imagesDir, line));
page.Format = (Format)Enum.Parse(typeof(Format), readValue(reader), false);
readTuple(reader, tuple);
page.MinFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[0]);
page.MagFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[1]);
String direction = readValue(reader);
page.UWrap = TextureWrap.ClampToEdge;
page.VWrap = TextureWrap.ClampToEdge;
if (direction == "x")
page.UWrap = TextureWrap.Repeat;
else if (direction == "y")
page.VWrap = TextureWrap.Repeat;
else if (direction == "xy")
page.UWrap = page.VWrap = TextureWrap.Repeat;
pages.Add(page);
} else {
AtlasRegion region = new AtlasRegion();
region.Name = line;
region.Page = page;
region.Rotate = Boolean.Parse(readValue(reader));
readTuple(reader, tuple);
int x = int.Parse(tuple[0]);
int y = int.Parse(tuple[1]);
readTuple(reader, tuple);
int width = int.Parse(tuple[0]);
int height = int.Parse(tuple[1]);
float invTexWidth = 1f / page.GetTextureWidth();
float invTexHeight = 1f / page.GetTextureHeight();
region.U = x * invTexWidth;
region.V = y * invTexHeight;
region.U2 = (x + width) * invTexWidth;
region.V2 = (y + height) * invTexHeight;
region.Width = Math.Abs(width);
region.Height = Math.Abs(height);
if (readTuple(reader, tuple) == 4) { // split is optional
region.Splits = new int[] {int.Parse(tuple[0]), int.Parse(tuple[1]),
int.Parse(tuple[2]), int.Parse(tuple[3])};
if (readTuple(reader, tuple) == 4) { // pad is optional, but only present with splits
region.Pads = new int[] {int.Parse(tuple[0]), int.Parse(tuple[1]),
int.Parse(tuple[2]), int.Parse(tuple[3])};
readTuple(reader, tuple);
}
}
region.OriginalWidth = int.Parse(tuple[0]);
region.OriginalHeight = int.Parse(tuple[1]);
readTuple(reader, tuple);
region.OffsetX = int.Parse(tuple[0]);
region.OffsetY = int.Parse(tuple[1]);
region.Index = int.Parse(readValue(reader));
regions.Add(region);
}
}
}
static String readValue (StreamReader reader) {
String line = reader.ReadLine();
int colon = line.IndexOf(':');
if (colon == -1) throw new Exception("Invalid line: " + line);
return line.Substring(colon + 1).Trim();
}
/** Returns the number of tuple values read (2 or 4). */
static int readTuple (StreamReader reader, String[] tuple) {
String line = reader.ReadLine();
int colon = line.IndexOf(':');
if (colon == -1) throw new Exception("Invalid line: " + line);
int i = 0, lastMatch = colon + 1;
for (i = 0; i < 3; i++) {
int comma = line.IndexOf(',', lastMatch);
if (comma == -1) {
if (i == 0) throw new Exception("Invalid line: " + line);
break;
}
tuple[i] = line.Substring(lastMatch, comma - lastMatch).Trim();
lastMatch = comma + 1;
}
tuple[i] = line.Substring(lastMatch).Trim();
return i + 1;
}
/** Returns the first region found with the specified name. This method uses string comparison to find the region, so the result
* should be cached rather than calling this method multiple times.
* @return The region, or null. */
public AtlasRegion FindRegion (String name) {
for (int i = 0, n = regions.Count; i < n; i++)
if (regions[i].Name == name) return regions[i];
return null;
}
}
public enum Format {
Alpha,
Intensity,
LuminanceAlpha,
RGB565,
RGBA4444,
RGB888,
RGBA8888
}
public enum TextureFilter {
Nearest,
Linear,
MipMap,
MipMapNearestNearest,
MipMapLinearNearest,
MipMapNearestLinear,
MipMapLinearLinear
}
public enum TextureWrap {
MirroredRepeat,
ClampToEdge,
Repeat
}
abstract public class AtlasPage {
public Format Format;
public TextureFilter MinFilter;
public TextureFilter MagFilter;
public TextureWrap UWrap;
public TextureWrap VWrap;
abstract public int GetTextureWidth ();
abstract public int GetTextureHeight ();
}
public class AtlasRegion {
public AtlasPage Page;
public float U, V;
public float U2, V2;
public int Width, Height;
public int Index;
public String Name;
public float OffsetX, OffsetY;
public int OriginalWidth, OriginalHeight;
public bool Rotate;
public int[] Splits;
public int[] Pads;
}
}

83
spine-csharp/src/Bone.cs Normal file
View File

@ -0,0 +1,83 @@
using System;
namespace Spine {
public class Bone {
static public bool yDown;
public BoneData Data { get; private set; }
public Bone Parent { get; private set; }
public float X { get; set; }
public float Y { get; set; }
public float Rotation { get; set; }
public float ScaleX { get; set; }
public float ScaleY { get; set; }
public float M00 { get; private set; }
public float M01 { get; private set; }
public float M10 { get; private set; }
public float M11 { get; private set; }
public float WorldX { get; private set; }
public float WorldY { get; private set; }
public float WorldRotation { get; private set; }
public float WorldScaleX { get; private set; }
public float WorldScaleY { get; private set; }
/** @param parent May be null. */
public Bone (BoneData data, Bone parent) {
if (data == null) throw new ArgumentNullException("data cannot be null.");
Data = data;
Parent = parent;
SetToBindPose();
}
/** Computes the world SRT using the parent bone and the local SRT. */
public void UpdateWorldTransform (bool flipX, bool flipY) {
Bone parent = Parent;
if (parent != null) {
WorldX = X * parent.M00 + Y * parent.M01 + parent.WorldX;
WorldY = X * parent.M10 + Y * parent.M11 + parent.WorldY;
WorldScaleX = parent.WorldScaleX * ScaleX;
WorldScaleY = parent.WorldScaleY * ScaleY;
WorldRotation = parent.WorldRotation + Rotation;
} else {
WorldX = X;
WorldY = Y;
WorldScaleX = ScaleX;
WorldScaleY = ScaleY;
WorldRotation = Rotation;
}
float radians = WorldRotation * (float)Math.PI / 180;
float cos = (float)Math.Cos(radians);
float sin = (float)Math.Sin(radians);
M00 = cos * WorldScaleX;
M10 = sin * WorldScaleX;
M01 = -sin * WorldScaleY;
M11 = cos * WorldScaleY;
if (flipX) {
M00 = -M00;
M01 = -M01;
}
if (flipY) {
M10 = -M10;
M11 = -M11;
}
if (yDown) {
M10 = -M10;
M11 = -M11;
}
}
public void SetToBindPose () {
BoneData data = Data;
X = data.X;
Y = data.Y;
Rotation = data.Rotation;
ScaleX = data.ScaleX;
ScaleY = data.ScaleY;
}
override public String ToString () {
return Data.Name;
}
}
}

View File

@ -0,0 +1,28 @@
using System;
namespace Spine {
public class BoneData {
/** May be null. */
public BoneData Parent { get; private set; }
public String Name { get; private set; }
public float Length { get; set; }
public float X { get; set; }
public float Y { get; set; }
public float Rotation { get; set; }
public float ScaleX { get; set; }
public float ScaleY { get; set; }
/** @param parent May be null. */
public BoneData (String name, BoneData parent) {
if (name == null) throw new ArgumentNullException("name cannot be null.");
Name = name;
Parent = parent;
ScaleX = 1;
ScaleY = 1;
}
override public String ToString () {
return Name;
}
}
}

551
spine-csharp/src/Json.cs Normal file
View File

@ -0,0 +1,551 @@
/*
* Copyright (c) 2012 Calvin Rien
*
* Based on the JSON parser by Patrick van Bergen
* http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html
*
* Simplified it so that it doesn't throw exceptions
* and can be used in Unity iPhone with maximum code stripping.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Spine
{
// Example usage:
//
// using UnityEngine;
// using System.Collections;
// using System.Collections.Generic;
// using MiniJSON;
//
// public class MiniJSONTest : MonoBehaviour {
// void Start () {
// var jsonString = "{ \"array\": [1.44,2,3], " +
// "\"object\": {\"key1\":\"value1\", \"key2\":256}, " +
// "\"string\": \"The quick brown fox \\\"jumps\\\" over the lazy dog \", " +
// "\"unicode\": \"\\u3041 Men\u00fa sesi\u00f3n\", " +
// "\"int\": 65536, " +
// "\"float\": 3.1415926, " +
// "\"bool\": true, " +
// "\"null\": null }";
//
// var dict = Json.Deserialize(jsonString) as Dictionary<string,object>;
//
// Debug.Log("deserialized: " + dict.GetType());
// Debug.Log("dict['array'][0]: " + ((List<object>) dict["array"])[0]);
// Debug.Log("dict['string']: " + (string) dict["string"]);
// Debug.Log("dict['float']: " + (float) dict["float"]);
// Debug.Log("dict['int']: " + (long) dict["int"]); // ints come out as longs
// Debug.Log("dict['unicode']: " + (string) dict["unicode"]);
//
// var str = Json.Serialize(dict);
//
// Debug.Log("serialized: " + str);
// }
// }
/// <summary>
/// This class encodes and decodes JSON strings.
/// Spec. details, see http://www.json.org/
///
/// JSON uses Arrays and Objects. These correspond here to the datatypes IList and IDictionary.
/// All numbers are parsed to floats.
/// </summary>
public static class Json {
/// <summary>
/// Parses the string json into a value
/// </summary>
/// <param name="json">A JSON string.</param>
/// <returns>An List&lt;object&gt;, a Dictionary&lt;string, object&gt;, a float, an integer,a string, null, true, or false</returns>
public static object Deserialize(string json) {
// save the string for debug information
if (json == null) {
return null;
}
return Parser.Parse(json);
}
sealed class Parser : IDisposable {
const string WHITE_SPACE = " \t\n\r";
const string WORD_BREAK = " \t\n\r{}[],:\"";
enum TOKEN {
NONE,
CURLY_OPEN,
CURLY_CLOSE,
SQUARED_OPEN,
SQUARED_CLOSE,
COLON,
COMMA,
STRING,
NUMBER,
TRUE,
FALSE,
NULL
};
StringReader json;
Parser(string jsonString) {
json = new StringReader(jsonString);
}
public static object Parse(string jsonString) {
using (var instance = new Parser(jsonString)) {
return instance.ParseValue();
}
}
public void Dispose() {
json.Dispose();
json = null;
}
Dictionary<string, object> ParseObject() {
Dictionary<string, object> table = new Dictionary<string, object>();
// ditch opening brace
json.Read();
// {
while (true) {
switch (NextToken) {
case TOKEN.NONE:
return null;
case TOKEN.COMMA:
continue;
case TOKEN.CURLY_CLOSE:
return table;
default:
// name
string name = ParseString();
if (name == null) {
return null;
}
// :
if (NextToken != TOKEN.COLON) {
return null;
}
// ditch the colon
json.Read();
// value
table[name] = ParseValue();
break;
}
}
}
List<object> ParseArray() {
List<object> array = new List<object>();
// ditch opening bracket
json.Read();
// [
var parsing = true;
while (parsing) {
TOKEN nextToken = NextToken;
switch (nextToken) {
case TOKEN.NONE:
return null;
case TOKEN.COMMA:
continue;
case TOKEN.SQUARED_CLOSE:
parsing = false;
break;
default:
object value = ParseByToken(nextToken);
array.Add(value);
break;
}
}
return array;
}
object ParseValue() {
TOKEN nextToken = NextToken;
return ParseByToken(nextToken);
}
object ParseByToken(TOKEN token) {
switch (token) {
case TOKEN.STRING:
return ParseString();
case TOKEN.NUMBER:
return ParseNumber();
case TOKEN.CURLY_OPEN:
return ParseObject();
case TOKEN.SQUARED_OPEN:
return ParseArray();
case TOKEN.TRUE:
return true;
case TOKEN.FALSE:
return false;
case TOKEN.NULL:
return null;
default:
return null;
}
}
string ParseString() {
StringBuilder s = new StringBuilder();
char c;
// ditch opening quote
json.Read();
bool parsing = true;
while (parsing) {
if (json.Peek() == -1) {
parsing = false;
break;
}
c = NextChar;
switch (c) {
case '"':
parsing = false;
break;
case '\\':
if (json.Peek() == -1) {
parsing = false;
break;
}
c = NextChar;
switch (c) {
case '"':
case '\\':
case '/':
s.Append(c);
break;
case 'b':
s.Append('\b');
break;
case 'f':
s.Append('\f');
break;
case 'n':
s.Append('\n');
break;
case 'r':
s.Append('\r');
break;
case 't':
s.Append('\t');
break;
case 'u':
var hex = new StringBuilder();
for (int i=0; i< 4; i++) {
hex.Append(NextChar);
}
s.Append((char) Convert.ToInt32(hex.ToString(), 16));
break;
}
break;
default:
s.Append(c);
break;
}
}
return s.ToString();
}
object ParseNumber() {
string number = NextWord;
//NO!! always serialize to a float
//if (number.IndexOf('.') == -1) {
// long parsedInt;
// Int64.TryParse(number, out parsedInt);
// return parsedInt;
//}
float parsedFloat;
float.TryParse(number, out parsedFloat);
return parsedFloat;
}
void EatWhitespace() {
while (WHITE_SPACE.IndexOf(PeekChar) != -1) {
json.Read();
if (json.Peek() == -1) {
break;
}
}
}
char PeekChar {
get {
return Convert.ToChar(json.Peek());
}
}
char NextChar {
get {
return Convert.ToChar(json.Read());
}
}
string NextWord {
get {
StringBuilder word = new StringBuilder();
while (WORD_BREAK.IndexOf(PeekChar) == -1) {
word.Append(NextChar);
if (json.Peek() == -1) {
break;
}
}
return word.ToString();
}
}
TOKEN NextToken {
get {
EatWhitespace();
if (json.Peek() == -1) {
return TOKEN.NONE;
}
char c = PeekChar;
switch (c) {
case '{':
return TOKEN.CURLY_OPEN;
case '}':
json.Read();
return TOKEN.CURLY_CLOSE;
case '[':
return TOKEN.SQUARED_OPEN;
case ']':
json.Read();
return TOKEN.SQUARED_CLOSE;
case ',':
json.Read();
return TOKEN.COMMA;
case '"':
return TOKEN.STRING;
case ':':
return TOKEN.COLON;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
return TOKEN.NUMBER;
}
string word = NextWord;
switch (word) {
case "false":
return TOKEN.FALSE;
case "true":
return TOKEN.TRUE;
case "null":
return TOKEN.NULL;
}
return TOKEN.NONE;
}
}
}
/// <summary>
/// Converts a IDictionary / IList object or a simple type (string, int, etc.) into a JSON string
/// </summary>
/// <param name="json">A Dictionary&lt;string, object&gt; / List&lt;object&gt;</param>
/// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns>
public static string Serialize(object obj) {
return Serializer.Serialize(obj);
}
sealed class Serializer {
StringBuilder builder;
Serializer() {
builder = new StringBuilder();
}
public static string Serialize(object obj) {
var instance = new Serializer();
instance.SerializeValue(obj);
return instance.builder.ToString();
}
void SerializeValue(object value) {
IList asList;
IDictionary asDict;
string asStr;
if (value == null) {
builder.Append("null");
}
else if ((asStr = value as string) != null) {
SerializeString(asStr);
}
else if (value is bool) {
builder.Append(value.ToString().ToLower());
}
else if ((asList = value as IList) != null) {
SerializeArray(asList);
}
else if ((asDict = value as IDictionary) != null) {
SerializeObject(asDict);
}
else if (value is char) {
SerializeString(value.ToString());
}
else {
SerializeOther(value);
}
}
void SerializeObject(IDictionary obj) {
bool first = true;
builder.Append('{');
foreach (object e in obj.Keys) {
if (!first) {
builder.Append(',');
}
SerializeString(e.ToString());
builder.Append(':');
SerializeValue(obj[e]);
first = false;
}
builder.Append('}');
}
void SerializeArray(IList anArray) {
builder.Append('[');
bool first = true;
foreach (object obj in anArray) {
if (!first) {
builder.Append(',');
}
SerializeValue(obj);
first = false;
}
builder.Append(']');
}
void SerializeString(string str) {
builder.Append('\"');
char[] charArray = str.ToCharArray();
foreach (var c in charArray) {
switch (c) {
case '"':
builder.Append("\\\"");
break;
case '\\':
builder.Append("\\\\");
break;
case '\b':
builder.Append("\\b");
break;
case '\f':
builder.Append("\\f");
break;
case '\n':
builder.Append("\\n");
break;
case '\r':
builder.Append("\\r");
break;
case '\t':
builder.Append("\\t");
break;
default:
int codepoint = Convert.ToInt32(c);
if ((codepoint >= 32) && (codepoint <= 126)) {
builder.Append(c);
}
else {
builder.Append("\\u" + Convert.ToString(codepoint, 16).PadLeft(4, '0'));
}
break;
}
}
builder.Append('\"');
}
void SerializeOther(object value) {
if (value is float
|| value is int
|| value is uint
|| value is long
|| value is float
|| value is sbyte
|| value is byte
|| value is short
|| value is ushort
|| value is ulong
|| value is decimal) {
builder.Append(value.ToString());
}
else {
SerializeString(value.ToString());
}
}
}
}
}

View File

@ -0,0 +1,171 @@
using System;
using System.Collections.Generic;
namespace Spine {
public class Skeleton {
public SkeletonData Data { get; private set; }
public List<Bone> Bones { get; private set; }
public List<Slot> Slots { get; private set; }
public List<Slot> DrawOrder { get; private set; }
public Skin Skin { get; set; }
public float R { get; set; }
public float G { get; set; }
public float B { get; set; }
public float A { get; set; }
public float Time { get; set; }
public bool FlipX { get; set; }
public bool FlipY { get; set; }
public Bone RootBone {
get {
return Bones.Count == 0 ? null : Bones[0];
}
}
public Skeleton (SkeletonData data) {
if (data == null) throw new ArgumentNullException("data cannot be null.");
Data = data;
Bones = new List<Bone>(Data.Bones.Count);
foreach (BoneData boneData in Data.Bones) {
Bone parent = boneData.Parent == null ? null : Bones[Data.Bones.IndexOf(boneData.Parent)];
Bones.Add(new Bone(boneData, parent));
}
Slots = new List<Slot>(Data.Slots.Count);
DrawOrder = new List<Slot>(Data.Slots.Count);
foreach (SlotData slotData in Data.Slots) {
Bone bone = Bones[Data.Bones.IndexOf(slotData.BoneData)];
Slot slot = new Slot(slotData, this, bone);
Slots.Add(slot);
DrawOrder.Add(slot);
}
R = 1;
G = 1;
B = 1;
A = 1;
}
/** Updates the world transform for each bone. */
public void UpdateWorldTransform () {
bool flipX = FlipX;
bool flipY = FlipY;
List<Bone> bones = Bones;
for (int i = 0, n = bones.Count; i < n; i++)
bones[i].UpdateWorldTransform(flipX, flipY);
}
/** Sets the bones and slots to their bind pose values. */
public void SetToBindPose () {
SetBonesToBindPose();
SetSlotsToBindPose();
}
public void SetBonesToBindPose () {
List<Bone> bones = this.Bones;
for (int i = 0, n = bones.Count; i < n; i++)
bones[i].SetToBindPose();
}
public void SetSlotsToBindPose () {
List<Slot> slots = this.Slots;
for (int i = 0, n = slots.Count; i < n; i++)
slots[i].SetToBindPose(i);
}
/** @return May be null. */
public Bone FindBone (String boneName) {
if (boneName == null) throw new ArgumentNullException("boneName cannot be null.");
List<Bone> bones = this.Bones;
for (int i = 0, n = bones.Count; i < n; i++) {
Bone bone = bones[i];
if (bone.Data.Name == boneName) return bone;
}
return null;
}
/** @return -1 if the bone was not found. */
public int FindBoneIndex (String boneName) {
if (boneName == null) throw new ArgumentNullException("boneName cannot be null.");
List<Bone> bones = this.Bones;
for (int i = 0, n = bones.Count; i < n; i++)
if (bones[i].Data.Name == boneName) return i;
return -1;
}
/** @return May be null. */
public Slot FindSlot (String slotName) {
if (slotName == null) throw new ArgumentNullException("slotName cannot be null.");
List<Slot> slots = this.Slots;
for (int i = 0, n = slots.Count; i < n; i++) {
Slot slot = slots[i];
if (slot.Data.Name == slotName) return slot;
}
return null;
}
/** @return -1 if the bone was not found. */
public int FindSlotIndex (String slotName) {
if (slotName == null) throw new ArgumentNullException("slotName cannot be null.");
List<Slot> slots = this.Slots;
for (int i = 0, n = slots.Count; i < n; i++)
if (slots[i].Data.Name.Equals(slotName)) return i;
return -1;
}
/** Sets a skin by name.
* @see #setSkin(Skin) */
public void SetSkin (String skinName) {
Skin skin = Data.FindSkin(skinName);
if (skin == null) throw new ArgumentException("Skin not found: " + skinName);
SetSkin(skin);
}
/** Sets the skin used to look up attachments not found in the {@link SkeletonData#getDefaultSkin() default skin}. Attachments
* from the new skin are attached if the corresponding attachment from the old skin was attached.
* @param newSkin May be null. */
public void SetSkin (Skin newSkin) {
if (Skin != null && newSkin != null) newSkin.AttachAll(this, Skin);
Skin = newSkin;
}
/** @return May be null. */
public Attachment GetAttachment (String slotName, String attachmentName) {
return GetAttachment(Data.FindSlotIndex(slotName), attachmentName);
}
/** @return May be null. */
public Attachment GetAttachment (int slotIndex, String attachmentName) {
if (attachmentName == null) throw new ArgumentNullException("attachmentName cannot be null.");
if (Skin != null) {
Attachment attachment = Skin.GetAttachment(slotIndex, attachmentName);
if (attachment != null) return attachment;
}
if (Data.DefaultSkin != null) return Data.DefaultSkin.GetAttachment(slotIndex, attachmentName);
return null;
}
/** @param attachmentName May be null. */
public void SetAttachment (String slotName, String attachmentName) {
if (slotName == null) throw new ArgumentNullException("slotName cannot be null.");
List<Slot> slots = Slots;
for (int i = 0, n = slots.Count; i < n; i++) {
Slot slot = slots[i];
if (slot.Data.Name == slotName) {
Attachment attachment = null;
if (attachmentName != null) {
attachment = GetAttachment(i, attachmentName);
if (attachment == null) throw new ArgumentNullException("Attachment not found: " + attachmentName + ", for slot: " + slotName);
}
slot.Attachment = attachment;
return;
}
}
throw new Exception("Slot not found: " + slotName);
}
public void Update (float delta) {
Time += delta;
}
}
}

View File

@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
namespace Spine {
public class SkeletonData {
public String Name { get; set; }
public List<BoneData> Bones { get; private set; } // Ordered parents first.
public List<SlotData> Slots { get; private set; } // Bind pose draw order.
public List<Skin> Skins { get; private set; }
/** May be null. */
public Skin DefaultSkin;
public List<Animation> Animations { get; private set; }
public SkeletonData () {
Bones = new List<BoneData>();
Slots = new List<SlotData>();
Skins = new List<Skin>();
Animations = new List<Animation>();
}
// --- Bones.
public void AddBone (BoneData bone) {
if (bone == null) throw new ArgumentNullException("bone cannot be null.");
Bones.Add(bone);
}
/** @return May be null. */
public BoneData FindBone (String boneName) {
if (boneName == null) throw new ArgumentNullException("boneName cannot be null.");
for (int i = 0, n = Bones.Count; i < n; i++) {
BoneData bone = Bones[i];
if (bone.Name == boneName) return bone;
}
return null;
}
/** @return -1 if the bone was not found. */
public int FindBoneIndex (String boneName) {
if (boneName == null) throw new ArgumentNullException("boneName cannot be null.");
for (int i = 0, n = Bones.Count; i < n; i++)
if (Bones[i].Name == boneName) return i;
return -1;
}
// --- Slots.
public void AddSlot (SlotData slot) {
if (slot == null) throw new ArgumentNullException("slot cannot be null.");
Slots.Add(slot);
}
/** @return May be null. */
public SlotData FindSlot (String slotName) {
if (slotName == null) throw new ArgumentNullException("slotName cannot be null.");
for (int i = 0, n = Slots.Count; i < n; i++) {
SlotData slot = Slots[i];
if (slot.Name == slotName) return slot;
}
return null;
}
/** @return -1 if the bone was not found. */
public int FindSlotIndex (String slotName) {
if (slotName == null) throw new ArgumentNullException("slotName cannot be null.");
for (int i = 0, n = Slots.Count; i < n; i++)
if (Slots[i].Name == slotName) return i;
return -1;
}
// --- Skins.
public void AddSkin (Skin skin) {
if (skin == null) throw new ArgumentNullException("skin cannot be null.");
Skins.Add(skin);
}
/** @return May be null. */
public Skin FindSkin (String skinName) {
if (skinName == null) throw new ArgumentNullException("skinName cannot be null.");
foreach (Skin skin in Skins)
if (skin.Name == skinName) return skin;
return null;
}
// --- Animations.
public void AddAnimation (Animation animation) {
if (animation == null) throw new ArgumentNullException("animation cannot be null.");
Animations.Add(animation);
}
/** @return May be null. */
public Animation FindAnimation (String animationName) {
if (animationName == null) throw new ArgumentNullException("animationName cannot be null.");
for (int i = 0, n = Animations.Count; i < n; i++) {
Animation animation = Animations[i];
if (animation.Name == animationName) return animation;
}
return null;
}
// ---
override public String ToString () {
return Name != null ? Name : base.ToString();
}
}
}

View File

@ -0,0 +1,256 @@
using System;
using System.IO;
using System.Collections.Generic;
namespace Spine {
public class SkeletonJson {
static public String TIMELINE_SCALE = "scale";
static public String TIMELINE_ROTATE = "rotate";
static public String TIMELINE_TRANSLATE = "translate";
static public String TIMELINE_ATTACHMENT = "attachment";
static public String TIMELINE_COLOR = "color";
static public String ATTACHMENT_REGION = "region";
static public String ATTACHMENT_REGION_SEQUENCE = "regionSequence";
private AttachmentLoader attachmentLoader;
public float Scale { get; set; }
public SkeletonJson (BaseAtlas atlas) {
this.attachmentLoader = new AtlasAttachmentLoader(atlas);
Scale = 1;
}
public SkeletonJson (AttachmentLoader attachmentLoader) {
this.attachmentLoader = attachmentLoader;
Scale = 1;
}
public SkeletonData readSkeletonData (String name, String json) {
if (json == null) throw new ArgumentNullException("json cannot be null.");
SkeletonData skeletonData = new SkeletonData();
skeletonData.Name = name;
var root = Json.Deserialize(json) as Dictionary<String, Object>;
// Bones.
foreach (Dictionary<String, Object> boneMap in (List<Object>)root["bones"]) {
BoneData parent = null;
if (boneMap.ContainsKey("parent")) {
parent = skeletonData.FindBone((String)boneMap["parent"]);
if (parent == null) throw new Exception("Parent bone not found: " + boneMap["parent"]);
}
BoneData boneData = new BoneData((String)boneMap["name"], parent);
boneData.Length = getFloat(boneMap, "length", 0) * Scale;
boneData.X = getFloat(boneMap, "x", 0) * Scale;
boneData.Y = getFloat(boneMap, "y", 0) * Scale;
boneData.Rotation = getFloat(boneMap, "rotation", 0);
boneData.ScaleX = getFloat(boneMap, "scaleX", 1);
boneData.ScaleY = getFloat(boneMap, "scaleY", 1);
skeletonData.AddBone(boneData);
}
// Slots.
if (root.ContainsKey("slots")) {
var slots = (List<Object>)root["slots"];
foreach (Dictionary<String, Object> slotMap in (List<Object>)slots) {
String slotName = (String)slotMap["name"];
String boneName = (String)slotMap["bone"];
BoneData boneData = skeletonData.FindBone(boneName);
if (boneData == null) throw new Exception("Slot bone not found: " + boneName);
SlotData slotData = new SlotData(slotName, boneData);
if (slotMap.ContainsKey("color")) {
String color = (String)slotMap["color"];
slotData.R = toColor(color, 0);
slotData.G = toColor(color, 1);
slotData.B = toColor(color, 2);
slotData.A = toColor(color, 3);
}
slotData.AttachmentName = (String)slotMap["attachment"];
skeletonData.AddSlot(slotData);
}
}
// Skins.
if (root.ContainsKey("skins")) {
Dictionary<String, Object> skinMap = (Dictionary<String, Object>)root["skins"];
foreach (KeyValuePair<String, Object> entry in skinMap) {
Skin skin = new Skin(entry.Key);
foreach (KeyValuePair<String, Object> slotEntry in (Dictionary<String, Object>)entry.Value) {
int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key);
foreach (KeyValuePair<String, Object> attachmentEntry in ((Dictionary<String, Object>)slotEntry.Value)) {
Attachment attachment = readAttachment(attachmentEntry.Key, (Dictionary<String, Object>)attachmentEntry.Value);
skin.AddAttachment(slotIndex, attachmentEntry.Key, attachment);
}
}
skeletonData.AddSkin(skin);
if (skin.Name == "default") skeletonData.DefaultSkin = skin;
}
}
// Animations.
if (root.ContainsKey("animations")) {
Dictionary<String, Object> animationMap = (Dictionary<String, Object>)root["animations"];
foreach (KeyValuePair<String, Object> entry in animationMap)
readAnimation(entry.Key, (Dictionary<String, Object>)entry.Value, skeletonData);
}
skeletonData.Bones.TrimExcess();
skeletonData.Slots.TrimExcess();
skeletonData.Skins.TrimExcess();
skeletonData.Animations.TrimExcess();
return skeletonData;
}
private Attachment readAttachment (String name, Dictionary<String, Object> map) {
if (map.ContainsKey("name")) name = (String)map["name"];
AttachmentType type = AttachmentType.region;
if (map.ContainsKey("type")) type = (AttachmentType)Enum.Parse(typeof(AttachmentType), (String)map["type"], false);
Attachment attachment = attachmentLoader.NewAttachment(type, name);
if (attachment is RegionAttachment) {
RegionAttachment regionAttachment = (RegionAttachment)attachment;
regionAttachment.X = getFloat(map, "x", 0) * Scale;
regionAttachment.Y = getFloat(map, "y", 0) * Scale;
regionAttachment.ScaleX = getFloat(map, "scaleX", 1);
regionAttachment.ScaleY = getFloat(map, "scaleY", 1);
regionAttachment.Rotation = getFloat(map, "rotation", 0);
regionAttachment.Width = getFloat(map, "width", 32) * Scale;
regionAttachment.Height = getFloat(map, "height", 32) * Scale;
regionAttachment.UpdateOffset();
}
return attachment;
}
private float getFloat (Dictionary<String, Object> map, String name, float defaultValue) {
if (!map.ContainsKey(name)) return (float)defaultValue;
return (float)map[name];
}
public static float toColor (String hexString, int colorIndex) {
if (hexString.Length != 8) throw new ArgumentException("Color hexidecimal length must be 8, recieved: " + hexString);
return Convert.ToInt32(hexString.Substring(colorIndex * 2, 2), 16) / (float)255;
}
private void readAnimation (String name, Dictionary<String, Object> map, SkeletonData skeletonData) {
var timelines = new List<Timeline>();
float duration = 0;
var bonesMap = (Dictionary<String, Object>)map["bones"];
foreach (KeyValuePair<String, Object> entry in bonesMap) {
String boneName = entry.Key;
int boneIndex = skeletonData.FindBoneIndex(boneName);
if (boneIndex == -1) throw new Exception("Bone not found: " + boneName);
Dictionary<String, Object> timelineMap = (Dictionary<String, Object>)entry.Value;
foreach (KeyValuePair<String, Object> timelineEntry in timelineMap) {
List<Object> values = (List<Object>)timelineEntry.Value;
String timelineName = (String)timelineEntry.Key;
if (timelineName.Equals(TIMELINE_ROTATE)) {
RotateTimeline timeline = new RotateTimeline(values.Count);
timeline.BoneIndex = boneIndex;
int frameIndex = 0;
foreach (Dictionary<String, Object> valueMap in values) {
float time = (float)valueMap["time"];
timeline.SetFrame(frameIndex, time, (float)valueMap["angle"]);
readCurve(timeline, frameIndex, valueMap);
frameIndex++;
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.Frames[timeline.FrameCount * 2 - 2]);
} else if (timelineName.Equals(TIMELINE_TRANSLATE) || timelineName.Equals(TIMELINE_SCALE)) {
TranslateTimeline timeline;
float timelineScale = 1;
if (timelineName.Equals(TIMELINE_SCALE))
timeline = new ScaleTimeline(values.Count);
else {
timeline = new TranslateTimeline(values.Count);
timelineScale = Scale;
}
timeline.BoneIndex = boneIndex;
int frameIndex = 0;
foreach (Dictionary<String, Object> valueMap in values) {
float time = (float)valueMap["time"];
float x = valueMap.ContainsKey("x") ? (float)valueMap["x"] : 0;
float y = valueMap.ContainsKey("y") ? (float)valueMap["y"] : 0;
timeline.SetFrame(frameIndex, time, (float)x * timelineScale, (float)y * timelineScale);
readCurve(timeline, frameIndex, valueMap);
frameIndex++;
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.Frames[timeline.FrameCount * 3 - 3]);
} else
throw new Exception("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")");
}
}
if (map.ContainsKey("slots")) {
Dictionary<String, Object> slotsMap = (Dictionary<String, Object>)map["slots"];
foreach (KeyValuePair<String, Object> entry in slotsMap) {
String slotName = entry.Key;
int slotIndex = skeletonData.FindSlotIndex(slotName);
Dictionary<String, Object> timelineMap = (Dictionary<String, Object>)entry.Value;
foreach (KeyValuePair<String, Object> timelineEntry in timelineMap) {
List<Dictionary<String, Object>> values = (List<Dictionary<String, Object>>)timelineEntry.Value;
String timelineName = (String)timelineEntry.Key;
if (timelineName.Equals(TIMELINE_COLOR)) {
ColorTimeline timeline = new ColorTimeline(values.Count);
timeline.SlotIndex = slotIndex;
int frameIndex = 0;
foreach (Dictionary<String, Object> valueMap in values) {
float time = (float)valueMap["time"];
String c = (String)valueMap["color"];
timeline.setFrame(frameIndex, time, toColor(c, 0), toColor(c, 1), toColor(c, 2), toColor(c, 3));
readCurve(timeline, frameIndex, valueMap);
frameIndex++;
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.Frames[timeline.FrameCount * 5 - 5]);
} else if (timelineName.Equals(TIMELINE_ATTACHMENT)) {
AttachmentTimeline timeline = new AttachmentTimeline(values.Count);
timeline.SlotIndex = slotIndex;
int frameIndex = 0;
foreach (Dictionary<String, Object> valueMap in values) {
float time = (float)valueMap["time"];
timeline.setFrame(frameIndex++, time, (String)valueMap["name"]);
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.Frames[timeline.FrameCount - 1]);
} else
throw new Exception("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")");
}
}
}
timelines.TrimExcess();
skeletonData.AddAnimation(new Animation(name, timelines, duration));
}
private void readCurve (CurveTimeline timeline, int frameIndex, Dictionary<String, Object> valueMap) {
if (!valueMap.ContainsKey("curve")) return;
Object curveObject = valueMap["curve"];
if (curveObject.Equals("stepped"))
timeline.SetStepped(frameIndex);
else if (curveObject.GetType() == typeof(List<float>)) {
List<float> curve = (List<float>)curveObject;
timeline.SetCurve(frameIndex, (float)curve[0], (float)curve[1], (float)curve[2], (float)curve[3]);
}
}
}
}

54
spine-csharp/src/Skin.cs Normal file
View File

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
namespace Spine {
/** Stores attachments by slot index and attachment name. */
public class Skin {
public String Name { get; private set; }
private Dictionary<Tuple<int, String>, Attachment> attachments = new Dictionary<Tuple<int, String>, Attachment>();
public Skin (String name) {
if (name == null) throw new ArgumentNullException("name cannot be null.");
Name = name;
}
public void AddAttachment (int slotIndex, String name, Attachment attachment) {
if (attachment == null) throw new ArgumentNullException("attachment cannot be null.");
attachments.Add(Tuple.Create<int, String>(slotIndex, name), attachment);
}
/** @return May be null. */
public Attachment GetAttachment (int slotIndex, String name) {
return attachments[Tuple.Create<int, String>(slotIndex, name)];
}
public void FindNamesForSlot (int slotIndex, List<String> names) {
if (names == null) throw new ArgumentNullException("names cannot be null.");
foreach (Tuple<int, String> key in attachments.Keys)
if (key.Item1 == slotIndex) names.Add(key.Item2);
}
public void FindAttachmentsForSlot (int slotIndex, List<Attachment> attachments) {
if (attachments == null) throw new ArgumentNullException("attachments cannot be null.");
foreach (KeyValuePair<Tuple<int, String>, Attachment> entry in this.attachments)
if (entry.Key.Item1 == slotIndex) attachments.Add(entry.Value);
}
override public String ToString () {
return Name;
}
/** Attach all attachments from this skin if the corresponding attachment from the old skin is currently attached. */
internal void AttachAll (Skeleton skeleton, Skin oldSkin) {
foreach (KeyValuePair<Tuple<int, String>, Attachment> entry in oldSkin.attachments) {
int slotIndex = entry.Key.Item1;
Slot slot = skeleton.Slots[slotIndex];
if (slot.Attachment == entry.Value) {
Attachment attachment = GetAttachment(slotIndex, entry.Key.Item2);
if (attachment != null) slot.Attachment = attachment;
}
}
}
}
}

61
spine-csharp/src/Slot.cs Normal file
View File

@ -0,0 +1,61 @@
using System;
namespace Spine {
public class Slot {
public SlotData Data { get; private set; }
public Bone Bone { get; private set; }
public Skeleton Skeleton { get; private set; }
public float R { get; set; }
public float G { get; set; }
public float B { get; set; }
public float A { get; set; }
/** May be null. */
private Attachment attachment;
public Attachment Attachment {
get {
return attachment;
}
set {
attachment = value;
attachmentTime = Skeleton.Time;
}
}
private float attachmentTime;
public float AttachmentTime {
get {
return Skeleton.Time - attachmentTime;
}
set {
attachmentTime = Skeleton.Time - value;
}
}
public Slot (SlotData data, Skeleton skeleton, Bone bone) {
if (data == null) throw new ArgumentNullException("data cannot be null.");
if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
if (bone == null) throw new ArgumentNullException("bone cannot be null.");
Data = data;
Skeleton = skeleton;
Bone = bone;
SetToBindPose();
}
internal void SetToBindPose (int slotIndex) {
R = Data.R;
G = Data.G;
B = Data.B;
A = Data.A;
Attachment = Data.AttachmentName == null ? null : Skeleton.GetAttachment(slotIndex, Data.AttachmentName);
}
public void SetToBindPose () {
SetToBindPose(Skeleton.Data.Slots.IndexOf(Data));
}
override public String ToString () {
return Data.Name;
}
}
}

View File

@ -0,0 +1,29 @@
using System;
namespace Spine {
public class SlotData {
public String Name { get; private set; }
public BoneData BoneData { get; private set; }
public float R { get; set; }
public float G { get; set; }
public float B { get; set; }
public float A { get; set; }
/** @param attachmentName May be null. */
public String AttachmentName { get; set; }
public SlotData (String name, BoneData boneData) {
if (name == null) throw new ArgumentNullException("name cannot be null.");
if (boneData == null) throw new ArgumentNullException("boneData cannot be null.");
Name = name;
BoneData = boneData;
R = 1;
G = 1;
B = 1;
A = 1;
}
override public String ToString () {
return Name;
}
}
}

View File

@ -0,0 +1,34 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("spine-xna")]
[assembly: AssemblyProduct("spine-xna")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type. Only Windows
// assemblies support COM.
[assembly: ComVisible(false)]
// On Windows, the following GUID is for the ID of the typelib if this
// project is exposed to COM. On other platforms, it unique identifies the
// title storage container when deploying this assembly to the device.
[assembly: Guid("bce68f54-1e09-449a-90a2-b7ca28f491a5")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
[assembly: AssemblyVersion("1.0.0.0")]

BIN
spine-xna/example/Game.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -0,0 +1,34 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("spine-xna-example")]
[assembly: AssemblyProduct("spine-xna-example")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type. Only Windows
// assemblies support COM.
[assembly: ComVisible(false)]
// On Windows, the following GUID is for the ID of the typelib if this
// project is exposed to COM. On other platforms, it unique identifies the
// title storage container when deploying this assembly to the device.
[assembly: Guid("078eb4ac-3a70-4ab4-b103-a048c6a15898")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
[assembly: AssemblyVersion("1.0.0.0")]

View File

@ -0,0 +1,285 @@
goblins.png
format: RGBA8888
filter: Linear,Linear
repeat: none
spear
rotate: false
xy: 2, 142
size: 22, 368
orig: 22, 368
offset: 0, 0
index: -1
goblingirl/head
rotate: false
xy: 26, 429
size: 103, 81
orig: 103, 81
offset: 0, 0
index: -1
goblin/head
rotate: false
xy: 26, 361
size: 103, 66
orig: 103, 66
offset: 0, 0
index: -1
goblin/torso
rotate: false
xy: 131, 414
size: 68, 96
orig: 68, 96
offset: 0, 0
index: -1
goblingirl/torso
rotate: false
xy: 26, 263
size: 68, 96
orig: 68, 96
offset: 0, 0
index: -1
dagger
rotate: false
xy: 26, 153
size: 26, 108
orig: 26, 108
offset: 0, 0
index: -1
goblin/right-lower-leg
rotate: false
xy: 201, 434
size: 36, 76
orig: 36, 76
offset: 0, 0
index: -1
goblingirl/right-lower-leg
rotate: false
xy: 54, 185
size: 36, 76
orig: 36, 76
offset: 0, 0
index: -1
goblin/left-upper-leg
rotate: false
xy: 96, 286
size: 33, 73
orig: 33, 73
offset: 0, 0
index: -1
goblin/pelvis
rotate: false
xy: 131, 369
size: 62, 43
orig: 62, 43
offset: 0, 0
index: -1
goblingirl/pelvis
rotate: false
xy: 131, 324
size: 62, 43
orig: 62, 43
offset: 0, 0
index: -1
goblin/right-foot
rotate: false
xy: 131, 289
size: 63, 33
orig: 63, 33
offset: 0, 0
index: -1
goblin/left-lower-leg
rotate: false
xy: 2, 70
size: 33, 70
orig: 33, 70
offset: 0, 0
index: -1
goblin/right-upper-leg
rotate: false
xy: 2, 5
size: 34, 63
orig: 34, 63
offset: 0, 0
index: -1
goblingirl/left-lower-leg
rotate: false
xy: 195, 342
size: 33, 70
orig: 33, 70
offset: 0, 0
index: -1
goblingirl/left-upper-leg
rotate: false
xy: 37, 81
size: 33, 70
orig: 33, 70
offset: 0, 0
index: -1
goblingirl/right-upper-leg
rotate: false
xy: 38, 16
size: 34, 63
orig: 34, 63
offset: 0, 0
index: -1
goblin/eyes-closed
rotate: false
xy: 38, 2
size: 34, 12
orig: 34, 12
offset: 0, 0
index: -1
goblin/undies
rotate: false
xy: 54, 154
size: 36, 29
orig: 36, 29
offset: 0, 0
index: -1
goblin/right-arm
rotate: false
xy: 72, 102
size: 23, 50
orig: 23, 50
offset: 0, 0
index: -1
goblin/left-foot
rotate: false
xy: 131, 256
size: 65, 31
orig: 65, 31
offset: 0, 0
index: -1
goblingirl/right-arm
rotate: false
xy: 196, 290
size: 28, 50
orig: 28, 50
offset: 0, 0
index: -1
goblingirl/left-shoulder
rotate: false
xy: 226, 294
size: 28, 46
orig: 28, 46
offset: 0, 0
index: -1
goblin/left-arm
rotate: false
xy: 198, 253
size: 37, 35
orig: 37, 35
offset: 0, 0
index: -1
goblingirl/left-foot
rotate: false
xy: 92, 223
size: 65, 31
orig: 65, 31
offset: 0, 0
index: -1
goblingirl/right-foot
rotate: false
xy: 92, 188
size: 63, 33
orig: 63, 33
offset: 0, 0
index: -1
goblin/undie-straps
rotate: false
xy: 92, 167
size: 55, 19
orig: 55, 19
offset: 0, 0
index: -1
goblingirl/left-arm
rotate: false
xy: 159, 219
size: 37, 35
orig: 37, 35
offset: 0, 0
index: -1
goblin/right-shoulder
rotate: false
xy: 97, 120
size: 39, 45
orig: 39, 45
offset: 0, 0
index: -1
goblingirl/right-shoulder
rotate: false
xy: 198, 206
size: 39, 45
orig: 39, 45
offset: 0, 0
index: -1
goblin/left-hand
rotate: false
xy: 157, 176
size: 36, 41
orig: 36, 41
offset: 0, 0
index: -1
goblin/neck
rotate: false
xy: 195, 163
size: 36, 41
orig: 36, 41
offset: 0, 0
index: -1
goblingirl/undie-straps
rotate: false
xy: 97, 99
size: 55, 19
orig: 55, 19
offset: 0, 0
index: -1
goblingirl/neck
rotate: false
xy: 138, 120
size: 35, 41
orig: 35, 41
offset: 0, 0
index: -1
goblingirl/left-hand
rotate: false
xy: 175, 121
size: 35, 40
orig: 35, 40
offset: 0, 0
index: -1
goblin/left-shoulder
rotate: false
xy: 212, 117
size: 29, 44
orig: 29, 44
offset: 0, 0
index: -1
goblingirl/eyes-closed
rotate: false
xy: 154, 97
size: 37, 21
orig: 37, 21
offset: 0, 0
index: -1
goblin/right-hand
rotate: false
xy: 193, 78
size: 36, 37
orig: 36, 37
offset: 0, 0
index: -1
goblingirl/right-hand
rotate: false
xy: 74, 39
size: 36, 37
orig: 36, 37
offset: 0, 0
index: -1
goblingirl/undies
rotate: false
xy: 74, 8
size: 36, 29
orig: 36, 29
offset: 0, 0
index: -1

View File

@ -0,0 +1,499 @@
{
"bones": [
{ "name": "root" },
{ "name": "hip", "parent": "root", "x": 0.64, "y": 114.41 },
{ "name": "left upper leg", "parent": "hip", "length": 50.39, "x": 14.45, "y": 2.81, "rotation": -89.09 },
{ "name": "left lower leg", "parent": "left upper leg", "length": 49.89, "x": 56.34, "y": 0.98, "rotation": -16.65 },
{ "name": "left foot", "parent": "left lower leg", "length": 46.5, "x": 58.94, "y": -7.61, "rotation": 102.43 },
{ "name": "right upper leg", "parent": "hip", "length": 42.45, "x": -20.07, "y": -6.83, "rotation": -97.49 },
{ "name": "right lower leg", "parent": "right upper leg", "length": 58.52, "x": 42.99, "y": -0.61, "rotation": -14.34 },
{ "name": "right foot", "parent": "right lower leg", "length": 45.45, "x": 64.88, "y": 0.04, "rotation": 110.3 },
{ "name": "torso", "parent": "hip", "length": 85.82, "x": -6.42, "y": 1.97, "rotation": 93.92 },
{ "name": "neck", "parent": "torso", "length": 18.38, "x": 81.67, "y": -6.34, "rotation": -1.51 },
{ "name": "head", "parent": "neck", "length": 68.28, "x": 20.93, "y": 11.59, "rotation": -13.92 },
{ "name": "right shoulder", "parent": "torso", "length": 37.24, "x": 76.02, "y": 18.14, "rotation": 133.88 },
{ "name": "right arm", "parent": "right shoulder", "length": 36.74, "x": 37.6, "y": 0.31, "rotation": 36.32 },
{ "name": "right hand", "parent": "right arm", "length": 15.32, "x": 36.9, "y": 0.34, "rotation": 2.35 },
{ "name": "left shoulder", "parent": "torso", "length": 35.43, "x": 74.04, "y": -20.38, "rotation": -156.96 },
{ "name": "left arm", "parent": "left shoulder", "length": 35.62, "x": 37.85, "y": -2.34, "rotation": 28.16 },
{ "name": "left hand", "parent": "left arm", "length": 11.52, "x": 35.62, "y": 0.07, "rotation": 2.7 },
{ "name": "pelvis", "parent": "hip", "x": 1.41, "y": -6.57 }
],
"slots": [
{ "name": "left shoulder", "bone": "left shoulder", "attachment": "left shoulder" },
{ "name": "left arm", "bone": "left arm", "attachment": "left arm" },
{ "name": "left hand item", "bone": "left hand", "attachment": "spear" },
{ "name": "left hand", "bone": "left hand", "attachment": "left hand" },
{ "name": "left foot", "bone": "left foot", "attachment": "left foot" },
{ "name": "left lower leg", "bone": "left lower leg", "attachment": "left lower leg" },
{ "name": "left upper leg", "bone": "left upper leg", "attachment": "left upper leg" },
{ "name": "neck", "bone": "neck", "attachment": "neck" },
{ "name": "torso", "bone": "torso", "attachment": "torso" },
{ "name": "pelvis", "bone": "pelvis", "attachment": "pelvis" },
{ "name": "right foot", "bone": "right foot", "attachment": "right foot" },
{ "name": "right lower leg", "bone": "right lower leg", "attachment": "right lower leg" },
{ "name": "undie straps", "bone": "pelvis", "attachment": "undie straps" },
{ "name": "undies", "bone": "pelvis", "attachment": "undies" },
{ "name": "right upper leg", "bone": "right upper leg", "attachment": "right upper leg" },
{ "name": "head", "bone": "head", "attachment": "head" },
{ "name": "eyes", "bone": "head" },
{ "name": "right shoulder", "bone": "right shoulder", "attachment": "right shoulder" },
{ "name": "right arm", "bone": "right arm", "attachment": "right arm" },
{ "name": "right hand item", "bone": "right hand", "attachment": "dagger" },
{ "name": "right hand", "bone": "right hand", "attachment": "right hand" }
],
"skins": {
"default": {
"left hand item": {
"dagger": { "x": 7.88, "y": -23.45, "rotation": 10.47, "width": 26, "height": 108 },
"spear": { "x": -4.55, "y": 39.2, "rotation": 13.04, "width": 22, "height": 368 }
},
"right hand item": {
"dagger": { "x": 6.51, "y": -24.15, "rotation": -8.06, "width": 26, "height": 108 }
}
},
"goblin": {
"neck": {
"neck": { "name": "goblin/neck", "x": 10.1, "y": 0.42, "rotation": -93.69, "width": 36, "height": 41 }
},
"undies": {
"undies": { "name": "goblin/undies", "x": 6.3, "y": 0.12, "rotation": 0.91, "width": 36, "height": 29 }
},
"right hand": {
"right hand": { "name": "goblin/right-hand", "x": 7.88, "y": 2.78, "rotation": 91.96, "width": 36, "height": 37 }
},
"right arm": {
"right arm": { "name": "goblin/right-arm", "x": 16.44, "y": -1.04, "rotation": 94.32, "width": 23, "height": 50 }
},
"head": {
"head": { "name": "goblin/head", "x": 25.73, "y": 2.33, "rotation": -92.29, "width": 103, "height": 66 }
},
"left shoulder": {
"left shoulder": { "name": "goblin/left-shoulder", "x": 15.56, "y": -2.26, "rotation": 62.01, "width": 29, "height": 44 }
},
"left arm": {
"left arm": {
"name": "goblin/left-arm",
"x": 16.7,
"y": -1.69,
"scaleX": 1.057,
"scaleY": 1.057,
"rotation": 33.84,
"width": 37,
"height": 35
}
},
"left hand": {
"left hand": {
"name": "goblin/left-hand",
"x": 3.47,
"y": 3.41,
"scaleX": 0.892,
"scaleY": 0.892,
"rotation": 31.14,
"width": 36,
"height": 41
}
},
"right lower leg": {
"right lower leg": { "name": "goblin/right-lower-leg", "x": 25.68, "y": -3.15, "rotation": 111.83, "width": 36, "height": 76 }
},
"right upper leg": {
"right upper leg": { "name": "goblin/right-upper-leg", "x": 20.35, "y": 1.47, "rotation": 97.49, "width": 34, "height": 63 }
},
"pelvis": {
"pelvis": { "name": "goblin/pelvis", "x": -5.61, "y": 0.76, "width": 62, "height": 43 }
},
"left lower leg": {
"left lower leg": { "name": "goblin/left-lower-leg", "x": 23.58, "y": -2.06, "rotation": 105.75, "width": 33, "height": 70 }
},
"left upper leg": {
"left upper leg": { "name": "goblin/left-upper-leg", "x": 29.68, "y": -3.87, "rotation": 89.09, "width": 33, "height": 73 }
},
"torso": {
"torso": { "name": "goblin/torso", "x": 38.09, "y": -3.87, "rotation": -94.95, "width": 68, "height": 96 }
},
"right shoulder": {
"right shoulder": { "name": "goblin/right-shoulder", "x": 15.68, "y": -1.03, "rotation": 130.65, "width": 39, "height": 45 }
},
"right foot": {
"right foot": { "name": "goblin/right-foot", "x": 23.56, "y": 9.8, "rotation": 1.52, "width": 63, "height": 33 }
},
"left foot": {
"left foot": { "name": "goblin/left-foot", "x": 24.85, "y": 8.74, "rotation": 3.32, "width": 65, "height": 31 }
},
"undie straps": {
"undie straps": { "name": "goblin/undie-straps", "x": -3.87, "y": 13.1, "scaleX": 1.089, "width": 55, "height": 19 }
},
"eyes": {
"eyes closed": { "name": "goblin/eyes-closed", "x": 32.21, "y": -21.27, "rotation": -88.92, "width": 34, "height": 12 }
}
},
"goblingirl": {
"left upper leg": {
"left upper leg": { "name": "goblingirl/left-upper-leg", "x": 30.21, "y": -2.95, "rotation": 89.09, "width": 33, "height": 70 }
},
"left lower leg": {
"left lower leg": { "name": "goblingirl/left-lower-leg", "x": 25.02, "y": -0.6, "rotation": 105.75, "width": 33, "height": 70 }
},
"left foot": {
"left foot": { "name": "goblingirl/left-foot", "x": 25.17, "y": 7.92, "rotation": 3.32, "width": 65, "height": 31 }
},
"right upper leg": {
"right upper leg": { "name": "goblingirl/right-upper-leg", "x": 19.69, "y": 2.13, "rotation": 97.49, "width": 34, "height": 63 }
},
"right lower leg": {
"right lower leg": { "name": "goblingirl/right-lower-leg", "x": 26.15, "y": -3.27, "rotation": 111.83, "width": 36, "height": 76 }
},
"right foot": {
"right foot": { "name": "goblingirl/right-foot", "x": 23.46, "y": 9.66, "rotation": 1.52, "width": 63, "height": 33 }
},
"torso": {
"torso": { "name": "goblingirl/torso", "x": 36.28, "y": -5.14, "rotation": -95.74, "width": 68, "height": 96 }
},
"left shoulder": {
"left shoulder": { "name": "goblingirl/left-shoulder", "x": 19.8, "y": -0.42, "rotation": 61.21, "width": 28, "height": 46 }
},
"left arm": {
"left arm": { "name": "goblingirl/left-arm", "x": 19.64, "y": -2.42, "rotation": 33.05, "width": 37, "height": 35 }
},
"left hand": {
"left hand": {
"name": "goblingirl/left-hand",
"x": 4.34,
"y": 2.39,
"scaleX": 0.896,
"scaleY": 0.896,
"rotation": 30.34,
"width": 35,
"height": 40
}
},
"neck": {
"neck": { "name": "goblingirl/neck", "x": 6.16, "y": -3.14, "rotation": -98.86, "width": 35, "height": 41 }
},
"head": {
"head": { "name": "goblingirl/head", "x": 27.71, "y": -4.32, "rotation": -85.58, "width": 103, "height": 81 }
},
"right shoulder": {
"right shoulder": { "name": "goblingirl/right-shoulder", "x": 14.46, "y": 0.45, "rotation": 129.85, "width": 39, "height": 45 }
},
"right arm": {
"right arm": { "name": "goblingirl/right-arm", "x": 16.85, "y": -0.66, "rotation": 93.52, "width": 28, "height": 50 }
},
"right hand": {
"right hand": { "name": "goblingirl/right-hand", "x": 7.21, "y": 3.43, "rotation": 91.16, "width": 36, "height": 37 }
},
"pelvis": {
"pelvis": { "name": "goblingirl/pelvis", "x": -3.87, "y": 3.18, "width": 62, "height": 43 }
},
"undie straps": {
"undie straps": { "name": "goblingirl/undie-straps", "x": -1.51, "y": 14.18, "width": 55, "height": 19 }
},
"undies": {
"undies": { "name": "goblingirl/undies", "x": 5.4, "y": 1.7, "width": 36, "height": 29 }
},
"eyes": {
"eyes closed": { "name": "goblingirl/eyes-closed", "x": 28, "y": -25.54, "rotation": -87.04, "width": 37, "height": 21 }
}
}
},
"animations": {
"walk": {
"bones": {
"left upper leg": {
"rotate": [
{ "time": 0, "angle": -26.55 },
{ "time": 0.1333, "angle": -8.78 },
{ "time": 0.2333, "angle": 9.51 },
{ "time": 0.3666, "angle": 30.74 },
{ "time": 0.5, "angle": 25.33 },
{ "time": 0.6333, "angle": 26.11 },
{ "time": 0.7333, "angle": -7.7 },
{ "time": 0.8666, "angle": -21.19 },
{ "time": 1, "angle": -26.55 }
],
"translate": [
{ "time": 0, "x": -1.32, "y": 1.7 },
{ "time": 0.3666, "x": -0.06, "y": 2.42 },
{ "time": 1, "x": -1.32, "y": 1.7 }
]
},
"right upper leg": {
"rotate": [
{ "time": 0, "angle": 42.45 },
{ "time": 0.1333, "angle": 52.1 },
{ "time": 0.2333, "angle": 8.53 },
{ "time": 0.5, "angle": -16.93 },
{ "time": 0.6333, "angle": 1.89 },
{
"time": 0.7333,
"angle": 28.06,
"curve": [ 0.462, 0.11, 1, 1 ]
},
{
"time": 0.8666,
"angle": 58.68,
"curve": [ 0.5, 0.02, 1, 1 ]
},
{ "time": 1, "angle": 42.45 }
],
"translate": [
{ "time": 0, "x": 6.23, "y": 0 },
{ "time": 0.2333, "x": 2.14, "y": 2.4 },
{ "time": 0.5, "x": 2.44, "y": 4.8 },
{ "time": 1, "x": 6.23, "y": 0 }
]
},
"left lower leg": {
"rotate": [
{ "time": 0, "angle": -22.98 },
{ "time": 0.1333, "angle": -63.5 },
{ "time": 0.2333, "angle": -73.76 },
{ "time": 0.5, "angle": 5.11 },
{ "time": 0.6333, "angle": -28.29 },
{ "time": 0.7333, "angle": 4.08 },
{ "time": 0.8666, "angle": 3.53 },
{ "time": 1, "angle": -22.98 }
],
"translate": [
{ "time": 0, "x": 0, "y": 0 },
{ "time": 0.2333, "x": 2.55, "y": -0.47 },
{ "time": 0.5, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 1, "x": 0, "y": 0 }
]
},
"left foot": {
"rotate": [
{ "time": 0, "angle": -3.69 },
{ "time": 0.1333, "angle": -10.42 },
{ "time": 0.2333, "angle": -5.01 },
{ "time": 0.3666, "angle": 3.87 },
{ "time": 0.5, "angle": -3.87 },
{ "time": 0.6333, "angle": 2.78 },
{ "time": 0.7333, "angle": 1.68 },
{ "time": 0.8666, "angle": -8.54 },
{ "time": 1, "angle": -3.69 }
]
},
"right shoulder": {
"rotate": [
{
"time": 0,
"angle": 5.29,
"curve": [ 0.264, 0, 0.75, 1 ]
},
{ "time": 0.6333, "angle": 6.65 },
{ "time": 1, "angle": 5.29 }
]
},
"right arm": {
"rotate": [
{
"time": 0,
"angle": -4.02,
"curve": [ 0.267, 0, 0.804, 0.99 ]
},
{
"time": 0.6333,
"angle": 19.78,
"curve": [ 0.307, 0, 0.787, 0.99 ]
},
{ "time": 1, "angle": -4.02 }
]
},
"right hand": {
"rotate": [
{ "time": 0, "angle": 8.98 },
{ "time": 0.6333, "angle": 0.51 },
{ "time": 1, "angle": 8.98 }
]
},
"left shoulder": {
"rotate": [
{
"time": 0,
"angle": 6.25,
"curve": [ 0.339, 0, 0.683, 1 ]
},
{
"time": 0.5,
"angle": -11.78,
"curve": [ 0.281, 0, 0.686, 0.99 ]
},
{ "time": 1, "angle": 6.25 }
],
"translate": [
{ "time": 0, "x": 1.15, "y": 0.23 }
]
},
"left hand": {
"rotate": [
{
"time": 0,
"angle": -21.23,
"curve": [ 0.295, 0, 0.755, 0.98 ]
},
{
"time": 0.5,
"angle": -27.28,
"curve": [ 0.241, 0, 0.75, 0.97 ]
},
{ "time": 1, "angle": -21.23 }
]
},
"left arm": {
"rotate": [
{
"time": 0,
"angle": 28.37,
"curve": [ 0.339, 0, 0.683, 1 ]
},
{
"time": 0.5,
"angle": 60.09,
"curve": [ 0.281, 0, 0.686, 0.99 ]
},
{ "time": 1, "angle": 28.37 }
]
},
"torso": {
"rotate": [
{ "time": 0, "angle": -10.28 },
{
"time": 0.1333,
"angle": -15.38,
"curve": [ 0.545, 0, 0.818, 1 ]
},
{
"time": 0.3666,
"angle": -9.78,
"curve": [ 0.58, 0.17, 0.669, 0.99 ]
},
{
"time": 0.6333,
"angle": -15.75,
"curve": [ 0.235, 0.01, 0.795, 1 ]
},
{
"time": 0.8666,
"angle": -7.06,
"curve": [ 0.209, 0, 0.816, 0.98 ]
},
{ "time": 1, "angle": -10.28 }
],
"translate": [
{ "time": 0, "x": -1.29, "y": 1.68 }
]
},
"right foot": {
"rotate": [
{ "time": 0, "angle": -5.25 },
{ "time": 0.2333, "angle": -1.91 },
{ "time": 0.3666, "angle": -6.45 },
{ "time": 0.5, "angle": -5.39 },
{ "time": 0.7333, "angle": -11.68 },
{ "time": 0.8666, "angle": 0.46 },
{ "time": 1, "angle": -5.25 }
]
},
"right lower leg": {
"rotate": [
{
"time": 0,
"angle": -3.39,
"curve": [ 0.316, 0.01, 0.741, 0.98 ]
},
{
"time": 0.1333,
"angle": -45.53,
"curve": [ 0.229, 0, 0.738, 0.97 ]
},
{ "time": 0.2333, "angle": -4.83 },
{ "time": 0.5, "angle": -19.53 },
{ "time": 0.6333, "angle": -64.8 },
{
"time": 0.7333,
"angle": -82.56,
"curve": [ 0.557, 0.18, 1, 1 ]
},
{ "time": 1, "angle": -3.39 }
],
"translate": [
{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 0.5, "x": 0, "y": 0 },
{ "time": 0.6333, "x": 2.18, "y": 0.21 },
{ "time": 1, "x": 0, "y": 0 }
]
},
"hip": {
"rotate": [
{ "time": 0, "angle": 0, "curve": "stepped" },
{ "time": 1, "angle": 0 }
],
"translate": [
{ "time": 0, "x": 0, "y": -4.16 },
{
"time": 0.1333,
"x": 0,
"y": -7.05,
"curve": [ 0.359, 0.47, 0.646, 0.74 ]
},
{ "time": 0.3666, "x": 0, "y": 6.78 },
{ "time": 0.5, "x": 0, "y": -6.13 },
{
"time": 0.6333,
"x": 0,
"y": -7.05,
"curve": [ 0.359, 0.47, 0.646, 0.74 ]
},
{ "time": 0.8666, "x": 0, "y": 6.78 },
{ "time": 1, "x": 0, "y": -4.16 }
]
},
"neck": {
"rotate": [
{ "time": 0, "angle": 3.6 },
{ "time": 0.1333, "angle": 17.49 },
{ "time": 0.2333, "angle": 6.1 },
{ "time": 0.3666, "angle": 3.45 },
{ "time": 0.5, "angle": 5.17 },
{ "time": 0.6333, "angle": 18.36 },
{ "time": 0.7333, "angle": 6.09 },
{ "time": 0.8666, "angle": 2.28 },
{ "time": 1, "angle": 3.6 }
]
},
"head": {
"rotate": [
{
"time": 0,
"angle": 3.6,
"curve": [ 0, 0, 0.704, 1.17 ]
},
{ "time": 0.1333, "angle": -0.2 },
{ "time": 0.2333, "angle": 6.1 },
{ "time": 0.3666, "angle": 3.45 },
{
"time": 0.5,
"angle": 5.17,
"curve": [ 0, 0, 0.704, 1.61 ]
},
{ "time": 0.6666, "angle": 1.1 },
{ "time": 0.7333, "angle": 6.09 },
{ "time": 0.8666, "angle": 2.28 },
{ "time": 1, "angle": 3.6 }
]
}
},
"slots": {
"eyes": {
"attachment": [
{ "time": 0.7, "name": "eyes closed" },
{ "time": 0.8, "name": null }
]
}
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

View File

@ -0,0 +1,166 @@
spineboy.png
format: RGBA8888
filter: Linear,Linear
repeat: none
head
rotate: false
xy: 1, 122
size: 121, 132
orig: 121, 132
offset: 0, 0
index: -1
torso
rotate: false
xy: 1, 28
size: 68, 92
orig: 68, 92
offset: 0, 0
index: -1
left-pant-bottom
rotate: false
xy: 1, 4
size: 44, 22
orig: 44, 22
offset: 0, 0
index: -1
right-pant-bottom
rotate: false
xy: 47, 8
size: 46, 18
orig: 46, 18
offset: 0, 0
index: -1
right-upper-leg
rotate: false
xy: 71, 50
size: 44, 70
orig: 44, 70
offset: 0, 0
index: -1
pelvis
rotate: false
xy: 95, 1
size: 63, 47
orig: 63, 47
offset: 0, 0
index: -1
left-upper-leg
rotate: false
xy: 117, 53
size: 33, 67
orig: 33, 67
offset: 0, 0
index: -1
right-foot
rotate: false
xy: 160, 224
size: 67, 30
orig: 67, 30
offset: 0, 0
index: -1
left-shoulder
rotate: false
xy: 124, 201
size: 34, 53
orig: 34, 53
offset: 0, 0
index: -1
left-ankle
rotate: false
xy: 229, 222
size: 25, 32
orig: 25, 32
offset: 0, 0
index: -1
left-foot
rotate: false
xy: 160, 192
size: 65, 30
orig: 65, 30
offset: 0, 0
index: -1
neck
rotate: false
xy: 124, 171
size: 34, 28
orig: 34, 28
offset: 0, 0
index: -1
right-arm
rotate: false
xy: 124, 124
size: 21, 45
orig: 21, 45
offset: 0, 0
index: -1
right-ankle
rotate: false
xy: 227, 190
size: 25, 30
orig: 25, 30
offset: 0, 0
index: -1
left-hand
rotate: false
xy: 147, 131
size: 35, 38
orig: 35, 38
offset: 0, 0
index: -1
left-arm
rotate: false
xy: 184, 161
size: 35, 29
orig: 35, 29
offset: 0, 0
index: -1
eyes-closed
rotate: false
xy: 221, 161
size: 34, 27
orig: 34, 27
offset: 0, 0
index: -1
right-lower-leg
rotate: false
xy: 152, 65
size: 51, 64
orig: 51, 64
offset: 0, 0
index: -1
right-foot-idle
rotate: false
xy: 184, 131
size: 53, 28
orig: 53, 28
offset: 0, 0
index: -1
left-lower-leg
rotate: false
xy: 205, 65
size: 49, 64
orig: 49, 64
offset: 0, 0
index: -1
right-shoulder
rotate: false
xy: 160, 12
size: 52, 51
orig: 52, 51
offset: 0, 0
index: -1
eyes
rotate: false
xy: 214, 36
size: 34, 27
orig: 34, 27
offset: 0, 0
index: -1
right-hand
rotate: false
xy: 214, 2
size: 32, 32
orig: 32, 32
offset: 0, 0
index: -1

View File

@ -0,0 +1,787 @@
{
"bones": [
{ "name": "root" },
{ "name": "hip", "parent": "root", "x": 0.64, "y": 114.41 },
{ "name": "left upper leg", "parent": "hip", "length": 50.39, "x": 14.45, "y": 2.81, "rotation": -89.09 },
{ "name": "left lower leg", "parent": "left upper leg", "length": 56.45, "x": 51.78, "y": 3.46, "rotation": -16.65 },
{ "name": "left foot", "parent": "left lower leg", "length": 46.5, "x": 64.02, "y": -8.67, "rotation": 102.43 },
{ "name": "right upper leg", "parent": "hip", "length": 45.76, "x": -18.27, "rotation": -101.13 },
{ "name": "right lower leg", "parent": "right upper leg", "length": 58.52, "x": 50.21, "y": 0.6, "rotation": -10.7 },
{ "name": "right foot", "parent": "right lower leg", "length": 45.45, "x": 64.88, "y": 0.04, "rotation": 110.3 },
{ "name": "torso", "parent": "hip", "length": 85.82, "x": -6.42, "y": 1.97, "rotation": 94.95 },
{ "name": "neck", "parent": "torso", "length": 18.38, "x": 83.64, "y": -1.78, "rotation": 0.9 },
{ "name": "head", "parent": "neck", "length": 68.28, "x": 19.09, "y": 6.97, "rotation": -8.94 },
{ "name": "right shoulder", "parent": "torso", "length": 49.95, "x": 81.9, "y": 6.79, "rotation": 130.6 },
{ "name": "right arm", "parent": "right shoulder", "length": 36.74, "x": 49.95, "y": -0.12, "rotation": 40.12 },
{ "name": "right hand", "parent": "right arm", "length": 15.32, "x": 36.9, "y": 0.34, "rotation": 2.35 },
{ "name": "left shoulder", "parent": "torso", "length": 44.19, "x": 78.96, "y": -15.75, "rotation": -156.96 },
{ "name": "left arm", "parent": "left shoulder", "length": 35.62, "x": 44.19, "y": -0.01, "rotation": 28.16 },
{ "name": "left hand", "parent": "left arm", "length": 11.52, "x": 35.62, "y": 0.07, "rotation": 2.7 },
{ "name": "pelvis", "parent": "hip", "x": 1.41, "y": -6.57 }
],
"slots": [
{ "name": "left shoulder", "bone": "left shoulder", "attachment": "left-shoulder" },
{ "name": "left arm", "bone": "left arm", "attachment": "left-arm" },
{ "name": "left hand", "bone": "left hand", "attachment": "left-hand" },
{ "name": "left foot", "bone": "left foot", "attachment": "left-foot" },
{ "name": "left lower leg", "bone": "left lower leg", "attachment": "left-lower-leg" },
{ "name": "left upper leg", "bone": "left upper leg", "attachment": "left-upper-leg" },
{ "name": "pelvis", "bone": "pelvis", "attachment": "pelvis" },
{ "name": "right foot", "bone": "right foot", "attachment": "right-foot" },
{ "name": "right lower leg", "bone": "right lower leg", "attachment": "right-lower-leg" },
{ "name": "right upper leg", "bone": "right upper leg", "attachment": "right-upper-leg" },
{ "name": "torso", "bone": "torso", "attachment": "torso" },
{ "name": "neck", "bone": "neck", "attachment": "neck" },
{ "name": "head", "bone": "head", "attachment": "head" },
{ "name": "eyes", "bone": "head", "attachment": "eyes" },
{ "name": "right shoulder", "bone": "right shoulder", "attachment": "right-shoulder" },
{ "name": "right arm", "bone": "right arm", "attachment": "right-arm" },
{ "name": "right hand", "bone": "right hand", "attachment": "right-hand" }
],
"skins": {
"default": {
"left shoulder": {
"left-shoulder": { "x": 23.74, "y": 0.11, "rotation": 62.01, "width": 34, "height": 53 }
},
"left arm": {
"left-arm": { "x": 15.11, "y": -0.44, "rotation": 33.84, "width": 35, "height": 29 }
},
"left hand": {
"left-hand": { "x": 0.75, "y": 1.86, "rotation": 31.14, "width": 35, "height": 38 }
},
"left foot": {
"left-foot": { "x": 24.35, "y": 8.88, "rotation": 3.32, "width": 65, "height": 30 }
},
"left lower leg": {
"left-lower-leg": { "x": 24.55, "y": -1.92, "rotation": 105.75, "width": 49, "height": 64 }
},
"left upper leg": {
"left-upper-leg": { "x": 26.12, "y": -1.85, "rotation": 89.09, "width": 33, "height": 67 }
},
"pelvis": {
"pelvis": { "x": -4.83, "y": 10.62, "width": 63, "height": 47 }
},
"right foot": {
"right-foot": { "x": 19.02, "y": 8.47, "rotation": 1.52, "width": 67, "height": 30 }
},
"right lower leg": {
"right-lower-leg": { "x": 23.28, "y": -2.59, "rotation": 111.83, "width": 51, "height": 64 }
},
"right upper leg": {
"right-upper-leg": { "x": 23.03, "y": 0.25, "rotation": 101.13, "width": 44, "height": 70 }
},
"torso": {
"torso": { "x": 44.57, "y": -7.08, "rotation": -94.95, "width": 68, "height": 92 }
},
"neck": {
"neck": { "x": 9.42, "y": -3.66, "rotation": -100.15, "width": 34, "height": 28 }
},
"head": {
"head": { "x": 53.94, "y": -5.75, "rotation": -86.9, "width": 121, "height": 132 }
},
"eyes": {
"eyes": { "x": 28.94, "y": -32.92, "rotation": -86.9, "width": 34, "height": 27 },
"eyes-closed": { "x": 28.77, "y": -32.86, "rotation": -86.9, "width": 34, "height": 27 }
},
"right shoulder": {
"right-shoulder": { "x": 25.86, "y": 0.03, "rotation": 134.44, "width": 52, "height": 51 }
},
"right arm": {
"right-arm": { "x": 18.34, "y": -2.64, "rotation": 94.32, "width": 21, "height": 45 }
},
"right hand": {
"right-hand": { "x": 6.82, "y": 1.25, "rotation": 91.96, "width": 32, "height": 32 }
}
}
},
"animations": {
"walk": {
"bones": {
"left upper leg": {
"rotate": [
{ "time": 0, "angle": -26.55 },
{ "time": 0.1333, "angle": -8.78 },
{ "time": 0.2666, "angle": 9.51 },
{ "time": 0.4, "angle": 30.74 },
{ "time": 0.5333, "angle": 25.33 },
{ "time": 0.6666, "angle": 26.11 },
{ "time": 0.8, "angle": -7.7 },
{ "time": 0.9333, "angle": -21.19 },
{ "time": 1.0666, "angle": -26.55 }
],
"translate": [
{ "time": 0, "x": -3, "y": -2.25 },
{ "time": 0.4, "x": -2.18, "y": -2.25 },
{ "time": 1.0666, "x": -3, "y": -2.25 }
]
},
"right upper leg": {
"rotate": [
{ "time": 0, "angle": 42.45 },
{ "time": 0.1333, "angle": 52.1 },
{ "time": 0.2666, "angle": 5.96 },
{ "time": 0.5333, "angle": -16.93 },
{ "time": 0.6666, "angle": 1.89 },
{
"time": 0.8,
"angle": 28.06,
"curve": [ 0.462, 0.11, 1, 1 ]
},
{
"time": 0.9333,
"angle": 58.68,
"curve": [ 0.5, 0.02, 1, 1 ]
},
{ "time": 1.0666, "angle": 42.45 }
],
"translate": [
{ "time": 0, "x": 8.11, "y": -2.36 },
{ "time": 0.1333, "x": 10.03, "y": -2.56 },
{ "time": 0.4, "x": 2.76, "y": -2.97 },
{ "time": 0.5333, "x": 2.76, "y": -2.81 },
{ "time": 0.9333, "x": 8.67, "y": -2.54 },
{ "time": 1.0666, "x": 8.11, "y": -2.36 }
]
},
"left lower leg": {
"rotate": [
{ "time": 0, "angle": -10.21 },
{ "time": 0.1333, "angle": -55.64 },
{ "time": 0.2666, "angle": -68.12 },
{ "time": 0.5333, "angle": 5.11 },
{ "time": 0.6666, "angle": -28.29 },
{ "time": 0.8, "angle": 4.08 },
{ "time": 0.9333, "angle": 3.53 },
{ "time": 1.0666, "angle": -10.21 }
]
},
"left foot": {
"rotate": [
{ "time": 0, "angle": -3.69 },
{ "time": 0.1333, "angle": -10.42 },
{ "time": 0.2666, "angle": -17.14 },
{ "time": 0.4, "angle": -2.83 },
{ "time": 0.5333, "angle": -3.87 },
{ "time": 0.6666, "angle": 2.78 },
{ "time": 0.8, "angle": 1.68 },
{ "time": 0.9333, "angle": -8.54 },
{ "time": 1.0666, "angle": -3.69 }
]
},
"right shoulder": {
"rotate": [
{
"time": 0,
"angle": 20.89,
"curve": [ 0.264, 0, 0.75, 1 ]
},
{
"time": 0.1333,
"angle": 3.72,
"curve": [ 0.272, 0, 0.841, 1 ]
},
{ "time": 0.6666, "angle": -278.28 },
{ "time": 1.0666, "angle": 20.89 }
],
"translate": [
{ "time": 0, "x": -7.84, "y": 7.19 },
{ "time": 0.1333, "x": -6.36, "y": 6.42 },
{ "time": 0.6666, "x": -11.07, "y": 5.25 },
{ "time": 1.0666, "x": -7.84, "y": 7.19 }
]
},
"right arm": {
"rotate": [
{
"time": 0,
"angle": -4.02,
"curve": [ 0.267, 0, 0.804, 0.99 ]
},
{
"time": 0.1333,
"angle": -13.99,
"curve": [ 0.341, 0, 1, 1 ]
},
{
"time": 0.6666,
"angle": 36.54,
"curve": [ 0.307, 0, 0.787, 0.99 ]
},
{ "time": 1.0666, "angle": -4.02 }
]
},
"right hand": {
"rotate": [
{ "time": 0, "angle": 22.92 },
{ "time": 0.4, "angle": -8.97 },
{ "time": 0.6666, "angle": 0.51 },
{ "time": 1.0666, "angle": 22.92 }
]
},
"left shoulder": {
"rotate": [
{ "time": 0, "angle": -1.47 },
{ "time": 0.1333, "angle": 13.6 },
{ "time": 0.6666, "angle": 280.74 },
{ "time": 1.0666, "angle": -1.47 }
],
"translate": [
{ "time": 0, "x": -1.76, "y": 0.56 },
{ "time": 0.6666, "x": -2.47, "y": 8.14 },
{ "time": 1.0666, "x": -1.76, "y": 0.56 }
]
},
"left hand": {
"rotate": [
{
"time": 0,
"angle": 11.58,
"curve": [ 0.169, 0.37, 0.632, 1.55 ]
},
{
"time": 0.1333,
"angle": 28.13,
"curve": [ 0.692, 0, 0.692, 0.99 ]
},
{
"time": 0.6666,
"angle": -27.42,
"curve": [ 0.117, 0.41, 0.738, 1.76 ]
},
{ "time": 0.8, "angle": -36.32 },
{ "time": 1.0666, "angle": 11.58 }
]
},
"left arm": {
"rotate": [
{ "time": 0, "angle": -8.27 },
{ "time": 0.1333, "angle": 18.43 },
{ "time": 0.6666, "angle": 0.88 },
{ "time": 1.0666, "angle": -8.27 }
]
},
"torso": {
"rotate": [
{ "time": 0, "angle": -10.28 },
{
"time": 0.1333,
"angle": -15.38,
"curve": [ 0.545, 0, 1, 1 ]
},
{
"time": 0.4,
"angle": -9.78,
"curve": [ 0.58, 0.17, 1, 1 ]
},
{ "time": 0.6666, "angle": -15.75 },
{ "time": 0.9333, "angle": -7.06 },
{ "time": 1.0666, "angle": -10.28 }
],
"translate": [
{ "time": 0, "x": -3.67, "y": 1.68 },
{ "time": 0.1333, "x": -3.67, "y": 0.68 },
{ "time": 0.4, "x": -3.67, "y": 1.97 },
{ "time": 0.6666, "x": -3.67, "y": -0.14 },
{ "time": 1.0666, "x": -3.67, "y": 1.68 }
]
},
"right foot": {
"rotate": [
{ "time": 0, "angle": -5.25 },
{ "time": 0.2666, "angle": -4.08 },
{ "time": 0.4, "angle": -6.45 },
{ "time": 0.5333, "angle": -5.39 },
{ "time": 0.8, "angle": -11.68 },
{ "time": 0.9333, "angle": 0.46 },
{ "time": 1.0666, "angle": -5.25 }
]
},
"right lower leg": {
"rotate": [
{ "time": 0, "angle": -3.39 },
{ "time": 0.1333, "angle": -45.53 },
{ "time": 0.2666, "angle": -2.59 },
{ "time": 0.5333, "angle": -19.53 },
{ "time": 0.6666, "angle": -64.8 },
{
"time": 0.8,
"angle": -82.56,
"curve": [ 0.557, 0.18, 1, 1 ]
},
{ "time": 1.0666, "angle": -3.39 }
]
},
"hip": {
"rotate": [
{ "time": 0, "angle": 0, "curve": "stepped" },
{ "time": 1.0666, "angle": 0 }
],
"translate": [
{ "time": 0, "x": 0, "y": 0 },
{
"time": 0.1333,
"x": 0,
"y": -7.61,
"curve": [ 0.272, 0.86, 1, 1 ]
},
{ "time": 0.4, "x": 0, "y": 8.7 },
{ "time": 0.5333, "x": 0, "y": -0.41 },
{
"time": 0.6666,
"x": 0,
"y": -7.05,
"curve": [ 0.235, 0.89, 1, 1 ]
},
{ "time": 0.8, "x": 0, "y": 2.92 },
{ "time": 0.9333, "x": 0, "y": 6.78 },
{ "time": 1.0666, "x": 0, "y": 0 }
]
},
"neck": {
"rotate": [
{ "time": 0, "angle": 3.6 },
{ "time": 0.1333, "angle": 17.49 },
{ "time": 0.2666, "angle": 6.1 },
{ "time": 0.4, "angle": 3.45 },
{ "time": 0.5333, "angle": 5.17 },
{ "time": 0.6666, "angle": 18.36 },
{ "time": 0.8, "angle": 6.09 },
{ "time": 0.9333, "angle": 2.28 },
{ "time": 1.0666, "angle": 3.6 }
]
},
"head": {
"rotate": [
{
"time": 0,
"angle": 3.6,
"curve": [ 0, 0, 0.704, 1.61 ]
},
{ "time": 0.1666, "angle": -0.2 },
{ "time": 0.2666, "angle": 6.1 },
{ "time": 0.4, "angle": 3.45 },
{
"time": 0.5333,
"angle": 5.17,
"curve": [ 0, 0, 0.704, 1.61 ]
},
{ "time": 0.7, "angle": 1.1 },
{ "time": 0.8, "angle": 6.09 },
{ "time": 0.9333, "angle": 2.28 },
{ "time": 1.0666, "angle": 3.6 }
]
}
}
},
"jump": {
"bones": {
"hip": {
"rotate": [
{ "time": 0, "angle": 0, "curve": "stepped" },
{ "time": 0.9333, "angle": 0, "curve": "stepped" },
{ "time": 1.3666, "angle": 0 }
],
"translate": [
{ "time": 0, "x": -11.57, "y": -3 },
{ "time": 0.2333, "x": -16.2, "y": -19.43 },
{
"time": 0.3333,
"x": 7.66,
"y": -8.48,
"curve": [ 0.057, 0.06, 0.712, 1 ]
},
{ "time": 0.3666, "x": 15.38, "y": 5.01 },
{ "time": 0.4666, "x": -7.84, "y": 57.22 },
{
"time": 0.6,
"x": -10.81,
"y": 96.34,
"curve": [ 0.241, 0, 1, 1 ]
},
{ "time": 0.7333, "x": -7.01, "y": 54.7 },
{ "time": 0.8, "x": -10.58, "y": 32.2 },
{ "time": 0.9333, "x": -31.99, "y": 0.45 },
{ "time": 1.0666, "x": -12.48, "y": -29.47 },
{ "time": 1.3666, "x": -11.57, "y": -3 }
],
"scale": [
{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 1.3666, "x": 1, "y": 1 }
]
},
"left upper leg": {
"rotate": [
{ "time": 0, "angle": 17.13 },
{ "time": 0.2333, "angle": 44.35 },
{ "time": 0.3333, "angle": 16.46 },
{ "time": 0.4, "angle": -9.88 },
{ "time": 0.4666, "angle": -11.42 },
{ "time": 0.5666, "angle": 23.46 },
{ "time": 0.7666, "angle": 71.82 },
{ "time": 0.9333, "angle": 65.53 },
{ "time": 1.0666, "angle": 51.01 },
{ "time": 1.3666, "angle": 17.13 }
],
"translate": [
{ "time": 0, "x": -3, "y": -2.25, "curve": "stepped" },
{ "time": 0.9333, "x": -3, "y": -2.25, "curve": "stepped" },
{ "time": 1.3666, "x": -3, "y": -2.25 }
],
"scale": [
{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 1.3666, "x": 1, "y": 1 }
]
},
"left lower leg": {
"rotate": [
{ "time": 0, "angle": -16.25 },
{ "time": 0.2333, "angle": -52.21 },
{ "time": 0.4, "angle": 15.04 },
{ "time": 0.4666, "angle": -8.95 },
{ "time": 0.5666, "angle": -39.53 },
{ "time": 0.7666, "angle": -27.27 },
{ "time": 0.9333, "angle": -3.52 },
{ "time": 1.0666, "angle": -61.92 },
{ "time": 1.3666, "angle": -16.25 }
],
"translate": [
{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 1.3666, "x": 0, "y": 0 }
],
"scale": [
{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 1.3666, "x": 1, "y": 1 }
]
},
"left foot": {
"rotate": [
{ "time": 0, "angle": 0.33 },
{ "time": 0.2333, "angle": 6.2 },
{ "time": 0.3333, "angle": 14.73 },
{ "time": 0.4, "angle": -15.54 },
{ "time": 0.4333, "angle": -21.2 },
{ "time": 0.5666, "angle": -7.55 },
{ "time": 0.7666, "angle": -0.67 },
{ "time": 0.9333, "angle": -0.58 },
{ "time": 1.0666, "angle": 14.64 },
{ "time": 1.3666, "angle": 0.33 }
],
"translate": [
{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 1.3666, "x": 0, "y": 0 }
],
"scale": [
{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 1.3666, "x": 1, "y": 1 }
]
},
"right upper leg": {
"rotate": [
{ "time": 0, "angle": 25.97 },
{ "time": 0.2333, "angle": 46.43 },
{ "time": 0.3333, "angle": 22.61 },
{ "time": 0.4, "angle": 2.13 },
{
"time": 0.4666,
"angle": 0.04,
"curve": [ 0, 0, 0.637, 0.98 ]
},
{ "time": 0.6, "angle": 65.55 },
{ "time": 0.7666, "angle": 64.93 },
{ "time": 0.9333, "angle": 41.08 },
{ "time": 1.0666, "angle": 66.25 },
{ "time": 1.3666, "angle": 25.97 }
],
"translate": [
{ "time": 0, "x": 5.74, "y": 0.61 },
{ "time": 0.2333, "x": 4.79, "y": 1.79 },
{ "time": 0.3333, "x": 6.05, "y": -4.55 },
{ "time": 0.9333, "x": 4.79, "y": 1.79, "curve": "stepped" },
{ "time": 1.0666, "x": 4.79, "y": 1.79 },
{ "time": 1.3666, "x": 5.74, "y": 0.61 }
],
"scale": [
{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 1.3666, "x": 1, "y": 1 }
]
},
"right lower leg": {
"rotate": [
{ "time": 0, "angle": -27.46 },
{ "time": 0.2333, "angle": -64.03 },
{ "time": 0.4, "angle": -48.36 },
{ "time": 0.5666, "angle": -76.86 },
{ "time": 0.7666, "angle": -26.89 },
{ "time": 0.9, "angle": -18.97 },
{ "time": 0.9333, "angle": -14.18 },
{ "time": 1.0666, "angle": -80.45 },
{ "time": 1.3666, "angle": -27.46 }
],
"translate": [
{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 1.3666, "x": 0, "y": 0 }
],
"scale": [
{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 1.3666, "x": 1, "y": 1 }
]
},
"right foot": {
"rotate": [
{ "time": 0, "angle": 1.08 },
{ "time": 0.2333, "angle": 16.02 },
{ "time": 0.3, "angle": 12.94 },
{ "time": 0.3333, "angle": 15.16 },
{ "time": 0.4, "angle": -14.7 },
{ "time": 0.4333, "angle": -12.85 },
{ "time": 0.4666, "angle": -19.18 },
{ "time": 0.5666, "angle": -15.82 },
{ "time": 0.6, "angle": -3.59 },
{ "time": 0.7666, "angle": -3.56 },
{ "time": 0.9333, "angle": 1.86 },
{ "time": 1.0666, "angle": 16.02 },
{ "time": 1.3666, "angle": 1.08 }
],
"translate": [
{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 1.3666, "x": 0, "y": 0 }
],
"scale": [
{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 1.3666, "x": 1, "y": 1 }
]
},
"torso": {
"rotate": [
{ "time": 0, "angle": -13.35 },
{ "time": 0.2333, "angle": -48.95 },
{ "time": 0.4333, "angle": -35.77 },
{ "time": 0.6, "angle": -4.59 },
{ "time": 0.7666, "angle": 14.61 },
{ "time": 0.9333, "angle": 15.74 },
{ "time": 1.0666, "angle": -32.44 },
{ "time": 1.3666, "angle": -13.35 }
],
"translate": [
{ "time": 0, "x": -3.67, "y": 1.68, "curve": "stepped" },
{ "time": 0.9333, "x": -3.67, "y": 1.68, "curve": "stepped" },
{ "time": 1.3666, "x": -3.67, "y": 1.68 }
],
"scale": [
{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 1.3666, "x": 1, "y": 1 }
]
},
"neck": {
"rotate": [
{ "time": 0, "angle": 12.78 },
{ "time": 0.2333, "angle": 16.46 },
{ "time": 0.4, "angle": 26.49 },
{ "time": 0.6, "angle": 15.51 },
{ "time": 0.7666, "angle": 1.34 },
{ "time": 0.9333, "angle": 2.35 },
{ "time": 1.0666, "angle": 6.08 },
{ "time": 1.3, "angle": 21.23 },
{ "time": 1.3666, "angle": 12.78 }
],
"translate": [
{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 1.3666, "x": 0, "y": 0 }
],
"scale": [
{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 1.3666, "x": 1, "y": 1 }
]
},
"head": {
"rotate": [
{ "time": 0, "angle": 5.19 },
{ "time": 0.2333, "angle": 20.27 },
{ "time": 0.4, "angle": 15.27 },
{ "time": 0.6, "angle": -24.69 },
{ "time": 0.7666, "angle": -11.02 },
{ "time": 0.9333, "angle": -24.38 },
{ "time": 1.0666, "angle": 11.99 },
{ "time": 1.3, "angle": 4.86 },
{ "time": 1.3666, "angle": 5.19 }
],
"translate": [
{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 1.3666, "x": 0, "y": 0 }
],
"scale": [
{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 1.3666, "x": 1, "y": 1 }
]
},
"left shoulder": {
"rotate": [
{
"time": 0,
"angle": 0.05,
"curve": [ 0, 0, 0.62, 1 ]
},
{
"time": 0.2333,
"angle": 279.66,
"curve": [ 0.218, 0.67, 0.66, 0.99 ]
},
{
"time": 0.5,
"angle": 62.27,
"curve": [ 0.462, 0, 0.764, 0.58 ]
},
{ "time": 0.9333, "angle": 28.91 },
{ "time": 1.0666, "angle": -8.62 },
{ "time": 1.1666, "angle": -18.43 },
{ "time": 1.3666, "angle": 0.05 }
],
"translate": [
{ "time": 0, "x": -1.76, "y": 0.56, "curve": "stepped" },
{ "time": 0.9333, "x": -1.76, "y": 0.56, "curve": "stepped" },
{ "time": 1.3666, "x": -1.76, "y": 0.56 }
],
"scale": [
{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 1.3666, "x": 1, "y": 1 }
]
},
"left hand": {
"rotate": [
{ "time": 0, "angle": 11.58, "curve": "stepped" },
{ "time": 0.9333, "angle": 11.58, "curve": "stepped" },
{ "time": 1.3666, "angle": 11.58 }
],
"translate": [
{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 1.3666, "x": 0, "y": 0 }
],
"scale": [
{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 1.3666, "x": 1, "y": 1 }
]
},
"left arm": {
"rotate": [
{ "time": 0, "angle": 0.51 },
{ "time": 0.4333, "angle": 12.82 },
{ "time": 0.6, "angle": 47.55 },
{ "time": 0.9333, "angle": 12.82 },
{ "time": 1.1666, "angle": -6.5 },
{ "time": 1.3666, "angle": 0.51 }
],
"translate": [
{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 1.3666, "x": 0, "y": 0 }
],
"scale": [
{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 1.3666, "x": 1, "y": 1 }
]
},
"right shoulder": {
"rotate": [
{
"time": 0,
"angle": 43.82,
"curve": [ 0, 0, 0.62, 1 ]
},
{
"time": 0.2333,
"angle": -8.74,
"curve": [ 0.304, 0.58, 0.709, 0.97 ]
},
{
"time": 0.5333,
"angle": -208.02,
"curve": [ 0.462, 0, 0.764, 0.58 ]
},
{ "time": 0.9333, "angle": -246.72 },
{ "time": 1.0666, "angle": -307.13 },
{ "time": 1.1666, "angle": 37.15 },
{ "time": 1.3666, "angle": 43.82 }
],
"translate": [
{ "time": 0, "x": -7.84, "y": 7.19, "curve": "stepped" },
{ "time": 0.9333, "x": -7.84, "y": 7.19, "curve": "stepped" },
{ "time": 1.3666, "x": -7.84, "y": 7.19 }
],
"scale": [
{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 1.3666, "x": 1, "y": 1 }
]
},
"right arm": {
"rotate": [
{ "time": 0, "angle": -4.02 },
{ "time": 0.6, "angle": 17.5 },
{ "time": 0.9333, "angle": -4.02 },
{ "time": 1.1666, "angle": -16.72 },
{ "time": 1.3666, "angle": -4.02 }
],
"translate": [
{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 1.3666, "x": 0, "y": 0 }
],
"scale": [
{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 1.3666, "x": 1, "y": 1 }
]
},
"right hand": {
"rotate": [
{ "time": 0, "angle": 22.92, "curve": "stepped" },
{ "time": 0.9333, "angle": 22.92, "curve": "stepped" },
{ "time": 1.3666, "angle": 22.92 }
],
"translate": [
{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" },
{ "time": 1.3666, "x": 0, "y": 0 }
],
"scale": [
{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 1.3666, "x": 1, "y": 1 }
]
},
"root": {
"rotate": [
{ "time": 0, "angle": 0 },
{ "time": 0.4333, "angle": -14.52 },
{ "time": 0.8, "angle": 9.86 },
{ "time": 1.3666, "angle": 0 }
],
"scale": [
{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
{ "time": 1.3666, "x": 1, "y": 1 }
]
}
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

View File

@ -0,0 +1,168 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{29CC4385-294A-4885-A3E8-FD4825E0CFDD}</ProjectGuid>
<ProjectTypeGuids>{6D335F3A-9D43-41b4-9D22-F6F17C4BE596};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Spine</RootNamespace>
<AssemblyName>spine-xna-example</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<XnaFrameworkVersion>v4.0</XnaFrameworkVersion>
<XnaPlatform>Windows</XnaPlatform>
<XnaProfile>HiDef</XnaProfile>
<XnaCrossPlatformGroupID>bf3b738e-f348-48d3-b35b-94bc118edb90</XnaCrossPlatformGroupID>
<XnaOutputType>Game</XnaOutputType>
<ApplicationIcon>Game.ico</ApplicationIcon>
<Thumbnail>GameThumbnail.png</Thumbnail>
<IsWebBootstrapper>false</IsWebBootstrapper>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\x86\Debug</OutputPath>
<DefineConstants>DEBUG;TRACE;WINDOWS</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<NoStdLib>true</NoStdLib>
<UseVSHostingProcess>false</UseVSHostingProcess>
<PlatformTarget>x86</PlatformTarget>
<XnaCompressContent>True</XnaCompressContent>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\x86\Release</OutputPath>
<DefineConstants>TRACE;WINDOWS</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<NoStdLib>true</NoStdLib>
<UseVSHostingProcess>false</UseVSHostingProcess>
<PlatformTarget>x86</PlatformTarget>
<XnaCompressContent>True</XnaCompressContent>
</PropertyGroup>
<PropertyGroup>
<StartupObject>Spine.ExampleProgram</StartupObject>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Xna.Framework.GamerServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Xna.Framework.Xact, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Xna.Framework.Video, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Xna.Framework.Avatar, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Xna.Framework.Net, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Xna.Framework.Storage, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<Reference Include="mscorlib">
<Private>False</Private>
</Reference>
<Reference Include="System">
<Private>False</Private>
</Reference>
<Reference Include="System.Xml">
<Private>False</Private>
</Reference>
<Reference Include="System.Core">
<Private>False</Private>
</Reference>
<Reference Include="System.Xml.Linq">
<Private>False</Private>
</Reference>
<Reference Include="System.Net">
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="src\ExampleGame.cs" />
<Compile Include="src\ExampleProgram.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Game.ico" />
<Content Include="GameThumbnail.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\spine-csharp\spine-csharp.csproj">
<Project>{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}</Project>
<Name>spine-csharp</Name>
</ProjectReference>
<ProjectReference Include="..\spine-xna.csproj">
<Project>{7F8F2327-C016-49C8-BB4D-F3F77971961E}</Project>
<Name>spine-xna</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.0,Profile=Client">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4 Client Profile %28x86 and x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
<Visible>False</Visible>
<ProductName>Windows Installer 3.1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Xna.Framework.4.0">
<Visible>False</Visible>
<ProductName>Microsoft XNA Framework Redistributable 4.0</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\Microsoft.Xna.GameStudio.targets" />
<!--
To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,71 @@
using System;
using System.IO;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Spine;
namespace Spine {
public class Example : Microsoft.Xna.Framework.Game {
GraphicsDeviceManager graphics;
SkeletonRenderer skeletonRenderer;
Skeleton skeleton;
Animation animation;
float time;
public Example () {
graphics = new GraphicsDeviceManager(this);
graphics.IsFullScreen = false;
graphics.PreferredBackBufferWidth = 640;
graphics.PreferredBackBufferHeight = 480;
}
protected override void Initialize () {
// TODO: Add your initialization logic here
base.Initialize();
}
protected override void LoadContent () {
skeletonRenderer = new SkeletonRenderer(GraphicsDevice);
Atlas atlas = new Atlas(GraphicsDevice, "data/spineboy.atlas");
SkeletonJson json = new SkeletonJson(atlas);
skeleton = new Skeleton(json.readSkeletonData("spineboy", File.ReadAllText("data/spineboy.json")));
animation = skeleton.Data.FindAnimation("walk");
skeleton.RootBone.X = 320;
skeleton.RootBone.Y = 440;
skeleton.UpdateWorldTransform();
}
protected override void UnloadContent () {
// TODO: Unload any non ContentManager content here
}
protected override void Update (GameTime gameTime) {
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
base.Update(gameTime);
}
protected override void Draw (GameTime gameTime) {
GraphicsDevice.Clear(Color.Black);
time += gameTime.ElapsedGameTime.Milliseconds / 1000f;
animation.Apply(skeleton, time, true);
skeleton.UpdateWorldTransform();
skeletonRenderer.Draw(skeleton);
base.Draw(gameTime);
}
}
}

View File

@ -0,0 +1,13 @@
using System;
namespace Spine {
#if WINDOWS || XBOX
static class ExampleProgram {
static void Main (string[] args) {
using (Example game = new Example()) {
game.Run();
}
}
}
#endif
}

116
spine-xna/spine-xna.csproj Normal file
View File

@ -0,0 +1,116 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{7F8F2327-C016-49C8-BB4D-F3F77971961E}</ProjectGuid>
<ProjectTypeGuids>{6D335F3A-9D43-41b4-9D22-F6F17C4BE596};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Spine</RootNamespace>
<AssemblyName>spine-xna</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<XnaFrameworkVersion>v4.0</XnaFrameworkVersion>
<XnaPlatform>Windows</XnaPlatform>
<XnaProfile>HiDef</XnaProfile>
<XnaCrossPlatformGroupID>f1fc4580-2d86-4a03-bd33-44b1703f36a1</XnaCrossPlatformGroupID>
<XnaOutputType>Library</XnaOutputType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\x86\Debug</OutputPath>
<DefineConstants>DEBUG;TRACE;WINDOWS</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<NoStdLib>true</NoStdLib>
<UseVSHostingProcess>false</UseVSHostingProcess>
<PlatformTarget>x86</PlatformTarget>
<XnaCompressContent>false</XnaCompressContent>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\x86\Release</OutputPath>
<DefineConstants>TRACE;WINDOWS</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<NoStdLib>true</NoStdLib>
<UseVSHostingProcess>false</UseVSHostingProcess>
<PlatformTarget>x86</PlatformTarget>
<XnaCompressContent>true</XnaCompressContent>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Xna.Framework.GamerServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Xna.Framework.Xact, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Xna.Framework.Video, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Xna.Framework.Avatar, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Xna.Framework.Net, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Xna.Framework.Storage, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<Reference Include="mscorlib">
<Private>False</Private>
</Reference>
<Reference Include="System">
<Private>False</Private>
</Reference>
<Reference Include="System.Xml">
<Private>False</Private>
</Reference>
<Reference Include="System.Core">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
<Private>False</Private>
</Reference>
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
<Private>False</Private>
</Reference>
<Reference Include="System.Net">
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="src\Atlas.cs" />
<Compile Include="src\SkeletonRenderer.cs" />
<Compile Include="src\SpriteBatcher.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\spine-csharp\spine-csharp.csproj">
<Project>{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}</Project>
<Name>spine-csharp</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\Microsoft.Xna.GameStudio.targets" />
<!--
To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

32
spine-xna/spine-xna.sln Normal file
View File

@ -0,0 +1,32 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual C# Express 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "spine-xna", "spine-xna.csproj", "{7F8F2327-C016-49C8-BB4D-F3F77971961E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "spine-xna-example", "example\spine-xna-example.csproj", "{29CC4385-294A-4885-A3E8-FD4825E0CFDD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "spine-csharp", "..\spine-csharp\spine-csharp.csproj", "{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Debug|x86.ActiveCfg = Debug|x86
{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Debug|x86.Build.0 = Debug|x86
{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Release|x86.ActiveCfg = Release|x86
{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Release|x86.Build.0 = Release|x86
{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Debug|x86.ActiveCfg = Debug|x86
{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Debug|x86.Build.0 = Debug|x86
{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Release|x86.ActiveCfg = Release|x86
{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Release|x86.Build.0 = Release|x86
{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Debug|x86.ActiveCfg = Debug|x86
{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Debug|x86.Build.0 = Debug|x86
{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Release|x86.ActiveCfg = Release|x86
{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

84
spine-xna/src/Atlas.cs Normal file
View File

@ -0,0 +1,84 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace Spine {
public class Atlas : BaseAtlas {
private GraphicsDevice device;
public Atlas (GraphicsDevice device, String atlasFile) {
this.device = device;
using (StreamReader reader = new StreamReader(atlasFile)) {
load(reader, Path.GetDirectoryName(atlasFile));
}
}
override protected AtlasPage NewAtlasPage (String path) {
XnaAtlasPage page = new XnaAtlasPage();
page.Texture = loadTexture(path);
return page;
}
private Texture2D loadTexture (string path) {
Texture2D file;
using (Stream fileStream = new FileStream(path, FileMode.Open)) {
file = Texture2D.FromStream(device, fileStream);
}
// Setup a render target to hold our final texture which will have premulitplied alpha values
RenderTarget2D result = new RenderTarget2D(device, file.Width, file.Height);
device.SetRenderTarget(result);
device.Clear(Color.Black);
// Multiply each color by the source alpha, and write in just the color values into the final texture
BlendState blendColor = new BlendState();
blendColor.ColorWriteChannels = ColorWriteChannels.Red | ColorWriteChannels.Green | ColorWriteChannels.Blue;
blendColor.AlphaDestinationBlend = Blend.Zero;
blendColor.ColorDestinationBlend = Blend.Zero;
blendColor.AlphaSourceBlend = Blend.SourceAlpha;
blendColor.ColorSourceBlend = Blend.SourceAlpha;
SpriteBatch spriteBatch = new SpriteBatch(device);
spriteBatch.Begin(SpriteSortMode.Immediate, blendColor);
spriteBatch.Draw(file, file.Bounds, Color.White);
spriteBatch.End();
// Now copy over the alpha values from the PNG source texture to the final one, without multiplying them
BlendState blendAlpha = new BlendState();
blendAlpha.ColorWriteChannels = ColorWriteChannels.Alpha;
blendAlpha.AlphaDestinationBlend = Blend.Zero;
blendAlpha.ColorDestinationBlend = Blend.Zero;
blendAlpha.AlphaSourceBlend = Blend.One;
blendAlpha.ColorSourceBlend = Blend.One;
spriteBatch.Begin(SpriteSortMode.Immediate, blendAlpha);
spriteBatch.Draw(file, file.Bounds, Color.White);
spriteBatch.End();
// Release the GPU back to drawing to the screen
device.SetRenderTarget(null);
return result as Texture2D;
}
}
public class XnaAtlasPage : AtlasPage {
public Texture2D Texture { get; set; }
override public int GetTextureWidth () {
return Texture.Width;
}
override public int GetTextureHeight () {
return Texture.Height;
}
}
}

View File

@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
namespace Spine {
public class SkeletonRenderer {
GraphicsDevice device;
SpriteBatcher batcher;
BasicEffect effect;
RasterizerState rasterizerState;
public SkeletonRenderer (GraphicsDevice device) {
this.device = device;
batcher = new SpriteBatcher();
effect = new BasicEffect(device);
effect.World = Matrix.Identity;
effect.View = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 1.0f), Vector3.Zero, Vector3.Up);
effect.Projection = Matrix.CreateOrthographicOffCenter(0, device.Viewport.Width, device.Viewport.Height, 0, 1, 0);
effect.TextureEnabled = true;
effect.VertexColorEnabled = true;
rasterizerState = new RasterizerState();
rasterizerState.CullMode = CullMode.None;
Bone.yDown = true;
}
public void Draw (Skeleton skeleton) {
List<Slot> drawOrder = skeleton.DrawOrder;
for (int i = 0, n = drawOrder.Count; i < n; i++) {
Slot slot = drawOrder[i];
Attachment attachment = slot.Attachment;
if (attachment == null) continue;
if (attachment is RegionAttachment) {
RegionAttachment regionAttachment = (RegionAttachment)attachment;
SpriteBatchItem item = batcher.CreateBatchItem();
item.Texture = ((XnaAtlasPage)regionAttachment.Region.Page).Texture;
byte r = (byte)(slot.R * 255);
byte g = (byte)(slot.G * 255);
byte b = (byte)(slot.B * 255);
byte a = (byte)(slot.A * 255);
item.vertexTL.Color.R = r;
item.vertexTL.Color.G = g;
item.vertexTL.Color.B = b;
item.vertexTL.Color.A = a;
item.vertexBL.Color.R = r;
item.vertexBL.Color.G = g;
item.vertexBL.Color.B = b;
item.vertexBL.Color.A = a;
item.vertexBR.Color.R = r;
item.vertexBR.Color.G = g;
item.vertexBR.Color.B = b;
item.vertexBR.Color.A = a;
item.vertexTR.Color.R = r;
item.vertexTR.Color.G = g;
item.vertexTR.Color.B = b;
item.vertexTR.Color.A = a;
regionAttachment.UpdateVertices(slot.Bone);
float[] vertices = regionAttachment.Vertices;
item.vertexTL.Position.X = vertices[RegionAttachment.X1];
item.vertexTL.Position.Y = vertices[RegionAttachment.Y1];
item.vertexTL.Position.Z = 0;
item.vertexBL.Position.X = vertices[RegionAttachment.X2];
item.vertexBL.Position.Y = vertices[RegionAttachment.Y2];
item.vertexBL.Position.Z = 0;
item.vertexBR.Position.X = vertices[RegionAttachment.X3];
item.vertexBR.Position.Y = vertices[RegionAttachment.Y3];
item.vertexBR.Position.Z = 0;
item.vertexTR.Position.X = vertices[RegionAttachment.X4];
item.vertexTR.Position.Y = vertices[RegionAttachment.Y4];
item.vertexTR.Position.Z = 0;
float[] uvs = regionAttachment.UVs;
item.vertexTL.TextureCoordinate.X = uvs[RegionAttachment.X1];
item.vertexTL.TextureCoordinate.Y = uvs[RegionAttachment.Y1];
item.vertexBL.TextureCoordinate.X = uvs[RegionAttachment.X2];
item.vertexBL.TextureCoordinate.Y = uvs[RegionAttachment.Y2];
item.vertexBR.TextureCoordinate.X = uvs[RegionAttachment.X3];
item.vertexBR.TextureCoordinate.Y = uvs[RegionAttachment.Y3];
item.vertexTR.TextureCoordinate.X = uvs[RegionAttachment.X4];
item.vertexTR.TextureCoordinate.Y = uvs[RegionAttachment.Y4];
}
}
device.RasterizerState = rasterizerState;
device.BlendState = BlendState.AlphaBlend;
foreach (EffectPass pass in effect.CurrentTechnique.Passes) {
pass.Apply();
batcher.Draw(device);
}
}
}
}

View File

@ -0,0 +1,240 @@
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
namespace Spine {
// #region License
// /*
// Microsoft Public License (Ms-PL)
// MonoGame - Copyright © 2009 The MonoGame Team
//
// All rights reserved.
//
// This license governs use of the accompanying software. If you use the software, you accept this license. If you do not
// accept the license, do not use the software.
//
// 1. Definitions
// The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under
// U.S. copyright law.
//
// A "contribution" is the original software, or any additions or changes to the software.
// A "contributor" is any person that distributes its contribution under this license.
// "Licensed patents" are a contributor's patent claims that read directly on its contribution.
//
// 2. Grant of Rights
// (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
// each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
// (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
// each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
//
// 3. Conditions and Limitations
// (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
// (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software,
// your patent license from such contributor to the software ends automatically.
// (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution
// notices that are present in the software.
// (D) If you distribute any portion of the software in source code form, you may do so only under this license by including
// a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object
// code form, you may only do so under a license that complies with this license.
// (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees
// or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent
// permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular
// purpose and non-infringement.
// */
// #endregion License
//
/// <summary>
/// This class handles the queueing of batch items into the GPU by creating the triangle tesselations
/// that are used to draw the sprite textures. This class supports int.MaxValue number of sprites to be
/// batched and will process them into short.MaxValue groups (strided by 6 for the number of vertices
/// sent to the GPU).
/// </summary>
public class SpriteBatcher {
/*
* Note that this class is fundamental to high performance for SpriteBatch games. Please exercise
* caution when making changes to this class.
*/
/// <summary>
/// Initialization size for the batch item list and queue.
/// </summary>
private const int InitialBatchSize = 256;
/// <summary>
/// The maximum number of batch items that can be processed per iteration
/// </summary>
private const int MaxBatchSize = short.MaxValue / 6; // 6 = 4 vertices unique and 2 shared, per quad
/// <summary>
/// Initialization size for the vertex array, in batch units.
/// </summary>
private const int InitialVertexArraySize = 256;
/// <summary>
/// The list of batch items to process.
/// </summary>
private readonly List<SpriteBatchItem> _batchItemList;
/// <summary>
/// The available SpriteBatchItem queue so that we reuse these objects when we can.
/// </summary>
private readonly Queue<SpriteBatchItem> _freeBatchItemQueue;
/// <summary>
/// Vertex index array. The values in this array never change.
/// </summary>
private short[] _index;
private VertexPositionColorTexture[] _vertexArray;
public SpriteBatcher () {
_batchItemList = new List<SpriteBatchItem>(InitialBatchSize);
_freeBatchItemQueue = new Queue<SpriteBatchItem>(InitialBatchSize);
EnsureArrayCapacity(InitialBatchSize);
}
/// <summary>
/// Create an instance of SpriteBatchItem if there is none available in the free item queue. Otherwise,
/// a previously allocated SpriteBatchItem is reused.
/// </summary>
/// <returns></returns>
public SpriteBatchItem CreateBatchItem () {
SpriteBatchItem item;
if (_freeBatchItemQueue.Count > 0)
item = _freeBatchItemQueue.Dequeue();
else
item = new SpriteBatchItem();
_batchItemList.Add(item);
return item;
}
/// <summary>
/// Resize and recreate the missing indices for the index and vertex position color buffers.
/// </summary>
/// <param name="numBatchItems"></param>
private void EnsureArrayCapacity (int numBatchItems) {
int neededCapacity = 6 * numBatchItems;
if (_index != null && neededCapacity <= _index.Length) {
// Short circuit out of here because we have enough capacity.
return;
}
short[] newIndex = new short[6 * numBatchItems];
int start = 0;
if (_index != null) {
_index.CopyTo(newIndex, 0);
start = _index.Length / 6;
}
for (var i = start; i < numBatchItems; i++) {
/*
* TL TR
* 0----1 0,1,2,3 = index offsets for vertex indices
* | /| TL,TR,BL,BR are vertex references in SpriteBatchItem.
* | / |
* | / |
* |/ |
* 2----3
* BL BR
*/
// Triangle 1
newIndex[i * 6 + 0] = (short)(i * 4);
newIndex[i * 6 + 1] = (short)(i * 4 + 1);
newIndex[i * 6 + 2] = (short)(i * 4 + 2);
// Triangle 2
newIndex[i * 6 + 3] = (short)(i * 4 + 1);
newIndex[i * 6 + 4] = (short)(i * 4 + 3);
newIndex[i * 6 + 5] = (short)(i * 4 + 2);
}
_index = newIndex;
_vertexArray = new VertexPositionColorTexture[4 * numBatchItems];
}
/// <summary>
/// Sorts the batch items and then groups batch drawing into maximal allowed batch sets that do not
/// overflow the 16 bit array indices for vertices.
/// </summary>
/// <param name="sortMode">The type of depth sorting desired for the rendering.</param>
public void Draw (GraphicsDevice device) {
// nothing to do
if (_batchItemList.Count == 0)
return;
// Determine how many iterations through the drawing code we need to make
int batchIndex = 0;
int batchCount = _batchItemList.Count;
// Iterate through the batches, doing short.MaxValue sets of vertices only.
while (batchCount > 0) {
// setup the vertexArray array
var startIndex = 0;
var index = 0;
Texture2D tex = null;
int numBatchesToProcess = batchCount;
if (numBatchesToProcess > MaxBatchSize) {
numBatchesToProcess = MaxBatchSize;
}
EnsureArrayCapacity(numBatchesToProcess);
// Draw the batches
for (int i = 0; i < numBatchesToProcess; i++, batchIndex++) {
SpriteBatchItem item = _batchItemList[batchIndex];
// if the texture changed, we need to flush and bind the new texture
var shouldFlush = !ReferenceEquals(item.Texture, tex);
if (shouldFlush) {
FlushVertexArray(device, startIndex, index);
tex = item.Texture;
startIndex = index = 0;
device.Textures[0] = tex;
}
// store the SpriteBatchItem data in our vertexArray
_vertexArray[index++] = item.vertexTL;
_vertexArray[index++] = item.vertexTR;
_vertexArray[index++] = item.vertexBL;
_vertexArray[index++] = item.vertexBR;
// Release the texture and return the item to the queue.
item.Texture = null;
_freeBatchItemQueue.Enqueue(item);
}
// flush the remaining vertexArray data
FlushVertexArray(device, startIndex, index);
// Update our batch count to continue the process of culling down
// large batches
batchCount -= numBatchesToProcess;
}
_batchItemList.Clear();
}
/// <summary>
/// Sends the triangle list to the graphics device. Here is where the actual drawing starts.
/// </summary>
/// <param name="start">Start index of vertices to draw. Not used except to compute the count of vertices to draw.</param>
/// <param name="end">End index of vertices to draw. Not used except to compute the count of vertices to draw.</param>
private void FlushVertexArray (GraphicsDevice device, int start, int end) {
if (start == end)
return;
var vertexCount = end - start;
device.DrawUserIndexedPrimitives(
PrimitiveType.TriangleList,
_vertexArray,
0,
vertexCount,
_index,
0,
(vertexCount / 4) * 2,
VertexPositionColorTexture.VertexDeclaration);
}
}
public class SpriteBatchItem {
public Texture2D Texture;
public VertexPositionColorTexture vertexTL;
public VertexPositionColorTexture vertexTR;
public VertexPositionColorTexture vertexBL;
public VertexPositionColorTexture vertexBR;
}
}