IK for spine-lua, spine-corona, spine-love.

This commit is contained in:
NathanSweet 2014-11-16 03:01:40 +01:00
parent c3e8058208
commit 7c065a76e2
95 changed files with 563 additions and 2611 deletions

View File

@ -10,7 +10,7 @@ local skeleton = spine.Skeleton.new(skeletonData)
function skeleton:createImage (attachment)
return display.newImage("examples/hero/images/" .. attachment.name .. ".png")
end
skeleton.group.x = 195
skeleton.group.x = 95
skeleton.group.y = 385
skeleton.flipX = false
skeleton.flipY = false
@ -19,10 +19,13 @@ skeleton:setToSetupPose()
-- AnimationStateData defines crossfade durations between animations.
local stateData = spine.AnimationStateData.new(skeletonData)
stateData.defaultMix = 0.2;
stateData:setMix("Walk", "Attack", 0)
stateData:setMix("Attack", "Run", 0)
stateData:setMix("Run", "Attack", 0)
-- AnimationState has a queue of animations and can apply them with crossfading.
local state = spine.AnimationState.new(stateData)
--state:setAnimationByName(0, "Idle", true, 0)
state:setAnimationByName(0, "Walk", true, 0)
state:setAnimationByName(0, "Idle", true)
local lastTime = 0
local animationTime = 0
@ -37,3 +40,16 @@ Runtime:addEventListener("enterFrame", function (event)
state:apply(skeleton)
skeleton:updateWorldTransform()
end)
Runtime:addEventListener("touch", function (event)
if event.phase ~= "ended" and event.phase ~= "cancelled" then return end
local name = state:getCurrent(0).animation.name
if name == "Idle" then
state:setAnimationByName(0, "Crouch", true)
elseif name == "Crouch" then
state:setAnimationByName(0, "Walk", true)
else
state:setAnimationByName(0, "Attack", false)
state:addAnimationByName(0, "Run", true, 0)
end
end)

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 72 KiB

File diff suppressed because one or more lines are too long

View File

@ -4,7 +4,7 @@
local spine = require "spine-corona.spine"
local json = spine.SkeletonJson.new()
json.scale = 1
json.scale = 0.6
local skeletonData = json:readSkeletonDataFile("examples/spineboy/spineboy.json")
local skeleton = spine.Skeleton.new(skeletonData)
@ -25,13 +25,14 @@ local bounds = spine.SkeletonBounds.new()
-- AnimationStateData defines crossfade durations between animations.
local stateData = spine.AnimationStateData.new(skeletonData)
stateData:setMix("walk", "jump", 0.2)
stateData:setMix("jump", "walk", 0.4)
stateData:setMix("jump", "run", 0.2)
-- AnimationState has a queue of animations and can apply them with crossfading.
local state = spine.AnimationState.new(stateData)
state:setAnimationByName(0, "drawOrder")
state:addAnimationByName(0, "jump", false, 0)
state:addAnimationByName(0, "walk", true, 0)
-- state:setAnimationByName(0, "test")
state:setAnimationByName(0, "walk", true)
state:addAnimationByName(0, "jump", false, 3)
state:addAnimationByName(0, "run", true, 0)
state.onStart = function (trackIndex)
print(trackIndex.." start: "..state:getCurrent(trackIndex).animation.name)

View File

@ -1,5 +1,5 @@
-- require "examples.spineboy"
-- require "examples.goblins"
-- require "examples.dragon"
require "examples.hero"
require "examples.spineboy.spineboy"
-- require "examples.goblins.goblins"
-- require "examples.dragon.dragon"
-- require "examples.hero.hero"

View File

@ -35,6 +35,7 @@ spine.SkeletonJson = require "spine-lua.SkeletonJson"
spine.SkeletonData = require "spine-lua.SkeletonData"
spine.BoneData = require "spine-lua.BoneData"
spine.SlotData = require "spine-lua.SlotData"
spine.IkConstraintData = require "spine-lua.IkConstraintData"
spine.Skin = require "spine-lua.Skin"
spine.RegionAttachment = require "spine-lua.RegionAttachment"
spine.MeshAttachment = require "spine-lua.MeshAttachment"
@ -42,6 +43,7 @@ spine.SkinnedMeshAttachment = require "spine-lua.SkinnedMeshAttachment"
spine.Skeleton = require "spine-lua.Skeleton"
spine.Bone = require "spine-lua.Bone"
spine.Slot = require "spine-lua.Slot"
spine.IkConstraint = require "spine-lua.IkConstraint"
spine.AttachmentType = require "spine-lua.AttachmentType"
spine.AttachmentLoader = require "spine-lua.AttachmentLoader"
spine.Animation = require "spine-lua.Animation"

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

View File

@ -31,16 +31,16 @@
local spine = require "spine-love.spine"
local json = spine.SkeletonJson.new()
json.scale = 1
json.scale = 0.6
local skeletonData = json:readSkeletonDataFile("data/spineboy.json")
local skeleton = spine.Skeleton.new(skeletonData)
function skeleton:createImage (attachment)
-- Customize where images are loaded.
return love.graphics.newImage("data/" .. attachment.name .. ".png")
return love.graphics.newImage("data/images/" .. attachment.name .. ".png")
end
skeleton.x = love.graphics.getWidth() / 2
skeleton.y = love.graphics.getHeight() / 2 + 150
skeleton.y = love.graphics.getHeight() / 2 + 250
skeleton.flipX = false
skeleton.flipY = false
skeleton.debugBones = true -- Omit or set to false to not draw debug lines on top of the images.
@ -50,13 +50,14 @@ skeleton:setToSetupPose()
-- AnimationStateData defines crossfade durations between animations.
local stateData = spine.AnimationStateData.new(skeletonData)
stateData:setMix("walk", "jump", 0.2)
stateData:setMix("jump", "walk", 0.4)
stateData:setMix("jump", "run", 0.2)
-- AnimationState has a queue of animations and can apply them with crossfading.
local state = spine.AnimationState.new(stateData)
state:setAnimationByName(0, "drawOrder")
state:addAnimationByName(0, "jump", false, 0)
state:addAnimationByName(0, "walk", true, 0)
-- state:setAnimationByName(0, "test")
state:setAnimationByName(0, "walk", true)
state:addAnimationByName(0, "jump", true, 3)
state:addAnimationByName(0, "run", true, 0)
state.onStart = function (trackIndex)
print(trackIndex.." start: "..state:getCurrent(trackIndex).animation.name)

View File

@ -35,11 +35,15 @@ spine.SkeletonJson = require "spine-lua.SkeletonJson"
spine.SkeletonData = require "spine-lua.SkeletonData"
spine.BoneData = require "spine-lua.BoneData"
spine.SlotData = require "spine-lua.SlotData"
spine.IkConstraintData = require "spine-lua.IkConstraintData"
spine.Skin = require "spine-lua.Skin"
spine.RegionAttachment = require "spine-lua.RegionAttachment"
spine.MeshAttachment = require "spine-lua.MeshAttachment"
spine.SkinnedMeshAttachment = require "spine-lua.SkinnedMeshAttachment"
spine.Skeleton = require "spine-lua.Skeleton"
spine.Bone = require "spine-lua.Bone"
spine.Slot = require "spine-lua.Slot"
spine.IkConstraint = require "spine-lua.IkConstraint"
spine.AttachmentType = require "spine-lua.AttachmentType"
spine.AttachmentLoader = require "spine-lua.AttachmentLoader"
spine.Animation = require "spine-lua.Animation"

View File

@ -109,72 +109,89 @@ end
Animation.CurveTimeline = {}
function Animation.CurveTimeline.new ()
local LINEAR = 0
local STEPPED = -1
local STEPPED = 1
local BEZIER = 2;
local BEZIER_SEGMENTS = 10
local BEZIER_SIZE = BEZIER_SEGMENTS * 2 - 1
local self = {
curves = {}
curves = {} -- type, x, y, ...
}
function self:setLinear (frameIndex)
self.curves[frameIndex * 6] = LINEAR
self.curves[frameIndex * BEZIER_SIZE] = LINEAR
end
function self:setStepped (frameIndex)
self.curves[frameIndex * 6] = STEPPED
self.curves[frameIndex * BEZIER_SIZE] = STEPPED
end
function self:setCurve (frameIndex, 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 subdiv1 = 1 / BEZIER_SEGMENTS
local subdiv2 = subdiv1 * subdiv1
local subdiv3 = subdiv2 * subdiv1;
local pre1 = 3 * subdiv1
local pre2 = 3 * subdiv2
local pre4 = 6 * subdiv2
local pre5 = 6 * subdiv3
local tmp1x = -cx1 * 2 + cx2
local tmp1y = -cy1 * 2 + cy2
local tmp2x = (cx1 - cx2) * 3 + 1
local tmp2y = (cy1 - cy2) * 3 + 1
local i = frameIndex * 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
local dfx = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv3
local dfy = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv3
local ddfx = tmp1x * pre4 + tmp2x * pre5
local ddfy = tmp1y * pre4 + tmp2y * pre5;
local dddfx = tmp2x * pre5
local dddfy = tmp2y * pre5
function self:getCurvePercent (frameIndex, percent)
local curveIndex = frameIndex * 6
local i = frameIndex * BEZIER_SIZE
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]
curves[i] = BEZIER
i = i + 1
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
local n = i + BEZIER_SIZE - 1
while i < n do
curves[i] = x
curves[i + 1] = y
dfx = dfx + ddfx
dfy = dfy + ddfy
ddfx = ddfx + dddfx
ddfy = ddfy + dddfy
x = x + dfx
y = y + dfy
i = i + 2
end
end
function self:getCurvePercent (frameIndex, percent)
local curves = self.curves
local i = frameIndex * BEZIER_SIZE
local type = curves[i]
if type == LINEAR then return percent end
if type == STEPPED then return 0 end
i = i + 1
local x
local n = i + BEZIER_SIZE - 1
local start = i
while i < n do
x = curves[i]
if x >= percent then
local prevX, prevY
if i == start then
prevX = 0
prevY = 0
else
prevX = curves[i - 2]
prevY = curves[i - 1]
end
return prevY + (curves[i + 1] - prevY) * (percent - prevX) / (x - prevX)
end
i = i + 2
end
local y = curves[i - 1]
return y + (1 - y) * (percent - x) / (1 - x) -- Last point is 1,1.
end
@ -183,7 +200,7 @@ end
Animation.RotateTimeline = {}
function Animation.RotateTimeline.new ()
local LAST_FRAME_TIME = -2
local PREV_FRAME_TIME = -2
local FRAME_VALUE = 1
local self = Animation.CurveTimeline.new()
@ -224,20 +241,20 @@ function Animation.RotateTimeline.new ()
-- Interpolate between the last frame and the current frame.
local frameIndex = binarySearch(frames, time, 2)
local lastFrameValue = frames[frameIndex - 1]
local prevFrameValue = frames[frameIndex - 1]
local frameTime = frames[frameIndex]
local percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime)
local percent = 1 - (time - frameTime) / (frames[frameIndex + PREV_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
local amount = frames[frameIndex + FRAME_VALUE] - prevFrameValue
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
amount = bone.data.rotation + (prevFrameValue + amount * percent) - bone.rotation
while amount > 180 do
amount = amount - 360
end
@ -252,7 +269,7 @@ end
Animation.TranslateTimeline = {}
function Animation.TranslateTimeline.new ()
local LAST_FRAME_TIME = -3
local PREV_FRAME_TIME = -3
local FRAME_X = 1
local FRAME_Y = 2
@ -289,15 +306,15 @@ function Animation.TranslateTimeline.new ()
-- 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 prevFrameX = frames[frameIndex - 2]
local prevFrameY = frames[frameIndex - 1]
local frameTime = frames[frameIndex]
local percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime)
local percent = 1 - (time - frameTime) / (frames[frameIndex + PREV_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
bone.x = bone.x + (bone.data.x + prevFrameX + (frames[frameIndex + FRAME_X] - prevFrameX) * percent - bone.x) * alpha
bone.y = bone.y + (bone.data.y + prevFrameY + (frames[frameIndex + FRAME_Y] - prevFrameY) * percent - bone.y) * alpha
end
return self
@ -305,7 +322,7 @@ end
Animation.ScaleTimeline = {}
function Animation.ScaleTimeline.new ()
local LAST_FRAME_TIME = -3
local PREV_FRAME_TIME = -3
local FRAME_X = 1
local FRAME_Y = 2
@ -325,15 +342,15 @@ function Animation.ScaleTimeline.new ()
-- 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 prevFrameX = frames[frameIndex - 2]
local prevFrameY = frames[frameIndex - 1]
local frameTime = frames[frameIndex]
local percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime)
local percent = 1 - (time - frameTime) / (frames[frameIndex + PREV_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 * (lastFrameX + (frames[frameIndex + FRAME_X] - lastFrameX) * percent) - bone.scaleX) * alpha
bone.scaleY = bone.scaleY + (bone.data.scaleY * (lastFrameY + (frames[frameIndex + FRAME_Y] - lastFrameY) * percent) - bone.scaleY) * alpha
bone.scaleX = bone.scaleX + (bone.data.scaleX * (prevFrameX + (frames[frameIndex + FRAME_X] - prevFrameX) * percent) - bone.scaleX) * alpha
bone.scaleY = bone.scaleY + (bone.data.scaleY * (prevFrameY + (frames[frameIndex + FRAME_Y] - prevFrameY) * percent) - bone.scaleY) * alpha
end
return self
@ -341,7 +358,7 @@ end
Animation.ColorTimeline = {}
function Animation.ColorTimeline.new ()
local LAST_FRAME_TIME = -5
local PREV_FRAME_TIME = -5
local FRAME_R = 1
local FRAME_G = 2
local FRAME_B = 3
@ -381,19 +398,19 @@ function Animation.ColorTimeline.new ()
else
-- 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 prevFrameR = frames[frameIndex - 4]
local prevFrameG = frames[frameIndex - 3]
local prevFrameB = frames[frameIndex - 2]
local prevFrameA = frames[frameIndex - 1]
local frameTime = frames[frameIndex]
local percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime)
local percent = 1 - (time - frameTime) / (frames[frameIndex + PREV_FRAME_TIME] - frameTime)
if percent < 0 then percent = 0 elseif percent > 255 then percent = 255 end
percent = self:getCurvePercent(frameIndex / 5 - 1, percent)
r = lastFrameR + (frames[frameIndex + FRAME_R] - lastFrameR) * percent
g = lastFrameG + (frames[frameIndex + FRAME_G] - lastFrameG) * percent
b = lastFrameB + (frames[frameIndex + FRAME_B] - lastFrameB) * percent
a = lastFrameA + (frames[frameIndex + FRAME_A] - lastFrameA) * percent
r = prevFrameR + (frames[frameIndex + FRAME_R] - prevFrameR) * percent
g = prevFrameG + (frames[frameIndex + FRAME_G] - prevFrameG) * percent
b = prevFrameB + (frames[frameIndex + FRAME_B] - prevFrameB) * percent
a = prevFrameA + (frames[frameIndex + FRAME_A] - prevFrameA) * percent
end
local slot = skeleton.slots[self.slotIndex]
if alpha < 1 then
@ -409,7 +426,7 @@ end
Animation.AttachmentTimeline = {}
function Animation.AttachmentTimeline.new ()
local self = {
frames = {},
frames = {}, -- time, ...
attachmentNames = {},
slotName = nil
}
@ -429,14 +446,20 @@ function Animation.AttachmentTimeline.new ()
function self:apply (skeleton, lastTime, time, firedEvents, alpha)
local frames = self.frames
if time < frames[0] then return end -- Time is before first frame.
if time < frames[0] then
if lastTime > time then self:apply(skeleton, lastTime, 999999, nil, 0) end
return
elseif lastTime > time then
lastTime = -1
end
local frameIndex
if time >= frames[#frames] then -- Time is after last frame.
if time >= frames[#frames] then
frameIndex = #frames
else
frameIndex = binarySearch1(frames, time) - 1
end
if frames[frameIndex] < lastTime then return end
local attachmentName = self.attachmentNames[frameIndex]
local slot = skeleton.slotsByName[self.slotName]
@ -561,7 +584,7 @@ end
Animation.FfdTimeline = {}
function Animation.FfdTimeline.new ()
local self = Animation.CurveTimeline.new()
self.frames = {}
self.frames = {} -- time, ...
self.frameVertices = {}
self.slotIndex = -1
@ -583,17 +606,13 @@ function Animation.FfdTimeline.new ()
if slot.attachment ~= attachment then return end
local frames = self.frames
if time < frames[0] then -- Time is before first frame.
slot.attachmentVerticesCount = 0
return
end
if time < frames[0] then return end -- Time is before first frame.
local frameVertices = self.frameVertices
local vertexCount = #frameVertices[0]
local vertices = slot.attachmentVertices
if #vertices < vertexCount then
vertices = {}
vertices[vertexCount] = 0
slot.attachmentVertices = vertices
elseif #vertices < vertexCount then
alpha = 1 -- Don't mix from uninitialized slot vertices.
@ -601,7 +620,7 @@ function Animation.FfdTimeline.new ()
slot.attachmentVerticesCount = vertexCount
if time >= frames[#frames] then -- Time is after last frame.
local lastVertices = frameVertices[#frames.Length]
local lastVertices = frameVertices[#frames]
if alpha < 1 then
for i = 0, vertexCount do
local vertex = vertices[i]
@ -642,6 +661,60 @@ function Animation.FfdTimeline.new ()
return self
end
Animation.IkConstraintTimeline = {}
function Animation.IkConstraintTimeline.new ()
local PREV_FRAME_TIME = -3
local PREV_FRAME_MIX = -2
local PREV_FRAME_BEND_DIRECTION = -1
local FRAME_MIX = 1
local self = Animation.CurveTimeline.new()
self.frames = {} -- time, mix, bendDirection, ...
self.ikConstraintIndex = -1
function self:getDuration ()
return self.frames[#self.frames - 2]
end
function self:getFrameCount ()
return (#self.frames + 1) / 3
end
function self:setFrame (frameIndex, time, mix, bendDirection)
frameIndex = frameIndex * 3
self.frames[frameIndex] = time
self.frames[frameIndex + 1] = mix
self.frames[frameIndex + 2] = bendDirection
end
function self:apply (skeleton, lastTime, time, firedEvents, alpha)
local frames = self.frames
if time < frames[0] then return end -- Time is before first frame.
local ikConstraint = skeleton.ikConstraints[ikConstraintIndex]
if time >= frames[#frames - 2] then -- Time is after last frame.
ikConstraint.mix = ikConstraint.mix + (frames[#frames - 1] - ikConstraint.mix) * alpha
ikConstraint.bendDirection = frames[#frames]
return
end
-- Interpolate between the previous frame and the current frame.
local frameIndex = binarySearch(frames, time, 3);
local prevFrameMix = frames[frameIndex + PREV_FRAME_MIX]
local frameTime = frames[frameIndex]
local percent = 1 - (time - frameTime) / (frames[frameIndex + PREV_FRAME_TIME] - frameTime)
if percent < 0 then percent = 0 elseif percent > 1 then percent = 1 end
percent = self:getCurvePercent(frameIndex / 3 - 1, percent)
local mix = prevFrameMix + (frames[frameIndex + FRAME_MIX] - prevFrameMix) * percent
ikConstraint.mix = ikConstraint.mix + (mix - ikConstraint.mix) * alpha
ikConstraint.bendDirection = frames[frameIndex + PREV_FRAME_BEND_DIRECTION]
end
return self
end
Animation.FlipXTimeline = {}
function Animation.FlipXTimeline.new ()
local self = {
@ -666,7 +739,7 @@ function Animation.FlipXTimeline.new ()
function self:apply (skeleton, lastTime, time, firedEvents, alpha)
local frames = self.frames
if time < frames[0] then
if lastTime > time then self:apply(skeleton, lastTime, 999999, null, 0) end
if lastTime > time then self:apply(skeleton, lastTime, 999999, nil, 0) end
return
elseif lastTime > time then
lastTime = -1

View File

@ -39,7 +39,7 @@ function Bone.new (data, skeleton, parent)
skeleton = skeleton,
parent = parent,
x = 0, y = 0,
rotation = 0,
rotation = 0, rotationIK = 0,
scaleX = 1, scaleY = 1,
flipX = false, flipY = false,
m00 = 0, m01 = 0, worldX = 0, -- a b x
@ -62,9 +62,9 @@ function Bone.new (data, skeleton, parent)
self.worldScaleY = self.scaleY
end
if (self.data.inheritRotation) then
self.worldRotation = parent.worldRotation + self.rotation
self.worldRotation = parent.worldRotation + self.rotationIK
else
self.worldRotation = self.rotation
self.worldRotation = self.rotationIK
end
self.worldFlipX = parent.worldFlipX ~= self.flipX
self.worldFlipY = parent.worldFlipY ~= self.flipY
@ -82,7 +82,7 @@ function Bone.new (data, skeleton, parent)
end
self.worldScaleX = self.scaleX
self.worldScaleY = self.scaleY
self.worldRotation = self.rotation
self.worldRotation = self.rotationIK
self.worldFlipX = skeletonFlipX ~= self.flipX
self.worldFlipY = skeletonFlipY ~= self.flipY
end
@ -110,12 +110,36 @@ function Bone.new (data, skeleton, parent)
self.x = data.x
self.y = data.y
self.rotation = data.rotation
self.rotationIK = self.rotation
self.scaleX = data.scaleX
self.scaleY = data.scaleY
self.flipX = data.flipX
self.flipY = data.flipY
end
function self:worldToLocal (worldCoords)
local dx = worldCoords[1] - self.worldX
local dy = worldCoords[2] - self.worldY
local m00 = self.m00
local m10 = self.m10
local m01 = self.m01
local m11 = self.m11
if self.worldFlipX ~= self.worldFlipY then
m00 = -m00
m11 = -m11
end
local invDet = 1 / (m00 * m11 - m01 * m10)
worldCoords[1] = dx * m00 * invDet - dy * m01 * invDet
worldCoords[2] = dy * m11 * invDet - dx * m10 * invDet
end
function self:localToWorld (localCoords)
local localX = localCoords[1]
local localY = localCoords[2]
localCoords[1] = localX * self.m00 + localY * self.m01 + self.worldX
localCoords[2] = localX * self.m10 + localY * self.m11 + self.worldY
end
self:setToSetupPose()
return self
end

150
spine-lua/IkConstraint.lua Normal file
View File

@ -0,0 +1,150 @@
-------------------------------------------------------------------------------
-- Spine Runtimes Software License
-- Version 2.1
--
-- Copyright (c) 2013, Esoteric Software
-- All rights reserved.
--
-- You are granted a perpetual, non-exclusive, non-sublicensable and
-- non-transferable license to install, execute and perform the Spine Runtimes
-- Software (the "Software") solely for internal use. Without the written
-- permission of Esoteric Software (typically granted by licensing Spine), you
-- may not (a) modify, translate, adapt or otherwise create derivative works,
-- improvements of the Software or develop new applications using the Software
-- or (b) remove, delete, alter or obscure any trademarks or any copyright,
-- trademark, patent or other intellectual property or proprietary rights
-- notices on or in the Software, including any copy thereof. Redistributions
-- in binary or source form must include this license and terms.
--
-- THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
-- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-- EVENT SHALL ESOTERIC SOFTARE 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.
-------------------------------------------------------------------------------
local IkConstraint = {}
function IkConstraint.new (data, skeleton)
if not data then error("data cannot be nil", 2) end
if not skeleton then error("skeleton cannot be nil", 2) end
local self = {
data = data,
skeleton = skeleton,
bones = {},
target = nil,
bendDirection = data.bendDirection,
mix = data.mix
}
function self:apply ()
local target = self.target
local bones = self.bones
local boneCount = #bones
if boneCount == 1 then
IkConstraint.apply1(bones[1], target.worldX, target.worldY, self.mix)
elseif boneCount == 2 then
IkConstraint.apply2(bones[1], bones[2], target.worldX, target.worldY, self.bendDirection, self.mix)
end
end
for i,boneData in ipairs(data.bones) do
table.insert(self.bones, skeleton:findBone(boneData.name))
end
self.target = skeleton:findBone(data.target.name)
return self
end
local radDeg = 180 / math.pi
local degRad = math.pi / 180
function IkConstraint.apply1 (bone, targetX, targetY, alpha)
local parentRotation
if not bone.data.inheritRotation or not bone.parent then
parentRotation = 0
else
parentRotation = bone.parent.worldRotation
end
local rotation = bone.rotation
local rotationIK = math.atan2(targetY - bone.worldY, targetX - bone.worldX) * radDeg - parentRotation
bone.rotationIK = rotation + (rotationIK - rotation) * alpha
end
local temp = {}
function IkConstraint.apply2 (parent, child, targetX, targetY, bendDirection, alpha)
local childRotation = child.rotation
local parentRotation = parent.rotation
if not alpha then
child.rotationIK = childRotation
parent.rotationIK = parentRotation
return
end
local positionX, positionY
local tempPosition = temp
local parentParent = parent.parent
if parentParent then
tempPosition[1] = targetX
tempPosition[2] = targetY
parentParent:worldToLocal(tempPosition)
targetX = (tempPosition[1] - parent.x) * parentParent.worldScaleX
targetY = (tempPosition[2] - parent.y) * parentParent.worldScaleY
else
targetX = targetX - parent.x
targetY = targetY - parent.y
end
if child.parent == parent then
positionX = child.x
positionY = child.y
else
tempPosition[1] = child.x
tempPosition[2] = child.y
child.parent:localToWorld(tempPosition)
parent:worldToLocal(tempPosition)
positionX = tempPosition[1]
positionY = tempPosition[2]
end
local childX = positionX * parent.worldScaleX
local childY = positionY * parent.worldScaleY
local offset = math.atan2(childY, childX)
local len1 = math.sqrt(childX * childX + childY * childY)
local len2 = child.data.length * child.worldScaleX
-- Based on code by Ryan Juckett with permission: Copyright (c) 2008-2009 Ryan Juckett, http://www.ryanjuckett.com/
local cosDenom = 2 * len1 * len2
if cosDenom < 0.0001 then
child.rotationIK = childRotation + (math.atan2(targetY, targetX) * radDeg - parentRotation - childRotation) * alpha
return
end
local cos = (targetX * targetX + targetY * targetY - len1 * len1 - len2 * len2) / cosDenom
if cos < -1 then
cos = -1
elseif cos > 1 then
cos = 1
end
local childAngle = math.acos(cos) * bendDirection
local adjacent = len1 + len2 * cos
local opposite = len2 * math.sin(childAngle)
local parentAngle = math.atan2(targetY * adjacent - targetX * opposite, targetX * adjacent + targetY * opposite)
local rotation = (parentAngle - offset) * radDeg - parentRotation
if rotation > 180 then
rotation = rotation - 360
elseif rotation < -180 then
rotation = rotation + 360
end
parent.rotationIK = parentRotation + rotation * alpha
rotation = (childAngle + offset) * radDeg - childRotation
if rotation > 180 then
rotation = rotation - 360
elseif rotation < -180 then
rotation = rotation + 360
end
child.rotationIK = childRotation + (rotation + parent.worldRotation - child.parent.worldRotation) * alpha
end
return IkConstraint

View File

@ -0,0 +1,45 @@
-------------------------------------------------------------------------------
-- Spine Runtimes Software License
-- Version 2.1
--
-- Copyright (c) 2013, Esoteric Software
-- All rights reserved.
--
-- You are granted a perpetual, non-exclusive, non-sublicensable and
-- non-transferable license to install, execute and perform the Spine Runtimes
-- Software (the "Software") solely for internal use. Without the written
-- permission of Esoteric Software (typically granted by licensing Spine), you
-- may not (a) modify, translate, adapt or otherwise create derivative works,
-- improvements of the Software or develop new applications using the Software
-- or (b) remove, delete, alter or obscure any trademarks or any copyright,
-- trademark, patent or other intellectual property or proprietary rights
-- notices on or in the Software, including any copy thereof. Redistributions
-- in binary or source form must include this license and terms.
--
-- THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
-- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-- EVENT SHALL ESOTERIC SOFTARE 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.
-------------------------------------------------------------------------------
local IkConstraintData = {}
function IkConstraintData.new (name)
if not name then error("name cannot be nil", 2) end
local self = {
name = name,
bones = {},
target = nil,
bendDirection = 1,
mix = 1
}
return self
end
return IkConstraintData

View File

@ -30,6 +30,7 @@
local Bone = require "spine-lua.Bone"
local Slot = require "spine-lua.Slot"
local IkConstraint = require "spine-lua.IkConstraint"
local AttachmentLoader = require "spine-lua.AttachmentLoader"
local Skeleton = {}
@ -42,16 +43,71 @@ function Skeleton.new (skeletonData)
slots = {},
slotsByName = {},
drawOrder = {},
ikConstraints = {},
r = 1, g = 1, b = 1, a = 1,
x = 0, y = 0,
skin = nil,
flipX = false, flipY = false,
time = 0
time = 0,
x = 0, y = 0
}
function self:updateWorldTransform ()
-- Caches information about bones and IK constraints. Must be called if bones or IK constraints are added or removed.
function self:updateCache ()
self.boneCache = {}
local boneCache = self.boneCache
local ikConstraints = self.ikConstraints
local ikConstraintsCount = #ikConstraints
local arrayCount = ikConstraintsCount + 1
while #boneCache < arrayCount do
table.insert(boneCache, {})
end
local nonIkBones = boneCache[1]
for i,bone in ipairs(self.bones) do
bone:updateWorldTransform()
local current = bone
local continueOuter
repeat
for ii,ikConstraint in ipairs(ikConstraints) do
local parent = ikConstraint.bones[0]
local child = ikConstraint.bones[#ikConstraint.bones - 1]
while true do
if current == child then
table.insert(boneCache[ii], bone)
table.insert(boneCache[ii + 1], bone)
ii = ikConstraintsCount
continueOuter = true
break
end
if child == parent then break end
child = child.parent
end
end
if continueOuter then break end
current = current.parent
until not current
table.insert(nonIkBones, bone)
end
end
-- Updates the world transform for each bone and applies IK constraints.
function self:updateWorldTransform ()
local bones = self.bones
for i,bone in ipairs(self.bones) do
bone.rotationIK = bone.rotation
end
local boneCache = self.boneCache
local ikConstraints = self.ikConstraints
local i = 1
local last = #boneCache
while true do
for ii,bone in ipairs(boneCache[i]) do
bone:updateWorldTransform()
end
if i == last then break end
ikConstraints[i]:apply()
i = i + 1
end
end
@ -64,6 +120,11 @@ function Skeleton.new (skeletonData)
for i,bone in ipairs(self.bones) do
bone:setToSetupPose()
end
for i,ikConstraint in ipairs(self.ikConstraints) do
ikConstraint.bendDirection = ikConstraint.data.bendDirection
ikConstraint.mix = ikConstraint.data.mix
end
end
function self:setSlotsToSetupPose ()
@ -176,6 +237,12 @@ function Skeleton.new (skeletonData)
table.insert(self.drawOrder, slot)
end
for i,ikConstraintData in ipairs(skeletonData.ikConstraints) do
table.insert(self.ikConstraints, IkConstraint.new(ikConstraintData, self))
end
self:updateCache()
return self
end
return Skeleton

View File

@ -31,12 +31,15 @@
local SkeletonData = {}
function SkeletonData.new ()
local self = {
version = 0, hash = 0,
width = 0, height = 0,
bones = {},
slots = {},
slotNameIndices = {},
skins = {},
events = {},
animations = {},
ikConstraints = {},
defaultSkin = nil
}
@ -93,6 +96,14 @@ function SkeletonData.new ()
return nil
end
function self:findIkConstraint (ikConstraintName)
if not ikConstraintName then error("ikConstraintName cannot be nil.", 2) end
for i,ikConstraint in ipairs(self.ikConstraints) do
if ikConstraint.name == ikConstraintName then return ikConstraint end
end
return nil
end
return self
end
return SkeletonData

View File

@ -34,6 +34,8 @@ local SlotData = require "spine-lua.SlotData"
local Skin = require "spine-lua.Skin"
local AttachmentLoader = require "spine-lua.AttachmentLoader"
local Animation = require "spine-lua.Animation"
local IkConstraintData = require "spine-lua.IkConstraintData"
local IkConstraint = require "spine-lua.IkConstraint"
local EventData = require "spine-lua.EventData"
local Event = require "spine-lua.Event"
local AttachmentType = require "spine-lua.AttachmentType"
@ -62,6 +64,15 @@ function SkeletonJson.new (attachmentLoader)
local root = spine.utils.readJSON(jsonText)
if not root then error("Invalid JSON: " .. jsonText, 2) end
-- Skeleton.
if root["skeleton"] then
local skeletonMap = root["skeleton"]
skeletonData.hash = skeletonMap["hash"]
skeletonData.version = skeletonMap["spine"]
skeletonData.width = skeletonMap["width"] or 0
skeletonData.height = skeletonMap["height"] or 0
end
-- Bones.
for i,boneMap in ipairs(root["bones"]) do
local boneName = boneMap["name"]
@ -101,6 +112,28 @@ function SkeletonJson.new (attachmentLoader)
table.insert(skeletonData.bones, boneData)
end
-- IK constraints.
if root["ik"] then
for i,ikMap in ipairs(root["ik"]) do
local ikConstraintData = IkConstraintData.new(ikMap["name"])
for i,boneName in ipairs(ikMap["bones"]) do
local bone = skeletonData:findBone(boneName)
if not bone then error("IK bone not found: " .. boneName) end
table.insert(ikConstraintData.bones, bone)
end
local targetName = ikMap["target"]
ikConstraintData.target = skeletonData:findBone(targetName)
if not ikConstraintData.target then error("Target bone not found: " .. targetName) end
if ikMap["bendPositive"] == false then ikConstraintData.bendDirection = -1 end
if ikMap["mix"] ~= nil then ikConstraintData.mix = ikMap["mix"] end
table.insert(skeletonData.ikConstraints, ikConstraintData)
end
end
-- Slots.
if root["slots"] then
for i,slotMap in ipairs(root["slots"]) do
@ -391,7 +424,6 @@ function SkeletonJson.new (attachmentLoader)
local frameIndex = 0
for i,valueMap in ipairs(values) do
local flip
timeline:setFrame(frameIndex, valueMap["time"], valueMap[field] or false)
frameIndex = frameIndex + 1
end
@ -405,6 +437,32 @@ function SkeletonJson.new (attachmentLoader)
end
end
local ik = map["ik"]
if ik then
for ikConstraintName,values in pairs(ik) do
local ikConstraint = skeletonData.findIkConstraint(ikConstraintName)
local timeline = IkConstraintTimeline.new()
for i,other in pairs(skeletonData.ikConstraints) do
if other == ikConstraint then
timeline.ikConstraintIndex = i
break
end
end
local frameIndex = 0
for i,valueMap in ipairs(values) do
local mix = 1
if valueMap["mix"] ~= nil then mix = valueMap["mix"] end
local bendPositive = 1
if valueMap["bendPositive"] == false then bendPositive = -1 end
timeline:setFrame(frameIndex, valueMap["time"], mix, bendPositive)
readCurve(timeline, frameIndex, valueMap)
frameIndex = frameIndex + 1
end
table.insert(timelines, timeline)
duration = math.max(duration, timeline:getDuration())
end
end
local ffd = map["ffd"]
if ffd then
for skinName,slotMap in pairs(ffd) do
@ -553,8 +611,9 @@ function SkeletonJson.new (attachmentLoader)
readCurve = function (timeline, frameIndex, valueMap)
local curve = valueMap["curve"]
if not curve then return end
if curve == "stepped" then
if not curve then
timeline:setLinear(frameIndex)
elseif curve == "stepped" then
timeline:setStepped(frameIndex)
else
timeline:setCurve(frameIndex, curve[1], curve[2], curve[3], curve[4])