Attack intervals?
Forum rules
Before you make a thread asking for help, read this.
Before you make a thread asking for help, read this.
Attack intervals?
Hey guys, I am a huge noob to lua and love, and I need help. I have a game, and in my game a character can shoot, but the only problem is that if you hold down space he will attack every single time it runs love.update. Now obviously this is bad cus he attacks like 200 times/second . To put it simply, it want it to unbind space for ~1 second everytime you attack. How can I do this?
Re: Attack intervals?
Hi and welcome to the forum.
You can implement time intervals by having a cooldown-timer.
You can implement time intervals by having a cooldown-timer.
Code: Select all
function love.load()
cooldown = 0
end
function love.update(dt)
cooldown = math.max(cooldown - dt,0)
if love.keyboard.isDown(' ') and cooldown == 0 then
cooldown = 1 -- cooldown-time in seconds
shoot()
end
end
Check out my blog on gamedev
Re: Attack intervals?
That works, but there's a more correct way to do it, that doesn't require limiting the timer and doesn't slowly offset it over time (though this only really matters where the timing is important, like in rhythm games). This method also accounts for lag spikes, where shots may have missed their queue over the period of the spike.
And for future reference, kikito's cron library does this as well, if you happen to have multiple things you need timed. This would be for more advanced to intermediate users, however, but it's a good option to look into regardless.
Code: Select all
function love.load()
-- initialize timer
cooldown = 0
end
function love.update(dt)
-- count down with delta time
cooldown = cooldown - dt
-- repeatedly check if both the space key is down, and if our timer is "expired"
while love.keyboard.isDown(' ') and cooldown <= 0 do
-- if the timer is expired, add to it until it isn't
cooldown = cooldown + 1
-- shoot as well whenever we add to the timer
shoot()
end
end
- Robin
- The Omniscient
- Posts: 6506
- Joined: Fri Feb 20, 2009 4:29 pm
- Location: The Netherlands
- Contact:
Re: Attack intervals?
Your method is flawed, though, because if you wait a minute before shooting, you can shoot 60 consecutive frames. Hell, you can sit for an hour, and fire for 3600 consecutive frames. If that doesn't insta-kill all your enemies I don't know what will.Kingdaro wrote:That works, but there's a more correct way to do it,
Help us help you: attach a .love.
Re: Attack intervals?
Kingdaros solution is better in one aspect: If the framerate is really low, less than one frame per cooldown, then the effective cooldown is raised to the framerate (if you follow my solution).
This small correction should make Kingdaros solution correct.
This will guarantee, that if you hold down space for one hour (3600 seconds), then you get exactly 3600 shots. My initial solution would only give approximately 3600 shots.
This small correction should make Kingdaros solution correct.
Code: Select all
function love.load()
-- initialize timer
cooldown = 0
end
function love.update(dt)
-- count down with delta time
cooldown = cooldown - dt
-- repeatedly check if both the space key is down, and if our timer is "expired"
while love.keyboard.isDown(' ') and cooldown <= 0 do
-- if the timer is expired, add to it until it isn't
cooldown = cooldown + 1
-- shoot as well whenever we add to the timer
shoot()
end
-- if no shot is fired, make sure cooldown is not negative.
cooldown = math.max(cooldown,0)
end
Check out my blog on gamedev
- kikito
- Inner party member
- Posts: 3153
- Joined: Sat Oct 03, 2009 5:22 pm
- Location: Madrid, Spain
- Contact:
Re: Attack intervals?
@kingdaro thanks for mentioning cron.
This post has made me think about how cron is used. The problem it has always tried to solve is the one you guys have just exemplified.
This is how cron.lua could be used to solve this problem:
Lately I have been thinking about removing the callbacks - I always use them just to set a flag to true, as in the previous example. I am considering changing cron so that you use "if" and "while" directly instead of callbacks, like so:
Expired would be nil when not expired, and the "number of iterations done" when expired. So one can use a loop:
This way we can do what kingdaro said, but also what robin said.
This post has made me think about how cron is used. The problem it has always tried to solve is the one you guys have just exemplified.
This is how cron.lua could be used to solve this problem:
Code: Select all
local cron = require 'cron'
local canFire = false
local cooldown = cron.after(1, function() canFire = true end)
cooldown:reset(1) -- start expired
function love.update(dt)
cooldown:update(dt)
if canFire and love.keyboard.isDown(' ') then
shoot()
canFire = false
cooldown:reset()
end
end
Lately I have been thinking about removing the callbacks - I always use them just to set a flag to true, as in the previous example. I am considering changing cron so that you use "if" and "while" directly instead of callbacks, like so:
Code: Select all
-- WARNING: This is not how cron.lua works at the moment
-- This is example code of how it could work in the future
local cron = require 'cron'
local cooldown = cron.after(1) -- fire every second
cooldown:reset(1) -- start expired
function love.update(dt)
local expired = cooldown:update(dt)
if expired and love.keyboard.isDown(' ') then
shoot()
cooldown:reset()
end
end
Code: Select all
-- WARNING: This is not how cron.lua works at the moment
... -- same as before
function love.update(dt)
local expired = cooldown:update(dt)
if expired and love.keyboard.isDown(' ') then
for i=1,expired do shoot() end -- notice the loop here
cooldown:reset()
end
end
When I write def I mean function.
Re: Attack intervals?
Thanks guys, I kinda combined all these ideas into one, and it works perfectly. Have a good day )))
-
- Party member
- Posts: 106
- Joined: Sat Jun 21, 2014 3:45 pm
Re: Attack intervals?
You could also make a global variable called curTime, for example, and increase it's value by deltaTime in love.update.
Then when you need to delay something, you just assign a variable on the object, ie.
Since curTime is incremented by the delta time, 0.2 is always 1/5 of a second.
And then you just check:
This is more of a personal preference though
Then when you need to delay something, you just assign a variable on the object, ie.
Code: Select all
object.delayUntilAction = curTime + 0.2
And then you just check:
Code: Select all
if curTime > object.delayUntilAction then
-- do stuff
end
Re: Attack intervals?
Or you can create a entire new file, for the timers, here is the timer lib, adapted from Gmod, pfhv
Code: Select all
timer = {}
local PAUSED = -1
local STOPPED = 0
local RUNNING = 1
local timerList = {}
local timerListSimple = {}
local function createTimer( name )
if ( timerList[name] == nil ) then
timerList[name] = {}
timerList[name].Status = STOPPED
return true
end
return false
end
function timer.exists( name )
return timerList[name] ~= nil
end
function timer.create( name, delay, reps, func, ... )
if ( timer.exists( name ) ) then
timer.remove( name )
end
timer.adjust( name, delay, reps, func, ... )
timer.start( name )
end
function timer.start( name )
if ( not timer.exists( name ) ) then return false end
timerList[name].n = 0
timerList[name].Status = RUNNING
timerList[name].Last = love.timer.getTime()
return true
end
function timer.adjust( name, delay, reps, func, ... )
local args = {...}
createTimer( name )
timerList[name].Delay = delay
timerList[name].Repetitions = reps
if ( func ~= nil ) then
timerList[name].Func = func
timerList[name].Args = args
end
return true
end
function timer.pause( name )
if ( not timer.exists( name ) ) then return false; end
if ( timerList[name].Status == RUNNING ) then
timerList[name].Diff = love.timer.getTime() - timerList[name].Last
timerList[name].Status = PAUSED
return true
end
return false
end
function timer.unPause( name )
if ( not timer.exists( name ) ) then return false; end
if ( timerList[name].Status == PAUSED ) then
timerList[name].Diff = nil
timerList[name].Status = RUNNING
return true
end
return false
end
-- toggle pause
function timer.toggle( name )
if ( timer.exists( name ) ) then
if ( timerList[name].Status == PAUSED ) then
return timer.unPause( name )
elseif ( timerList[name].Status == RUNNING ) then
return timer.pause( name )
end
end
return false
end
function timer.stop( name )
if ( not timer.exists( name ) ) then return false; end
if ( timerList[name].Status ~= STOPPED ) then
timerList[name].Status = STOPPED
return true
end
return false
end
-- check all timers
function timer.check()
for key, value in pairs( timerList ) do
if ( value.Status == PAUSED ) then
value.Last = love.timer.getTime() - value.Diff
elseif ( value.Status == RUNNING and ( value.Last + value.Delay ) <= love.timer.getTime() ) then
value.Last = love.timer.getTime()
value.n = value.n + 1
if ( value.n >= value.Repetitions and value.Repetitions ~= 0) then
Stop( key )
end
value.Func(unpack(value.Args))
end
end
-- Run Simple timers
for key, value in pairs( timerListSimple ) do
if ( value.Finish <= love.timer.getTime() ) then
table.remove( timerListSimple, key )
value.Func(unpack(value.Args))
end
end
end
function timer.remove( name )
timerList[ name ] = nil
end
function timer.simple( delay, func, ... )
local new_timer = {}
new_timer.Finish = love.timer.getTime() + delay
new_timer.Func = func
new_timer.Args = {...}
table.insert( timerListSimple, new_timer )
return true
end
timer.Simple = timer.simple
timer.Remove = timer.remove
timer.UnPause = timer.unPause
timer.Pause = timer.pause
Code: Select all
function onusedAttack()
canuseattack = false
timer.Simple(4, function() canuseattack = true end)
end
Who is online
Users browsing this forum: Google [Bot] and 5 guests