Jump to content

how to delay processing of a function?


The_Real_Al

Recommended Posts

Just to be clear: when you add your function to OnTick it causes a drag in performance or you are worried that it will cause a drag in performance?

 

I'm not an expert but I've been under the impression that all games operate in a continual loop; doing the draw each 'tick', doing the logic, checking for button presses etc. If this is the case, presumably using an if control statement minimises the overhead, and whatever you are doing is presumably negligible next to LoS, AI, world drawing.

 

I assumed the onTick was a way of hooking into the natural loop of the game. My question would be after I've added something to the OnTick, can I remove it (like removing listeners or observers).

Link to comment
Share on other sites

EDIT: Answered my own question. Add a function to OnTick that executes functions in a table and checks first if the table is empty and doesn't do anything if it is. Add your desired function to that table and have it remove itself from the table once executed.

 

Here is a short program that shows it working:

print('Start')timesExecuted = 0onTickTable = {}local newFunction = function()            print(timesExecuted)            if(timesExecuted > 5) then             return timesExecuted            else             return nil            end         endonTickTable[1] = newFunctionlocal checkTickTable = function()         local removeIndex = 1             local functionToRemove = {}             for i,method in ipairs(onTickTable) do             if method ~= nil then             local temp = method()             if temp == nil then                print('fail')             else                print('success')                                  functionToRemove[removeIndex] = i                removeIndex = removeIndex + 1             endend             end             for i=1, removeIndex-1 do             onTickTable[functionToRemove[i]] = nil             endend-- Mock OnTick functionwhile(timesExecuted < 10) do    local temp = checkTickTable() -- Add this to OnTick    timesExecuted = timesExecuted + 1endprint('Finish')

Output:

 

Start
0
fail
1
fail
2
fail
3
fail
4
fail
5
fail
6
success
Finish
 
After 6 is printed, the function is removed from the table and never executes again. Therefore no fear of overhead. The only overhead you have now comes from the checkTicksTable which effectively does nothing because the table is empty.
 
 Because of the way Lua uses nil to determine whether the end of an array has been met I'm not sure if this works if onTickTable look like {function1, function2, nil, function3}. But still, with only one function, you should be fine.
 
In your function you'd be checking that the current ticks value is greater than 5secs + the ticks value at the time the function was created. This may take a little more work passing value around but I'm fairly sure it's all possible.
Link to comment
Share on other sites

Is Khalua capable of coroutines?

 

http://www.lua.org/pil/9.html

 

I have no clue. Stormy's suggestion looks nice and that's how I probably would try to handle it.

 

 

 

Lua has no wait function.

Although, question about the above - if you're using ticks isn't that depend on the speed of the computer, rather than real time? Or would that actually be an advantage?

 

I asked myself the same question a few days / weeks ago and found that this thread explains it pretty well:

http://www.java-gaming.org/index.php?topic=25361.0


and linked from the above, this one:

http://www.java-gaming.org/topics/game-loops/24220/view.html

Link to comment
Share on other sites

What does that mean: 'lua has no wait function'?. Punk! Lua, that is.

Loss of performance is a mere hunch. I had two or three 'OnTick' Events firing and some stutter when moving. Something i don't seem to have with vanilla but nothing i can definitely pin on the events.

 

Here is a short program that shows it working

Thanks a lot. That will take time for me to compute. Self-delete doesn't sound like it's fit for re-occuring functions, right?

 

and linked from the above, this one

Lol, the first example looks strangely familiar to something i tried. The one he calls 'Bad Bad Bad'.

Link to comment
Share on other sites

Thanks a lot. That will take time for me to compute. Self-delete doesn't sound like it's fit for re-occuring functions, right?

 

Depends what you mean by reoccurring. The method I proposed would be best suited to a strength potion for example. After x amount of time stop performing the function. But you could drink potions again. Adding the function to the table again and then removing it after x has passed.

 

If you mean that the function always needs to keep doing something provided a condition is metthen adding something to OnTick where you check a condition first should be sufficient to avoid the overhead.

 

For example, reloading is in the OnTick method. It constantly checks for r being pressed If r is pressed then it checks for ammo. That check for ammo involves looping through every item in the inventory (for semi-auto weapons anyway). I've never noticed a performance loss on it.

 

The one thing I implemented that caused a massive overhead was a very nasty recursive loop when I did explosions a while back.

Link to comment
Share on other sites

The method I proposed would be best suited to a strength potion for example. After x amount of time stop performing the function. But you could drink potions again. Adding the function to the table again and then removing it after x has passed.

 

Forgive my ignorance but this hellish program of yours could also be written in mayan for all that i understand of it.

But i believe that should do.

 

To give this some context:

I want to simulate a dialog between two zombies. Right now it will fire both 'Say()' at the same time.

I'd like it to be  1st 'zombie:Say("Text")' --> wait until the line has faded out --> 2nd 'zombie:Say("Text").

 

So basically as you said, measure the ticks it takes for the 'Say()'-texture to fade and then insert a loop into your program that counts them down before executing the second 'Say()' command.

 

Come to think about it, it should be possible to set the non-existence of this texture as an object as the condition for the loop to stop.

Link to comment
Share on other sites

Okay so here's a little program I've tested in zomboid:

 

http://www.mediafire.com/download/lk48iyy5e178fhg/Scheduler.zip

 

Essentially, the effects can largely be summed up from the temp.lua and you don't really need to pay much attention to the other two classes. The code there is as follows:

local checkFunction = function(ticksAtCreation, ticks)	print("ticksAtCreation"..ticksAtCreation)	print("ticks"..ticks)	return ticks - ticksAtCreation> 5endlocal performFunction = function()	print("Action performed")endlocal postFunction = function()	return nilendlocal newEvent = ScheduledEvent:new(ticks, checkFunction, performFunction, postFunction)EventManager.onTickTable[1] = newEvent

I create three local variables that each hold a function. The check function should return a boolean. It checks the conditions at any given moment and returns true or false. For example, in the above example it checks if 5 ticks have passed (this is a very short amount of time).

 

The ScheduledEventManager is forced to pass those exact parameters to that function. However, if you could access a global variable (like the in-game date, or the player) you could have any type of condition here (e.g. the player has an item in their inventory or a certain date has been reached).

 

The performFunction is the action that is performed when the condition is met. In this case, it just prints out to the console.

 

The postFunction should return the next ScheduledEvent that you want to execute. After the performFunction executes, the postFunction is called and the value it returns replaces the 1 index in the onTickTable.

 

A ScheduledEvent takes those functions in its constructor. So if you want a new function to execute after the current one you'd write three more functions, add them to a ScheduledEvent, have the postFunction of the first one return that object. If you don't want any more to execute, do as I do and return nil.

 

The last step is adding the ScheduledEvent we wish to execute first to the EventManager's onTickTable.

 

I've left some debug statements in there for assistance.

Link to comment
Share on other sites

I should point out that there is one major thing missing from this implementation.

 

I haven't been bothered to look into this properly but the table in Lua is not an ideal collection. When a table is looped over the nil element identifies the end of the table. With a table consisting of {1, 2, nil, 3, 4} the loop ends when it hits the nil and doesn't go to elements 3, 4.

 

Ideally the implementation above needs two things:

 

1. The ability to append an item to the end of the collection. Instead of calling EventManager.onTickTable[1] = newEvent, I should be calling an EventManager.addEvent(newEvent) which adds it on to the end of a table.

 

A table resembling {event1, event2} before calling the method should look like {event1, event2, newEvent} afterwards.

 

2. To be able to remove items from the table and shuffle everything to the left when it happens. So when event2 executes, it is removed from the table and the table looks like: {event1, newEvent} rather than {event1, nil, newEvent}.

 

I would have tried to do it but for some reason I couldn't define a  function properly... I tried adding something like this:

 

function ScheduledEventManager:append(newEvent)

    self.onTickTable[nextAvailableIndex]

end

 

but I was getting an error to do with the reference to self. I think I've dome something wrong with meta table. Maybe RJ or RoboMat can see it. Its probably really obvious.

 

Essentially, because of these limitations, you can only have 1 series of scheduled events operating at a time. Oh well.

Link to comment
Share on other sites

That ...is ...magnificent.

 

 

 

When a table is looped over the nil element identifies the end of the table

 

The program already loops over the events in the metatable, couldn't a 'table.remove' for all nil entries just do the trick? Like this:

for i,event in ipairs(o.onTickTable) do            if event == nil then                table.remove(o.onTickTable[i])            else                if event.condition(event.ticksAtCreation, ticks) then                    event.performAction()                    o.onTickTable[1] = event.postAction()                end            end       end

 

 

 

Oh and the boys would like to have a word:

Link to comment
Share on other sites

I'm going to bundle this up into an object, but this will do nicely: 

local size = 0local table = {}function add(value)	if(value ~= nil) then		table[size+1] = value		size = size + 1	endendfunction remove(index)	if((index ~= nil) and (index < size) and (index > 0)) then		for i=index, size do			if(i+1 > size) then				table[i] = nil			else				table[i] = table[i+1]			end		end		size = size - 1	elseif(index == size) then		table[index] = nil		size = size - 1	endend
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...