# Tweens on physics objects.

I just learnt about tweens (thanks @Ignatz), and tried to implement them.

``````Map = class()

function Map:init()
self.bodies = {}
self.maps = {self.CLASSIC, self.OPEN, self.SMAZE, self.LMAZE, self.BUMPERS, self.SLIDERS, self.SQUEEZE, self.SPINNER}
self.currentMap = (mapnumber == 0 and math.random(1, #self.maps)) or mapnumber
self.changetime = 0
self:resetMap()
self.lastchange = ElapsedTime
--[[
slider1 = physicsBox(50, HEIGHT/3)
slider1.type = STATIC
slider1.position = vec2(WIDTH/2, HEIGHT/6)
tween(5, slider1.linearVelocity, {y = HEIGHT/1.2})--, {loop = tween.loop.pingpong})
table.insert(self.bodies, slider1)
]]
end

function Map:resetMap()
for k,v in pairs(self.bodies) do
v:destroy()
end
self.bodies = {}
self:createEdge(0, 0, WIDTH, 0)
self:createEdge(0, 0, 0, HEIGHT)
self:createEdge(WIDTH, 0, WIDTH, HEIGHT)
self:createEdge(0, HEIGHT, WIDTH, HEIGHT)
self.maps[self.currentMap](self)
end

function Map:draw()
stroke(255)
strokeWidth(5)
fill(255)
if self.changetime == 0 then
for k,v in pairs(self.bodies) do
drawBody(v)
end
else
local t = ElapsedTime - self.lastchange
local _,d = math.modf(t/self.changetime)
if d < 1 - 1/self.changetime or ElapsedTime * 10 < 0.5 + math.floor(ElapsedTime * 10) then
for k,v in pairs(self.bodies) do
drawBody(v)
end
end
if d > 1 - DeltaTime then
self:resetMap()
end
end
end

function Map:SLIDERS()
slider1 = physicsBox(50, HEIGHT/3)
slider1.type = KINEMATIC
slider1.position = vec2(WIDTH/2, HEIGHT/6)
tween(1, slider1.position, {y = HEIGHT/1.2})--, {loop = tween.loop.pingpong})
table.insert(self.bodies, slider1)
end

function physicsBox(w, h)
local body = physics.body(POLYGON, vec2(-w/2, h/2), vec2(-w/2, -h/2), vec2(w/2, -h/2), vec2(w/2, h/2))
return body
end
``````

However, the slider doesn’t move. What am I doing wrong?

It’s hard to say without seeing (and testing) the rest of the code, ie the setup and draw functions.

Why are you using a physics object? You shouldn’t need to to keep setting the physics position because it does that itself. If you do it, why bother with physics?

Similarly, you shouldn’t need to keep setting linear velocity (via a tween). You should use applyLinearImpulse and let the physics adjust velocity.

So again, why use physics if you’re overriding its calculations of velocity and position?

Pretty much I wanted a sliding object that moved back and forth, but was not affected by other things hitting it. A static object can’t move, a dynamic object will get knocked around (I guess I could set the mass really high), and a kinematic object won’t bounce back after hitting the end of its path.
Yeah, I only tried adjusting the velocity when I couldn’t get the position to change.
The relevant code in the setup and draw is pretty much just initing and drawing the map.

Just use a sprite, that won’t be affected by anything else

But then things won’t bounce off it.

why don’t you post all your code?

Well, it’s a bit long (~18 000 characters) XD.
I’ll post it bit by bit.

``````function setModes()
displayMode(FULLSCREEN_NO_BUTTONS)
supportedOrientations(LANDSCAPE_RIGHT)
ellipseMode(CENTER)
spriteMode(CENTER)
rectMode(CENTER)
textAlign(CENTER)
font("Futura-CondensedMedium")
CENTRE = vec2(WIDTH/2, HEIGHT/2)
end

function parameters()
psize = 25
shotsize = 10
jumplen = 30
turnaccel = 0.05
screensave = false
alpha = 150
c = {color(255,0,0,alpha), color(0,0,255,alpha), color(0,255,0,alpha), color(255,255,0,alpha)}
mapnames = {"CLASSIC", "SLAUGHTER", "SMALL MAZE", "LARGE MAZE", "4 CIRCLES", "CROSSHAIRS", "SLIDERS", "SQUEEZE", "SPINNER"}
mapnames[0] = "RANDOM MAP"
end

function settings()
players = 2
bots = 1
startlives = 3
mapnumber = 0
gravity = false
if screensave then
players = 4
bots = 4
end
end

function setup()
setModes()
parameters()
settings()
physics.gravity(0,0)
physics.continuous = true
end

function draw()
background(0)
screen:draw()
noStroke()
fill(255, 255 - 255 * math.max(0, (ElapsedTime - lastchange)))
rect(WIDTH/2, HEIGHT/2, WIDTH, HEIGHT)
end

function touched(t)
screen:touched(t)
end

function collide(c)
if screen.collide then
screen:collide(c)
end
end

lastchange = ElapsedTime
score = {0,0,0,0}
self.buttons = {}
table.insert(self.buttons, Button(WIDTH/4, HEIGHT/2, 100, "QUIT", function() close() end))
table.insert(self.buttons, Button(WIDTH/2, HEIGHT/2, 100, "PLAY", function() screen = Game() end))
table.insert(self.buttons, Button(WIDTH * .75, HEIGHT/2, 100, "SETTINGS", function() screen = Settings() end))
backgroundMesh = mesh()
backgroundMesh.vertices = {vec2(0,0), vec2(WIDTH,0), vec2(WIDTH,HEIGHT), vec2(WIDTH,HEIGHT), vec2(0,HEIGHT), vec2(0,0)}
backgroundMesh.colors = {c[1], c[2], c[3], c[3], c[4], c[1]}
end

backgroundMesh:draw()
fontSize(100)
fill(255)
text("SPIN AND SHOOT 3", WIDTH/2, HEIGHT * .75)
for k,v in pairs(self.buttons) do
v:draw()
end
if screensave and ElapsedTime - lastchange > 1 then
screen = Game()
end
end

for k,v in pairs(self.buttons) do
v:touched(t)
end
end

Settings = class()

function Settings:init()
lastchange = ElapsedTime
screensave = false
self.buttons = {}
self.buttons.players = Button(WIDTH/4, HEIGHT/2, 100, "", function() togglePlayers() end)
self.buttons.startlives = Button(WIDTH * .75, HEIGHT/2, 100, "", function() toggleStartlives() end)
self.buttons.bots = Button(WIDTH/4, HEIGHT/5, 100, "", function() toggleBots() end)
self.buttons.maps = Button(WIDTH/2, HEIGHT/5, 100, "", function() toggleMaps() end)
self.buttons.gravity = Button(WIDTH * .75, HEIGHT/5, 100, "", function() toggleGravity() end)
end

function Settings:draw()
backgroundMesh:draw()
self.buttons.players.m = players .. " PLAYERS"
self.buttons.startlives.m = startlives .. ((startlives == 1 and " LIFE") or " LIVES")
self.buttons.bots.m = ((bots == 0 and "NO") or bots) .. " BOT" .. ((bots ~= 1 and "S") or "")
self.buttons.maps.m = mapnames[mapnumber]
self.buttons.gravity.m = ((gravity and "") or "NO ") .. "GRAVITY"
fontSize(100)
fill(255)
text("SETTINGS", WIDTH/2, HEIGHT * .75)
for k,v in pairs(self.buttons) do
v:draw()
end
end

function Settings:touched(t)
for k,v in pairs(self.buttons) do
v:touched(t)
end
end

function togglePlayers()
players = (players - 1) % 3 + 2
bots = math.min(bots, players)
end

function toggleStartlives()
startlives = (startlives + 2) % 6
end

function toggleBots()
bots = (bots + 1) % (players + 1)
end

function toggleMaps()
mapnumber = (mapnumber + 1) % (#mapnames + 1)
end

function toggleGravity()
gravity = not gravity
end
End = class()

function End:init()
lastchange = ElapsedTime
self.buttons = {}
table.insert(self.buttons, Button(WIDTH/4, HEIGHT/2, 100, "QUIT", function() close() end))
table.insert(self.buttons, Button(WIDTH * .75, HEIGHT/2, 100, "MENU", function() screen = Menu() end))
table.insert(self.buttons, Button(WIDTH/2, HEIGHT/2, 100, "PLAY", function() screen = Game() end))
end

function End:draw()
fill(c[winner].r, c[winner].g, c[winner].b, 50)
rect(CENTRE.x, CENTRE.y, WIDTH, HEIGHT)
fontSize(100)
if winner == 0 then
fill(255)
text("DRAW", WIDTH/2, HEIGHT * .75)
else
fill(c[winner])
text("WINNER", WIDTH/2, HEIGHT * .75)
text("WINNER", WIDTH/2, HEIGHT * .75)
end
fontSize(50)
for i=1,players do
fill(c[i])
text(score[i], WIDTH/2 - (players - 1) * 50 + (i - 1) * 100, HEIGHT/4)
text(score[i], WIDTH/2 - (players - 1) * 50 + (i - 1) * 100, HEIGHT/4)
end
for k,v in pairs(self.buttons) do
v:draw()
end
if screensave and ElapsedTime - lastchange > 1 then
screen = Game()
end
end

function End:touched(t)
if ElapsedTime > lastchange + 0.5 then
for k,v in pairs(self.buttons) do
v:touched(t)
end
end
end
``````
``````Game = class()

function Game:init()
lastchange = ElapsedTime
p = {}
for i = 1,players do
p[i] = Spinner(i, WIDTH/2 * w(i) + WIDTH/4, HEIGHT/2 * h(i) + HEIGHT * .75 - math.ceil(players/2) * HEIGHT/4, c[i].r, c[i].g, c[i].b, i > players - bots)
end
self.shots = {}
self.shotBreaks = {}
self.map = Map()
winner = nil
end

function Game:draw()
for k,v in pairs(p) do
v:drawBack()
end
noStroke()
if gravity then
fill(10)
ellipse(CENTRE.x, CENTRE.y, 100)
end
for k,v in pairs(self.shotBreaks) do
v:draw()
if ElapsedTime > v.starttime + 1 then
table.remove(self.shotBreaks, k)
end
end
for k,v in pairs(p) do
v:draw()
else
winner = k
end
if gravity and not v.dead then
v.b:applyForce((CENTRE - v.b.position)/5)
end
end
winner = 0
endGame()
elseif dead == players - 1 then
endGame()
end
stroke(255)
strokeWidth(5)
for k,v in pairs(self.shots) do
v:draw()
if gravity then
v.b:applyForce((CENTRE - v.b.position)/10)
end
end
self.map:draw()
end

function Game:touched(t)
for k,v in pairs(p) do
v:touched(t)
end
end

function Game:collide(c)
for k,v in pairs(p) do
if isHit(c, v.b) then
for j,w in pairs(self.shots) do
if isHit(c, w.b) then
v:getHit()
w.b:destroy()
table.remove(self.shots, j)
return
end
end
--v.b.angularVelocity = 0
end
end
for i,v in pairs(self.shots) do
for j,w in pairs(self.shots) do
if i ~= j and hit(c, v.b, w.b) then
table.insert(self.shotBreaks, ShotBreak(c.position, v.c.r, v.c.g, v.c.b, w.c.r, w.c.g, w.c.b))
v.b:destroy()
w.b:destroy()
table.remove(self.shots, math.max(i,j))
table.remove(self.shots, math.min(i,j))
end
end
end
end

function endGame()
if winner ~= 0 then
p[winner]:die()
score[winner] = score[winner] + 1
end
for k,v in pairs(screen.shots) do
v.b:destroy()
table.remove(screen.shots, k)
end
screen.map:clear()
screen = End()
end
Spinner = class()

function Spinner:init(player, centrex, centrey, r, g, b, bot)
self.player = player
--    self.b = physics.body(POLYGON, vec2(-len/2,-wid/2), vec2(len/2,-wid/2), vec2(len/2,wid/2), vec2(-len/2,wid/2))
self.b = physics.body(CIRCLE, psize)
self.b.position = vec2(centrex, centrey)
self.b.angle = angle(CENTRE - self.b.position) + 180
self.b.restitution = 1
self.b.linearDamping = 5
self.b.friction = 0
self.b.info = "PLAYER"
--self.b.fixedRotation = true
self.centrex = centrex
self.centrey = centrey
self.rwidth = WIDTH/2
self.rheight = HEIGHT/math.ceil(players/2)
self.c1 = color(r,g,b,255)
self.c2 = color(r,g,b,100)
self.c3 = color(r,g,b,50)
self.c4 = color(r,g,b,40)
self.touch = false
self.invincible = true
self.lastinvincible = ElapsedTime
self.invincibledelay = 0.5
self.health = startlives
self.bot = bot
self.turn = turnaccel
self.angularVelocity = 0
end

function Spinner:drawBack()
strokeWidth(5)
stroke(self.c2)
if self.canshoot then
fill(self.c3)
elseif self.canshoot and self.bot then
--fill(self.c3)
fill(self.c4)
else
--fill(self.c4)
fill(0)
end
rect(self.centrex, self.centrey, self.rwidth, self.rheight)
if self.health <= 0 then
self:die()
end
if startlives > 1 then
fill(self.c2)
fontSize(200)
text(self.health, self.centrex, self.centrey)
end
end
end

function Spinner:draw()
pushMatrix()
translate(self.b.position:unpack())
rotate(self.b.angle)
if self.invincible and ElapsedTime * 10 - math.floor(ElapsedTime * 10) < 0.5 then
fill(self.c2)
else
fill(self.c1)
end
stroke(255)
strokeWidth(5)
ellipse(0, 0, 2 * psize)
line(0, 0, psize, 0)
popMatrix()
--self.b.linearVelocity = self.b.linearVelocity * 0.9
if not self.touch then
self.b:applyAngularImpulse(self.turn)
end
self.canshoot = math.abs(self.b.angularVelocity) > 300
self.invincible = ElapsedTime < self.lastinvincible + self.invincibledelay
if self.b.linearVelocity:len() > 1000 then
self.b.linearVelocity = 1000 * self.b.linearVelocity:normalize()
end
if math.abs(self.b.angularVelocity) > 2000 then
self.b.angularVelocity = 2000 * self.b.angularVelocity/math.abs(self.b.angularVelocity)
end
if self.bot then
self:botAI()
end
end
end

function Spinner:touched(t)
if t.x > self.centrex - self.rwidth/2 and t.x < self.centrex + self.rwidth/2 and t.y > self.centrey - self.rheight/2 and t.y < self.centrey + self.rheight/2 and not self.dead and not self.bot then
if t.state == BEGAN then
self.touch = true
end
if t.state == ENDED then
self.touch = false
self:jump()
end
end
end

function Spinner:jump()
self.b:applyLinearImpulse(jumplen * dirvec(self.b.angle))
self.turn = -self.turn
self:shoot()
self.b.angularVelocity = 0
end

function Spinner:shoot()
if self.canshoot then
table.insert(screen.shots, Shot(self.b.position + (psize + 2 * shotsize) * dirvec(self.b.angle), 50 * math.sqrt(math.abs(self.b.angularVelocity)) * dirvec(self.b.angle), self.c1))
self.canshoot = false
end
end

function Spinner:getHit()
if self.invincible then
self.invincible = false
else
self.health = self.health - 1
self:setInvincible()
end
end

function Spinner:setInvincible()
self.invincible = true
self.lastinvincible = ElapsedTime
end

function Spinner:die()
if self.b then
self.b:destroy()
self.b = nil
end
end

function Spinner:botAI()
self.touch = self.canshoot
local ray = physics.raycast(self.b.position + (psize) * dirvec(self.b.angle), self.b.position + 1000 * dirvec(self.b.angle))
if ray then
if math.abs(ray.normal:angleBetween(-dirvec(self.b.angle))) then
if ray.body.info == "PLAYER" and self.canshoot then
self:jump()
return
end
--line(self.b.position.x, self.b.position.y, ray.point.x, ray.point.y)
for i = -1,1 do
local newRay = physics.raycast(ray.point, ray.point + 1000 * (-dirvec(self.b.angle)):rotate(2 * (-dirvec(self.b.angle)):angleBetween(ray.normal) + i * 0.05))
if newRay then
if newRay.body.info == "PLAYER" and newRay.body.position:dist(self.b.position) > psize and self.canshoot then
self:jump()
return
end
--line(ray.point.x, ray.point.y, newRay.point.x, newRay.point.y)
for j = -1,1 do
local lastRay = physics.raycast(newRay.point, newRay.point + 1000 * (ray.point - newRay.point):rotate(2 * (ray.point - newRay.point):angleBetween(newRay.normal) + j * 0.05))
if lastRay then
if lastRay.body.info == "PLAYER" and lastRay.body.position:dist(self.b.position) > psize and self.canshoot then
self:jump()
return
end
end
end
end
end
for k,v in pairs(screen.shots) do
--[[
local shotray = physics.raycast(v.b.position + (shotsize + 1) * v.b.linearVelocity:normalize(), v.b.position + 300 * v.b.linearVelocity:normalize())
if shotray then
if shotray.body.info == "PLAYER" and shotray.body.position:dist(self.b.position) < psize then
self:jump()
return
end
end
]]
for i = 1,4 do
if (v.b.position + i * v.b.linearVelocity * DeltaTime):dist(self.b.position) <= psize + shotsize then
self:jump()
return
end
end
end
--[[
if screen.map.changetime ~= 0 then
if math.random(500) == 1 then
self:jump()
return
end
end
]]
end
end
end
Shot = class()

function Shot:init(pos, v, c)
self.b = physics.body(CIRCLE, shotsize)
self.b.restitution = 1
self.b.mass = 10
self.b.position = pos
self.b.linearVelocity = v
self.c = c
end
``````
``````function Shot:draw()
fill(self.c)
drawBody(self.b)
end

ShotBreak = class()

function ShotBreak:init(pos, r1, g1, b1, r2, g2, b2)
self.pos = pos
self.starttime = ElapsedTime
self.c1 = color(r1, g1, b1, 255)
self.c2 = color(r2, g2, b2, 255)
self.lights = {}
for i = 1,20 do
table.insert(self.lights, Light(pos, self.c1))
table.insert(self.lights, Light(pos, self.c2))
end
end

function ShotBreak:draw()
local t = ElapsedTime - self.starttime
self.c1.a = 255 - 255 * math.min(1, t)
self.c2.a = 255 - 255 * math.min(1, t)
--print(self.c1, self.c2)
for k,v in pairs(self.lights) do
v:draw()
end
end
Light = class()

function Light:init(pos, c)
self.pos = pos
self.c = c
self.dir = math.random(360)
self.speed = 3 * math.random()
self.size = 20 * math.random()
self.v = self.speed * dirvec(self.dir)
end

function Light:draw()
self.pos = self.pos + self.v
self.v = self.v * 0.9
fill(self.c)
ellipse(self.pos.x, self.pos.y, self.size)
end
``````
``````Map = class()

function Map:init()
self.bodies = {}
--[[
self:createEdge(0, 0, WIDTH, 0)
self:createEdge(0, 0, 0, HEIGHT)
self:createEdge(WIDTH, 0, WIDTH, HEIGHT)
self:createEdge(0, HEIGHT, WIDTH, HEIGHT)
--]]
self.maps = {self.CLASSIC, self.OPEN, self.SMAZE, self.LMAZE, self.BUMPERS, self.CROSSHAIRS, self.SLIDERS,  self.SQUEEZE, self.SPINNER}
self.currentMap = (mapnumber == 0 and math.random(1, #self.maps)) or mapnumber
self.changetime = 0
self:resetMap()
self.lastchange = ElapsedTime
--[[
print(i)
local f = maps[i]
print(f)
self:f()
--]]
--[[
slider1 = physicsBox(50, HEIGHT/3)
slider1.type = STATIC
slider1.position = vec2(WIDTH/2, HEIGHT/6)
tween(1, slider1.linearVelocity, {y = HEIGHT/1.2})--, {loop = tween.loop.pingpong})
table.insert(self.bodies, slider1)
--]]
end

function Map:resetMap()
for k,v in pairs(self.bodies) do
v:destroy()
end
self.bodies = {}
self:createEdge(0, 0, WIDTH, 0)
self:createEdge(0, 0, 0, HEIGHT)
self:createEdge(WIDTH, 0, WIDTH, HEIGHT)
self:createEdge(0, HEIGHT, WIDTH, HEIGHT)
self.maps[self.currentMap](self)
end

function Map:draw()
stroke(255)
strokeWidth(5)
fill(255)
if self.changetime == 0 then
for k,v in pairs(self.bodies) do
drawBody(v)
end
else
local t = ElapsedTime - self.lastchange
local _,d = math.modf(t/self.changetime)
if d < 1 - 1/self.changetime or ElapsedTime * 10 < 0.5 + math.floor(ElapsedTime * 10) then
for k,v in pairs(self.bodies) do
drawBody(v)
end
end
if d > 1 - DeltaTime then
self:resetMap()
end
end
end

function Map:CLASSIC()
local body = physics.body(CIRCLE, 100)
body.position = CENTRE
body.restitution = 1
body.type = STATIC
table.insert(self.bodies, body)
end

function Map:OPEN()
self:createEdge(HEIGHT/3, 0, 0, HEIGHT/3)
self:createEdge(0, HEIGHT/1.5, HEIGHT/3, HEIGHT)
self:createEdge(WIDTH - HEIGHT/3, HEIGHT, WIDTH, HEIGHT/1.5)
self:createEdge(WIDTH, HEIGHT/3, WIDTH - HEIGHT/3, 0)
end

function Map:SMAZE()
numw,numh = 5,3
m = Maze(numw, numh)
z = m.maze
for i = 1,numw do
for j = 1,numh do
local x, y = (i - 1) * WIDTH/numw, HEIGHT - (j - 1) * HEIGHT/numh
if z[i][j][1] == 1 then self:createEdge(x, y, x + WIDTH/numw, y) end
if z[i][j][2] == 1 then self:createEdge(x, y, x, y - HEIGHT/numh) end
end
end
self.changetime = 5
end

function Map:LMAZE()
numw,numh = 7,5
m = Maze(numw, numh)
z = m.maze
for i = 1,numw do
for j = 1,numh do
local x, y = (i - 1) * WIDTH/numw, HEIGHT - (j - 1) * HEIGHT/numh
if z[i][j][1] == 1 then self:createEdge(x, y, x + WIDTH/numw, y) end
if z[i][j][2] == 1 then self:createEdge(x, y, x, y - HEIGHT/numh) end
end
end
self.changetime = 5
end

function Map:BUMPERS()
for i=1,4 do
local bumper = physics.body(CIRCLE, 50)
bumper.position = CENTRE + vec2(math.random(-100,100), math.random(-100,100))
bumper.restitution = 1
bumper.mass = 1000
bumper.linearVelocity = math.random(200) * dirvec(math.random(360))
table.insert(self.bodies, bumper)
end
end

function Map:CROSSHAIRS()
local body = physicsBox(50, HEIGHT/3)
body.type = STATIC
body.position = vec2(WIDTH/2, HEIGHT/6)
table.insert(self.bodies, body)
local body = physicsBox(50, HEIGHT/3)
body.type = STATIC
body.position = vec2(WIDTH/2, HEIGHT/1.2)
table.insert(self.bodies, body)
local body = physicsBox(WIDTH/3, 50)
body.type = STATIC
body.position = vec2(WIDTH/6, HEIGHT/2)
table.insert(self.bodies, body)
local body = physicsBox(WIDTH/3, 50)
body.type = STATIC
body.position = vec2(WIDTH/1.2, HEIGHT/2)
table.insert(self.bodies, body)
end

function Map:SLIDERS()
slider1 = physicsBox(50, HEIGHT/3)
slider1.type = KINEMATIC
slider1.position = vec2(WIDTH/2, HEIGHT/6)
tween(5, slider1.position, {y = HEIGHT/1.2}, {loop = tween.loop.pingpong})
table.insert(self.bodies, slider1)
slider2 = physicsBox(50, HEIGHT/3)
slider2.type = KINEMATIC
slider2.position = vec2(WIDTH/2, HEIGHT/1.2)
tween(5, slider2.position, {y = HEIGHT/6}, {loop = tween.loop.pingpong})
table.insert(self.bodies, slider2)
slider3 = physicsBox(WIDTH/3, 50)
slider3.type = KINEMATIC
slider3.position = vec2(WIDTH/6, HEIGHT/2)
tween(5, slider3.position, {x = WIDTH/1.2}, {loop = tween.loop.pingpong})
table.insert(self.bodies, slider3)
slider4 = physicsBox(WIDTH/3, 50)
slider4.type = KINEMATIC
slider4.position = vec2(WIDTH/1.2, HEIGHT/2)
tween(5, slider4.position, {x = WIDTH/6}, {loop = tween.loop.pingpong})
table.insert(self.bodies, slider4)
--[[
local vbar = physics.body(POLYGON, vec2(-25,-100), vec2(25,-100), vec2(25,100), vec2(-25,100))
vbar.categories = {1}
vbar.restitution = 1
vbar.mass = 100000
vbar.fixedRotation = true
vbar.position = CENTRE
vbar.linearVelocity = vec2(0,100)
local hbar = physics.body(POLYGON, vec2(-100,-25), vec2(-100,25), vec2(100,25), vec2(100,-25))
hbar.categories = {2}
hbar.restitution = 1
hbar.mass = 100000
hbar.fixedRotation = true
hbar.position = CENTRE
hbar.linearVelocity = vec2(100,0)
table.insert(self.bodies, vbar)
table.insert(self.bodies, hbar)
local vbar = physics.body(POLYGON, vec2(-25,-100), vec2(25,-100), vec2(25,100), vec2(-25,100))
vbar.categories = {3}
vbar.restitution = 1
vbar.mass = 100000
vbar.fixedRotation = true
vbar.position = CENTRE
vbar.linearVelocity = vec2(0,-100)
local hbar = physics.body(POLYGON, vec2(-100,-25), vec2(-100,25), vec2(100,25), vec2(100,-25))
hbar.categories = {4}
hbar.restitution = 1
hbar.mass = 100000
hbar.fixedRotation = true
hbar.position = CENTRE
hbar.linearVelocity = vec2(-100,0)
table.insert(self.bodies, vbar)
table.insert(self.bodies, hbar)
]]
end

function Map:SQUEEZE()
local speed = 50
local up = physics.body(EDGE, vec2(0,0), vec2(WIDTH,0))
up.type = KINEMATIC
up.linearVelocity = vec2(0,speed)
local right = physics.body(EDGE, vec2(0,0), vec2(0,HEIGHT))
right.type = KINEMATIC
right.linearVelocity = vec2(speed,0)
local left = physics.body(EDGE, vec2(WIDTH,0), vec2(WIDTH,HEIGHT))
left.type = KINEMATIC
left.linearVelocity = vec2(-speed,0)
local down = physics.body(EDGE, vec2(0,HEIGHT), vec2(WIDTH,HEIGHT))
down.type = KINEMATIC
down.linearVelocity = vec2(0,-speed)
table.insert(self.bodies, up)
table.insert(self.bodies, right)
table.insert(self.bodies, left)
table.insert(self.bodies, down)
local body = physics.body(CIRCLE, 50)
body.position = CENTRE
body.restitution = 1
body.type = STATIC
table.insert(self.bodies, body)
self.changetime = 5
end

function Map:SPINNER()
box1 = physicsBox(50, HEIGHT - 100)
box1.type = KINEMATIC
box1.position = CENTRE
box1.restitution = 1
box1.angularVelocity = 20
table.insert(self.bodies, box1)
box2 = physicsBox(HEIGHT - 100, 50)
box2.type = KINEMATIC
box2.position = CENTRE
box2.restitution = 1
box2.angularVelocity = 20
table.insert(self.bodies, box2)
end

function Map:clear()
for k,v in pairs(self.bodies) do
v:destroy()
end
end

function Map:createEdge(ax, ay, bx, by)
local body = physicsEdge(ax, ay, bx, by)
body.restitution = 1
table.insert(self.bodies, body)
end
Maze = class()
-- Code by Ignatz
function Maze:init(n,m)
local m=m or n --set m=n if not provided
t={} for i=1,n do t[i]={} end --create 2D maze
local stack={} --holds visited cells
local visitedCells=0
local totalCells=n*m
--set up little array to help with finding neighbours
local nb={{x=0,y=-1},{x=-1,y=0},{x=1,y=0},{x=0,y=1}}
--generate random starting cell
local cell={x=math.random(1,n),y=math.random(1,m)}
self.firstX,self.firstY=cell.x,cell.y
t[cell.x][cell.y]={1,1,1,1} -- top, left, right, bottom (1=wall)
visitedCells = visitedCells + 1
--loop through
while visitedCells<totalCells do
local prevVisitedCells=visitedCells
local r={1,2,3,4}
while #r~=0 do
--pick a random neighbour from the four directions
local rr=math.random(1,#r)
local s=r[rr]
table.remove(r,rr)
--work out x and y positions of neighbour
local x,y=cell.x+nb[s].x,cell.y+nb[s].y
--must be a valid cell that hasnt been visited
if x>0 and x<=n and y>0 and y<=m and t[x][y]==nil then
t[x][y]={1,1,1,1} -- top, right,bottom,left (1=wall)
t[cell.x][cell.y][s]=0 --break down wall in current cell
t[x][y][5-s]=0 --and neighbour
visitedCells = visitedCells + 1
table.insert(stack,{x=cell.x,y=cell.y}) --add previous cell to stack in case we need it
cell.x,cell.y=x,y --make neighbour the current cell
break
end
end
if prevVisitedCells==visitedCells then
--no unvisited neighbours found, go back to previous cell from stack
cell.x,cell.y=stack[#stack].x,stack[#stack].y
table.remove(stack,#stack)
end
end
self.maze=t
end
``````
``````Button = class()

function Button:init(x,y,size,m,action)
self.pos = vec2(x,y)
self.size = size
self.m = m
self.action = action
self.hit = 0
end

function Button:draw()
pushMatrix()
translate(self.pos:unpack())
stroke(255)
strokeWidth(5)
if self.hit == 1 and self.ts ~= ENDED then fill(150) else fill(255) end
ellipse(0,0,2*self.size)
fill(0)
fontSize(self.size/4)
text(self.m,0,0)
popMatrix()
end

function Button:touched(t)
self.ts = t.state
if (vec2(t.x,t.y) - self.pos):len() <= self.size then
self.hit = 1
if self.ts == ENDED then
self.action()
end
else
self.hit = 0
end
end

function drawBody(body)
pushMatrix()
translate(body.x, body.y)
rotate(body.angle)
if body.shapeType == POLYGON then
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
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
end
popMatrix()
end

function physicsEdge(ax, ay, bx, by)
local body = physics.body(EDGE, vec2(ax,ay), vec2(bx,by))
return body
end

function physicsBox(w, h)
local body = physics.body(POLYGON, vec2(-w/2, h/2), vec2(-w/2, -h/2), vec2(w/2, -h/2), vec2(w/2, h/2))
return body
end

function w(i)
return (1-(i%2))
end

function h(i)
return math.floor((i-1)/2)
end

function dirvec(a)
end

function angle(vec)
return math.deg(vec2(1,0):angleBetween(vec))
end

function hit(c, a, b)
if (c.bodyA == a and c.bodyB == b) or (c.bodyA == b and c.bodyB == a) then
return true
else
return false
end
end

function isHit(c, a)
if c.bodyA == a or c.bodyB == a then
return true
else
return false
end
end
``````

Any suggestions about coding/gameplay/maps/bots would be appreciated too, especially if you can explain why the bots sometimes jump twice into their own bullet.

don’t post it in bits, put it in a gist and post a link

much better and easier to work with

So do can you explain why the tween doesn’t work?

If you want a moving platform in a physics game that won’t get knocked about, the best approach is to make it `KINEMATIC` and set its linear velocity. Set start and end points defining its path, and change the velocity when it gets near a start or end point.

@MattthewLXXIII Here’s an example of using a tweens on kinematic physics objects. One tween moves an object up and down the screen. The other tween moves an object around the screen to opposite corners.

``````displayMode(FULLSCREEN)

function setup()
p1={x=100,y=100}
p2={x=100,y=HEIGHT-100}
p3={x=WIDTH-100,y=HEIGHT-100}
p4={x=WIDTH-100,y=100}
p5={x=100,y=100}

physics.continuous=true
c1=physics.body(CIRCLE,40)
c1.x=WIDTH/2
c1.y=40
c1.type=KINEMATIC
c1.restitution=1

c2=physics.body(CIRCLE,40)
c2.x=40
c2.y=HEIGHT/2
c2.type=KINEMATIC
c2.restitution=1

e1=physics.body(EDGE,vec2(0,0),vec2(WIDTH,0))
e2=physics.body(EDGE,vec2(0,0),vec2(0,HEIGHT))
e3=physics.body(EDGE,vec2(WIDTH,0),vec2(WIDTH,HEIGHT))
e4=physics.body(EDGE,vec2(0,HEIGHT),vec2(WIDTH,HEIGHT))

tab={}
for z=1,20 do
b=physics.body(CIRCLE,20)
b.x=math.random(WIDTH)
b.y=math.random(HEIGHT-50,HEIGHT)
b.restitution=1.1
table.insert(tab,b)
end

sp1={x=c1.x,y=c1.y}
tween(4,sp1,{x=WIDTH/2,y=HEIGHT-40},{loop=tween.loop.pingpong})

sp2={x=c2.x,y=c2.y}
tween.path(8,sp2,{p1,p3,p4,p2,p5},{easing=tween.easing.linear,loop=tween.loop.forever})
end

function draw()
background(40, 40, 50)
fill(255, 0, 0, 255)

c1.x=sp1.x
c1.y=sp1.y
ellipse(c1.x,c1.y,80)

c2.x=sp2.x
c2.y=sp2.y
ellipse(c2.x,c2.y,80)

fill(255)
for a,b in pairs(tab) do
ellipse(b.x,b.y,40)
end
end
``````

Thanks @dave1707, I didn’t realise I had to call the tween variable in the draw function. @yojimbo2000 I didn’t want to do that as I would have to change stuff in the draw function, but I guess I have to.