Calculator Community > Lua |
Chipmunk Physics |
(1/4) > >> |
Jim Bauwens:
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) 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 ! |
leafy:
Wow, that's fantastic! I still find it amazing that Chipmunk can even run on a calculator, so this is all very cool stuff :D I'm more familiar with Box2D myself, but I've heard that Chipmunk is better at handling large numbers of objects, so it should be interesting to see what people can come up with. |
Jim Bauwens:
Yeah, it's indeed a very nice addition :) However, don't expect super complex and graphically advanced games to be possible :P It's still a calculator, and the Lua bindings are of course a bit slower than the C one. (This is why Ndless 3.2 will be awesome :D). But a (good functioning) Angry Birds clone should be possible. |
3rik:
Thanks for gathering these resources! :) space = pSpace(9.8) got turned into space = pSpace(9.8) in your code. |
cyanophycean314:
I'm not sure if I'm going to give this a try in my programs, but it is interesting. --- Quote from: 3rik on July 26, 2012, 04:36:17 pm ---Thanks for gathering these resources! :) space = pSpace(9.8) got turned into space = pSpace(9.8) in your code. --- End quote --- Yeah, I saw that. 8) |
Navigation |
Message Index |
Next page |