# Ragdoll physics without box2d

Here’s what I’ve come up with this morning, just a little test to see what’s possible but I need to improve my physics and I’d also like to see what others come up with for realistic movement and such.

``````function setup()
--Movement point (the head in this case)
mp = vec2(WIDTH/2,HEIGHT/2)
--Movement point delta for additional velocity, not used at the moment
mpdt = vec2()
--Vector table for body points
vtbl = {}
vtbl[2] = vec2(0,-30)--pelvis
vtbl[3] = vec2(5,-55)--right knee
vtbl[4] = vec2(-5,-55)--left knee
vtbl[5] = vec2(5,-75)--right foot
vtbl[6] = vec2(-5,-75)--left foot
vtbl[7] = vec2(0,-5)--shoulder joint
vtbl[8] = vec2(5,-20)--right elbow
vtbl[9] = vec2(-5,-20)--left elbow
vtbl[10] = vec2(5,-35)--right hand
vtbl[11] = vec2(-5,-35)--left hand
--Velocity table (if used)
vel = {}
--Point table for final positions of joints (table of points that gets drawn)
p = {}
--For all the joints create the table of velocity for each joint and the point of each joint
for i = 1,#vtbl do
vel[i] = vec2(0,0)
p[i] = mp+vtbl[i]
end
end

function touched(t)
if t.state == BEGAN or t.state == MOVING then
--Set body position
mp = vec2(t.x,t.y)
--Set velocity of joints
--mpdt = vec2(t.deltaX,t.deltaY)/5
end
for i=1,#vtbl do
if i > 1 then
vel[i] = vel[i] + mpdt
end
end
end

function draw()

background(0)
--Set the gravity variable
local grav = vec2(0,-0.1)
--Localise table vtbl, call it v for the drawing
local v = vtbl
for i=1,#vtbl do
vel[i] = vel[i] + grav*3
vel[i] = vel[i] * 0.9
end

--Create and update all joint positions and create *bones*
vtbl[1] = mp
vtbl[2] = vtbl[1]+(vtbl[2]-vtbl[1]):normalize()*30 + vel[2]
vtbl[3] = vtbl[2]+(vtbl[3]-vtbl[2]):normalize()*25 + vel[3]
vtbl[4] = vtbl[2]+(vtbl[4]-vtbl[2]):normalize()*25 + vel[4]
vtbl[5] = vtbl[3]+(vtbl[5]-vtbl[3]):normalize()*20 + vel[5]
vtbl[6] = vtbl[4]+(vtbl[6]-vtbl[4]):normalize()*20 + vel[6]
vtbl[7] = vtbl[1]+(vtbl[2]-vtbl[1]):normalize()*5
vtbl[8] = vtbl[7]+(vtbl[8]-vtbl[7]):normalize()*20 + vel[8]
vtbl[9] = vtbl[7]+(vtbl[9]-vtbl[7]):normalize()*20 + vel[9]
vtbl[10] = vtbl[8]+(vtbl[10]-vtbl[8]):normalize()*15 + vel[10]
vtbl[11] = vtbl[9]+(vtbl[11]-vtbl[9]):normalize()*15 + vel[11]
--Create spacing between elbows,hands and knees
if vtbl[3]:dist(vtbl[4]) < 10 then
vtbl[3] = vtbl[3]+(vtbl[3]-vtbl[4]):normalize()*0.5
vtbl[4] = vtbl[4]+(vtbl[4]-vtbl[3]):normalize()*0.5
end
if vtbl[9]:dist(vtbl[8]) < 20 then
vtbl[8] = vtbl[8]+(vtbl[8]-vtbl[9]):normalize()*0.5
vtbl[9] = vtbl[9]+(vtbl[9]-vtbl[8]):normalize()*0.5
end
if vtbl[10]:dist(vtbl[11]) < 30 then
vtbl[10] = vtbl[10]+(vtbl[10]-vtbl[11]):normalize()*0.5
vtbl[11] = vtbl[11]+(vtbl[11]-vtbl[10]):normalize()*0.5
end
--Draw the body
strokeWidth(5)
stroke(200,200,200,255)
line(v[1].x,v[1].y,v[2].x,v[2].y)
line(v[2].x,v[2].y,v[3].x,v[3].y)
line(v[2].x,v[2].y,v[4].x,v[4].y)
line(v[3].x,v[3].y,v[5].x,v[5].y)
line(v[4].x,v[4].y,v[6].x,v[6].y)
line(v[7].x,v[7].y,v[8].x,v[8].y)
line(v[7].x,v[7].y,v[9].x,v[9].y)
line(v[8].x,v[8].y,v[10].x,v[10].y)
line(v[9].x,v[9].y,v[11].x,v[11].y)
ellipse(v[1].x,v[1].y,20)

end
``````

If you have any input on this then please share!

Also if I do this:

``````local v = vtbl
for i=1,#vtbl do
local vi = v[i]
--if vi.x < 10 then vtbl[i].x = 10.1 elseif vi.x > WIDTH-10 then vtbl[i].x = WIDTH-10.1 elseif vi.y < 10 then vtbl[i].y = 10.1 elseif vi.y > HEIGHT-10 then vtbl[i].y = HEIGHT-10.1 end
if vi.y < 20 then
vtbl[i].y = 20
else
vel[i] = vel[i] + grav*5
vel[i] = vel[i]*0.8
end
end
``````

This happens - http://youtu.be/coOwMUbcXo4
Anyone got any ideas?

Updated code:

``````function setup()
--Movement point (the head in this case)
mp = vec2(WIDTH/2,HEIGHT/2)
--Movement point delta for additional velocity, not used at the moment
mpdt = vec2()
--Vector table for body points
vtbl = {}
vtbl[2] = vec2(0,-30)--pelvis
vtbl[3] = vec2(5,-55)--right knee
vtbl[4] = vec2(-5,-55)--left knee
vtbl[5] = vec2(5,-75)--right foot
vtbl[6] = vec2(-5,-75)--left foot
vtbl[7] = vec2(0,-5)--shoulder joint
vtbl[8] = vec2(5,-20)--right elbow
vtbl[9] = vec2(-5,-20)--left elbow
vtbl[10] = vec2(5,-35)--right hand
vtbl[11] = vec2(-5,-35)--left hand
--Velocity table (if used)
vel = {}
--Point table for final positions of joints (table of points that gets drawn)
p = {}
--For all the joints create the table of velocity for each joint and the point of each joint
for i = 1,#vtbl do
vel[i] = vec2(0,0)
p[i] = mp+vtbl[i]
end
td = nil
end

function touched(t)
mp = vec2(t.x,t.y)
td = t
if t.state == ENDED then
td = nil
end
end

function draw()

background(0)
--Set the gravity variable
local grav = vec2(0,-0.1)
--Localise table vtbl, call it v for the drawing
local v = vtbl
if td ~= nil then
vel[1] = vel[1] + (mp-v[1]):normalize()*(vec2(td.x,td.y):dist(v[1])/40)
vel[1] = vel[1] * 0.85
else
if v[1].y < 10 then
v[1].y = 10
vel[1] = vec2()
else
vel[1] = vel[1] + grav*5
end
vel[1] = vec2(vel[1].x*0.93,vel[1].y*0.95)
end

--Create spacing between elbows,hands and knees
if vtbl[3]:dist(vtbl[4]) < 15 then
vtbl[3] = vtbl[3]+(vtbl[3]-vtbl[4]):normalize()*1
vtbl[4] = vtbl[4]+(vtbl[3]-vtbl[4]):normalize()*-1
vel[3] = vel[3]+grav
vel[4] = vel[4]+grav
end
if vtbl[9]:dist(vtbl[8]) < 15 then
vtbl[8] = vtbl[8]+(vtbl[8]-vtbl[9]):normalize()*1
vtbl[9] = vtbl[9]+(vtbl[8]-vtbl[9]):normalize()*-1
vel[8] = vel[8]+grav
vel[9] = vel[9]+grav
end
if vtbl[10]:dist(vtbl[11]) < 20 then
vtbl[10] = vtbl[10]+(vtbl[10]-vtbl[11]):normalize()*1
vtbl[11] = vtbl[11]+(vtbl[10]-vtbl[11]):normalize()*-1
vel[10] = vel[10]+grav
vel[11] = vel[11]+grav
end
for i=1,#vtbl do
local vi = v[i]
if i > 1 then
if vi.y < 10 then
vtbl[i].y = 10
vel[i] = vel[i] + vec2(0,0.1)
else
vel[i] = vel[i] + grav*5
vel[i] = vel[i]*0.85
end
end
end

--Create and update all joint positions and create *bones*
vtbl[1] = vtbl[1] + vel[1]
vtbl[2] = vtbl[1]+(vtbl[2]-vtbl[1]):normalize()*30 + vel[2]
vtbl[3] = vtbl[2]+(vtbl[3]-vtbl[2]):normalize()*25 + vel[3]
vtbl[4] = vtbl[2]+(vtbl[4]-vtbl[2]):normalize()*25 + vel[4]
vtbl[5] = vtbl[3]+(vtbl[5]-vtbl[3]):normalize()*20 + vel[5]
vtbl[6] = vtbl[4]+(vtbl[6]-vtbl[4]):normalize()*20 + vel[6]
vtbl[7] = vtbl[1]+(vtbl[2]-vtbl[1]):normalize()*5
vtbl[8] = vtbl[7]+(vtbl[8]-vtbl[7]):normalize()*20 + vel[8]
vtbl[9] = vtbl[7]+(vtbl[9]-vtbl[7]):normalize()*20 + vel[9]
vtbl[10] = vtbl[8]+(vtbl[10]-vtbl[8]):normalize()*15 + vel[10]
vtbl[11] = vtbl[9]+(vtbl[11]-vtbl[9]):normalize()*15 + vel[11]
--Draw the body
strokeWidth(5)
stroke(200,200,200,255)
line(v[1].x,v[1].y,v[2].x,v[2].y)
line(v[2].x,v[2].y,v[3].x,v[3].y)
line(v[2].x,v[2].y,v[4].x,v[4].y)
line(v[3].x,v[3].y,v[5].x,v[5].y)
line(v[4].x,v[4].y,v[6].x,v[6].y)
line(v[7].x,v[7].y,v[8].x,v[8].y)
line(v[7].x,v[7].y,v[9].x,v[9].y)
line(v[8].x,v[8].y,v[10].x,v[10].y)
line(v[9].x,v[9].y,v[11].x,v[11].y)
ellipse(v[1].x,v[1].y,20)

end
``````

Interesting. I’m definitely gonna make some time to play around with this… maybe move it to a class so I can have multiple ragdolls at once…

Yeah I was thinking the same thing once I got better physics

Boy, that little dude in your video is going to have a sore head!

Nice work…

Great job @Luatee. Makes me want to play Ragdoll Masters now.

@Luatee, very cool! For some reason, this made me think of a game where you have to get as close to the ground as possible without touching it… So I took your code and modified it a bit to make that Hope you don’t mind. Here’s the code

``````

--# Main
function setup()
Game:init()
end

function touched(t)
elseif roundend then
Won:touched(touch)
else Game:touched(t)
end
end

function draw()
elseif roundend then
Won:draw()
else Game:draw()
end
end

-- you can accept and set parameters here
self.x = x
end

-- Codea does not automatically call this method
background(0)

text("You failed! Tap to reset.", WIDTH/2, HEIGHT/2)
end

-- Codea does not automatically call this method
Game:init()
end

--# Game
Game = class()

function Game:init()
--Movement point (the head in this case)
mp = vec2(WIDTH/2,HEIGHT/2)
lowest = WIDTH/2

--Movement point delta for additional velocity, not used at the moment
mpdt = vec2()
--Vector table for body points
vtbl = {}
vtbl[2] = vec2(0,-30)--pelvis
vtbl[3] = vec2(5,-55)--right knee
vtbl[4] = vec2(-5,-55)--left knee
vtbl[5] = vec2(5,-75)--right foot
vtbl[6] = vec2(-5,-75)--left foot
vtbl[7] = vec2(0,-5)--shoulder joint
vtbl[8] = vec2(5,-20)--right elbow
vtbl[9] = vec2(-5,-20)--left elbow
vtbl[10] = vec2(5,-35)--right hand
vtbl[11] = vec2(-5,-35)--left hand
--Velocity table (if used)
vel = {}
--Point table for final positions of joints (table of points that gets drawn)
p = {}
--For all the joints create the table of velocity for each joint and the point of each joint
for i = 1,#vtbl do
vel[i] = vec2(0,0)
p[i] = mp+vtbl[i]
end
td = nil
end

function Game:touched(t)
mp = vec2(WIDTH/2 + (math.random (-1, 1)),t.y)
td = t
if t.state == ENDED then
td = nil
end
end

function Game:draw()
fill(193, 193, 193, 255)

background(0)

--Set the gravity variable
if roundend then
grav = vec2(0,0)
else grav = vec2(0,-0.1)
end

--Localise table vtbl, call it v for the drawing
local v = vtbl
if td ~= nil then
vel[1] = vel[1] + (mp-v[1]):normalize()*(vec2(td.x,td.y):dist(v[1])/40)
vel[1] = vel[1] * 0.85
else
if v[1].y < 10 then
v[1].y = 10
vel[1] = vec2()
else
vel[1] = vel[1] + grav*5
end
vel[1] = vec2(vel[1].x*0.93,vel[1].y*0.95)
end

--Create spacing between elbows,hands and knees
if vtbl[3]:dist(vtbl[4]) < 15 then
vtbl[3] = vtbl[3]+(vtbl[3]-vtbl[4]):normalize()*1
vtbl[4] = vtbl[4]+(vtbl[3]-vtbl[4]):normalize()*-1
vel[3] = vel[3]+grav
vel[4] = vel[4]+grav
end
if vtbl[9]:dist(vtbl[8]) < 15 then
vtbl[8] = vtbl[8]+(vtbl[8]-vtbl[9]):normalize()*1
vtbl[9] = vtbl[9]+(vtbl[8]-vtbl[9]):normalize()*-1
vel[8] = vel[8]+grav
vel[9] = vel[9]+grav
end
if vtbl[10]:dist(vtbl[11]) < 20 then
vtbl[10] = vtbl[10]+(vtbl[10]-vtbl[11]):normalize()*1
vtbl[11] = vtbl[11]+(vtbl[10]-vtbl[11]):normalize()*-1
vel[10] = vel[10]+grav
vel[11] = vel[11]+grav
end
for i=1,#vtbl do
local vi = v[i]
if i > 1 then
if vi.y < 10 then
vtbl[i].y = 10
vel[i] = vel[i] + vec2(0,0.1)
else
vel[i] = vel[i] + grav*5
vel[i] = vel[i]*0.85
end
end
end

--Create and update all joint positions and create *bones*
vtbl[1] = vtbl[1] + vel[1]
vtbl[2] = vtbl[1]+(vtbl[2]-vtbl[1]):normalize()*30 + vel[2]
vtbl[3] = vtbl[2]+(vtbl[3]-vtbl[2]):normalize()*25 + vel[3]
vtbl[4] = vtbl[2]+(vtbl[4]-vtbl[2]):normalize()*25 + vel[4]
vtbl[5] = vtbl[3]+(vtbl[5]-vtbl[3]):normalize()*20 + vel[5]
vtbl[6] = vtbl[4]+(vtbl[6]-vtbl[4]):normalize()*20 + vel[6]
vtbl[7] = vtbl[1]+(vtbl[2]-vtbl[1]):normalize()*5
vtbl[8] = vtbl[7]+(vtbl[8]-vtbl[7]):normalize()*20 + vel[8]
vtbl[9] = vtbl[7]+(vtbl[9]-vtbl[7]):normalize()*20 + vel[9]
vtbl[10] = vtbl[8]+(vtbl[10]-vtbl[8]):normalize()*15 + vel[10]
vtbl[11] = vtbl[9]+(vtbl[11]-vtbl[9]):normalize()*15 + vel[11]
--Draw the body
strokeWidth(5)
stroke(200,200,200,255)
line(v[1].x,v[1].y,v[2].x,v[2].y)
line(v[2].x,v[2].y,v[3].x,v[3].y)
line(v[2].x,v[2].y,v[4].x,v[4].y)
line(v[3].x,v[3].y,v[5].x,v[5].y)
line(v[4].x,v[4].y,v[6].x,v[6].y)
line(v[7].x,v[7].y,v[8].x,v[8].y)
line(v[7].x,v[7].y,v[9].x,v[9].y)
line(v[8].x,v[8].y,v[10].x,v[10].y)
line(v[9].x,v[9].y,v[11].x,v[11].y)
ellipse(v[1].x,v[1].y,20)

--[[
for that = 1, 11 do
print(that.. ":  ".. v[that].y)
end
--]]

if v[1].y <= 11 then
end

print(v[1].y)

if v[1].y < lowest then
lowest = v[1].y
end

plowest = math.floor(lowest/10)

if v[1].y > 500 then
roundend = true
end

if roundend then
v[1].y = 500
print(plowest)
end

line(0, 500, WIDTH, 500)
fill(255, 255, 255, 255)
fontSize(20)
text("Reach the line to submit your fall", WIDTH/2, 700)
end

--# Won
Won = class()

function Won:init(x)
-- you can accept and set parameters here
self.x = x
end

function Won:draw()
-- Codea does not automatically call this method
background(0)

text("Congratulations! Your score was: ".. plowest, WIDTH/2, HEIGHT/1.8)
text("(Lower score is better)", WIDTH/2, HEIGHT/2)
text("Tap to reset", WIDTH/2, HEIGHT/2.2)
end

function Won:touched(touch)
-- Codea does not automatically call this method
Game:init()
roundend = false
end
``````

Definitely not the best way to do it, but it works and its fun.

Gameplay is simple: You are falling and you tap higher up to go up. You want to come as close as the ground as possible without hitting it.

Here’s it with the ragdoll quickly converted to a class, now it has 20 ragdolls randomly placed at the start. Looks nice, but at 20 it slows down quite a bit…

Interestingly, the slow down is drawing, not the maths… if you comment out the draw, but calculate the movement it’s still 60 fps… so it’s the slow line performance in Codea… added noSmooth() to speed it up which takes it up to 45fps or so.

``````

--# Main
function setup()
--Set the gravity variable
grav = vec2(0,-0.1)

dolls = {}
for i=1,20 do
table.insert(dolls, RagDoll(math.random(WIDTH), math.random(HEIGHT)))
end
end

function touched(t)
for k,v in ipairs(dolls) do
v.mp = vec2(t.x,t.y)
v.td = t
if t.state == ENDED then
v.td = nil
end
end
end

function draw()
noSmooth()
background(0)
--Draw the body
for k,v in ipairs(dolls) do
v:move()
v:draw()
end
end
--# RagDoll
RagDoll = class()

function RagDoll:init(x,y)
--Movement point (the head in this case)
self.mp = vec2(x,y)
--Movement point delta for additional velocity, not used at the moment
self.mpdt = vec2()
--Vector table for body points
self.vtbl = {}
self.vtbl[2] = vec2(0,-30)--pelvis
self.vtbl[3] = vec2(5,-55)--right knee
self.vtbl[4] = vec2(-5,-55)--left knee
self.vtbl[5] = vec2(5,-75)--right foot
self.vtbl[6] = vec2(-5,-75)--left foot
self.vtbl[7] = vec2(0,-5)--shoulder joint
self.vtbl[8] = vec2(5,-20)--right elbow
self.vtbl[9] = vec2(-5,-20)--left elbow
self.vtbl[10] = vec2(5,-35)--right hand
self.vtbl[11] = vec2(-5,-35)--left hand
--Velocity table (if used)
self.vel = {}
--Point table for final positions of joints (table of points that gets drawn)
self.p = {}
--For all the joints create the table of velocity for each joint and the point of each joint
for i = 1,#self.vtbl do
self.vel[i] = vec2(0,0)
self.p[i] = self.mp+self.vtbl[i]
end
self.td = nil
end

function RagDoll:draw()
--Draw the body
local v = self.vtbl
strokeWidth(5)
stroke(200,200,200,255)
line(v[1].x,v[1].y,v[2].x,v[2].y)
line(v[2].x,v[2].y,v[3].x,v[3].y)
line(v[2].x,v[2].y,v[4].x,v[4].y)
line(v[3].x,v[3].y,v[5].x,v[5].y)
line(v[4].x,v[4].y,v[6].x,v[6].y)
line(v[7].x,v[7].y,v[8].x,v[8].y)
line(v[7].x,v[7].y,v[9].x,v[9].y)
line(v[8].x,v[8].y,v[10].x,v[10].y)
line(v[9].x,v[9].y,v[11].x,v[11].y)
ellipse(v[1].x,v[1].y,20)

end

function RagDoll:move()
local vel = self.vel
local mp = self.mp
local v = self.vtbl
local td = self.td
local vtbl = self.vtbl

if td ~= nil then
vel[1] = vel[1] + (mp-v[1]):normalize()*(vec2(td.x,td.y):dist(v[1])/40)
vel[1] = vel[1] * 0.85
else
if v[1].y < 10 then
v[1].y = 10
vel[1] = vec2()
else
vel[1] = vel[1] + grav*5
end
vel[1] = vec2(vel[1].x*0.93,vel[1].y*0.95)
end

--Create spacing between elbows,hands and knees
if vtbl[3]:dist(vtbl[4]) < 15 then
vtbl[3] = vtbl[3]+(vtbl[3]-vtbl[4]):normalize()*1
vtbl[4] = vtbl[4]+(vtbl[3]-vtbl[4]):normalize()*-1
vel[3] = vel[3]+grav
vel[4] = vel[4]+grav
end
if vtbl[9]:dist(vtbl[8]) < 15 then
vtbl[8] = vtbl[8]+(vtbl[8]-vtbl[9]):normalize()*1
vtbl[9] = vtbl[9]+(vtbl[8]-vtbl[9]):normalize()*-1
vel[8] = vel[8]+grav
vel[9] = vel[9]+grav
end
if vtbl[10]:dist(vtbl[11]) < 20 then
vtbl[10] = vtbl[10]+(vtbl[10]-vtbl[11]):normalize()*1
vtbl[11] = vtbl[11]+(vtbl[10]-vtbl[11]):normalize()*-1
vel[10] = vel[10]+grav
vel[11] = vel[11]+grav
end
for i=1,#vtbl do
local vi = v[i]
if i > 1 then
if vi.y < 10 then
vtbl[i].y = 10
vel[i] = vel[i] + vec2(0,0.1)
else
vel[i] = vel[i] + grav*5
vel[i] = vel[i]*0.85
end
end
end

--Create and update all joint positions and create *bones*
vtbl[1] = vtbl[1] + vel[1]
vtbl[2] = vtbl[1]+(vtbl[2]-vtbl[1]):normalize()*30 + vel[2]
vtbl[3] = vtbl[2]+(vtbl[3]-vtbl[2]):normalize()*25 + vel[3]
vtbl[4] = vtbl[2]+(vtbl[4]-vtbl[2]):normalize()*25 + vel[4]
vtbl[5] = vtbl[3]+(vtbl[5]-vtbl[3]):normalize()*20 + vel[5]
vtbl[6] = vtbl[4]+(vtbl[6]-vtbl[4]):normalize()*20 + vel[6]
vtbl[7] = vtbl[1]+(vtbl[2]-vtbl[1]):normalize()*5
vtbl[8] = vtbl[7]+(vtbl[8]-vtbl[7]):normalize()*20 + vel[8]
vtbl[9] = vtbl[7]+(vtbl[9]-vtbl[7]):normalize()*20 + vel[9]
vtbl[10] = vtbl[8]+(vtbl[10]-vtbl[8]):normalize()*15 + vel[10]
vtbl[11] = vtbl[9]+(vtbl[11]-vtbl[9]):normalize()*15 + vel[11]
end

function RagDoll:touched(touch)
-- Codea does not automatically call this method
end
``````

Thanks guys, I thought I might aswell no ones tried it yet, the maths for the ragdoll at the moment is pretty simple, the only thing that I think would slow it down would be the indexing of tables all the time and the drawing of lines, I might recreate it when im home as that was a pretty quick mock up of what I wanted to achieve, but it works none the less, thanks for the comments and feel free to use it however you want. Im thinking of adding dismemberment once rewritten