# Physics Ragdoll

I really enjoyed playing with @Luatee’s Ragdoll surfer, and was pretty impressed by the Ragdoll specifically. So, I wondered if I could accomplish something similar with the built-in physics.

After a bit of fiddling, I’ve got something that I think is fairly realistic.

Video:

EDIT: I’m not sure why the colors are all wonky in the video, YouTube must have added some kind of filter when I uploaded it. Sorry about that.

EDIT 2: I think I’ve fixed that issue.

Code:

``````
--# Main
-- Ragdoll Physics

function setup()
DebugDraw = PhysicsDebugDraw()

guy = Ragdoll(WIDTH / 2, HEIGHT / 2)

floor = physics.body(EDGE, vec2(0,0), vec2(WIDTH,0))
end

function draw()
background(40, 40, 50)

DebugDraw:draw()
end

function touched(t)
DebugDraw:touched(t)
end

--# Ragdoll
Ragdoll = class()

local bodyBox = function(width, height, x, y)
local box = physics.body(POLYGON, vec2(-width / 2, -height / 2), vec2(width / 2, -height / 2), vec2(width / 2, height / 2), vec2(-width / 2, height / 2))
box.restitution = 0.1
box.friction = 0.4
box.position = vec2(x, y)

return box
end

local bodyJoint = function(lower, upper, b1, b2, x, y)
local joint = physics.joint(REVOLUTE, b1, b2, vec2(x, y))
joint.enableLimit = true
joint.lowerLimit = lower
joint.upperLimit = upper

return joint
end

function Ragdoll:init(x, y)
self.bones, self.joints = {}, {}

-- Sizes
local torsoWidth, torsoHeight = 30, 20
local upperArmWidth, upperArmHeight = 13, 36
local lowerArmWidth, lowerArmHeight = 12, 34
local upperLegWidth, upperLegHeight = 15, 44
local lowerLegWidth, lowerLegHeight = 12, 40

-- Bones

-- Torso
self.bones[2] = bodyBox(torsoWidth, torsoHeight, x, y - headSize / 2 - torsoHeight / 2)

self.bones[3] = bodyBox(torsoWidth, torsoHeight, x, y - headSize / 2 - torsoHeight * 1.5)

self.bones[4] = bodyBox(torsoWidth, torsoHeight, x, y - headSize / 2 - torsoHeight * 2.5)

-- Arms
self.bones[5] = bodyBox(upperArmWidth, upperArmHeight, x - torsoWidth / 2 - upperArmWidth / 2, y - headSize / 2 - upperArmHeight / 2)

self.bones[6] = bodyBox(upperArmWidth, upperArmHeight, x + torsoWidth / 2 + upperArmWidth / 2, y - headSize / 2 - upperArmHeight / 2)

self.bones[7] = bodyBox(lowerArmWidth, lowerArmHeight, x - torsoWidth / 2 - upperArmWidth / 2, y - headSize / 2 - upperArmHeight - lowerArmHeight / 2)

self.bones[8] = bodyBox(lowerArmWidth, lowerArmHeight, x + torsoWidth / 2 + upperArmWidth / 2, y - headSize / 2 - upperArmHeight - lowerArmHeight / 2)

-- Legs
self.bones[9] = bodyBox(upperLegWidth, upperLegHeight, x - upperLegWidth / 2, y - headSize / 2 - torsoHeight * 3 - upperLegHeight / 2)

self.bones[10] = bodyBox(upperLegWidth, upperLegHeight, x + upperLegWidth / 2, y - headSize / 2 - torsoHeight * 3 - upperLegHeight / 2)

self.bones[11] = bodyBox(lowerLegWidth, lowerLegHeight, x - upperLegWidth / 2, y - headSize / 2 - torsoHeight * 3 - upperLegHeight - lowerLegHeight / 2)

self.bones[12] = bodyBox(lowerLegWidth, lowerLegHeight, x + upperLegWidth / 2, y - headSize / 2 - torsoHeight * 3 - upperLegHeight - lowerLegHeight / 2)

-- Joints

self.joints[1] = bodyJoint(-40, 40, self.bones[2], self.bones[1], x, y - headSize / 2)

-- Upper arms to torso
self.joints[2] = bodyJoint(-85, 130, self.bones[2], self.bones[5], x - torsoWidth / 2, y - headSize / 2)
self.joints[3] = bodyJoint(-130, 85, self.bones[2], self.bones[6], x + torsoWidth / 2, y - headSize / 2)

-- Upper arms to lower arms
self.joints[4] = bodyJoint(-130, 10, self.bones[5], self.bones[7], x - torsoWidth / 2 - upperArmWidth / 2, y - headSize / 2 - upperArmHeight)
self.joints[5] = bodyJoint(-10, 130, self.bones[6], self.bones[8], x + torsoWidth / 2 + upperArmWidth / 2, y - headSize / 2 - upperArmHeight)

-- Torsos (Shoulders -> Stomach -> Hips)
self.joints[6] = bodyJoint(-15, 15, self.bones[2], self.bones[3], x, y - headSize / 2 - torsoHeight)
self.joints[7] = bodyJoint(-15, 15, self.bones[3], self.bones[4], x, y - headSize / 2 - torsoHeight * 2)

-- Hips to upper legs
self.joints[8] = bodyJoint(-25, 45, self.bones[4], self.bones[9], x - upperLegWidth / 2, y - headSize / 2 - torsoHeight * 3)
self.joints[9] = bodyJoint(-45, 25, self.bones[4], self.bones[10], x + upperLegWidth / 2, y - headSize / 2 - torsoHeight * 3)

-- Upper legs to lower legs
self.joints[10] = bodyJoint(-25, 115, self.bones[9], self.bones[11], x - upperLegWidth / 2, y - headSize / 2 - torsoHeight * 3 - upperLegHeight)
self.joints[11] = bodyJoint(-115, 25, self.bones[10], self.bones[12], x + upperLegWidth / 2, y - headSize / 2 - torsoHeight * 3 - upperLegHeight)

for i, bone in ipairs(self.bones) do
end

for i, joint in ipairs(self.joints) do
end
end
--# PhysicsDebugDraw
PhysicsDebugDraw = class()

function PhysicsDebugDraw:init()
self.bodies = {}
self.joints = {}
self.touchMap = {}
self.contacts = {}
end

table.insert(self.bodies,body)
end

table.insert(self.joints,joint)
end

function PhysicsDebugDraw:clear()
-- deactivate all bodies

for i,body in ipairs(self.bodies) do
body:destroy()
end

for i,joint in ipairs(self.joints) do
joint:destroy()
end

self.bodies = {}
self.joints = {}
self.contacts = {}
self.touchMap = {}
end

function PhysicsDebugDraw:draw()

pushStyle()
smooth()
strokeWidth(5)
stroke(128,0,128)

local gain = 2.0
local damp = 0.5
for k,v in pairs(self.touchMap) do
local worldAnchor = v.body:getWorldPoint(v.anchor)
local touchPoint = v.tp
local diff = touchPoint - worldAnchor
local vel = v.body:getLinearVelocityFromWorldPoint(worldAnchor)
v.body:applyForce( (1/1) * diff * gain - vel * damp, worldAnchor)

line(touchPoint.x, touchPoint.y, worldAnchor.x, worldAnchor.y)
end

stroke(0,255,0,255)
strokeWidth(5)
for k,joint in pairs(self.joints) do
local a = joint.anchorA
local b = joint.anchorB
line(a.x,a.y,b.x,b.y)
end

stroke(255,255,255,255)
noFill()

for i,body in ipairs(self.bodies) do
pushMatrix()
translate(body.x, body.y)
rotate(body.angle)

if body.type == STATIC then
stroke(255,255,255,255)
elseif body.type == DYNAMIC then
stroke(150,255,150,255)
elseif body.type == KINEMATIC then
stroke(150,150,255,255)
end

if body.shapeType == POLYGON then
strokeWidth(3.0)
local points = body.points
for j = 1,#points do
a = points[j]
b = points[(j % #points)+1]
line(a.x, a.y, b.x, b.y)
end
elseif body.shapeType == CHAIN or body.shapeType == EDGE then
strokeWidth(3.0)
local points = body.points
for j = 1,#points-1 do
a = points[j]
b = points[j+1]
line(a.x, a.y, b.x, b.y)
end
elseif body.shapeType == CIRCLE then
strokeWidth(3.0)
end

popMatrix()
end

stroke(255, 0, 0, 255)
fill(255, 0, 0, 255)

for k,v in pairs(self.contacts) do
for m,n in ipairs(v.points) do
ellipse(n.x, n.y, 10, 10)
end
end

popStyle()
end

function PhysicsDebugDraw:touched(touch)
local touchPoint = vec2(touch.x, touch.y)
if touch.state == BEGAN then
for i,body in ipairs(self.bodies) do
if body.type == DYNAMIC and body:testPoint(touchPoint) then
self.touchMap[touch.id] = {tp = touchPoint, body = body, anchor = body:getLocalPoint(touchPoint)}
return true
end
end
elseif touch.state == MOVING and self.touchMap[touch.id] then
self.touchMap[touch.id].tp = touchPoint
return true
elseif touch.state == ENDED and self.touchMap[touch.id] then
self.touchMap[touch.id] = nil
return true;
end
return false
end

function PhysicsDebugDraw:collide(contact)
if contact.state == BEGAN then
self.contacts[contact.id] = contact
sound(SOUND_HIT, 2643)
elseif contact.state == MOVING then
self.contacts[contact.id] = contact
elseif contact.state == ENDED then
self.contacts[contact.id] = nil
end
end
``````

The code borrows the PhysicsDebugDraw class from the physics example because I didn’t want to waste time drawing and touching the bodies when I could just use this existing code.

I’ve updated the code to include a bridge, allow for any color on the physics bodies, and include texturing (which is disabled by default because it doesn’t look great with a mashup of Codea textures)

Here is the link to the latest: https://gist.github.com/JakAttak/0ac4bb579c17f1a2f93e

thumbs up?although i have a strange and wrong feeling?the guy might suffer from cruelties

=D> =D>

I love it

This is great @JakAttak, I’d love to try something like this: https://m.youtube.com/watch?v=rbUBAr7S_Wk