I can see doing something like what you've done for transient objects in-game (bullets, enemy etc), but not for general game state. If you've made the decision that you want to run the function, why not just run the function now rather than add it to a list that runs the function later. You need to take care not to over-use this functionality, as I can guarantee that it's not as efficient as simply calling the function directly.
Here's a 'for instance' that I've played with on and off for a while now: I have a short routine that adds pre-sync and post-sync handlers to run though a list of functions to call - whenever SYNC is called, they get triggered automatically.
Here's the code that adds those handlers to the list:
AddSyncEventName(EVENT_STAGE_PRE, 0, "UpdateTiming")
AddSyncEventName(EVENT_STAGE_PRE, 50, "UpdateBulletsEvent")
AddSyncEventName(EVENT_STAGE_PRE, 60, "UpdateBackdropEvent")
AddSyncEventName(EVENT_STAGE_POST, 50, "ScanKeyState")
select Controller
case USE_MOUSE
AddSyncEventName(EVENT_STAGE_POST, 50, "UpdateMouseVectors")
endcase
case USE_JOYSTICK
AddSyncEventName(EVENT_STAGE_POST, 50, "UpdateJoystickVectors")
endcase
endselect
AddSyncEventName(EVENT_STAGE_POST, 60, "UpdateShip")
Once those handlers are added, that list isn't changed. Each function gets called at the appropriate place and in the appropriate order every time SYNC is called.
Each of those handlers in turn could issue their own computed function calls - in the program I took the above code from, the UpdateBulletsEvent function does that to give each bullet its own type and behaviour, but only when it decides that it's correct to do so (in this code, that's when there has been a detectable interval of 1ms or greater since the last time it was called):
select FireMode
case 0
AddBullet(OffsetX, OffsetY, CurrentAngle, BulletConfig.Speed, BulletConfig.Life, "Standard", IMG_BULLET )
inc BulletsFired
endcase
case 1
AddBullet(OffsetX, OffsetY, CurrentAngle, BulletConfig.Speed, BulletConfig.Life, "Standard", IMG_BULLET )
AddBullet(OffsetX, OffsetY, CurrentAngle - 15.0, BulletConfig.Speed, BulletConfig.Life, "CurveLeft", IMG_BULLET )
AddBullet(OffsetX, OffsetY, CurrentAngle + 15.0, BulletConfig.Speed, BulletConfig.Life, "CurveRight", IMG_BULLET )
inc BulletsFired, 3
endcase
case 2
AddBullet(OffsetX, OffsetY, CurrentAngle, BulletConfig.Speed, BulletConfig.Life, "Standard", IMG_BULLET )
AddBullet(OffsetX, OffsetY, CurrentAngle - 15.0, BulletConfig.Speed, BulletConfig.Life, "CurveLeft", IMG_BULLET )
AddBullet(OffsetX, OffsetY, CurrentAngle + 15.0, BulletConfig.Speed, BulletConfig.Life, "CurveRight", IMG_BULLET )
AddBullet(OffsetX, OffsetY, CurrentAngle - 20.0, BulletConfig.Speed, BulletConfig.Life, "CurveLeft", IMG_BULLET )
AddBullet(OffsetX, OffsetY, CurrentAngle + 20.0, BulletConfig.Speed, BulletConfig.Life, "CurveRight", IMG_BULLET )
inc BulletsFired, 5
endcase
endselect
`...
function UpdateBulletsEvent()
if TimeElapsed() <> 0.0 then UpdateBullets( TimeElapsed() )
endfunction
function UpdateBullets( TimePassed as float )
`...
call function ptr Bullets(i).FnPtr, i, TimePassed
`...
endfunction
function AddBullet(x as float, y as float, Angle as float, Speed as float, Lifetime as float, Name as string, Image as integer)
` ...
Bullets().FnPtr = get ptr to function("__BulletMove_" + Name)
` ...
endfunction
function __BulletMove_Standard(i as integer, TimePassed as float)
local This as Bullet_t
This = Bullets(i)
inc This.X, (-sin(This.Angle) * This.Speed) * TimePassed
inc This.Y, (cos(This.Angle) * This.Speed) * TimePassed
Bullets(i) = This
endfunction
function __BulletMove_CurveLeft(i as integer, TimePassed as float)
`...
endfunction
function __BulletMove_CurveRight(i as integer, TimePassed as float)
`...
endfunction
However, I haven't shifted everything into the events list - adding bullets is triggered in my main loop.
The choices I would make are:
1. If it doesn't need to be called via CALL FUNCTION, then don't even if you can.
2. If it does need to be called in this way, try to make a static list that will be reused.
3. Only make dynamic calls if there is no other choice.
The further down that list you go, the less efficient your processing will be.
Here's the code for what I've called 'Sync events':
remstart
EventHandler library
Add pre and post sync event handlers.
Do not add new handlers during sync events as this will result in undefined behaviour
Public functions
void InitialiseEventHandlers(Lookup as integer)
Start up the sync event system
The lookup table is not used - this is for consistancy.
void ShutdownEventHandlers()
Tear down the sync event system
int FindSyncEventName(Stage as integer, EventHandlerName as string)
Search for a sync event function by name.
Returns -1 if not found, or it's internal index if found.
void AddSyncEventName(Stage as integer, Priority as integer, EventHandlerName as string)
Add a new sync event, or change the priority of an existing one
void RemoveSyncEventName(Stage as integer, EventHandlerName as string)
Remove a sync event
void ClearEventHandlers()
Removes all sync events
Private functions
void __PreSyncEventHandler()
void __PostSyncEventHandler()
remend
#constant EVENT_STAGE_PRE 0
#constant EVENT_STAGE_POST 1
type EventHandler_t
Stage as integer
Priority as integer
Sequence as integer
EventHandler as dword
EventHandlerName as string
endtype
type SyncEventHandlerDetail_t
Initialised as integer
IsDirty as integer
Sequence as integer
endtype
dim SyncEventHandlers() as EventHandler_t
global SyncEventHandlerDetail as SyncEventHandlerDetail_t
function InitialiseEventHandlers()
if SyncEventHandlerDetail.Initialised = 0
SyncEventHandlerDetail.Initialised = 1
` Ensure that the list is completely empty
undim SyncEventHandlers()
dim SyncEventHandlers() as EventHandler_t
` Add the sync callbacks
set presync callback "__PreSyncEventHandler"
set postsync callback "__PostSyncEventHandler"
SyncEventHandlerDetail.IsDirty = 0
SyncEventHandlerSequence = 0
endif
endfunction
function ShutdownEventHandlers()
SyncEventHandlerDetail.Initialised = 0
` Disable the sync callbacks
set presync callback 0
set postsync callback 0
` Clear the list
undim SyncEventHandlers()
endfunction
function FindSyncEventName(Stage as integer, EventHandlerName as string)
for i = 0 to array count( SyncEventHandlers() )
if SyncEventHandlers(i).Stage = Stage and SyncEventHandlers(i).EventHandlerName = EventHandlerName
exitfunction i
endif
next
endfunction -1
function AddSyncEventName(Stage as integer, Priority as integer, EventHandlerName as string)
local p as integer
i = FindSyncEventName(Stage, EventHandlerName)
if i <> -1
set array index SyncEventHandlers(), i
else
p = get ptr to function(EventHandlerName)
if p = 0 then exitfunction 0
array insert at bottom SyncEventHandlers()
SyncEventHandlers().Stage = Stage
SyncEventHandlers().Sequence = SyncEventHandlerDetail.Sequence
SyncEventHandlers().EventHandler = get ptr to function(EventHandlerName)
SyncEventHandlers().EventHandlerName = lower$(EventHandlerName)
inc SyncEventHandlerSequence
endif
SyncEventHandlers().Priority = Priority
SyncEventHandlerDetail.IsDirty = 1
endfunction 1
function RemoveSyncEventName(Stage as integer, EventHandlerName as string)
i = FindSyncEventName(Stage, EventHandlerName)
if i <> -1 then array delete element SyncEventHandlers(), i
endfunction
function ClearEventHandlers()
` Clear the list
empty array SyncEventHandlers()
endfunction
function __PreSyncEventHandler()
` If something has modified the list, sort it
if SyncEventHandlerDetail.IsDirty
SyncEventHandlerDetail.IsDirty = 0
sort array SyncEventHandlers(), 1, 2, 3
endif
` Start at the top of the list
array index to top SyncEventHandlers()
` Move through the list, calling each handler, until we either reach the end of the list, or the 'pre' events have been handled
while array index valid( SyncEventHandlers() )
if SyncEventHandlers().Stage <> EVENT_STAGE_PRE then exit
call function ptr SyncEventHandlers().EventHandler
next array index SyncEventHandlers()
endwhile
` At this point, the current array item will either be:
` Invalid - everything in the array processed
` Pointing at the first EVENT_STATE_POST item
endfunction
function __PostSyncEventHandler()
` List already initialised from the 'pre' events, so just carry on from the point it left off
` Move through the list, calling each handler, until we either reach the end of the list, or the 'post' events have been handled
while array index valid( SyncEventHandlers() )
if SyncEventHandlers().Stage <> EVENT_STAGE_POST then exit
call function ptr SyncEventHandlers().EventHandler
next array index SyncEventHandlers()
endwhile
endfunction