Skip to content

Instantly share code, notes, and snippets.

@randrews
Created August 8, 2012 04:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save randrews/3291998 to your computer and use it in GitHub Desktop.
Save randrews/3291998 to your computer and use it in GitHub Desktop.
Example code for nice-feeling player movement in Love
require 'point'
function love.load()
math.randomseed(os.time())
love.physics.setMeter(32)
love.graphics.setBackgroundColor(64, 120, 64)
world = love.physics.newWorld(0, 0)
crates = { makeCrate(world, 5, 5),
makeCrate(world, 5, 6) }
player = makePlayer(world)
end
function makeCrate(world, x, y)
local b = love.physics.newBody(world, x*32 + 16, y*32 + 16, 'dynamic')
local s = love.physics.newRectangleShape(0, 0, 32, 32)
love.physics.newFixture(b, s)
b:setFixedRotation(true)
b:setMass(5)
b:setLinearDamping(4)
return {body=b, shape=s}
end
function makePlayer(world)
local b = love.physics.newBody(world, 48, 48, 'dynamic')
local s = love.physics.newCircleShape(16)
love.physics.newFixture(b, s)
b:setMass(1)
return {body=b, shape=s}
end
function love.draw()
local g = love.graphics
-- Draw player
g.setColor(160, 64, 64)
g.circle('fill', player.body:getX(), player.body:getY(), 16)
-- Draw crates
g.setColor(180, 120, 90)
for _, c in ipairs(crates) do
g.rectangle('fill', c.body:getX()-16, c.body:getY()-16, 32, 32)
end
end
--------------------------------------------------
function love.update(dt)
local k = love.keyboard.isDown
local kd = 0
local dir = point(0, 0)
if k('up') then dir = dir + point.up ; kd = kd + 1 end
if k('down') then dir = dir + point.down ; kd = kd + 1 end
if k('left') then dir = dir + point.left ; kd = kd + 1 end
if k('right') then dir = dir + point.right ; kd = kd + 1 end
if kd > 0 then
player.body:setLinearDamping(0)
else
player.body:setLinearDamping(8)
end
max_speed(player.body, 320)
local f = dir * 320
player.body:applyForce(f.x, f.y)
if kd == 1 then
dampenSidewaysVelocity(player.body, dir, dt)
end
for _, c in ipairs(crates) do
local sq = point(
math.floor(c.body:getX() / 32),
math.floor(c.body:getY() / 32))
nudgeToSquare(c.body, sq, 20)
end
world:update(dt)
end
function max_speed(body, spd)
local x, y = body:getLinearVelocity()
if x*x + y*y > spd*spd then
local a = math.atan2(y,x)
body:setLinearVelocity(spd * math.cos(a),
spd * math.sin(a))
end
end
function dampenSidewaysVelocity(body, dir, dt)
local a = 1 - 4 * dt
if a > 1.0 then a = 1.0 elseif a < 0 then a = 0 end
local v = point(body:getLinearVelocity())
if dir.y == 0 then v.y = v.y * a end
if dir.x == 0 then v.x = v.x * a end
body:setLinearVelocity(v())
end
function nudgeToSquare(body, sq, acc)
local y = body:getY() - 16
local ty = sq.y * 32
local f = acc * (ty - y)
body:applyForce(0, f)
local x = body:getX() - 16
local tx = sq.x * 32
local f = acc * (tx - x)
body:applyForce(f, 0)
end
module(..., package.seeall)
local methods = {}
local instance = {__index=methods}
function new(x,y)
local tbl = {x=x, y=y}
return setmetatable(tbl, instance)
end
north = new(0, -1) ; up = north
south = new(0, 1) ; down = south
west = new(-1, 0) ; left = west
east = new(1, 0) ; right = east
local mt = getmetatable(_M)
mt.__call=function(t,x,y)
return t.new(x,y)
end
function methods:copy()
return new(self.x, self.y)
end
function methods:ortho(pt2)
return self.x == pt2.x or self.y == pt2.y
end
function methods:toward(pt2)
if not self:ortho(pt2) then error(self .. ' not in a straight line with ' .. pt2)
else
local v = pt2 - self
if v.x > 0 then v.x=1 end
if v.x < 0 then v.x=-1 end
if v.y > 0 then v.y=1 end
if v.y < 0 then v.y=-1 end
return v
end
end
function methods:adjacent(pt2)
local d = pt2-self
return (d.x == 0 or d.y == 0) and (math.abs(d.x+d.y) == 1)
end
function instance.__add(pt1, pt2)
assert(pt1 and pt2)
return new(pt1.x+pt2.x, pt1.y+pt2.y)
end
function instance.__sub(pt1, pt2)
assert(pt1 and pt2)
return new(pt1.x-pt2.x, pt1.y-pt2.y)
end
function instance.__mul(pt1, pt2)
assert(pt1 and pt2)
if type(pt2) == 'number' then
return new(pt1.x * pt2, pt1.y * pt2)
else
return new(pt1.x*pt2.x, pt1.y*pt2.y)
end
end
methods.translate = instance.__add
function instance.__tostring(pt)
return string.format('(%d, %d)', pt.x, pt.y)
end
function instance.__call(pt)
return pt.x, pt.y
end
function instance.__eq(pt1, pt2)
return pt1.x == pt2.x and pt1.y == pt2.y
end
-- A point is "less than" a point if each
-- coord is less than the corresponding one
function instance.__lt(pt1, pt2)
return pt1.x < pt2.x and pt1.y < pt2.y
end
function instance.__le(pt1, pt2)
return pt1.x <= pt2.x and pt1.y <= pt2.y
end
function test()
local p = point(2,3)
assert(p.x == 2 and p.y == 3)
assert(tostring(p) == "(2, 3)")
p = p + point(1,1)
assert(tostring(p) == "(3, 4)")
local p2 = p:copy()
p2.y = p2.y-1
assert(tostring(p) == "(3, 4)")
assert(tostring(p2) == "(3, 3)")
assert(p2 + point(1, 1) == point(4, 4))
local o1, o2 = point(3, 3), point(3, 5)
assert(o1:ortho(o2))
assert(o2-o1 == point(0, 2))
assert(o1:toward(o2) == point(0, 1))
local a1, a2, a3 = point(2, 2), point(1, 2), point(3, 3)
assert(a1:adjacent(a2))
assert(a2:adjacent(a1))
assert(not a2:adjacent(a3))
assert(not a1:adjacent(a3))
assert(not a1:adjacent(a1))
assert(a2 <= a1)
assert(a1 < a3)
assert(a3 > a1)
assert(not(a2 < a1))
end
test() -- Run the tests on load, error if any fail
@gimp213
Copy link

gimp213 commented May 15, 2022

ay sorry bro ima gonna steal this code

@randrews
Copy link
Author

ay sorry bro ima gonna steal this code

Go for it but I doubt it'll work as-is at this point, it's for a version of Love2d that's several years old and the APIs have changed since then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment