Jump to content

Question about timer's / execute a script every 2 seconds


Mordred
 Share

Recommended Posts

Hello, it's me again :)

 

I watched an tutorial of Aggror where he did show how to setup timers. Now i have an idea where i might use those timers, but i ran into a problem.

 

What i want to achieve is the following:

- Use an item

- start an 10 seconds timer

- while that timer run's every 2 seconds some code shall run

 

My problem is:

if i start the timer, the resulting time is float, and thus it maybe smth. like 6.8356. Now my first approach was to "floor" that value (to remove the fraction), so it results in 6. The following step i did was to use the modulus % to check if the fraction of "6 / 2" is 0, if yes --> fire the script, if no --> do nothing.

 

Now as you can imagine the problem is....the "timer" does not only check every 1 or 2 seconds, but several times within a second and so the results the timer delivers, well, are like 6.8356, 6.84164, 6.84736 and thus i do have 3x the fraction of 0 after flooring / using the modulus.

(But if i do not floor the "timer" i'll mostly never ever get an fraction of 0, because 1.99999 or 2.00001 are never divided by 2 with a 0 fraction).

 

So my next idea to prevent that happening was to setup a counter variable and check it every run, my if statement does look like this:

- local timefrac = math.floor(self.timer) % 2

- if timefrac == 0 and math.floor(self.timer) > 0 and counter == 0 and self.timer < 2 then

 

Now, as you can imagine yourself, it's quite some work to setup the code that way, especially if you may intent to use this code in different places with different timefactors. So i'm wondering if someone could point me an easier way to achiev this. I might have to mention that this code runs in "function Script:UpdateWorld()" and the "StartTimer" is called once the attached entity is "used". Maybe it's easier to understand if i provice the code, so i gonna attach it to this posting.

Logging.txt

Link to comment
Share on other sites

You could just have 2 timers. One that does something every 2 seconds and one that disables the 2 second one when it reaches 10 seconds.

 

I've made an entity script that is a timer. You can hook things up in the flowgraph. Attach this to a pivot in your scene.

 

http://www.leadwerks.com/werkspace/files/file/412-le3-timer-node/

 

 

Another example of this would be to make a timer class that accepts script functions as the callback to fire when it ticks. I think I had one of those laying around somewhere. Let me see if I can find it or just make a new one as it's fairly simple. If you wanted to try then look at AnimationManager to see how you can make a "class" and it even allows for calling script functions as callbacks.

Link to comment
Share on other sites

Here you go. Usage is:

 

At the top of your script where you use this:

 

import "Scripts/Timer.lua"

 

 

Inside Start() create and start:

 

self.timer = Timer:Create(5000, self, self.OnTick) -- You must make a function in your script named function Script:OnTick()
self.timer:Start()

 

Inside UpdateWorld():

 

[size=4]self.timer:Update()[/size]
[size=4]

[/size]

 

 

For your problem create 2 instances of this class. One that ticks every 2 seconds and one that ticks ever 10 seconds. Start both at startup. Then in your 2 second one do whatever it is you want to do. In your 10 second one stop the 2 second one and the 10 second one with:

 

self.TwoSecondTimer:Stop()

 

self.TenSecondTimer:Stop()

Timer.lua

  • Upvote 1
Link to comment
Share on other sites

Mhm, sorry Rick, but i somehow do not get it. I added the "import" row at the top, i do understand what this does, so no problem. I started both timers in the "use" function (not the "start" function because the timer only shall executed once the item is used), so far no problem too. I added an statement "if self.timer2 then"... thinking that the "timer2" is only "true" every 2 seconds, but here's my problem. "timer2" is always true. I do not understand how to access the value that actually say's "2 seconds are over, now execute the following code". I added the new code in case you might want to see it.

 

Btw. i did try to say "if self.timer2 < 2000 then" or "if self.timer2.interval <= 2000" but it keeps saying that i try to access "timer2 (a nil value)"

 

-- give access to code in "GetEntityNeighbors.lua"
import "Scripts/Functions/GetEntityNeighbors.lua"
import "Scripts/Timer.lua"

Script.target = nil --entity "Target"
Script.damage = 0 -- float "Damage"
Script.teamid = 2 --choice "Team" "Neutral,Good,Bad"
Script.active = false

function Script:OnTick()
end

function Script:Use()
   self.timer = Timer:Create(10000, self, self.OnTick)
   self.timer:Start()
   self.timer2 = Timer:Create(2000, self, self.OnTick)
   self.timer2:Start()

   self.sound = Sound:Load("Sound/Impact/axe_chopping_wood.wav")
   self.sound:Play()

   -- load entities within range (30) of self.entity into "entities", only load entities that have scripts attached
   local entities = GetEntityNeighbors(self.entity,30,true)
   local k,entity
   -- loop thrrough the result of "entities". k = key, entity = result
   for k,entity in pairs(entities) do
       -- only select entities with teamid == 1 ("good")
       if entity.script.teamid == 1 then
           -- and if the entity has at least 1 health remaining
           if entity.script.health>0 then
               local d = self.entity:GetDistance(entity)
               -- set self.target = resulting entity
               self.target = entity
           end
       end
   end
end

local counter = 0

function Script:UpdateWorld()
   self.timer:Update()
end

function Script:UpdateWorld()
   if self.timer2 then
       self.target.script.hud.script:Skillgain("strength", "woodcutting")
       self.target.script.inventory.script:AddItem("logs", 1)
       self.target.script.inventory.script:AddItem("insects", 3)
       self.target.script.inventory.script:AddItem("bark", 5)
       self.target.script.inventory.script:AddItem("twigs", 3)
       self.target.script.inventory.script:AddItem("leafs", 5)
   end
   if self.timer then
       self.timer:Stop()
       self.timer2:Stop()
   end
end

Link to comment
Share on other sites

The self.OnTick part of the Create() is where you specify your own function that you define in the entity script. It's a user created function that gets called when the interval is up on the timer. You need to define your own and they should be different for each timer since you'll be doing different stuff in them. That function gets called by the timer when the interval is reached. An example would be like:

 

function Script:OnTick()
end

 

But you should make your own function names to show what timer they belong to. Maybe like:

 

function Script:timer2_OnTick()
end

function Script:timer1_OnTick()
end

 

It would probably be best to create the timers in the Start() and just start them in the use.

 

Why do you have 2 UpdateWorld() functions? You should only have 1. Don't do the if check on them either. The code you want to actually do the thing you want to make should be in those user defined functions that you passed to the Create() method. All you need to do in UpdateWorld() is call the timer's Update() method.

Link to comment
Share on other sites

Okay, thanks to your explanations i now got that up n running. Thanks for mentioning the 2nd "UpdateWorld()" function, i didnt see that smile.png it's fixed now.

 

Well, the timer and such now works, the interval is inserted in miliseconds i guess? Don't i have to multiply with "GetSpeed()"? I'm asking, because the timer are finished long before my sound ends (the soundfile takes roughly 9 seconds to loop through, the timer does stop after about 4 seconds).

 

Mhmm okay, i did a bit more testing. It seems that if i use 2 timers:

self.timer = Timer:Create(10000 * Time:GetSpeed(), self, self.OnTick)

self.timer2 = Timer:Create(2000 * Time:GetSpeed(), self, self.OnTick2)

even though that "timer" is set to 10.000, the timer2 only ticks 4 times (starting at 0 --> the moment i "use" the object). If i add 2.000 to "timer" i get the expected 5 ticks.

And it doesn't matter if i add "Time:GetCurrent()", the initial value of 2000 miliseconds is in real to low somehow. Instead of 2000 miliseconds per tick, it's roughly only 1000. That's strange.

Link to comment
Share on other sites

Sorry, yes. Go into the Timer:Start() function and add

 

self.lastTick = Time:GetCurrent()

 

this will make it so it doesn't fire right away the firs time. I missed that

 

After making that change you don't have to multiple by Time:GetSpeed(). It uses Time:GetCurrent() inside to track the time.

Link to comment
Share on other sites

Yea, cool, it now doens't fire the script at "launch" directly anymore. But the timevalues still do not represent real miliseconds and i'm missing one "tick". I did it as follows, and i believe, if i understood you correctly, that it should be correct. Last issue is, that it doesn't count the "last" tick (thus at 10 seconds) at all, the function OnTick2 is only called 4 times instead of 5 times this way.

Sorry to bother you again, but i didnt really understand how the "Timer.lua" works, if i would understand that it might be easier for me to figure out why it is at it is smile.png

 

(in case you read this before i edited the posting: Forget what i told about the "timer is less than 2 seconds", that was my fault, my audiosample is 15 sec, not 9, thus i just thought the timer was to "fast" because the audio still played hehe)

 

function Script:Start()
self.timer = Timer:Create(10000, self, self.OnTick)
self.timer2 = Timer:Create(2000, self, self.OnTick2)
end

function Script:OnTick()
self.timer:Stop()
self.timer2:Stop()
end

function Script:OnTick2()
self.target.script.hud.script:Skillgain("strength", "woodcutting")
self.target.script.inventory.script:AddItem("logs", 1)
self.target.script.inventory.script:AddItem("insects", 3)
self.target.script.inventory.script:AddItem("bark", 5)
self.target.script.inventory.script:AddItem("twigs", 3)
self.target.script.inventory.script:AddItem("leafs", 5)
end

function Script:Use()
self.timer:Start()
self.timer2:Start()
[...]
end
[...]
function Script:UpdateWorld()
self.timer:Update()
self.timer2:Update()
end

Link to comment
Share on other sites

Try switching the order of your timer updates. Update timer2 before timer. The thing is your last tick would be on 10 seconds exactly for timer2, but because timer (your 10 second tick) is evaluated first it would fire it's function first, which you are stopping the other timer in so it won't fire then.

 

Or make your 10 second one 11 or something like that.

 

You could also now just have 1 timer that fires every 2 seconds and then make a counter in your script that you increase inside this timer and when it reaches 5 you do something. That would possibly avoid the timing issue between the 2 timers that might happen.

  • Upvote 1
Link to comment
Share on other sites

Ahh okay, thanks for mentioning that all, i think i slowly get a hang on it. It didn't actually pop up in my mind to simply switch both timers but now that i think about it it actually makes totally sense smile.png

 

Thanks a lot, for the timer.lua file, and for your time explaining an helping me (and hopefully others too, i think such a timer is very usefull for most players smile.png)

 

[Edit]

Well, yepp, that work's better. I still have to add 100 ms to the "longer" timer, but yea it makes sense now. Actually i thought that the starting time of both timers would be the same, but hell yes, they're using a value of like 0.00001 seconds to run or so.

Link to comment
Share on other sites

You might be better off in this case making 1 timer and a counter variable because on different machines, or even the same machine, things might end up slightly off. In this specific case having 1 timer and a counter would make sure that doesn't happen at all. Because this timer isn't async it can end up off if things take longer to do in your scene.

 

Also make a change in the Timer:Update(). Say >= instead of just > That way if it's exact it'll fire the callback.

  • Upvote 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...