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