Browse Source

Fancy laser and lerping and people from both directions;

bjorn 10 years ago
parent
commit
c5b616fc51
6 changed files with 322 additions and 18 deletions
  1. 224 0
      deps/flux.lua
  2. 19 1
      game.lua
  3. 19 0
      hud.lua
  4. 2 1
      person.lua
  5. 55 16
      pigeon.lua
  6. 3 0
      require.lua

+ 224 - 0
deps/flux.lua

@@ -0,0 +1,224 @@
+--
+-- flux
+--
+-- Copyright (c) 2014, rxi
+--
+-- This library is free software; you can redistribute it and/or modify it
+-- under the terms of the MIT license. See LICENSE for details.
+--
+
+local flux = { _version = "0.1.4" }
+flux.__index = flux
+
+flux.tweens = {}
+flux.easing = { linear = function(p) return p end }
+
+local easing = {
+  quad    = "p * p",
+  cubic   = "p * p * p",
+  quart   = "p * p * p * p",
+  quint   = "p * p * p * p * p",
+  expo    = "2 ^ (10 * (p - 1))",
+  sine    = "-math.cos(p * (math.pi * .5)) + 1",
+  circ    = "-(math.sqrt(1 - (p * p)) - 1)",
+  back    = "p * p * (2.7 * p - 1.7)",
+  elastic = "-(2^(10 * (p - 1)) * math.sin((p - 1.075) * (math.pi * 2) / .3))"
+}
+
+local makefunc = function(str, expr)
+  local load = loadstring or load
+  return load("return function(p) " .. str:gsub("%$e", expr) .. " end")()
+end
+
+for k, v in pairs(easing) do
+  flux.easing[k .. "in"] = makefunc("return $e", v)
+  flux.easing[k .. "out"] = makefunc([[
+    p = 1 - p
+    return 1 - ($e)
+  ]], v)
+  flux.easing[k .. "inout"] = makefunc([[
+    p = p * 2 
+    if p < 1 then
+      return .5 * ($e)
+    else
+      p = 2 - p
+      return .5 * (1 - ($e)) + .5
+    end 
+  ]], v)
+end
+
+
+
+local tween = {}
+tween.__index = tween
+
+local function makefsetter(field)
+  return function(self, x)
+    local mt = getmetatable(x)
+    if type(x) ~= "function" and not (mt and mt.__call) then
+      error("expected function or callable", 2)
+    end
+    local old = self[field]
+    self[field] = old and function() old() x() end or x
+    return self
+  end
+end
+
+local function makesetter(field, checkfn, errmsg)
+  return function(self, x)
+    if checkfn and not checkfn(x) then
+      error(errmsg:gsub("%$x", tostring(x)), 2)
+    end
+    self[field] = x
+    return self
+  end
+end
+
+tween.ease  = makesetter("_ease",
+                         function(x) return flux.easing[x] end,
+                         "bad easing type '$x'")
+tween.delay = makesetter("_delay",
+                         function(x) return type(x) == "number" end,
+                         "bad delay time; expected number")
+tween.onstart     = makefsetter("_onstart")
+tween.onupdate    = makefsetter("_onupdate")
+tween.oncomplete  = makefsetter("_oncomplete")
+
+
+function tween.new(obj, time, vars)
+  local self = setmetatable({}, tween)
+  self.obj = obj
+  self.rate = time > 0 and 1 / time or 0
+  self.progress = time > 0 and 0 or 1
+  self._delay = 0
+  self._ease = "quadout"
+  self.vars = {}
+  for k, v in pairs(vars) do
+    if type(v) ~= "number" then
+      error("bad value for key '" .. k .. "'; expected number")
+    end
+    self.vars[k] = v
+  end
+  return self
+end
+
+
+function tween:init()
+  for k, v in pairs(self.vars) do
+    local x = self.obj[k]
+    if type(x) ~= "number" then
+      error("bad value on object key '" .. k .. "'; expected number")
+    end
+    self.vars[k] = { start = x, diff = v - x }
+  end
+  self.inited = true
+end
+
+
+function tween:after(...)
+  local t
+  if select("#", ...) == 2 then
+    t = tween.new(self.obj, ...)
+  else
+    t = tween.new(...)
+  end
+  t.parent = self.parent
+  self:oncomplete(function() flux.add(self.parent, t) end)
+  return t
+end
+
+
+function tween:stop()
+  flux.remove(self.parent, self)
+end
+
+
+
+function flux.group()
+  return setmetatable({}, flux)
+end
+
+
+function flux:to(obj, time, vars)
+  return flux.add(self, tween.new(obj, time, vars))
+end
+
+
+function flux:update(deltatime)
+  for i = #self, 1, -1 do
+    local t = self[i]
+    if t._delay > 0 then
+      t._delay = t._delay - deltatime
+    else
+      if not t.inited then
+        flux.clear(self, t.obj, t.vars)
+        t:init()
+      end
+      if t._onstart then
+        t._onstart()
+        t._onstart = nil
+      end
+      t.progress = t.progress + t.rate * deltatime 
+      local p = t.progress
+      local x = p >= 1 and 1 or flux.easing[t._ease](p)
+      for k, v in pairs(t.vars) do
+        t.obj[k] = v.start + x * v.diff
+      end
+      if t._onupdate then t._onupdate() end
+      if p >= 1 then
+        flux.remove(self, i)
+        if t._oncomplete then t._oncomplete() end
+      end
+    end
+  end
+end
+
+
+function flux:clear(obj, vars)
+  for t in pairs(self[obj]) do
+    if t.inited then
+      for k in pairs(vars) do t.vars[k] = nil end
+    end
+  end
+end
+
+
+function flux:add(tween)
+  -- Add to object table, create table if it does not exist
+  local obj = tween.obj
+  self[obj] = self[obj] or {}
+  self[obj][tween] = true
+  -- Add to array
+  table.insert(self, tween)
+  tween.parent = self
+  return tween
+end
+
+
+function flux:remove(x)
+  if type(x) == "number" then
+    -- Remove from object table, destroy table if it is empty
+    local obj = self[x].obj
+    self[obj][self[x]] = nil
+    if not next(self[obj]) then self[obj] = nil end
+    -- Remove from array
+    self[x] = self[#self]
+    return table.remove(self)
+  end
+  for i, v in pairs(self) do
+    if v == x then
+      return flux.remove(self, i)
+    end
+  end
+end
+
+
+
+local bound = {
+  to      = function(...) return flux.to(flux.tweens, ...) end,
+  update  = function(...) return flux.update(flux.tweens, ...) end,
+  remove  = function(...) return flux.remove(flux.tweens, ...) end,
+}
+setmetatable(bound, flux)
+
+return bound

+ 19 - 1
game.lua

@@ -3,6 +3,7 @@ Game = class()
 function Game:load()
   self.pigeon = Pigeon()
   self.people = {Person()}
+  self.hud = Hud()
 end
 
 function Game:update()
@@ -12,19 +13,36 @@ function Game:update()
   end)
 
   if love.math.random() < .5 * ls.tickrate then
-    lume.push(self.people, Person())
+    local p = Person()
+
+    if love.math.random() < .5 then
+      p.x = 0
+      p.direction = 1
+    else
+      p.x = 800
+      p.direction = -1
+    end
+
+    lume.push(self.people, p)
   end
 end
 
 function Game:draw()
+  local g = love.graphics
+  flux.update(ls.dt)
+  g.setColor(0, 50, 0)
+  g.rectangle('fill', 0, 0, 800, 600)
   self.pigeon:draw()
   lume.each(self.people, function(person)
     person:draw()
   end)
+  self.hud:draw()
 end
 
 function Game:keypressed(key)
   if key == 'escape' then
     love.event.quit()
   end
+
+  self.pigeon:keypressed(key)
 end

+ 19 - 0
hud.lua

@@ -0,0 +1,19 @@
+Hud = class()
+
+function Hud:init()
+  self.font = love.graphics.newFont(12)
+end
+
+function Hud:draw()
+  local g = love.graphics
+  g.setColor(255, 255, 255)
+
+  local barWidth = 100
+  local barHeight = 10
+  local p = ctx.pigeon
+  g.rectangle('fill', 2, 2, barWidth * (p.health / p.maxHealth), barHeight)
+  g.rectangle('line', 2, 2, barWidth, barHeight)
+
+  g.setFont(self.font)
+  g.print('Lives: ' .. p.lives, barWidth + 10, 2)
+end

+ 2 - 1
person.lua

@@ -2,13 +2,14 @@ Person = class()
 
 function Person:init()
   self.x = 800
+  self.direction = -1
   self.y = 500
   self.w = 20
   self.h = 40
 end
 
 function Person:update()
-  self.x = self.x - 100 * ls.tickrate
+  self.x = self.x + 100 * self.direction * ls.tickrate
 end
 
 function Person:draw()

+ 55 - 16
pigeon.lua

@@ -3,11 +3,19 @@ Pigeon = class()
 function Pigeon:init()
   self.x = 0
   self.y = 500
+  self.prevx = self.x
+  self.prevy = self.y
   self.w = 20
   self.h = 40
-  self.speed = 100
+  self.speed = 200
   self.direction = {x = 1, y = 0}
+  self.targetDirection = {x = 1, y = 0}
   self.laser = false
+  self.laserLength = 0
+
+  self.gravity = 0--180
+  self.jumpspeed = 120
+  self.vy = 0
 
   self.lives = 3
   self.health = 100
@@ -15,40 +23,60 @@ function Pigeon:init()
 end
 
 function Pigeon:update()
+  self.prevx = self.x
+  self.prevy = self.y
+
   if love.keyboard.isDown('left') then
     self.x = self.x - self.speed * ls.tickrate
-    self.direction.x = -1
+    self.targetDirection.x = -1
   elseif love.keyboard.isDown('right') then
     self.x = self.x + self.speed * ls.tickrate
-    self.direction.x = 1
+    self.targetDirection.x = 1
   end
 
   if love.keyboard.isDown('up') then
-    self.direction.y = -1
+    self.targetDirection.y = -1
+    self.y = self.y - self.speed * ls.tickrate
   elseif love.keyboard.isDown('down') then
-    self.direction.y = 1
+    self.targetDirection.y = 1
+    self.y = self.y + self.speed * ls.tickrate
   else
-    self.direction.y = 0
+    self.targetDirection.y = 0
+  end
+
+  flux.to(self.direction, .4, self.targetDirection):ease('expoout')
+
+  self.vy = self.vy + self.gravity * ls.tickrate
+  self.y = self.y + self.vy * ls.tickrate
+
+  if self.y + self.h / 2 > 600 then
+    self.vy = math.abs(self.vy) * -1
   end
 
   self.laser = love.keyboard.isDown(' ')
 
   if self.laser then
+    self.laserTween = flux.to(self, 1, {laserLength = 1000}):ease('expoout')
     local kills = 0
 
     local x1, y1 = self.x, self.y
-    local x2, y2 = self.x + self.direction.x * 1000, self.y + self.direction.y * 1000
+    local x2, y2 = self.x + self.direction.x * self.laserLength, self.y + self.direction.y * self.laserLength
 
-    lume.each(ctx.people, function(person, i)
-      if math.hlora(x1, y1, x2, y2, person.x - person.w / 2, person.y - person.h / 2, self.w, self.h) then
+    for i, person in lume.ripairs(ctx.people) do
+      if math.hlora(x1, y1, x2, y2, person.x - person.w / 2, person.y - person.h / 2, person.w, person.h) then
         kills = kills + 1
         table.remove(ctx.people, i)
       end
-    end)
+    end
 
     if kills > 0 then
-      self.w = self.w + 5 * kills
-      self.h = self.h + 10 * kills
+      flux.to(self, .6, {w = self.w + 5 * kills, h = self.h + 10 * kills}):ease('elasticout')
+      self.health = math.min(self.health + 15 * kills, self.maxHealth)
+    end
+  else
+    if self.laserTween then
+      self.laserTween:stop()
+      self.laserLength = 0
     end
   end
 
@@ -61,6 +89,8 @@ function Pigeon:update()
 
     self.health = self.maxHealth
     self.lives = self.lives - 1
+    self.w = self.w / 2
+    self.h = self.h / 2
   end
 
   self.health = self.health - 10 * ls.tickrate
@@ -68,14 +98,23 @@ end
 
 function Pigeon:draw()
   local g = love.graphics
+  local x = lume.lerp(self.prevx, self.x, ls.accum / ls.tickrate)
+  local y = lume.lerp(self.prevy, self.y, ls.accum / ls.tickrate)
   g.setColor(255, 255, 255)
-  g.rectangle('line', self.x - self.w / 2, self.y - self.h / 2, self.w, self.h)
+  g.rectangle('line', x - self.w / 2, y - self.h / 2, self.w, self.h)
 
   if self.laser then
-    local x1, y1 = self.x, self.y
-    local x2, y2 = self.x + self.direction.x * 1000, self.y + self.direction.y * 1000
+    local x2, y2 = x + self.direction.x * self.laserLength, y + self.direction.y * self.laserLength
 
     g.setColor(255, 0, 0)
-    g.line(x1, y1, x2, y2)
+    g.setLineWidth(self.w / 5)
+    g.line(x, y, x2, y2)
+    g.setLineWidth(1)
+  end
+end
+
+function Pigeon:keypressed(key)
+  if key == 'z' then
+    self.vy = -self.jumpspeed
   end
 end

+ 3 - 0
require.lua

@@ -1,4 +1,5 @@
 require 'deps/class'
+flux = require 'deps/flux'
 lume = require 'deps/lume'
 lurker = require 'deps/lurker'
 ls = require 'deps/lovestep/lovestep'
@@ -8,3 +9,5 @@ require 'context'
 require 'game'
 require 'person'
 require 'pigeon'
+
+require 'hud'