16
Lua / Chipmunk Physics
« on: July 26, 2012, 03:59:10 pm »
Hi all.
3.2 integrates the Chipmunk physics engine with Lua bindings. This allows us create fun games with the physics.
However there is just one problem. It's pretty complex.
So here is some useful info if you want to start programming.
1. The API documentation for 3.2
Download it at http://education.ti.com/nspire/scripting-api .
It's a must have in order to find all the API function names (duh :P )
2. The online chipmunk docs
http://chipmunk-physics.net/release/Chipmunk-5.x/Chipmunk-5.3.4-Docs/
Even though it describes the C API, it shows how the concepts of the engine and how it's structured.
And most Lua functions have similar names to the C ones.
You will find yourself checking this resource often when you start programming with the engine ;)
3. A tutorial (although for the C API)
http://www.alexandre-gomes.com/articles/chipmunk/
This tutorial helps you understand the concepts and inner workings of the physics engine.
4. Example code (by me)
platform.apilevel = '2.0'
require 'physics'
LARGE=physics.misc.INFINITY()
ZERO =physics.Vect(0,0)
------------------
-- Space Object --
------------------
pSpace = class()
function pSpace:init(gravity)
self.space = physics.Space()
:setGravity(physics.Vect(0, gravity))
:setSleepTimeThreshold(.5)
self.objects = {}
end
function pSpace:step(n)
self.space:step(n)
end
function pSpace:addObj(obj, x, y)
obj.body:setPos(physics.Vect(x, y))
self.space:addBody(obj.body)
self.space:addShape(obj.shape)
if obj.added then obj:added(self) end
table.insert(self.objects, obj)
return self
end
function pSpace:addTerrainObj(obj, x, y)
obj.body:setPos(physics.Vect(x, y))
--self.space:addBody(obj.body)
for _, ss in ipairs(obj.segments) do
self.space:addStaticShape(ss)
end
table.insert(self.objects, obj)
return self
end
function pSpace:addStaticObj(obj, x, y)
obj.body:setPos(physics.Vect(x, y))
--self.space:addBody(obj.body)
self.space:addStaticShape(obj.shape)
table.insert(self.objects, obj)
return self
end
function pSpace:paint(gc)
for _, obj in ipairs(self.objects) do
obj:paint(gc)
end
end
----------------
-- Box Object --
----------------
pBox = class()
function pBox:init(w,h,mass,color)
self.h=h
self.w=w
self.mass=mass
self.color=color
self.verts = {
physics.Vect(-w/2, -h/2),
physics.Vect(-w/2, h/2),
physics.Vect(w/2 , h/2),
physics.Vect(w/2 , -h/2)
}
self.body = physics.Body(1, 1)
self.body:setMass(mass)
self.body:setMoment(physics.misc.momentForPoly(mass, self.verts, ZERO))
self.body:setVel(ZERO)
self.shape = physics.PolyShape(self.body, self.verts, ZERO)
self.shape:setRestitution(0.6)
self.shape:setFriction(0.6)
end
function pBox:paint(gc)
local coords = self.shape:points()
local polycoords = {}
for k, vert in ipairs(coords) do
table.insert(polycoords, vert:x())
table.insert(polycoords, vert:y())
end
local x,y=coords[1]:x(),coords[1]:y()
table.insert(polycoords, x)
table.insert(polycoords, y)
gc:setColorRGB(self.color)
gc:fillPolygon(polycoords)
gc:setColorRGB(0,0,0)
gc:drawPolyLine(polycoords)
end
-----------------
-- Ball Object --
-----------------
pBall = class()
function pBall:init(r,mass,color)
self.r=r
self.mass=mass
self.color=color
self.body = physics.Body(1, 1)
self.body:setMass(mass)
self.body:setMoment(physics.misc.momentForCircle(mass, 0, r, ZERO))
self.body:setVel(ZERO)
self.shape = physics.CircleShape(self.body, r, ZERO)
self.shape:setRestitution(0.6)
self.shape:setFriction(0.6)
--[[
self.segment = physics.SegmentShape(self.body, physics.Vect(0,-r), physics.Vect(0,0), 1)
self.segment:setRestitution(0)
self.segment:setFriction(0)
--]]
end
function pBall:added(spaceObj)
--spaceObj.space:addShape(self.segment)
end
function pBall:paint(gc)
local pos=self.body:pos()
local x, y = pos:x(), pos:y()
local r = self.r
gc:setColorRGB(self.color)
gc:fillArc(x-r, y-r, 2*r+1, 2*r+1, 0, 360)
gc:setColorRGB(0)
gc:drawArc(x-r, y-r, 2*r, 2*r, 0, 360)
local x2,y2
local angle=self.body:angle()
x2 = math.cos(angle)*self.r+x
y2 = math.sin(angle)*self.r+y
gc:drawLine(x,y,x2,y2)
end
------------------
-- Terrain code --
------------------
function getMidPoint(line)
local xdif = math.abs(line[3]-line[1])
local ydif = math.abs(line[4]-line[2])
local x = math.min(line[3], line[1]) + xdif/2
local y = math.min(line[4], line[2]) + ydif/2
return x, y
end
function terrain(bline, range, roughness, tms)
local lines = {bline}
local lines2 = {}
local midX, midY
for times=1, tms do
for index, line in pairs(lines) do
midX, midY = getMidPoint(line)
midY = midY + math.random(-range, range)
table.insert(lines2, {line[1], line[2], midX, midY})
table.insert(lines2, {midX, midY, line[3], line[4]})
end
lines = lines2
lines2 = {}
range = range * (roughness*2^-roughness)
end
return lines
end
------------------------
-- Terrain to physics --
------------------------
pTerrain = class()
function pTerrain:init(bline, range, roughness, times)
self.lines = terrain(bline, range, roughness, times)
self.segments = {}
self.mass = LARGE
self.body = physics.Body(1, 1)
self.body:setMass(self.mass)
self.inertia = 0
local a, b, ss
for index, line in ipairs(self.lines) do
a = physics.Vect(line[1], line[2])
b = physics.Vect(line[3], line[4])
self.inertia = self.inertia + physics.misc.momentForSegment(self.mass/#self.lines, a, b)
ss = physics.SegmentShape(self.body, a, b, 1)
ss:setRestitution(0)
ss:setFriction(1)
table.insert(self.segments, ss)
end
self.body:setMoment(self.inertia)
self.body:setVel(ZERO)
end
function pTerrain:paint(gc)
local a,b
for index, ss in ipairs(self.segments) do
a = ss:a()
b = ss:b()
gc:drawLine(a:x(), a:y(), b:x(), b:y())
end
end
function on.construction()
space = pSpace(9. 8)
timer.start(0.01)
count=0
ter = pTerrain({0,120,318,120}, 60, 0.6, 5)
space:addTerrainObj(ter, 0,0)
ball = pBall(10, 5, 0xaaffaa)
space:addObj(ball, 160,10)
end
function physicsUpdate()
space:step(0.1)
count = count + 1
if count == 2 then
platform.window:invalidate()
count = 1
end
end
py,px=100,160
function on.paint(gc)
space:paint(gc)
gc:fillRect(px, py, 4, 4)
end
kaction = {up={0,-5}, down={0,5}, left={-5,0}, right={5,0}}
function on.arrowKey(key)
px=px+kaction[1]
py=py+kaction[2]
end
function on.enterKey()
local box = pBox(20, 20, 5, math.random(2^16))
space:addObj(box, px, py)
end
function on.timer()
physicsUpdate()
end
platform.apilevel = '2.0'
require 'physics'
LARGE=physics.misc.INFINITY()
ZERO =physics.Vect(0,0)
------------------
-- Space Object --
------------------
pSpace = class()
function pSpace:init(gravity)
self.space = physics.Space()
:setGravity(physics.Vect(0, gravity))
:setSleepTimeThreshold(.5)
--:resizeActiveHash(30, 500)
self.objects = {}
end
function pSpace:step(n)
self.space:step(n)
end
function pSpace:addObj(obj, x, y)
obj.body:setPos(physics.Vect(x, y))
self.space:addBody(obj.body)
self.space:addShape(obj.shape)
if obj.added then obj:added(self) end
table.insert(self.objects, obj)
return self
end
function pSpace:addTerrainObj(obj, x, y)
obj.body:setPos(physics.Vect(x, y))
--self.space:addBody(obj.body)
for _, ss in ipairs(obj.segments) do
self.space:addStaticShape(ss)
end
table.insert(self.objects, obj)
return self
end
function pSpace:addStaticObj(obj, x, y)
obj.body:setPos(physics.Vect(x, y))
--self.space:addBody(obj.body)
self.space:addStaticShape(obj.shape)
table.insert(self.objects, obj)
return self
end
function pSpace:addComplexObj(obj, x, y)
for _, childObj in ipairs(obj.objects) do
self:addObj(childObj.obj, x+childObj.x, y+childObj.y)
end
for _, constraint in ipairs(obj.constraints) do
self.space:addConstraint(constraint)
end
table.insert(self.objects, obj)
return self
end
function pSpace:paint(gc)
for _, obj in ipairs(self.objects) do
obj:paint(gc)
end
end
----------------
-- Box Object --
----------------
pBox = class()
function pBox:init(w, h, mass, color, f, e, group)
self.h=h
self.w=w
self.mass=mass
self.color=color
self.verts = {
physics.Vect(-w/2, -h/2),
physics.Vect(-w/2, h/2),
physics.Vect(w/2 , h/2),
physics.Vect(w/2 , -h/2)
}
self.body = physics.Body(1, 1)
self.body:setMass(mass)
self.body:setMoment(physics.misc.momentForPoly(mass, self.verts, ZERO))
self.body:setVel(ZERO)
self.shape = physics.PolyShape(self.body, self.verts, ZERO)
self.shape:setRestitution(e or 0.6)
self.shape:setFriction(f or 0.6)
if group then self.shape:setGroup(group) end
end
function pBox:paint(gc)
local coords = self.shape:points()
local polycoords = {}
for k, vert in ipairs(coords) do
table.insert(polycoords, vert:x())
table.insert(polycoords, vert:y())
end
local x,y=coords[1]:x(),coords[1]:y()
table.insert(polycoords, x)
table.insert(polycoords, y)
gc:setColorRGB(self.color)
gc:fillPolygon(polycoords)
gc:setColorRGB(0,0,0)
gc:drawPolyLine(polycoords)
end
-----------------
-- Ball Object --
-----------------
pBall = class()
function pBall:init(r,mass,color, f, e, group)
self.r=r
self.mass=mass
self.color=color
self.body = physics.Body(1, 1)
self.body:setMass(mass)
self.body:setMoment(physics.misc.momentForCircle(mass, 0, r, ZERO))
self.body:setVel(ZERO)
self.shape = physics.CircleShape(self.body, r, ZERO)
self.shape:setRestitution(e or 0.6)
self.shape:setFriction(f or 0.6)
if group then self.shape:setGroup(group) end
end
function pBall:paint(gc)
local pos=self.body:pos()
local x, y = pos:x(), pos:y()
local r = self.r
gc:setColorRGB(self.color)
gc:fillArc(x-r, y-r, 2*r+1, 2*r+1, 0, 360)
gc:setColorRGB(0)
gc:drawArc(x-r, y-r, 2*r, 2*r, 0, 360)
local x2,y2
local angle=self.body:angle()
x2 = math.cos(angle)*self.r+x
y2 = math.sin(angle)*self.r+y
gc:drawLine(x,y,x2,y2)
end
pCar = class()
function pCar:init()
local group = 5
local wheelR = 5 -- wheel Radius
local wheelM = 5 -- wheel Mass
local wheelF = 0.9 -- wheel Friction
local wheelE = 0.0 -- wheel Elasticity
local chassisM = 10 -- Chassis Mass
local chassisW = 24 -- Chassis Width
local chassisH = 10 -- Chassis Height
local chassisF = 0.7 -- Chassis Friction
local chassisE = 0.0 -- Chassis Elasticity
self.wheel1 = pBall(wheelR, wheelM, 0x55CC55, wheelF, wheelE, group)
self.wheel2 = pBall(wheelR, wheelM+8, 0x55CC55, wheelF, wheelE, group)
self.chassis = pBox(chassisW, chassisH, chassisM, 0x5555CC, chassisF, chassisE, group)
self.joint1 = physics.GrooveJoint(self.chassis.body, self.wheel1.body, physics.Vect(-10, 5), physics.Vect(-10, 25), ZERO)
self.joint2 = physics.GrooveJoint(self.chassis.body, self.wheel2.body, physics.Vect( 10, 5), physics.Vect( 10, 25), ZERO)
self.spring1 = physics.DampedSpring(self.chassis.body, self.wheel1.body, physics.Vect(-10, 0), ZERO, 17, 10, 10)
self.spring2 = physics.DampedSpring(self.chassis.body, self.wheel2.body, physics.Vect( 10, 0), ZERO, 17, 10, 10)
self.Fmotor = physics.SimpleMotor(self.chassis.body, self.wheel2.body, 0):setMaxForce(1000)
self.Pmotor = physics.SimpleMotor(self.chassis.body, self.wheel2.body, 0):setMaxForce(1000)
self.gear = physics.GearJoint(self.wheel1.body, self.wheel2.body, 0, 1)
self.objects = {
{obj = self.wheel1 , x = -10 ,y = 7},
{obj = self.wheel2 , x = 10 , y = 7},
{obj = self.chassis, x = 0 ,y = -7},
}
self.constraints = {self.joint1, self.joint2, self.spring1, self.spring2, self.Fmotor, self.Pmotor, self.gear}
end
function pCar:paint(gc)
self.wheel1:paint(gc)
self.wheel2:paint(gc)
self.chassis:paint(gc)
end
------------------
-- Terrain code --
------------------
function getMidPoint(line)
local xdif = math.abs(line[3]-line[1])
local ydif = math.abs(line[4]-line[2])
local x = math.min(line[3], line[1]) + xdif/2
local y = math.min(line[4], line[2]) + ydif/2
return x, y
end
function terrain(bline, range, roughness, tms)
local lines = {bline}
local lines2 = {}
local midX, midY
for times=1, tms do
for index, line in pairs(lines) do
midX, midY = getMidPoint(line)
midY = midY + math.random(-range, range)
table.insert(lines2, {line[1], line[2], midX, midY})
table.insert(lines2, {midX, midY, line[3], line[4]})
end
lines = lines2
lines2 = {}
range = range * (roughness*2^-roughness)
end
return lines
end
------------------------
-- Terrain to physics --
------------------------
pTerrain = class()
function pTerrain:init(bline, range, roughness, times)
self.lines = terrain(bline, range, roughness, times)
self.segments = {}
self.mass = LARGE
self.body = physics.Body(1, 1)
self.body:setMass(self.mass)
self.inertia = 0
local a, b, ss
for index, line in ipairs(self.lines) do
a = physics.Vect(line[1], line[2])
b = physics.Vect(line[3], line[4])
self.inertia = self.inertia + physics.misc.momentForSegment(self.mass/#self.lines, a, b)
ss = physics.SegmentShape(self.body, a, b, 1)
ss:setRestitution(0)
ss:setFriction(1)
table.insert(self.segments, ss)
end
self.body:setMoment(self.inertia)
self.body:setVel(ZERO)
end
function pTerrain:paint(gc)
local a,b
for index, ss in ipairs(self.segments) do
a = ss:a()
b = ss:b()
gc:drawLine(a:x(), a:y(), b:x(), b:y())
end
end
function on.construction()
space = pSpace(9. 8)
timer.start(0.02)
count=0
--[[
floor = pBox(300, 20, LARGE, 0xCC5555, 0.9)
space:addStaticObj(floor, 150, 200)
--]]
ter = pTerrain({0,120,318,120}, 60, 0.6, 5)
space:addTerrainObj(ter, 0,0)
car = pCar()
space:addComplexObj(car, 50, 50)
end
function on.tabKey()
car.Pmotor:setMaxForce(10000)
car.Pmotor:setRate(car.Pmotor:rate()-0.3)
end
function on.escapeKey()
car.Pmotor:setMaxForce(0)
car.Pmotor:setRate(0)
end
function physicsUpdate()
space:step(0.1)
count = count + 1
if count == 2 then
platform.window:invalidate()
count = 1
end
end
py,px=100,160
function on.paint(gc)
local f = math.abs(car.Pmotor:rate())
gc:drawString("Force: " .. f, 2, 2, "top")
space:paint(gc)
gc:fillRect(px, py, 4, 4)
end
kaction = {up={0,-5}, down={0,5}, left={-5,0}, right={5,0}}
function on.arrowKey(key)
px=px+kaction[1]
py=py+kaction[2]
end
function on.enterKey()
local box = pBox(20, 20, 5, math.random(2^16))
space:addObj(box, px, py)
end
function on.timer()
physicsUpdate()
end
I spend some time making this code, so please take care of it ;)
5. Take a look at other exisiting activities for the TI-Nspire that use the physics engine
http://education.ti.com/calculators/tisciencenspired/US/Activities/Detail?sa=5029&id=17888
And last but not least ...
Have fun !
3.2 integrates the Chipmunk physics engine with Lua bindings. This allows us create fun games with the physics.
However there is just one problem. It's pretty complex.
So here is some useful info if you want to start programming.
1. The API documentation for 3.2
Download it at http://education.ti.com/nspire/scripting-api .
It's a must have in order to find all the API function names (duh :P )
2. The online chipmunk docs
http://chipmunk-physics.net/release/Chipmunk-5.x/Chipmunk-5.3.4-Docs/
Even though it describes the C API, it shows how the concepts of the engine and how it's structured.
And most Lua functions have similar names to the C ones.
You will find yourself checking this resource often when you start programming with the engine ;)
3. A tutorial (although for the C API)
http://www.alexandre-gomes.com/articles/chipmunk/
This tutorial helps you understand the concepts and inner workings of the physics engine.
4. Example code (by me)
Spoiler For Basic start:
platform.apilevel = '2.0'
require 'physics'
LARGE=physics.misc.INFINITY()
ZERO =physics.Vect(0,0)
------------------
-- Space Object --
------------------
pSpace = class()
function pSpace:init(gravity)
self.space = physics.Space()
:setGravity(physics.Vect(0, gravity))
:setSleepTimeThreshold(.5)
self.objects = {}
end
function pSpace:step(n)
self.space:step(n)
end
function pSpace:addObj(obj, x, y)
obj.body:setPos(physics.Vect(x, y))
self.space:addBody(obj.body)
self.space:addShape(obj.shape)
if obj.added then obj:added(self) end
table.insert(self.objects, obj)
return self
end
function pSpace:addTerrainObj(obj, x, y)
obj.body:setPos(physics.Vect(x, y))
--self.space:addBody(obj.body)
for _, ss in ipairs(obj.segments) do
self.space:addStaticShape(ss)
end
table.insert(self.objects, obj)
return self
end
function pSpace:addStaticObj(obj, x, y)
obj.body:setPos(physics.Vect(x, y))
--self.space:addBody(obj.body)
self.space:addStaticShape(obj.shape)
table.insert(self.objects, obj)
return self
end
function pSpace:paint(gc)
for _, obj in ipairs(self.objects) do
obj:paint(gc)
end
end
----------------
-- Box Object --
----------------
pBox = class()
function pBox:init(w,h,mass,color)
self.h=h
self.w=w
self.mass=mass
self.color=color
self.verts = {
physics.Vect(-w/2, -h/2),
physics.Vect(-w/2, h/2),
physics.Vect(w/2 , h/2),
physics.Vect(w/2 , -h/2)
}
self.body = physics.Body(1, 1)
self.body:setMass(mass)
self.body:setMoment(physics.misc.momentForPoly(mass, self.verts, ZERO))
self.body:setVel(ZERO)
self.shape = physics.PolyShape(self.body, self.verts, ZERO)
self.shape:setRestitution(0.6)
self.shape:setFriction(0.6)
end
function pBox:paint(gc)
local coords = self.shape:points()
local polycoords = {}
for k, vert in ipairs(coords) do
table.insert(polycoords, vert:x())
table.insert(polycoords, vert:y())
end
local x,y=coords[1]:x(),coords[1]:y()
table.insert(polycoords, x)
table.insert(polycoords, y)
gc:setColorRGB(self.color)
gc:fillPolygon(polycoords)
gc:setColorRGB(0,0,0)
gc:drawPolyLine(polycoords)
end
-----------------
-- Ball Object --
-----------------
pBall = class()
function pBall:init(r,mass,color)
self.r=r
self.mass=mass
self.color=color
self.body = physics.Body(1, 1)
self.body:setMass(mass)
self.body:setMoment(physics.misc.momentForCircle(mass, 0, r, ZERO))
self.body:setVel(ZERO)
self.shape = physics.CircleShape(self.body, r, ZERO)
self.shape:setRestitution(0.6)
self.shape:setFriction(0.6)
--[[
self.segment = physics.SegmentShape(self.body, physics.Vect(0,-r), physics.Vect(0,0), 1)
self.segment:setRestitution(0)
self.segment:setFriction(0)
--]]
end
function pBall:added(spaceObj)
--spaceObj.space:addShape(self.segment)
end
function pBall:paint(gc)
local pos=self.body:pos()
local x, y = pos:x(), pos:y()
local r = self.r
gc:setColorRGB(self.color)
gc:fillArc(x-r, y-r, 2*r+1, 2*r+1, 0, 360)
gc:setColorRGB(0)
gc:drawArc(x-r, y-r, 2*r, 2*r, 0, 360)
local x2,y2
local angle=self.body:angle()
x2 = math.cos(angle)*self.r+x
y2 = math.sin(angle)*self.r+y
gc:drawLine(x,y,x2,y2)
end
------------------
-- Terrain code --
------------------
function getMidPoint(line)
local xdif = math.abs(line[3]-line[1])
local ydif = math.abs(line[4]-line[2])
local x = math.min(line[3], line[1]) + xdif/2
local y = math.min(line[4], line[2]) + ydif/2
return x, y
end
function terrain(bline, range, roughness, tms)
local lines = {bline}
local lines2 = {}
local midX, midY
for times=1, tms do
for index, line in pairs(lines) do
midX, midY = getMidPoint(line)
midY = midY + math.random(-range, range)
table.insert(lines2, {line[1], line[2], midX, midY})
table.insert(lines2, {midX, midY, line[3], line[4]})
end
lines = lines2
lines2 = {}
range = range * (roughness*2^-roughness)
end
return lines
end
------------------------
-- Terrain to physics --
------------------------
pTerrain = class()
function pTerrain:init(bline, range, roughness, times)
self.lines = terrain(bline, range, roughness, times)
self.segments = {}
self.mass = LARGE
self.body = physics.Body(1, 1)
self.body:setMass(self.mass)
self.inertia = 0
local a, b, ss
for index, line in ipairs(self.lines) do
a = physics.Vect(line[1], line[2])
b = physics.Vect(line[3], line[4])
self.inertia = self.inertia + physics.misc.momentForSegment(self.mass/#self.lines, a, b)
ss = physics.SegmentShape(self.body, a, b, 1)
ss:setRestitution(0)
ss:setFriction(1)
table.insert(self.segments, ss)
end
self.body:setMoment(self.inertia)
self.body:setVel(ZERO)
end
function pTerrain:paint(gc)
local a,b
for index, ss in ipairs(self.segments) do
a = ss:a()
b = ss:b()
gc:drawLine(a:x(), a:y(), b:x(), b:y())
end
end
function on.construction()
space = pSpace(9. 8)
timer.start(0.01)
count=0
ter = pTerrain({0,120,318,120}, 60, 0.6, 5)
space:addTerrainObj(ter, 0,0)
ball = pBall(10, 5, 0xaaffaa)
space:addObj(ball, 160,10)
end
function physicsUpdate()
space:step(0.1)
count = count + 1
if count == 2 then
platform.window:invalidate()
count = 1
end
end
py,px=100,160
function on.paint(gc)
space:paint(gc)
gc:fillRect(px, py, 4, 4)
end
kaction = {up={0,-5}, down={0,5}, left={-5,0}, right={5,0}}
function on.arrowKey(key)
px=px+kaction[1]
py=py+kaction[2]
end
function on.enterKey()
local box = pBox(20, 20, 5, math.random(2^16))
space:addObj(box, px, py)
end
function on.timer()
physicsUpdate()
end
Spoiler For A bit more advanced (motors/springs/etc):
platform.apilevel = '2.0'
require 'physics'
LARGE=physics.misc.INFINITY()
ZERO =physics.Vect(0,0)
------------------
-- Space Object --
------------------
pSpace = class()
function pSpace:init(gravity)
self.space = physics.Space()
:setGravity(physics.Vect(0, gravity))
:setSleepTimeThreshold(.5)
--:resizeActiveHash(30, 500)
self.objects = {}
end
function pSpace:step(n)
self.space:step(n)
end
function pSpace:addObj(obj, x, y)
obj.body:setPos(physics.Vect(x, y))
self.space:addBody(obj.body)
self.space:addShape(obj.shape)
if obj.added then obj:added(self) end
table.insert(self.objects, obj)
return self
end
function pSpace:addTerrainObj(obj, x, y)
obj.body:setPos(physics.Vect(x, y))
--self.space:addBody(obj.body)
for _, ss in ipairs(obj.segments) do
self.space:addStaticShape(ss)
end
table.insert(self.objects, obj)
return self
end
function pSpace:addStaticObj(obj, x, y)
obj.body:setPos(physics.Vect(x, y))
--self.space:addBody(obj.body)
self.space:addStaticShape(obj.shape)
table.insert(self.objects, obj)
return self
end
function pSpace:addComplexObj(obj, x, y)
for _, childObj in ipairs(obj.objects) do
self:addObj(childObj.obj, x+childObj.x, y+childObj.y)
end
for _, constraint in ipairs(obj.constraints) do
self.space:addConstraint(constraint)
end
table.insert(self.objects, obj)
return self
end
function pSpace:paint(gc)
for _, obj in ipairs(self.objects) do
obj:paint(gc)
end
end
----------------
-- Box Object --
----------------
pBox = class()
function pBox:init(w, h, mass, color, f, e, group)
self.h=h
self.w=w
self.mass=mass
self.color=color
self.verts = {
physics.Vect(-w/2, -h/2),
physics.Vect(-w/2, h/2),
physics.Vect(w/2 , h/2),
physics.Vect(w/2 , -h/2)
}
self.body = physics.Body(1, 1)
self.body:setMass(mass)
self.body:setMoment(physics.misc.momentForPoly(mass, self.verts, ZERO))
self.body:setVel(ZERO)
self.shape = physics.PolyShape(self.body, self.verts, ZERO)
self.shape:setRestitution(e or 0.6)
self.shape:setFriction(f or 0.6)
if group then self.shape:setGroup(group) end
end
function pBox:paint(gc)
local coords = self.shape:points()
local polycoords = {}
for k, vert in ipairs(coords) do
table.insert(polycoords, vert:x())
table.insert(polycoords, vert:y())
end
local x,y=coords[1]:x(),coords[1]:y()
table.insert(polycoords, x)
table.insert(polycoords, y)
gc:setColorRGB(self.color)
gc:fillPolygon(polycoords)
gc:setColorRGB(0,0,0)
gc:drawPolyLine(polycoords)
end
-----------------
-- Ball Object --
-----------------
pBall = class()
function pBall:init(r,mass,color, f, e, group)
self.r=r
self.mass=mass
self.color=color
self.body = physics.Body(1, 1)
self.body:setMass(mass)
self.body:setMoment(physics.misc.momentForCircle(mass, 0, r, ZERO))
self.body:setVel(ZERO)
self.shape = physics.CircleShape(self.body, r, ZERO)
self.shape:setRestitution(e or 0.6)
self.shape:setFriction(f or 0.6)
if group then self.shape:setGroup(group) end
end
function pBall:paint(gc)
local pos=self.body:pos()
local x, y = pos:x(), pos:y()
local r = self.r
gc:setColorRGB(self.color)
gc:fillArc(x-r, y-r, 2*r+1, 2*r+1, 0, 360)
gc:setColorRGB(0)
gc:drawArc(x-r, y-r, 2*r, 2*r, 0, 360)
local x2,y2
local angle=self.body:angle()
x2 = math.cos(angle)*self.r+x
y2 = math.sin(angle)*self.r+y
gc:drawLine(x,y,x2,y2)
end
pCar = class()
function pCar:init()
local group = 5
local wheelR = 5 -- wheel Radius
local wheelM = 5 -- wheel Mass
local wheelF = 0.9 -- wheel Friction
local wheelE = 0.0 -- wheel Elasticity
local chassisM = 10 -- Chassis Mass
local chassisW = 24 -- Chassis Width
local chassisH = 10 -- Chassis Height
local chassisF = 0.7 -- Chassis Friction
local chassisE = 0.0 -- Chassis Elasticity
self.wheel1 = pBall(wheelR, wheelM, 0x55CC55, wheelF, wheelE, group)
self.wheel2 = pBall(wheelR, wheelM+8, 0x55CC55, wheelF, wheelE, group)
self.chassis = pBox(chassisW, chassisH, chassisM, 0x5555CC, chassisF, chassisE, group)
self.joint1 = physics.GrooveJoint(self.chassis.body, self.wheel1.body, physics.Vect(-10, 5), physics.Vect(-10, 25), ZERO)
self.joint2 = physics.GrooveJoint(self.chassis.body, self.wheel2.body, physics.Vect( 10, 5), physics.Vect( 10, 25), ZERO)
self.spring1 = physics.DampedSpring(self.chassis.body, self.wheel1.body, physics.Vect(-10, 0), ZERO, 17, 10, 10)
self.spring2 = physics.DampedSpring(self.chassis.body, self.wheel2.body, physics.Vect( 10, 0), ZERO, 17, 10, 10)
self.Fmotor = physics.SimpleMotor(self.chassis.body, self.wheel2.body, 0):setMaxForce(1000)
self.Pmotor = physics.SimpleMotor(self.chassis.body, self.wheel2.body, 0):setMaxForce(1000)
self.gear = physics.GearJoint(self.wheel1.body, self.wheel2.body, 0, 1)
self.objects = {
{obj = self.wheel1 , x = -10 ,y = 7},
{obj = self.wheel2 , x = 10 , y = 7},
{obj = self.chassis, x = 0 ,y = -7},
}
self.constraints = {self.joint1, self.joint2, self.spring1, self.spring2, self.Fmotor, self.Pmotor, self.gear}
end
function pCar:paint(gc)
self.wheel1:paint(gc)
self.wheel2:paint(gc)
self.chassis:paint(gc)
end
------------------
-- Terrain code --
------------------
function getMidPoint(line)
local xdif = math.abs(line[3]-line[1])
local ydif = math.abs(line[4]-line[2])
local x = math.min(line[3], line[1]) + xdif/2
local y = math.min(line[4], line[2]) + ydif/2
return x, y
end
function terrain(bline, range, roughness, tms)
local lines = {bline}
local lines2 = {}
local midX, midY
for times=1, tms do
for index, line in pairs(lines) do
midX, midY = getMidPoint(line)
midY = midY + math.random(-range, range)
table.insert(lines2, {line[1], line[2], midX, midY})
table.insert(lines2, {midX, midY, line[3], line[4]})
end
lines = lines2
lines2 = {}
range = range * (roughness*2^-roughness)
end
return lines
end
------------------------
-- Terrain to physics --
------------------------
pTerrain = class()
function pTerrain:init(bline, range, roughness, times)
self.lines = terrain(bline, range, roughness, times)
self.segments = {}
self.mass = LARGE
self.body = physics.Body(1, 1)
self.body:setMass(self.mass)
self.inertia = 0
local a, b, ss
for index, line in ipairs(self.lines) do
a = physics.Vect(line[1], line[2])
b = physics.Vect(line[3], line[4])
self.inertia = self.inertia + physics.misc.momentForSegment(self.mass/#self.lines, a, b)
ss = physics.SegmentShape(self.body, a, b, 1)
ss:setRestitution(0)
ss:setFriction(1)
table.insert(self.segments, ss)
end
self.body:setMoment(self.inertia)
self.body:setVel(ZERO)
end
function pTerrain:paint(gc)
local a,b
for index, ss in ipairs(self.segments) do
a = ss:a()
b = ss:b()
gc:drawLine(a:x(), a:y(), b:x(), b:y())
end
end
function on.construction()
space = pSpace(9. 8)
timer.start(0.02)
count=0
--[[
floor = pBox(300, 20, LARGE, 0xCC5555, 0.9)
space:addStaticObj(floor, 150, 200)
--]]
ter = pTerrain({0,120,318,120}, 60, 0.6, 5)
space:addTerrainObj(ter, 0,0)
car = pCar()
space:addComplexObj(car, 50, 50)
end
function on.tabKey()
car.Pmotor:setMaxForce(10000)
car.Pmotor:setRate(car.Pmotor:rate()-0.3)
end
function on.escapeKey()
car.Pmotor:setMaxForce(0)
car.Pmotor:setRate(0)
end
function physicsUpdate()
space:step(0.1)
count = count + 1
if count == 2 then
platform.window:invalidate()
count = 1
end
end
py,px=100,160
function on.paint(gc)
local f = math.abs(car.Pmotor:rate())
gc:drawString("Force: " .. f, 2, 2, "top")
space:paint(gc)
gc:fillRect(px, py, 4, 4)
end
kaction = {up={0,-5}, down={0,5}, left={-5,0}, right={5,0}}
function on.arrowKey(key)
px=px+kaction[1]
py=py+kaction[2]
end
function on.enterKey()
local box = pBox(20, 20, 5, math.random(2^16))
space:addObj(box, px, py)
end
function on.timer()
physicsUpdate()
end
I spend some time making this code, so please take care of it ;)
5. Take a look at other exisiting activities for the TI-Nspire that use the physics engine
http://education.ti.com/calculators/tisciencenspired/US/Activities/Detail?sa=5029&id=17888
And last but not least ...
Have fun !