Initial Corona runtime.

This commit is contained in:
NathanSweet 2013-02-20 01:13:59 +01:00
commit e2120ca07b
43 changed files with 1639 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
spine-cpp/Debug/*
spine-libgdx/bin/*

22
LICENSE Normal file
View File

@ -0,0 +1,22 @@
Copyright (c) 2013, Esoteric Software
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,12 @@
settings = {
orientation = {
default = "portrait",
supported = { "portrait", }
},
iphone = {
plist = {
UIStatusBarHidden = false,
UIPrerenderedIcon = true, -- set to false for "shine" overlay
}
},
}

8
spine-corona/config.lua Normal file
View File

@ -0,0 +1,8 @@
application = {
content = {
width = 320,
height = 480,
scale = "letterBox",
fps = 60,
},
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
spine-corona/data/eyes.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
spine-corona/data/head.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
spine-corona/data/neck.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@ -0,0 +1,101 @@
{
"bones": [
{ "name": "root", "length": 0 },
{ "name": "hip", "parent": "root", "length": 0, "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", "length": 0, "x": 1.41, "y": -6.57 }
],
"slots": [
{ "name": "template", "bone": "root", "color": "ff898c86" },
{ "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": {
"template": {
"spineboy": { "y": 167.82, "width": 145, "height": 341 }
},
"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 }
}
}
}
}

View File

@ -0,0 +1,278 @@
{
"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 }
]
}
}
}

BIN
spine-corona/data/torso.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

36
spine-corona/main.lua Normal file
View File

@ -0,0 +1,36 @@
local spine = require "spine.spine"
-- Optional attachment resolver customizes where images are loaded. Eg, could use an image sheet.
local attachmentResolver = spine.AttachmentResolver.new()
function attachmentResolver:createImage (attachment)
return display.newImage("data/" .. attachment.name .. ".png")
end
local json = spine.SkeletonJson.new(attachmentResolver)
json.scale = 1
local skeletonData = json:readSkeletonDataFile("data/spineboy-skeleton.json")
local walkAnimation = json:readAnimationFile(skeletonData, "data/spineboy-walk.json")
-- Optional second parameter can be the group for the Skeleton to use. Eg, could be an image group.
local skeleton = spine.Skeleton.new(skeletonData)
skeleton.x = 150
skeleton.y = 325
skeleton.flipX = false
skeleton.flipY = false
skeleton.debug = true -- Omit or set to false to not draw debug lines on top of the images.
skeleton:setToBindPose()
local lastTime = 0
local animationTime = 0
Runtime:addEventListener("enterFrame", function (event)
-- Compute time in seconds since last frame.
local currentTime = event.time / 1000
local delta = currentTime - lastTime
lastTime = currentTime
-- Accumulate time and pose skeleton using animation.
animationTime = animationTime + delta
walkAnimation:apply(skeleton, animationTime, true)
skeleton:updateWorldTransform()
end)

View File

@ -0,0 +1,397 @@
local utils = require "spine.utils"
local Animation = {}
function Animation.new (timelines, duration)
if not timelines then error("timelines cannot be nil", 2) end
local self = {
timelines = timelines,
duration = duration
}
function self:apply (skeleton, time, loop)
if not skeleton then error("skeleton cannot be nil.", 2) end
if loop and duration then time = time % duration end
for i,timeline in ipairs(self.timelines) do
timeline:apply(skeleton, time, 1)
end
end
function self:mix (skeleton, time, loop, alpha)
if not skeleton then error("skeleton cannot be nil.", 2) end
if loop and duration then time = time % duration end
for i,timeline in ipairs(self.timelines) do
timeline:apply(skeleton, time, alpha)
end
end
return self
end
local function binarySearch (values, target, step)
local low = 0
local high = math.floor((#values + 1) / step - 2)
if high == 0 then return step end
local current = math.floor(high / 2)
while true do
if values[(current + 1) * step] <= target then
low = current + 1
else
high = current
end
if low == high then return (low + 1) * step end
current = math.floor((low + high) / 2)
end
end
local function linearSearch (values, target, step)
for i = 0, #values, step do
if (values[i] > target) then return i end
end
return -1
end
Animation.CurveTimeline = {}
function Animation.CurveTimeline.new ()
local LINEAR = 0
local STEPPED = -1
local BEZIER_SEGMENTS = 10
local self = {
curves = {}
}
function self:setLinear (keyframeIndex)
self.curves[keyframeIndex * 6] = LINEAR
end
function self:setStepped (keyframeIndex)
self.curves[keyframeIndex * 6] = STEPPED
end
function self:setCurve (keyframeIndex, cx1, cy1, cx2, cy2)
local subdiv_step = 1 / BEZIER_SEGMENTS
local subdiv_step2 = subdiv_step * subdiv_step
local subdiv_step3 = subdiv_step2 * subdiv_step
local pre1 = 3 * subdiv_step
local pre2 = 3 * subdiv_step2
local pre4 = 6 * subdiv_step2
local pre5 = 6 * subdiv_step3
local tmp1x = -cx1 * 2 + cx2
local tmp1y = -cy1 * 2 + cy2
local tmp2x = (cx1 - cx2) * 3 + 1
local tmp2y = (cy1 - cy2) * 3 + 1
local i = keyframeIndex * 6
local curves = self.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
end
function self:getCurvePercent (keyframeIndex, percent)
local curveIndex = keyframeIndex * 6
local curves = self.curves
local dfx = curves[curveIndex]
if not dfx then return percent end -- linear
if dfx == STEPPED then return 0 end
local dfy = curves[curveIndex + 1]
local ddfx = curves[curveIndex + 2]
local ddfy = curves[curveIndex + 3]
local dddfx = curves[curveIndex + 4]
local dddfy = curves[curveIndex + 5]
local x = dfx
local y = dfy
local i = BEZIER_SEGMENTS - 2
while true do
if x >= percent then
local lastX = x - dfx
local lastY = y - dfy
return lastY + (y - lastY) * (percent - lastX) / (x - lastX)
end
if i == 0 then break end
i = i - 1
dfx = dfx + ddfx
dfy = dfy + ddfy
ddfx = ddfx + dddfx
ddfy = ddfy + dddfy
x = x + dfx
y = y + dfy
end
return y + (1 - y) * (percent - x) / (1 - x) -- Last point is 1,1.
end
return self
end
Animation.RotateTimeline = {}
function Animation.RotateTimeline.new ()
local LAST_FRAME_TIME = -2
local FRAME_VALUE = 1
local self = Animation.CurveTimeline.new()
self.frames = {}
function self:getDuration ()
return self.frames[#self.frames - 1]
end
function self:getKeyframeCount ()
return (#self.frames + 1) / 2
end
function self:setKeyframe (keyframeIndex, time, value)
keyframeIndex = keyframeIndex * 2
self.frames[keyframeIndex] = time
self.frames[keyframeIndex + 1] = value
end
function self:apply (skeleton, time, alpha)
local frames = self.frames
if time < frames[0] then return end -- Time is before first frame.
local bone = skeleton.bones[self.boneIndex]
if time >= frames[#frames - 1] then -- Time is after last frame.
local amount = bone.data.rotation + frames[#frames] - bone.rotation
while amount > 180 do
amount = amount - 360
end
while amount < -180 do
amount = amount + 360
end
bone.rotation = bone.rotation + amount * alpha
return
end
-- Interpolate between the last frame and the current frame.
local frameIndex = binarySearch(frames, time, 2)
local lastFrameValue = frames[frameIndex - 1]
local frameTime = frames[frameIndex]
local percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime)
if percent < 0 then percent = 0 elseif percent > 1 then percent = 1 end
percent = self:getCurvePercent(frameIndex / 2 - 1, percent)
local amount = frames[frameIndex + FRAME_VALUE] - lastFrameValue
while amount > 180 do
amount = amount - 360
end
while amount < -180 do
amount = amount + 360
end
amount = bone.data.rotation + (lastFrameValue + amount * percent) - bone.rotation
while amount > 180 do
amount = amount - 360
end
while amount < -180 do
amount = amount + 360
end
bone.rotation = bone.rotation + amount * alpha
end
return self
end
Animation.TranslateTimeline = {}
function Animation.TranslateTimeline.new ()
local LAST_FRAME_TIME = -3
local FRAME_X = 1
local FRAME_Y = 2
local self = Animation.CurveTimeline.new()
self.frames = {}
function self:getDuration ()
return self.frames[#self.frames - 2]
end
function self:getKeyframeCount ()
return (#self.frames + 1) / 3
end
function self:setKeyframe (keyframeIndex, time, x, y)
keyframeIndex = keyframeIndex * 3
self.frames[keyframeIndex] = time
self.frames[keyframeIndex + 1] = x
self.frames[keyframeIndex + 2] = y
end
function self:apply (skeleton, time, alpha)
local frames = self.frames
if time < frames[0] then return end -- Time is before first frame.
local bone = skeleton.bones[self.boneIndex]
if time >= frames[#frames - 2] then -- Time is after last frame.
bone.x = bone.x + (bone.data.x + frames[#frames - 1] - bone.x) * alpha
bone.y = bone.y + (bone.data.y + frames[#frames] - bone.y) * alpha
return
end
-- Interpolate between the last frame and the current frame.
local frameIndex = binarySearch(frames, time, 3)
local lastFrameX = frames[frameIndex - 2]
local lastFrameY = frames[frameIndex - 1]
local frameTime = frames[frameIndex]
local percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime)
if percent < 0 then percent = 0 elseif percent > 1 then percent = 1 end
percent = self:getCurvePercent(frameIndex / 3 - 1, percent)
bone.x = bone.x + (bone.data.x + lastFrameX + (frames[frameIndex + FRAME_X] - lastFrameX) * percent - bone.x) * alpha
bone.y = bone.y + (bone.data.y + lastFrameY + (frames[frameIndex + FRAME_Y] - lastFrameY) * percent - bone.y) * alpha
end
return self
end
Animation.ScaleTimeline = {}
function Animation.ScaleTimeline.new ()
local LAST_FRAME_TIME = -3
local FRAME_X = 1
local FRAME_Y = 2
local self = Animation.TranslateTimeline.new()
function self:apply (skeleton, time, alpha)
local frames = self.frames
if time < frames[0] then return end -- Time is before first frame.
local bone = skeleton.bones[self.boneIndex]
if time >= frames[#frames - 2] then -- Time is after last frame.
bone.scaleX = bone.scaleX + (bone.data.scaleX - 1 + frames[#frames - 1] - bone.scaleX) * alpha
bone.scaleY = bone.scaleY + (bone.data.scaleY - 1 + frames[#frames] - bone.scaleY) * alpha
return
end
-- Interpolate between the last frame and the current frame.
local frameIndex = binarySearch(frames, time, 3)
local lastFrameX = frames[frameIndex - 2]
local lastFrameY = frames[frameIndex - 1]
local frameTime = frames[frameIndex]
local percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime)
if percent < 0 then percent = 0 elseif percent > 1 then percent = 1 end
percent = self:getCurvePercent(frameIndex / 3 - 1, percent)
bone.scaleX = bone.scaleX + (bone.data.scaleX - 1 + lastFrameX + (frames[frameIndex + FRAME_X] - lastFrameX) * percent - bone.scaleX) * alpha
bone.scaleY = bone.scaleY + (bone.data.scaleY - 1 + lastFrameY + (frames[frameIndex + FRAME_Y] - lastFrameY) * percent - bone.scaleY) * alpha
end
return self
end
Animation.ColorTimeline = {}
function Animation.ColorTimeline.new ()
local LAST_FRAME_TIME = -5
local FRAME_R = 1
local FRAME_G = 2
local FRAME_B = 3
local FRAME_A = 4
local self = Animation.CurveTimeline.new()
self.frames = {}
function self:getDuration ()
return self.frames[#self.frames - 4]
end
function self:getKeyframeCount ()
return (#self.frames + 1) / 5
end
function self:setKeyframe (keyframeIndex, time, r, g, b, a)
keyframeIndex = keyframeIndex * 5
self.frames[keyframeIndex] = time
self.frames[keyframeIndex + 1] = r
self.frames[keyframeIndex + 2] = g
self.frames[keyframeIndex + 3] = b
self.frames[keyframeIndex + 4] = a
end
function self:apply (skeleton, time, alpha)
local frames = self.frames
if time < frames[0] then return end -- Time is before first frame.
local slot = skeleton.slots[self.slotIndex]
if time >= frames[#frames - 4] then -- Time is after last frame.
local r = frames[#frames - 3]
local g = frames[#frames - 2]
local b = frames[#frames - 1]
local a = frames[#frames]
slot:setColor(r, g, b, a)
return
end
-- Interpolate between the last frame and the current frame.
local frameIndex = binarySearch(frames, time, 5)
local lastFrameR = frames[frameIndex - 4]
local lastFrameG = frames[frameIndex - 3]
local lastFrameB = frames[frameIndex - 2]
local lastFrameA = frames[frameIndex - 1]
local frameTime = frames[frameIndex]
local percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime)
if percent < 0 then percent = 0 elseif percent > 255 then percent = 255 end
percent = self:getCurvePercent(frameIndex / 5 - 1, percent)
local r = lastFrameR + (frames[frameIndex + FRAME_R] - lastFrameR) * percent
local g = lastFrameG + (frames[frameIndex + FRAME_G] - lastFrameG) * percent
local b = lastFrameB + (frames[frameIndex + FRAME_B] - lastFrameB) * percent
local a = lastFrameA + (frames[frameIndex + FRAME_A] - lastFrameA) * percent
if alpha < 1 then
slot:setColor(slot.r + (r - color.r) * alpha, slot.g + (g - color.g) * alpha, slot.b + (b - color.b) * alpha, slot.a + (a - color.a) * alpha)
else
slot:setColor(r, g, b, a)
end
end
return self
end
Animation.AttachmentTimeline = {}
function Animation.AttachmentTimeline.new ()
local self = Animation.CurveTimeline.new()
self.frames = {}
self.attachmentNames = {}
function self:getDuration ()
return self.frames[#self.frames]
end
function self:getKeyframeCount ()
return #self.frames + 1
end
function self:setKeyframe (keyframeIndex, time, attachmentName)
self.frames[keyframeIndex] = time
self.attachmentNames[keyframeIndex] = attachmentName
end
function self:apply (skeleton, time, alpha)
local frames = self.frames
if time < frames[0] then return end -- Time is before first frame.
local frameIndex
if time >= frames[#frames] then -- Time is after last frame.
frameIndex = #frames
else
frameIndex = binarySearch(frames, time, 1) - 1
end
local attachmentName = self.attachmentNames[frameIndex]
local attachment
if attachmentName then attachment = skeleton:getAttachment(self.slotName, attachmentName) end
skeleton:findSlot(self.slotName):setAttachment(attachment)
end
return self
end
return Animation

View File

@ -0,0 +1,30 @@
local AttachmentResolver = {
failed = {}
}
function AttachmentResolver.new ()
local self = {
images = {}
}
function self:resolve (skeleton, attachment)
local image = self:createImage(attachment)
if image then
image:setReferencePoint(display.CenterReferencePoint);
image.width = attachment.width
image.height = attachment.height
else
print("Error creating image: " .. attachment.name)
image = AttachmentResolver.failed
end
skeleton.images[attachment] = image
return image
end
function self:createImage (attachment)
return display.newImage(attachment.name .. ".png")
end
return self
end
return AttachmentResolver

View File

@ -0,0 +1,54 @@
local Bone = {}
function Bone.new (data, parent)
if not data then error("data cannot be nil", 2) end
local self = {
data = data,
parent = parent
}
function self:updateWorldTransform (flipX, flipY)
local parent = self.parent
if parent then
self.worldX = self.x * parent.m00 + self.y * parent.m01 + parent.worldX
self.worldY = self.x * parent.m10 + self.y * parent.m11 + parent.worldY
self.worldScaleX = parent.worldScaleX * self.scaleX
self.worldScaleY = parent.worldScaleY * self.scaleY
self.worldRotation = parent.worldRotation + self.rotation
else
self.worldX = self.x
self.worldY = self.y
self.worldScaleX = self.scaleX
self.worldScaleY = self.scaleY
self.worldRotation = self.rotation
end
local radians = math.rad(self.worldRotation)
local cos = math.cos(radians)
local sin = math.sin(radians)
self.m00 = cos * self.worldScaleX
self.m10 = sin * self.worldScaleX
self.m01 = -sin * self.worldScaleY
self.m11 = cos * self.worldScaleY
if flipX then
self.m00 = -self.m00
self.m01 = -self.m01
end
if flipY then
self.m10 = -self.m10
self.m11 = -self.m11
end
end
function self:setToBindPose ()
local data = self.data
self.x = data.x
self.y = data.y
self.rotation = data.rotation
self.scaleX = data.scaleX
self.scaleY = data.scaleY
end
return self
end
return Bone

View File

@ -0,0 +1,13 @@
local BoneData = {}
function BoneData.new (name, parent)
if not name then error("name cannot be nil", 2) end
local self = {
name = name,
parent = parent
}
return self
end
return BoneData

View File

@ -0,0 +1,12 @@
local RegionAttachment = {}
function RegionAttachment.new (name)
if not name then error("name cannot be nil", 2) end
local self = {
name = name
}
return self
end
return RegionAttachment

View File

@ -0,0 +1,172 @@
local utils = require "spine.utils"
local Bone = require "spine.Bone"
local Slot = require "spine.Slot"
local AttachmentResolver = require "spine.AttachmentResolver"
local Skeleton = {}
function Skeleton.new (skeletonData, group)
if not skeletonData then error("skeletonData cannot be nil", 2) end
local self = group or display.newGroup()
self.data = skeletonData
self.bones = {}
self.slots = {}
self.drawOrder = {}
self.images = {}
for i,boneData in ipairs(skeletonData.bones) do
local parent
if boneData.parent then parent = self.bones[utils.indexOf(skeletonData.bones, boneData.parent)] end
table.insert(self.bones, Bone.new(boneData, parent))
end
for i,slotData in ipairs(skeletonData.slots) do
local bone = self.bones[utils.indexOf(skeletonData.bones, slotData.boneData)]
local slot = Slot.new(slotData, self, bone)
table.insert(self.slots, slot)
table.insert(self.drawOrder, slot)
end
function self:updateWorldTransform ()
for i,bone in ipairs(self.bones) do
bone:updateWorldTransform(self.flipX, self.flipY)
end
for i,slot in ipairs(self.drawOrder) do
if slot.attachment then
local image = self.images[slot.attachment]
if not image then image = self.data.attachmentResolver:resolve(self, slot.attachment) end
if image ~= AttachmentResolver.failed then
image.x = slot.bone.worldX + slot.attachment.x * slot.bone.m00 + slot.attachment.y * slot.bone.m01
image.y = -(slot.bone.worldY + slot.attachment.x * slot.bone.m10 + slot.attachment.y * slot.bone.m11)
image.rotation = -(slot.bone.worldRotation + slot.attachment.rotation)
image.xScale = slot.bone.worldScaleX + slot.attachment.scaleX - 1
image.yScale = slot.bone.worldScaleY + slot.attachment.scaleY - 1
if self.flipX then
image.xScale = -image.xScale
image.rotation = -image.rotation
end
if self.flipY then
image.yScale = -image.yScale
image.rotation = -image.rotation
end
image:setFillColor(slot.r, slot.g, slot.b, slot.a)
self:insert(image)
end
end
end
if self.debug then
for i,bone in ipairs(self.bones) do
if not bone.line then bone.line = display.newLine(0, 0, bone.data.length, 0) end
bone.line.x = bone.worldX
bone.line.y = -bone.worldY
bone.line.rotation = -bone.worldRotation
if self.flipX then
bone.line.xScale = -1
bone.line.rotation = -bone.line.rotation
else
bone.line.xScale = 1
end
if self.flipY then
bone.line.yScale = -1
bone.line.rotation = -bone.line.rotation
else
bone.line.yScale = 1
end
bone.line:setColor(255, 0, 0)
self:insert(bone.line)
if not bone.circle then bone.circle = display.newCircle(0, 0, 3) end
bone.circle.x = bone.worldX
bone.circle.y = -bone.worldY
bone.circle:setFillColor(0, 255, 0)
self:insert(bone.circle)
end
end
end
function self:setToBindPose ()
self:setBonesToBindPose()
self:setSlotsToBindPose()
end
function self:setBonesToBindPose ()
for i,bone in ipairs(self.bones) do
bone:setToBindPose()
end
end
function self:setSlotsToBindPose ()
for i,slot in ipairs(self.slots) do
slot:setToBindPose()
end
end
function self:getRootBone ()
return self.bones[1]
end
function self:findSlot (slotName)
if not slotName then error("slotName cannot be nil.", 2) end
for i,slot in ipairs(self.slots) do
if slot.data.name == slotName then return slot end
end
return nil
end
function self:setSkin (skinName)
local newSkin
if skinName then
newSkin = self.data:findSkin(skinName)
if not newSkin then error("Skin not found: " .. skinName, 2) end
if self.skin then
-- Attach all attachments from the new skin if the corresponding attachment from the old skin is currently attached.
for k,v in self.skin.attachments do
local attachment = v[3]
local slotIndex = v[1]
local slot = self.slots[slotIndex]
if slot.attachment == attachment then
local name = v[2]
local newAttachment = newSkin:getAttachment(slotIndex, name)
if newAttachment then slot:setAttachment(newAttachment) end
end
end
end
end
self.skin = newSkin
end
function self:getAttachment (slotName, attachmentName)
if not slotName then error("slotName cannot be nil.", 2) end
if not attachmentName then error("attachmentName cannot be nil.", 2) end
local slotIndex = self.data:findSlotIndex(slotName)
if slotIndex == -1 then error("Slot not found: " .. slotName, 2) end
if self.data.defaultSkin then
local attachment = self.data.defaultSkin:getAttachment(slotIndex, attachmentName)
if attachment then return attachment end
end
if self.skin then return self.skin:getAttachment(slotIndex, attachmentName) end
return nil
end
function self:setAttachment (slotName, attachmentName)
if not slotName then error("slotName cannot be nil.", 2) end
if not attachmentName then error("attachmentName cannot be nil.", 2) end
for i,slot in ipairs(self.slots) do
if slot.data.name == slotName then
slot:setAttachment(self:getAttachment(slotName, attachmentName))
return
end
end
error("Slot not found: " + slotName, 2)
end
function self:update (delta)
self.time = self.time + delta
end
return self
end
return Skeleton

View File

@ -0,0 +1,55 @@
local SkeletonData = {}
function SkeletonData.new (attachmentResolver)
if not attachmentResolver then error("attachmentResolver cannot be nil", 2) end
local self = {
attachmentResolver = attachmentResolver,
bones = {},
slots = {},
skins = {}
}
function self:findBone (boneName)
if not boneName then error("boneName cannot be nil.", 2) end
for i,bone in ipairs(self.bones) do
if bone.name == boneName then return bone end
end
return nil
end
function self:findBoneIndex (boneName)
if not boneName then error("boneName cannot be nil.", 2) end
for i,bone in ipairs(self.bones) do
if bone.name == boneName then return i end
end
return -1
end
function self:findSlot (slotName)
if not slotName then error("slotName cannot be nil.", 2) end
for i,slot in ipairs(self.slots) do
if slot.name == slotName then return slot end
end
return nil
end
function self:findSlotIndex (slotName)
if not slotName then error("slotName cannot be nil.", 2) end
for i,slot in ipairs(self.slots) do
if slot.name == slotName then return i end
end
return -1
end
function self:findSkin (skinName)
if not skinName then error("skinName cannot be nil.", 2) end
for i,skin in ipairs(self.skins) do
if skin.name == skinName then return skin end
end
return nil
end
return self
end
return SkeletonData

View File

@ -0,0 +1,255 @@
local utils = require "spine.utils"
local SkeletonData = require "spine.SkeletonData"
local BoneData = require "spine.BoneData"
local SlotData = require "spine.SlotData"
local Skin = require "spine.Skin"
local RegionAttachment = require "spine.RegionAttachment"
local AttachmentResolver = require "spine.AttachmentResolver"
local Animation = require "spine.Animation"
local json = require "json"
local TIMELINE_SCALE = "scale"
local TIMELINE_ROTATE = "rotate"
local TIMELINE_TRANSLATE = "translate"
local TIMELINE_ATTACHMENT = "attachment"
local TIMELINE_COLOR = "color"
local ATTACHMENT_REGION = "region"
local ATTACHMENT_ANIMATED_REGION = "animatedRegion"
local SkeletonJson = {}
function SkeletonJson.new (attachmentResolver)
if not attachmentResolver then attachmentResolver = AttachmentResolver.new() end
local self = {
attachmentResolver = attachmentResolver,
scale = 1
}
function self:readSkeletonDataFile (fileName, base)
return self:readSkeletonData(utils.readFile(fileName, base))
end
local readAttachment
function self:readSkeletonData (jsonText)
local skeletonData = SkeletonData.new(self.attachmentResolver)
local root = json.decode(jsonText)
if not root then error("Invalid JSON: " .. jsonText, 2) end
-- Bones.
for i,boneMap in ipairs(root["bones"]) do
local boneName = boneMap["name"]
local parent = nil
local parentName = boneMap["parent"]
if parentName then
parent = skeletonData:findBone(parentName)
if not parent then error("Parent bone not found: " .. parentName) end
end
local boneData = BoneData.new(boneName, parent)
boneData.length = (boneMap["length"] or 0) * self.scale
boneData.x = (boneMap["x"] or 0) * self.scale
boneData.y = (boneMap["y"] or 0) * self.scale
boneData.rotation = (boneMap["rotation"] or 0)
boneData.scaleX = (boneMap["scaleX"] or 1)
boneData.scaleY = (boneMap["scaleY"] or 1)
table.insert(skeletonData.bones, boneData)
end
-- Slots.
if root["slots"] then
for i,slotMap in ipairs(root["slots"]) do
local slotName = slotMap["name"]
local boneName = slotMap["bone"]
local boneData = skeletonData:findBone(boneName)
if not boneData then error("Slot bone not found: " .. boneName) end
local slotData = SlotData.new(slotName, boneData)
local color = slotMap["color"]
if color then
slotData:setColor(
tonumber(color:sub(1, 2), 16),
tonumber(color:sub(3, 4), 16),
tonumber(color:sub(5, 6), 16),
tonumber(color:sub(7, 8), 16)
)
end
slotData.attachmentName = slotMap["attachment"]
table.insert(skeletonData.slots, slotData)
end
end
-- Skins.
map = root["skins"]
if map then
for skinName,skinMap in pairs(map) do
local skin = Skin.new(skinName)
for slotName,slotMap in pairs(skinMap) do
local slotIndex = skeletonData:findSlotIndex(slotName)
for attachmentName,attachmentMap in pairs(slotMap) do
local attachment = readAttachment(attachmentName, attachmentMap, self.scale)
skin:addAttachment(slotIndex, attachmentName, attachment)
end
end
if skin.name == "default" then
skeletonData.defaultSkin = skin
else
table.insert(skeletonData.skins, skin)
end
end
end
return skeletonData
end
readAttachment = function (name, map, scale)
name = map["name"] or name
local attachment
local type = map["type"] or ATTACHMENT_REGION
if type == ATTACHMENT_REGION then
attachment = RegionAttachment.new(name)
else
error("Unknown attachment type: " .. type .. " (" + name + ")")
end
attachment.x = (map["x"] or 0) * scale
attachment.y = (map["y"] or 0) * scale
attachment.scaleX = (map["scaleX"] or 1)
attachment.scaleY = (map["scaleY"] or 1)
attachment.rotation = (map["rotation"] or 0)
attachment.width = map["width"] * scale
attachment.height = map["height"] * scale
return attachment
end
function self:readAnimationFile (skeletonData, fileName, base)
return self:readAnimation(skeletonData, utils.readFile(fileName, base))
end
local readCurve
function self:readAnimation (skeletonData, jsonText)
local timelines = {}
local duration = 0
local root = json.decode(jsonText)
if not root then error("Invalid JSON: " .. jsonText, 2) end
local bonesMap = root["bones"]
for boneName,propertyMap in pairs(bonesMap) do
local boneIndex = skeletonData:findBoneIndex(boneName)
if boneIndex == -1 then error("Bone not found: " .. boneName) end
for timelineType,values in pairs(propertyMap) do
if timelineType == TIMELINE_ROTATE then
local timeline = Animation.RotateTimeline.new()
timeline.boneIndex = boneIndex
local keyframeIndex = 0
for i,valueMap in ipairs(values) do
local time = valueMap["time"]
timeline:setKeyframe(keyframeIndex, time, valueMap["angle"])
readCurve(timeline, keyframeIndex, valueMap)
keyframeIndex = keyframeIndex + 1
end
table.insert(timelines, timeline)
duration = math.max(duration, timeline:getDuration())
elseif timelineType == TIMELINE_TRANSLATE or timelineType == TIMELINE_SCALE then
local timeline
local timelineScale = 1
if timelineType == TIMELINE_SCALE then
timeline = Animation.ScaleTimeline.new()
else
timeline = Animation.TranslateTimeline.new()
timelineScale = self.scale
end
timeline.boneIndex = boneIndex
local keyframeIndex = 0
for i,valueMap in ipairs(values) do
local time = valueMap["time"]
local x = (valueMap["x"] or 0) * timelineScale
local y = (valueMap["y"] or 0) * timelineScale
timeline:setKeyframe(keyframeIndex, time, x, y)
readCurve(timeline, keyframeIndex, valueMap)
keyframeIndex = keyframeIndex + 1
end
table.insert(timelines, timeline)
duration = math.max(duration, timeline:getDuration())
else
error("Invalid timeline type for a bone: " .. timelineType .. " (" .. boneName .. ")")
end
end
end
local slotsMap = root["slots"]
if slotsMap then
for slotName,propertyMap in pairs(slotsMap) do
local slotIndex = skeletonData:findSlotIndex(slotName)
for timelineType,values in pairs(propertyMap) do
if timelineType == TIMELINE_COLOR then
local timeline = Animation.ColorTimeline.new()
timeline.slotIndex = slotIndex
local keyframeIndex = 0
for i,valueMap in ipairs(values) do
local time = valueMap["time"]
local color = valueMap["color"]
timeline:setKeyframe(
keyframeIndex, time,
tonumber(color:sub(1, 2), 16),
tonumber(color:sub(3, 4), 16),
tonumber(color:sub(5, 6), 16),
tonumber(color:sub(7, 8), 16)
)
readCurve(timeline, keyframeIndex, valueMap)
keyframeIndex = keyframeIndex + 1
end
table.insert(timelines, timeline)
duration = math.max(duration, timeline:getDuration())
elseif timelineType == TIMELINE_ATTACHMENT then
local timeline = Animation.AttachmentTimeline.new()
timeline.slotName = slotName
local keyframeIndex = 0
for i,valueMap in ipairs(values) do
local time = valueMap["time"]
local attachmentName = valueMap["name"]
if attachmentName == json.null then attachmentName = nil end
timeline:setKeyframe(keyframeIndex, time, attachmentName)
keyframeIndex = keyframeIndex + 1
end
table.insert(timelines, timeline)
duration = math.max(duration, timeline:getDuration())
else
error("Invalid frame type for a slot: " .. timelineType .. " (" .. slotName .. ")")
end
end
end
end
return Animation.new(timelines, duration)
end
readCurve = function (timeline, keyframeIndex, valueMap)
local curve = valueMap["curve"]
if not curve then return end
if curve == "stepped" then
timeline:setStepped(keyframeIndex)
else
timeline:setCurve(keyframeIndex, curve[1], curve[2], curve[3], curve[4])
end
end
return self
end
return SkeletonJson

View File

@ -0,0 +1,39 @@
local Skin = {}
function Skin.new (name)
if not name then error("name cannot be nil", 2) end
local self = {
name = name,
attachments = {}
}
function self:addAttachment (slotIndex, name, attachment)
if not name then error("name cannot be nil.", 2) end
self.attachments[slotIndex .. ":" .. name] = { slotIndex, name, attachment }
end
function self:getAttachment (slotIndex, name)
if not name then error("name cannot be nil.", 2) end
local values = self.attachments[slotIndex .. ":" .. name]
if not values then return nil end
return values[3]
end
function self:findNamesForSlot (slotIndex)
local names = {}
for k,v in self.attachments do
if v[1] == slotIndex then table.insert(names, v[2]) end
end
end
function self:findAttachmentsForSlot (slotIndex)
local attachments = {}
for k,v in self.attachments do
if v[1] == slotIndex then table.insert(attachments, v[3]) end
end
end
return self
end
return Skin

View File

@ -0,0 +1,54 @@
local utils = require "spine.utils"
local Slot = {}
function Slot.new (slotData, skeleton, bone)
if not slotData then error("slotData cannot be nil", 2) end
if not skeleton then error("skeleton cannot be nil", 2) end
if not bone then error("bone cannot be nil", 2) end
local self = {
data = slotData,
skeleton = skeleton,
bone = bone
}
function self:setColor (r, g, b, a)
self.r = r
self.g = g
self.b = b
self.a = a
end
function self:setAttachment (attachment)
if self.attachment and self.attachment ~= attachment and self.skeleton.images[self.attachment] then
self.skeleton.images[self.attachment]:removeSelf()
self.skeleton.images[self.attachment] = nil
end
self.attachment = attachment
self.attachmentTime = self.skeleton.time
end
function self:setAttachmentTime (time)
self.attachmentTime = self.skeleton.time - time
end
function self:getAttachmentTime ()
return self.skeleton.time - self.attachmentTime
end
function self:setToBindPose ()
local data = self.data
self:setColor(data.r, data.g, data.b, data.a)
local attachment
if data.attachmentName then attachment = self.skeleton:getAttachment(data.name, data.attachmentName) end
self:setAttachment(attachment)
end
self:setColor(255, 255, 255, 255)
return self
end
return Slot

View File

@ -0,0 +1,23 @@
local SlotData = {}
function SlotData.new (name, boneData)
if not name then error("name cannot be nil", 2) end
if not boneData then error("boneData cannot be nil", 2) end
local self = {
name = name,
boneData = boneData
}
function self:setColor (r, g, b, a)
self.r = r
self.g = g
self.b = b
self.a = a
end
self:setColor(255, 255, 255, 255)
return self;
end
return SlotData

View File

@ -0,0 +1,17 @@
local spine = {}
spine.utils = require "spine.utils"
spine.SkeletonJson = require "spine.SkeletonJson"
spine.SkeletonData = require "spine.SkeletonData"
spine.BoneData = require "spine.BoneData"
spine.SlotData = require "spine.SlotData"
spine.Skin = require "spine.Skin"
spine.RegionAttachment = require "spine.RegionAttachment"
spine.Skeleton = require "spine.Skeleton"
spine.Bone = require "spine.Bone"
spine.Slot = require "spine.Slot"
spine.AttachmentResolver = require "spine.AttachmentResolver"
spine.Animation = require "spine.Animation"
return spine

View File

@ -0,0 +1,59 @@
local utils = {}
utils.readFile = function (fileName, base)
if not base then base = system.ResourceDirectory; end
local path = system.pathForFile(fileName, base)
local file = io.open(path, "r")
if not file then return nil; end
local contents = file:read("*a")
io.close(file)
return contents
end
function tablePrint (tt, indent, done)
done = done or {}
indent = indent or 0
if type(tt) == "table" then
local sb = {}
for key, value in pairs (tt) do
table.insert(sb, string.rep (" ", indent)) -- indent it
if type (value) == "table" and not done [value] then
done [value] = true
table.insert(sb, "{\n");
table.insert(sb, tablePrint (value, indent + 2, done))
table.insert(sb, string.rep (" ", indent)) -- indent it
table.insert(sb, "}\n");
elseif "number" == type(key) then
table.insert(sb, string.format("\"%s\"\n", tostring(value)))
else
table.insert(sb, string.format(
"%s = \"%s\"\n", tostring (key), tostring(value)))
end
end
return table.concat(sb)
else
return tt .. "\n"
end
end
function utils.print (value)
if "nil" == type(value) then
print(tostring(nil))
elseif "table" == type(value) then
print(tablePrint(value))
elseif "string" == type(value) then
print(value)
else
print(tostring(value))
end
end
function utils.indexOf (haystack, needle)
for i,value in ipairs(haystack) do
if value == needle then return i end
end
return nil
end
return utils