- Editado
[Corona] Help to position bounding box on a bone
Hi,
I've spent holiday break learning Spine, and the only thing I haven't figured out how to do, is how to attach a binding box to a bone so I can apply physics to it. I
I've read other forum posts related to other runtimes and I know that realWorldX = bone.worldX + skeleton.x and I tried to do that in the code below but my bounding box is still attached to the skeleton's origin.
Also, I had to fix the rotation by 90 degrees as it was not position correctly, so I'm sure I'm doing something wrong in my code
---
PHYSICS METHOD 1
---
---
USING BOUNDING BOXES
---
---
local bounds = spine.SkeletonBounds.new()
bounds:update(skeleton, true)
local vertices1 = skeleton:getAttachment ("bb-torso", "bb-torso").vertices
local bone1 = skeleton:findBone ("torso")
local polygon1 = display.newPolygon (skeleton.group, 0, 0, vertices1)
polygon1:setFillColor(0, 0, 1, 1)
polygon1.strokeWidth = 1
polygon1:setStrokeColor(1, 0, 0, 1)
polygon1.x = bone1.data.x + bone1.parent.worldX
polygon1.y = -bone1.data.y + bone1.parent.worldY
polygon1.rotation = -90
physics.addBody(polygon1, "static")
and here is the enterFrame function in case is needed, I have this block of code before the SkeletonBounds, vertices and the polygon
Runtime:addEventListener("enterFrame", function(event)
---
Compute time in seconds since last frame
local currentTime = event.time/1000
local delta = currentTime - lastTime
lastTime = currentTime
---
Update the state with the delta time, apply it, and update the world transforms
state:update(delta)
state:apply(skeleton)
skeleton:updateWorldTransform()
end)
Here is the image of spineBoy where you can see how the torso gets attached to the skeleton's origin
Image removed due to the lack of support for HTTPS. | Show Anyway
Could you plese help me position my bounding box correctly?
Thanks in advance, I'm one of the backers of the original Kickstarter project, but it wasn't until this holiday break that I had time to go deep into learning Spine. Congratulations for making such a brilliant piece of software
Regards
Hector
Sorry I didn't get to this sooner so you'd have more time during the holidays to play.
The vertices on a bounding box attachment are relative to the bone the attachment is attached to. The bone's world transform needs to be applied to position the polygon in the right place:
local worldVertices = {}
boundingBox:computeWorldVertices(skeleton.x, skeleton.y, slot.bone, worldVertices)
Then you would draw worldVertices. I see you want to use a Corona polygon though. In that case it would be something like (untested):
local torso = skeleton:findBone("torso")
local vertices = skeleton:getAttachment ("bb-torso", "bb-torso").vertices
local polygon = display.newPolygon(skeleton.group, 0, 0, vertices)
polygon:setFillColor(0, 0, 1, 1)
polygon.strokeWidth = 1
polygon:setStrokeColor(1, 0, 0, 1)
physics.addBody(polygon, "static")
Runtime:addEventListener("enterFrame", function (event)
---
Compute time in seconds since last frame
local currentTime = event.time / 1000
local delta = currentTime - lastTime
lastTime = currentTime
---
Update the state with the delta time, apply it, and update the world transforms
state:update(delta)
state:apply(skeleton)
skeleton:updateWorldTransform()
polygon.x = skeleton.x + bone.worldX
polygon.y = skeleton.y + bone.worldY
polygon.rotation = bone.worldRotation
polygon.xScale = bone.worldScaleX
polygon.yScale = bone.worldScaleY
end)
The idea is to pose the skeleton with the animation which modifies the bones' local transform (x/y/rotaton/scalex/scaley), updateWorldTransform so the bones' world transform is computed, then update the polygon's transform.
Corona docs say, "vertices: An array of x and y coordinates. These coordinates will automatically be re-centered about the center of the polygon." This is suboptimal documentation. I guess the polygon position is the polygon's centroid. You could compute the centroid (you can use my code here) so you know the distance from where Spine wants the origin (0,0) of the vertices (which is at the bone position) and where Corona puts the origin (the Corona polygon position). You'll probably need to use Corona's setReferencePoint so the polygon uses the bone position as the origin. If you don't then, for example, when the bone rotates the polygon will rotate around its centroid when what you want is for it to rotate around the bone.
Hopefully this helps a little.
Hi Nate,
Thanks a lot for reviewing my question, it helped me a lot, I'm almost there, check how my physics bounding box looks so far: Image removed due to the lack of support for HTTPS. | Show Anyway
I just want the bounding box to draw a display object where to put physics on for the bounding box, so even if the drawn display polygon doesn't look well positioned due to the reference point (I can always make the display object invisible), the physics polygon drawn by the Corona's display.newPolygon() function is almost good enough, I just need to flip the body horizontally from what I can see, but not sure how.
Here is how I drew the physics polygon passing a table with the bounding box/bone's vertices
local torso = skeleton:findBone("torso")
local vertices = skeleton:getAttachment ("bb-torso", "bb-torso").vertices
local polygon = display.newPolygon(skeleton.group, 0, 0, vertices)
polygon:setFillColor(0, 0, 1, 1)
polygon.strokeWidth = 1
polygon:setStrokeColor(1, 0, 0, 1)
physics.addBody(polygon, "static", {shape=vertices})
And here is how I' have updated the Runtime function based on your suggestion:
Runtime:addEventListener("enterFrame", function(event)
---
Compute time in seconds since last frame
local currentTime = event.time/1000
local delta = currentTime - lastTime
lastTime = currentTime
---
Update the state with the delta time, apply it, and update the world transforms
state:update(delta)
state:apply(skeleton)
skeleton:updateWorldTransform()
---
Update bounding box's position
polygon.x = -skeleton.x - torso.worldX
polygon.y = -skeleton.y - torso.worldY
polygon.rotation = -torso.worldRotation
polygon.xScale = torso.worldScaleX
polygon.yScale = -torso.worldScaleY
end)
Any idea how can I finalize to position the physics polygon for the bounding box on the torso?
Thanks in advance
Hector
Looks neat! I'm not sure what you are asking though?
Thx Nate! Bounding box is backwards on the horizontal axis (right side of the box should be on the left), so I just wanted to see if you had any idea how to turned it in the correct orientation. I think it has to do with the order the vertices get drawn, so let me keep trying to put them in the correct order.
Regards
Hector
Not sure why that would be, sorry. Careful it isn't a coincidence that it shows up almost correctly. Maybe test with a few different bounding boxes.
Hi Nate, Hector for your posts, and any others who can help,
Firstly I hope adding onto this thread helps
I supported and bought spine in the first kickstarter, cos it looked awesome, so i have to say well done to the esoteric
team on creating and building on it. Second i use corona sdk as my coding environment, i was new to
game coding and corona has helped me learn alot. I realise theres other platforms but for now corona works for me.
I have been wanting to use spine to create in game animations, but i have been creating games with
physics simulation. As a result i have struggled to integrate the two. With the advent of bounding boxes
and some better lua support for meshes and other features i wanted to have another go at using spine in my game
animation.
I am sure i am not alone in corona sdk land.
Would you or your team please be able to take some time out of your busy schedule to help answer some of my questions
I am happy to post a few links back in the corona forums to spread your solutions if that helps too.
Use cases involving spine boy using box2d physics bodies and spine animation.
Use case 1: Walking
How can i use spine animation to move bounding box physics objects based on spine animation?
Use case 2:
If the skeleton falls i want to disable any spine animation, remove the joints, or runtime enter frame event restrictions, so that the skeleton breaks apart and the body parts/part breaks off and tumbles using physics.
Use case 3:
Can this skeleton react to collisions from other objects eg a cannon ball and topple over.
Use case 4:
Can we make the object a ragdoll, by applying joints, and have the spine skeleton joints react to physics. If so,
how do we find the correctly bone attachment points? Can we restrict joint mobility movement, eg cant bend an elbow backwards?
how do i traverse the pine skeleton to create these joints, eg ensuring i am
connecting the skeleton bones correctly and that they are aligned correctly (to prevent spine and box2D conflicting)?
heres the corona reference for joints if needed.
https://docs.coronalabs.com/guide/physics/physicsJoints/index.html#pivot local pivotJoint =
physics.newJoint( "pivot", bodyA, bodyB, anchor_x, anchor_y ) what would be the method to create a
joint using bones
Posts that i have been reading that see to to provide soem good fodder are: but no solution yet.
corona
[Corona] Help to position bounding box on a bone
Corona SDK: Example on using atlas and Bounding box
[Corona]How to make a bone follow a physics bounding Box
Performance issue in corona SDK
other
BB position like in SPINE
good code - cant quite get it to work
Box2D Controlled Character Movement
Make Spine character's parts follow box2d Bodies
Also heres my code attempt and resulting inverted and mis-scaled bounding boxes. image included as well.
Its my understanding that the errors might possibly have to do with my worldtransform calculations, or a difference in y axis sign between spine and corona.
I would really appreciate your, and anybody elses, assistance to get me over this conceptual hurdle i (an most likely others) face.
Thanks
Kadlugan aka Nick
PS As i understand the lua runtime is going through a revamp as we speak, feel free to comment how or if this has an effect.
---
This example shows simple usage of displaying a skeleton with queued animations.
local spine = require "spine-corona.spine"
local physics = require "physics"
physics.setDrawMode(
---
"normal" )
"hybrid" )
---
"debug" )
physics.start( true ); physics.setGravity( 0, 10 )
local rootDir = "examples/myExamples/spineboyPhysics/"
local json = spine.SkeletonJson.new()
json.scale = 0.4
---
so it fits on screen
local skeletonData = json:readSkeletonDataFile(rootDir .. "spineboy.json")
local skeleton = spine.Skeleton.new(skeletonData)
---
local sprites = spine.GetAtlasSprites( "examples/myExamples/spineboyPhysics/spineboy.atlas" )
local sprites = spine.GetAtlasSprites( rootDir .. "spineboy.atlas" )
sprites.ATLAS_HELPER_setup(skeleton)
skeleton.group.x = display.contentWidth * 0.5
---
skeleton.group.y = display.contentHeight * 0.9
skeleton.group.y = display.contentHeight * 0.5
skeleton.flipX = false
skeleton.flipY = false
skeleton.debug = true
---
Omit or set to false to not draw debug lines on top of the images.
skeleton.debugAabb = true
skeleton:setToSetupPose()
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", "run", 0.2)
---
AnimationState has a queue of animations and can apply them with crossfading.
local state = spine.AnimationState.new(stateData)
---
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)
end
state.onEnd = function (trackIndex)
---
print(trackIndex.." end: "..state:getCurrent(trackIndex).animation.name)
end
state.onComplete = function (trackIndex, loopCount)
---
print(trackIndex.." complete: "..state:getCurrent(trackIndex).animation.name..", "..loopCount)
end
state.onEvent = function (trackIndex, event)
---
print(trackIndex.." event: "..state:getCurrent(trackIndex).animation.name..", "..event.data.name..", "..event.intValue..", "..event.floatValue..", '"..(event.stringValue or "").."'")
end
---
bounds:update(skeleton, true)
local bbPolygons = {}
local worldVertices = {}
for i, bb in ipairs(bounds.boundingBoxes) do
local bbName = bb.name
local boneName = string.sub(bb.name, 4)
---
name format eg "torso" from "bb-torso"
---
I would prefer a more direct way of finding attachment to slot to bone reference
---
Is there one?
print(bbName, boneName)
local bone = skeleton:findBone(boneName)
local attachment = skeleton:getAttachment (bbName, bbName)
---
local attachment = bone:getLoadedAttachment()
---
is this function possible, to avoid name format fudge above
---
or can i get it frome a slot search.
---
what is the best way
local vertices = attachment.vertices
---
where do i use this.
---
attachment:computeWorldVertices (skeleton.x, skeleton.y, bone, worldVertices)
local polygon = display.newPolygon(skeleton.group, 0, 0, vertices)
polygon:setFillColor(1, 0, 0, 1)
polygon.strokeWidth = 1
polygon:setStrokeColor(1, 0, 0, 1)
---
to make it easier to access the various elements later?? or is there a better way
polygon.attachment = attachment
polygon.bone = bone
polygon.vertices = vertices
---
Do i need to manipulate these vertices to flip their
---
y position
---
add the physics body
physics.addBody(polygon, "static", {shape=vertices})
table.insert(bbPolygons, polygon)
end
local lastTime = 0
Runtime:addEventListener("enterFrame", function (event)
---
Compute time in seconds since last frame.
local currentTime = event.time / 1000
local delta = currentTime - lastTime
lastTime = currentTime
---
Update the state with the delta time, apply it, and update the world transforms.
state:update(delta)
state:apply(skeleton)
skeleton:updateWorldTransform()
---
update the bounding box polygons
for i, polygon in ipairs(bbPolygons) do
---
polygon.attachment:computeWorldVertices (skeleton.x, skeleton.y, polygon.bone, worldVertices)
---
is this needed each frame?? Can i use it to fix my problems with orientations?
---
Update the bounding boxes position
polygon.x = skeleton.x + polygon.bone.worldX
polygon.y = skeleton.y + polygon.bone.worldY
polygon.rotation = polygon.bone.worldRotation
polygon.xScale = polygon.bone.worldScaleX
polygon.yScale = polygon.bone.worldScaleY
end
end)
This is quite a complex topic to cover briefly. As such I don't have a good answer for you at the moment. The new Lua runtime will be pretty much as close to the old runtime on the API level as possible. I have not yet started work on the Corona specific parts.
My plan is to actually write tutorials for the use cases you mentioned once I'm done updating all the runtimes to the latest and greatest. I'm afraid I can't give an ETA on this. Let me try to briefly answer the use cases. I'm sorry that this might not be super helpful.
Use case 1: You can attach bounding boxes directly in Spine (which I assume you already know), then get their world position at runtime. You want to use the SkeletonBounds "class" and call SkeletonBounds.update(yourSkeleton, true). This will update the vertices of all bounding box attachments and should be in the correct coordinate system. You just need to add that to your EnterFrame listener and should get what you need.
Use case 2: Simply don't call AnimationState.update|apply/Skeleton.updateWorldTransform then set the worldX/worldY and a/b/c/d entries of each bone to what the physics engine gives you. Use Bone:rotateWorld to set the rotation, and simply set worldX/worldY.
Use case 3: This is very involved, especially if you want to keep playing back a skeleton animation. I don't have a good answer for this at the moment.
Use case 4: This is essentially like use case 2 if I understood correctly. For joint constraints, you'll have to use the facilities provided by Corona's physics engine. Spine doesn't have any special support for that.
Thanks BadLogic/Mario
Really appreciate your quick reply to say that your on the case. Right now thats ok to say that things are a work in progress. What i am pleased to hear is that you've got the same intent as I do for spine in my game ideas. I'll try some of the tips you suggested and post back with any success I have. Keep up the good work.
PS Ive been looking in on the spine-lua fork of the runtime you are working on. Sorry i probably cant provide much assistance, other than what Ive read elsewhere. eg
the optimisations suggested here (in waffle link) to improve the speed of lua processing. ie localisation of functions and metatable use. You seem to be doing this is the latest revamp
https://waffle.io/EsotericSoftware/spine-runtimes/cards/577150b93c67531e0011c5deThe ability to directly and efficiently reference the bounding box to a bone more directly and vice versa to allow more obvious referencing. Pardon my ignorance if i'm looking at this the wrong way. Even though i have been reviewing the code, Im really not completely across how best to access the functions efficiently. I was thinking that given the tree nature of the skeleton, that having a few more handles to move between attachments and bones might be sensible. Slots appear to be able to access their parent bone, but attachments can't(?) access their parent slot, and in turn bone.
Without a find bone function using 'ipairs'. It seems like you have might have had a reason and tried to separate the skin from bones to keep them at arms length?
Could this be improved with a slot field in the attachment class? Eg:
---
BoundingBoxAttachment.lua:
local BoundingBoxAttachment = {}
function BoundingBoxAttachment.new (name, slot)
---
...
local self = {
name = name,
type = AttachmentType.boundingbox,
vertices = {},
slot = slot
---
or bone = bone <
---
does adding this help then access back to the slot, and bone
}
---
... etc
---
Slot.lua:
local Slot = {}
function Slot.new (slotData, bone)
---
...
local self = {
data = slotData,
bone = bone,
r = 1, g = 1, b = 1, a = 1,
attachment = nil,
attachmentTime = 0,
attachmentVertices = nil,
attachmentVerticesCount = 0
}
---
... etc
---
Spineboy.lua:
---
from my previously posted code. Seems like you
for i, bb in ipairs(bounds.boundingBoxes) do
local bbName = bb.name
local boneName = string.sub(bb.name, 4)
---
name format eg "torso" from "bb-torso"
---
easy to mess up this syntax method
local bone = skeleton:findBone(boneName)
local attachment = skeleton:getAttachment (bbName, bbName)
local vertices = attachment.vertices
---
use these variables to then create polygons
---
verses with the few mods allow:
for i, bb in ipairs(bounds.boundingBoxes) do
local bbSlot = bb.slot
local bbBone = bb.slot.bone
---
or bb:getBone
---
essentially chase the references back
local bbAttachment = bbBone: getLoadedAttachment()
---
or bb.bone.attachment
local vertices = bb.vertices
---
use these variables to then create polygons
As I said, not sure if this really is a sensible way of approaching the problem. Or whether this makes things messy when you start modifying skins and attachments on the fly. eg adding new weapons or clothes to character, or loosing limbs etc.
- Checking that the error statements in some procedures. I seem to quite often hard fail with errors, and don't get nice
eg in Skin.lua has a few
function self:getAttachment (slotIndex, name)
if not name then error("name cannot be nil.", 2) end
if not slotIndex then error("slotindex cannot be nil.", 2) end
---
Add this
...
function self:addAttachment (slotIndex, name, attachment)
if not name then error("name cannot be nil.", 2) end
if not slotIndex then error("slotindex cannot be nil.", 2) end
---
Add this
if not attachment then error("attachment cannot be nil.", 2) end
---
Add this
...
end
.. etc
Sorry i'm adding this here rather than a bug request, or pull request. Just an observation.
Anyway, I realise your far more across the object model than I am, and that some of my ideas are likely born from ignorance. However I hope writing them down at least makes them part of the discussion for others following this thread. Thanks for your time. If any of this is worth discussion let me know, but don't let it take you away from the actual task at hand of the Runtimes updates and tutorials.
BTW Ive added the zip attachment with my .lua .spine and .atlas file, in case that allows you to speed up examples. ie have draw some bounding boxes on spineboy. etc Feel free to use or not.
- Yeah, I try to revamp this across the board.
- We curently try to keep the runtimes in sync with the reference runtime for libGDX/Java. Such a change would require the blessing of Nate. We'll see
Thanks for the code. Once I get to the tutorials on physics integration I'll likely rip some of it!
Gotcha, No Probs.
Congrats on the update BadLogic. Thanks for all your efforts. :clap: The examples look great, and great to have the mesh features working so well.
I noticed that you had a comma missing (but fixed it a couple of days ago) in SkeletonJson ln 566. I tried doing a pull request to let you know but i didnt have permissions and my fork was way behind making it difficult - i am not a power user of git by any means.
Anyway, I have attached a full bounding box model of spineboy to save you some time with building the physics examples. my last attached zip only had the head body and legs. Im not sure if how i have added them to the bones as separate slots is how you need but hope they help. I have tried to use a common naming convention of adding "bb-" to the bone name.
Looking forward to the physics examples. cheers
That's awesome! I'm still in update mode as we made some massive (and awesome) changes to AnimationState. Will ping you here once I commence working on the physics tutorial.
Roger that. Thanks
PING
Howdy badlogic, how goes the physics demo you mentioned? I can see you've been pretty active on the forums, so i am guessing various things are on your plate. Any chance of a quick very rough example to get things started before xmas? thx
Sorry, very unlikely to happen before xmas. I'm finishing up our new UE4 plugin and then have to port the latest 3.6.0-beta changes
Ok, thanks Badlogic. Hope the new plugin / 3.6.0 goes well. I will work on other things in the meantime. Have a good xmas and look forward to the examples in the new year. cheers
Hey Badlogic, now that we are into the new year, i just wanted to check how your going on the examples.
cheers Nick
Soon
ok. thx
Hey Badlogic,
A couple of months have passed since i last touched base. Any progress on a demonstration of spine use with physics (corona sdk, others).
If its still far off, perhaps if you can provide a simple single example, then perhaps the community can build on that. Might make building an example easier. I assume those examples i provided with bounding boxes are enough for a ragdoll, or basic jump example?
cheers
Nick
Hey. I'm sorry for the repeated delay on this. We are in a constant resource struggle to work on the runtimes themselves versus auxiliary things like examples, e.g. physics integration.
I'll try to come up with a simple sample (minus an accompanying tutorial text) next week based on your example code. I've opened an issue here [corona] Provide physics integration example · #892
Thanks Badlogic, Sounds like a good plan. :clap: