diff options
-rw-r--r-- | libs/hump/timer.lua | 245 | ||||
-rw-r--r-- | libs/shack.lua | 146 | ||||
-rw-r--r-- | scenes/level1_scene.lua | 10 | ||||
-rw-r--r-- | utils/screen_shaker.lua | 26 |
4 files changed, 333 insertions, 94 deletions
diff --git a/libs/hump/timer.lua b/libs/hump/timer.lua index 8315f68..54f06f0 100644 --- a/libs/hump/timer.lua +++ b/libs/hump/timer.lua @@ -28,6 +28,7 @@ local Timer = {} Timer.__index = Timer local function _nothing_() end +local unpack = unpack or table.unpack local function updateTimerHandle(handle, dt) -- handle: { @@ -90,7 +91,7 @@ function Timer:cancel(handle) end function Timer:clear() - self.functions = {} + self.functions = {} end function Timer:script(f) @@ -101,99 +102,157 @@ function Timer:script(f) end) end -Timer.tween = setmetatable({ - -- helper functions - out = function(f) -- 'rotates' a function - return function(s, ...) return 1 - f(1-s, ...) end - end, - chain = function(f1, f2) -- concatenates two functions - return function(s, ...) return (s < .5 and f1(2*s, ...) or 1 + f2(2*s-1, ...)) * .5 end - end, - - -- useful tweening functions - linear = function(s) return s end, - quad = function(s) return s*s end, - cubic = function(s) return s*s*s end, - quart = function(s) return s*s*s*s end, - quint = function(s) return s*s*s*s*s end, - sine = function(s) return 1-math.cos(s*math.pi/2) end, - expo = function(s) return 2^(10*(s-1)) end, - circ = function(s) return 1 - math.sqrt(1-s*s) end, - - back = function(s,bounciness) - bounciness = bounciness or 1.70158 - return s*s*((bounciness+1)*s - bounciness) - end, - - bounce = function(s) -- magic numbers ahead - local a,b = 7.5625, 1/2.75 - return math.min(a*s^2, a*(s-1.5*b)^2 + .75, a*(s-2.25*b)^2 + .9375, a*(s-2.625*b)^2 + .984375) - end, - - elastic = function(s, amp, period) - amp, period = amp and math.max(1, amp) or 1, period or .3 - return (-amp * math.sin(2*math.pi/period * (s-1) - math.asin(1/amp))) * 2^(10*(s-1)) - end, -}, { - --- register new tween -__call = function(tween, self, len, subject, target, method, after, ...) - -- recursively collects fields that are defined in both subject and target into a flat list - local function tween_collect_payload(subject, target, out) - for k,v in pairs(target) do - local ref = subject[k] - assert(type(v) == type(ref), 'Type mismatch in field "'..k..'".') - if type(v) == 'table' then - tween_collect_payload(ref, v, out) - else - local ok, delta = pcall(function() return (v-ref)*1 end) - assert(ok, 'Field "'..k..'" does not support arithmetic operations') - out[#out+1] = {subject, k, delta} - end - end - return out - end - - method = tween[method or 'linear'] -- see __index - local payload, t, args = tween_collect_payload(subject, target, {}), 0, {...} - - local last_s = 0 - return self:during(len, function(dt) - t = t + dt - local s = method(math.min(1, t/len), unpack(args)) - local ds = s - last_s - last_s = s - for _, info in ipairs(payload) do - local ref, key, delta = unpack(info) - ref[key] = ref[key] + delta * ds - end - end, after) -end, - --- fetches function and generated compositions for method `key` -__index = function(tweens, key) - if type(key) == 'function' then return key end +local function func_tween(tween, self, len, subject, target, method, after, + setters_and_getters, ...) + -- recursively collects fields that are defined in both subject and target into a flat list + -- re-use of ref is confusing + local to_func_tween = {} + local function set_and_get(subject, k, v) + setters_and_getters = setters_and_getters or {} + + local setter, getter + if setters_and_getters[k] then + setter, getter = unpack(setters_and_getters[k]) + else + setter = subject['set'..k] + getter = subject['get'..k] + end + assert(setter and getter, + "key's value in subject is nil with no set/getter") + + if to_func_tween[subject] == nil then + to_func_tween[subject] = {} + end + + ref = {getter(subject)} + to_func_tween[subject][k] = {ref, setter} + if type(v) == 'number' or #ref == 1 then + v = {v} + end + return ref, v + end + + local function tween_collect_payload(subject, target, out) + for k,v in pairs(target) do + + -- this might not be the smoothest way to do this + local ref = subject[k] + if ref == nil then + ref, v = set_and_get(subject, k, v) + end + assert(type(v) == type(ref), 'Type mismatch in field "'..k..'". ' + ..type(v)..' vs '.. type(ref)) + if type(v) == 'table' then + tween_collect_payload(ref, v, out) + else + local ok, delta = pcall(function() return (v-ref)*1 end) + assert(ok, 'Field "'..k..'" does not support arithmetic operations') + out[#out+1] = {subject, k, delta} + end + end + return out + end + + method = tween[method or 'linear'] -- see __index + local payload, t, args = tween_collect_payload(subject, target, {}), 0, {...} + + local last_s = 0 + return self:during(len, function(dt) + t = t + dt + local s = method(math.min(1, t/len), unpack(args)) + local ds = s - last_s + last_s = s + for _, info in ipairs(payload) do + local ref, key, delta = unpack(info) + ref[key] = ref[key] + delta * ds + end + for ref, t in pairs(to_func_tween) do + for key, value in pairs(t) do + local setter_args, setter = unpack(value) + if not pcall(function() setter(ref, unpack(setter_args)) end) then + setter(unpack(setter_args)) + end + end + end + end, after) +end - assert(type(key) == 'string', 'Method must be function or string.') - if rawget(tweens, key) then return rawget(tweens, key) end +local function plain_tween(tween, self, len, subject, target, method, after, ...) + return func_tween(tween, self, len, subject, target, method, after, nil, ...) +end - local function construct(pattern, f) - local method = rawget(tweens, key:match(pattern)) - if method then return f(method) end - return nil - end - local out, chain = rawget(tweens,'out'), rawget(tweens,'chain') - return construct('^in%-([^-]+)$', function(...) return ... end) +local function def_tween(func) + return setmetatable( + { + -- helper functions + out = function(f) -- 'rotates' a function + return function(s, ...) return 1 - f(1-s, ...) end + end, + chain = function(f1, f2) -- concatenates two functions + return function(s, ...) return (s < .5 and f1(2*s, ...) or 1 + f2(2*s-1, ...)) * .5 end + end, + + -- useful tweening functions + linear = function(s) return s end, + quad = function(s) return s*s end, + cubic = function(s) return s*s*s end, + quart = function(s) return s*s*s*s end, + quint = function(s) return s*s*s*s*s end, + sine = function(s) return 1-math.cos(s*math.pi/2) end, + expo = function(s) return 2^(10*(s-1)) end, + circ = function(s) return 1 - math.sqrt(1-s*s) end, + + back = function(s,bounciness) + bounciness = bounciness or 1.70158 + return s*s*((bounciness+1)*s - bounciness) + end, + + bounce = function(s) -- magic numbers ahead + local a,b = 7.5625, 1/2.75 + return math.min(a*s^2, a*(s-1.5*b)^2 + .75, a*(s-2.25*b)^2 + .9375, a*(s-2.625*b)^2 + .984375) + end, + + elastic = function(s, amp, period) + amp, period = amp and math.max(1, amp) or 1, period or .3 + return (-amp * math.sin(2*math.pi/period * (s-1) - math.asin(1/amp))) * 2^(10*(s-1)) + end, + + + }, { + + -- register new tween + __call = func, + + -- fetches function and generated compositions for method `key` + __index = function(tweens, key) + if type(key) == 'function' then return key end + + assert(type(key) == 'string', 'Method must be function or string.') + if rawget(tweens, key) then return rawget(tweens, key) end + + local function construct(pattern, f) + local method = rawget(tweens, key:match(pattern)) + if method then return f(method) end + return nil + end + + local out, chain = rawget(tweens,'out'), rawget(tweens,'chain') + return construct('^in%-([^-]+)$', function(...) return ... end) or construct('^out%-([^-]+)$', out) - or construct('^in%-out%-([^-]+)$', function(f) return chain(f, out(f)) end) - or construct('^out%-in%-([^-]+)$', function(f) return chain(out(f), f) end) + or construct('^in%-out%-([^-]+)$', function(f) return chain(f, out(f)) end) + or construct('^out%-in%-([^-]+)$', function(f) return chain(out(f), f) end) or error('Unknown interpolation method: ' .. key) -end}) + end}) +end + + +Timer.tween = def_tween(plain_tween) +Timer.func_tween = def_tween(func_tween) -- Timer instancing function Timer.new() - return setmetatable({functions = {}, tween = Timer.tween}, Timer) + return setmetatable({functions = {}, tween = Timer.tween}, Timer) end -- default instance @@ -202,14 +261,14 @@ local default = Timer.new() -- module forwards calls to default instance local module = {} for k in pairs(Timer) do - if k ~= "__index" then - module[k] = function(...) return default[k](default, ...) end - end + if k ~= "__index" then + module[k] = function(...) return default[k](default, ...) end + end end module.tween = setmetatable({}, { - __index = Timer.tween, - __newindex = function(k,v) Timer.tween[k] = v end, - __call = function(t, ...) return default:tween(...) end, + __index = Timer.tween, + __newindex = function(k,v) Timer.tween[k] = v end, + __call = function(t, ...) return default:tween(...) end, }) -return setmetatable(module, {__call = Timer.new}) +return setmetatable(module, {__call = Timer.new})
\ No newline at end of file diff --git a/libs/shack.lua b/libs/shack.lua new file mode 100644 index 0000000..337bc09 --- /dev/null +++ b/libs/shack.lua @@ -0,0 +1,146 @@ +-- shack.lua v0.1 + +-- Copyright (c) 2015 Ulysse Ramage +-- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +-- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +local shack = { + shaking = 0, + shakingTarget = 0, + + rotation = 0, + rotationTarget = 0, + + scale = { x = 1, y = 1 }, + scaleTarget = { x = 1, y = 1 }, + + shear = { x = 0, y = 0 }, + shearTarget = { x = 0, y = 0 }, + + width = love.graphics.getWidth(), + height = love.graphics.getHeight() +} +setmetatable(shack, shack) + +--[[ Private ]]-- + +local function lerp(a, b, k) --smooth transitions + if a == b then + return a + else + if math.abs(a-b) < 0.005 then return b else return a * (1-k) + b * k end + end +end + +--[[ Public ]]-- + +function shack:setDimensions(width, height) + self.width, self.height = width, height + return self +end + +function shack:update(dt) + + local _speed = 7 + + self.shaking = lerp(self.shaking, self.shakingTarget, _speed*dt) + self.rotation = lerp(self.rotation, self.rotationTarget, _speed*dt) + + self.scale.x = lerp(self.scale.x, self.scaleTarget.x, _speed*dt) + self.scale.y = lerp(self.scale.y, self.scaleTarget.y, _speed*dt) + + self.shear.x = lerp(self.shear.x, self.shearTarget.x, _speed*dt) + self.shear.y = lerp(self.shear.y, self.shearTarget.y, _speed*dt) + +end + +function shack:apply() + love.graphics.translate(self.width*.5, self.height*.5) + love.graphics.rotate((math.random()-.5)*self.rotation) + love.graphics.scale(self.scale.x, self.scale.y) + love.graphics.translate(-self.width*.5, -self.height*.5) + + love.graphics.translate((math.random()-.5)*self.shaking, (math.random()-.5)*self.shaking) + + love.graphics.shear(self.shear.x*.01, self.shear.y*.01) + + return self +end + +-- + +function shack:setShake(shaking) + self.shaking = shaking or 0 + return self +end + +function shack:setRotation(rotation) + self.rotation = rotation or 0 + return self +end + +function shack:setShear(x, y) + self.shear = { x = x or 0, y = y or 0 } + return self +end + +function shack:setScale(x, y) + if not y then + local _s = x or 1 + self.scale = { x = _s, y = _s } + else + self.scale = { x = x or 1, y = y or 1 } + end + return self +end + +function shack:setShakeTarget(shaking) + self.shakingTarget = shaking or 0 + return self +end + +function shack:setRotationTarget(rotation) + self.rotationTarget = rotation or 0 + return self +end + +function shack:setScaleTarget(x, y) + if not y then + local _s = x or 1 + self.scaleTarget = { x = _s, y = _s } + else + self.scaleTarget = { x = x or 1, y = y or 1 } + end + return self +end + +function shack:setShearTarget(x, y) + self.shearTarget = { x = x or 0, y = y or 0 } + return self +end + +-- + +function shack:getShake() return self.shaking end +function shack:getShakeTarget() return self.shakingTarget end + +function shack:getRotation() return self.rotation end +function shack:getRotationTarget() return self.rotationTarget end + +function shack:getScale() return self.scale.x, self.scale.y end +function shack:getScaleTarget() return self.scaleTarget.x, self.scaleTarget.y end + +function shack:getShear() return self.shear.x, self.shear.y end +function shack:getShearTarget() return self.shearTarget.x, self.shearTarget.y end + +--[[ Aliases ]]-- + +function shack:shake(...) return self:setShake(...) end +function shack:rotate(...) return self:setRotation(...) end +function shack:zoom(...) return self:setScale(...) end +function shack:tilt(...) return self:setShear(...) end + +--[[ End ]]-- + +return shack
\ No newline at end of file diff --git a/scenes/level1_scene.lua b/scenes/level1_scene.lua index 30406df..f532703 100644 --- a/scenes/level1_scene.lua +++ b/scenes/level1_scene.lua @@ -1,11 +1,12 @@ level1 = {} -- Level1 State - +require("utils.screen_shaker") Map = require("utils.map") -- imports require("utils.constants") function level1:init() constants.resetColors() + screenShake = ScreenShaker() map = Map("level1") end @@ -14,9 +15,11 @@ end function level1:update(dt) map:update(dt) + screenShake:update(dt) end function level1:draw() + screenShake:draw() map:draw() end @@ -28,4 +31,9 @@ function level1:leave() map = nil end + +function printMessage() + print('Hello') +end + return level1
\ No newline at end of file diff --git a/utils/screen_shaker.lua b/utils/screen_shaker.lua new file mode 100644 index 0000000..ca996af --- /dev/null +++ b/utils/screen_shaker.lua @@ -0,0 +1,26 @@ +Class = require("libs.hump.class") +Shack = require("libs.shack") +local Timer = require("libs.hump.Timer") + +ScreenShaker = Class { + init = function(self) + local width, height = love.graphics.getDimensions() + Shack:setDimensions(width, height) + Timer.every(10, shakeScreen) + end +} + +function ScreenShaker:update(dt) + Timer.update(dt) + Shack:update(dt) +end + +function ScreenShaker:draw() + Shack:apply() +end + +function shakeScreen() + Shack:setShake(20) +end + +return ScreenShaker
\ No newline at end of file |