mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 14:24:53 +08:00
Generic C# runtime & XNA runtime.
This commit is contained in:
parent
d5d5839a15
commit
4a354f4565
9
.gitignore
vendored
9
.gitignore
vendored
@ -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
|
||||
34
spine-csharp/Properties/AssemblyInfo.cs
Normal file
34
spine-csharp/Properties/AssemblyInfo.cs
Normal 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")]
|
||||
89
spine-csharp/spine-csharp.csproj
Normal file
89
spine-csharp/spine-csharp.csproj
Normal 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>
|
||||
390
spine-csharp/src/Animation.cs
Normal file
390
spine-csharp/src/Animation.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
24
spine-csharp/src/Attachments/AtlasAttachmentLoader.cs
Normal file
24
spine-csharp/src/Attachments/AtlasAttachmentLoader.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
spine-csharp/src/Attachments/Attachment.cs
Normal file
16
spine-csharp/src/Attachments/Attachment.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
8
spine-csharp/src/Attachments/AttachmentLoader.cs
Normal file
8
spine-csharp/src/Attachments/AttachmentLoader.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
6
spine-csharp/src/Attachments/AttachmentType.cs
Normal file
6
spine-csharp/src/Attachments/AttachmentType.cs
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
namespace Spine {
|
||||
public enum AttachmentType {
|
||||
region, regionSequence
|
||||
}
|
||||
}
|
||||
134
spine-csharp/src/Attachments/RegionAttachment.cs
Normal file
134
spine-csharp/src/Attachments/RegionAttachment.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
177
spine-csharp/src/BaseAtlas.cs
Normal file
177
spine-csharp/src/BaseAtlas.cs
Normal 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
83
spine-csharp/src/Bone.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
28
spine-csharp/src/BoneData.cs
Normal file
28
spine-csharp/src/BoneData.cs
Normal 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
551
spine-csharp/src/Json.cs
Normal 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<object>, a Dictionary<string, object>, 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<string, object> / List<object></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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
171
spine-csharp/src/Skeleton.cs
Normal file
171
spine-csharp/src/Skeleton.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
111
spine-csharp/src/SkeletonData.cs
Normal file
111
spine-csharp/src/SkeletonData.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
256
spine-csharp/src/SkeletonJson.cs
Normal file
256
spine-csharp/src/SkeletonJson.cs
Normal 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
54
spine-csharp/src/Skin.cs
Normal 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
61
spine-csharp/src/Slot.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
29
spine-csharp/src/SlotData.cs
Normal file
29
spine-csharp/src/SlotData.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
34
spine-xna/Properties/AssemblyInfo.cs
Normal file
34
spine-xna/Properties/AssemblyInfo.cs
Normal 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
BIN
spine-xna/example/Game.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
BIN
spine-xna/example/GameThumbnail.png
Normal file
BIN
spine-xna/example/GameThumbnail.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.6 KiB |
34
spine-xna/example/Properties/AssemblyInfo.cs
Normal file
34
spine-xna/example/Properties/AssemblyInfo.cs
Normal 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")]
|
||||
285
spine-xna/example/data/goblins.atlas
Normal file
285
spine-xna/example/data/goblins.atlas
Normal 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
|
||||
499
spine-xna/example/data/goblins.json
Normal file
499
spine-xna/example/data/goblins.json
Normal 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 }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
spine-xna/example/data/goblins.png
Normal file
BIN
spine-xna/example/data/goblins.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 145 KiB |
166
spine-xna/example/data/spineboy.atlas
Normal file
166
spine-xna/example/data/spineboy.atlas
Normal 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
|
||||
787
spine-xna/example/data/spineboy.json
Normal file
787
spine-xna/example/data/spineboy.json
Normal 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 }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
spine-xna/example/data/spineboy.png
Normal file
BIN
spine-xna/example/data/spineboy.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 65 KiB |
168
spine-xna/example/spine-xna-example.csproj
Normal file
168
spine-xna/example/spine-xna-example.csproj
Normal 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>
|
||||
71
spine-xna/example/src/ExampleGame.cs
Normal file
71
spine-xna/example/src/ExampleGame.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
13
spine-xna/example/src/ExampleProgram.cs
Normal file
13
spine-xna/example/src/ExampleProgram.cs
Normal 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
116
spine-xna/spine-xna.csproj
Normal 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
32
spine-xna/spine-xna.sln
Normal 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
84
spine-xna/src/Atlas.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
100
spine-xna/src/SkeletonRenderer.cs
Normal file
100
spine-xna/src/SkeletonRenderer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
240
spine-xna/src/SpriteBatcher.cs
Normal file
240
spine-xna/src/SpriteBatcher.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user