Skip to content

Instantly share code, notes, and snippets.

@jrasanen
Forked from randrews/main.lua
Created July 17, 2020 16:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jrasanen/c216f096c392db83fc44061ebc98bf07 to your computer and use it in GitHub Desktop.
Save jrasanen/c216f096c392db83fc44061ebc98bf07 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment