IK for spine-lua, spine-corona, spine-love.
@ -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)
|
||||
BIN
spine-corona/examples/spineboy/images/eye_indifferent.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
spine-corona/examples/spineboy/images/eye_surprised.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
spine-corona/examples/spineboy/images/front_bracer.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
spine-corona/examples/spineboy/images/front_fist_closed.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
spine-corona/examples/spineboy/images/front_fist_open.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
spine-corona/examples/spineboy/images/front_foot.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
spine-corona/examples/spineboy/images/front_foot_bend1.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
spine-corona/examples/spineboy/images/front_foot_bend2.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
spine-corona/examples/spineboy/images/front_shin.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
spine-corona/examples/spineboy/images/front_thigh.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
spine-corona/examples/spineboy/images/front_upper_arm.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
spine-corona/examples/spineboy/images/goggles.png
Normal file
|
After Width: | Height: | Size: 172 KiB |
BIN
spine-corona/examples/spineboy/images/gun.png
Normal file
|
After Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 318 KiB |
BIN
spine-corona/examples/spineboy/images/mouth_grind.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
spine-corona/examples/spineboy/images/mouth_oooo.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
spine-corona/examples/spineboy/images/mouth_smile.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
spine-corona/examples/spineboy/images/muzzle.png
Normal file
|
After Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 8.5 KiB |
BIN
spine-corona/examples/spineboy/images/rear_bracer.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
spine-corona/examples/spineboy/images/rear_foot.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
spine-corona/examples/spineboy/images/rear_foot_bend1.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
spine-corona/examples/spineboy/images/rear_foot_bend2.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
spine-corona/examples/spineboy/images/rear_shin.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
spine-corona/examples/spineboy/images/rear_thigh.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
spine-corona/examples/spineboy/images/rear_upper_arm.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 72 KiB |
@ -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)
|
||||
@ -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"
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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 }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 24 KiB |
BIN
spine-love/data/images/eye_indifferent.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
spine-love/data/images/eye_surprised.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
BIN
spine-love/data/images/front_bracer.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
spine-love/data/images/front_fist_closed.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
spine-love/data/images/front_fist_open.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
spine-love/data/images/front_foot.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
spine-love/data/images/front_foot_bend1.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
spine-love/data/images/front_foot_bend2.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
spine-love/data/images/front_shin.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
spine-love/data/images/front_thigh.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
spine-love/data/images/front_upper_arm.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
spine-love/data/images/goggles.png
Normal file
|
After Width: | Height: | Size: 172 KiB |
BIN
spine-love/data/images/gun.png
Normal file
|
After Width: | Height: | Size: 170 KiB |
BIN
spine-love/data/images/head.png
Normal file
|
After Width: | Height: | Size: 318 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
BIN
spine-love/data/images/mouth_grind.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
spine-love/data/images/mouth_oooo.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
spine-love/data/images/mouth_smile.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
spine-love/data/images/muzzle.png
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
spine-love/data/images/neck.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
BIN
spine-love/data/images/rear_bracer.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
spine-love/data/images/rear_foot.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
spine-love/data/images/rear_foot_bend1.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
spine-love/data/images/rear_foot_bend2.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
spine-love/data/images/rear_shin.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
spine-love/data/images/rear_thigh.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
spine-love/data/images/rear_upper_arm.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
BIN
spine-love/data/images/torso.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 8.8 KiB |
@ -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)
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
@ -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
|
||||
45
spine-lua/IkConstraintData.lua
Normal 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
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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])
|
||||
|
||||