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()
    screen = Menu()
    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

Menu = class()

function Menu:init()
    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

function Menu:draw()
    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

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

Settings = class()

function Settings:init()
    lastchange = ElapsedTime
    screensave = false
    self.buttons = {}
    table.insert(self.buttons, Button(WIDTH/2, HEIGHT/2, 100, "MENU", function() screen = Menu() end))
    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()
    local dead = 0
    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()
        if v.dead then
            dead = dead + 1
        else
            winner = k
        end
        if gravity and not v.dead then
            v.b:applyForce((CENTRE - v.b.position)/5)
        end
    end
    if dead == players then
        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.dead = false
    self.bot = bot
    self.turn = turnaccel
    self.angularVelocity = 0
end

function Spinner:drawBack()
    if not self.dead then
        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()
    if not self.dead then
        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
    self.dead = true
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.mask = {0}
    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.mask = {0}
    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.mask = {0}
    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.mask = {0}
    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
        ellipse(0, 0, body.radius*2)
    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)
    return vec2(math.cos(math.rad(a)),math.sin(math.rad(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

Oh, like this? https://gist.github.com/anonymous/0de1ca51eaf934aecd018247a214a0ab

much better and easier to work with :slight_smile:

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.