Wait times
FIFO & Variables in DinkC
Not sure if this has been discussed before, but something in this document that stands out is the following line:
In fact, wait(0) is effectively the same as wait(1), and both have an effect.
Although the basics of the game loop are explained, the wait system is never really examined, leading to a slight degree of mysticism surrounding it.
Script suspension works by storing the state of the script in a big list which is then checked every engine iteration before the frame is drawn to the screen. If the amount of specified (real) time has passed or has been exceeded, the script is resumed from where it left off. Typically, the slowest part of a single iteration is frame-drawing, which you can check in yedink is around 16 milliseconds, assuming you're at 60FPS. Which you should be, unless you're having fun with the frame limiter.
What this means is that if you have a wait of 1, the time that the script will be resumed will be 1 millisecond from the time of invocation. Of course, if it takes 16ms to render a frame, it will be resumed upon the next iteration, as will any wait of 16 milliseconds or less. Therefore, any waiting durations from 1 to 16 are equivalent to zero.
Durations of 17 to 32 will resume after two frames, meaning 17 to 31 are equivalent to 32 and so on and so forth *sniffs*. For short waits, it may be worth considering just how many individual frames you're attempting to skip before resumption.
In fact, wait(0) is effectively the same as wait(1), and both have an effect.
Although the basics of the game loop are explained, the wait system is never really examined, leading to a slight degree of mysticism surrounding it.
Script suspension works by storing the state of the script in a big list which is then checked every engine iteration before the frame is drawn to the screen. If the amount of specified (real) time has passed or has been exceeded, the script is resumed from where it left off. Typically, the slowest part of a single iteration is frame-drawing, which you can check in yedink is around 16 milliseconds, assuming you're at 60FPS. Which you should be, unless you're having fun with the frame limiter.
What this means is that if you have a wait of 1, the time that the script will be resumed will be 1 millisecond from the time of invocation. Of course, if it takes 16ms to render a frame, it will be resumed upon the next iteration, as will any wait of 16 milliseconds or less. Therefore, any waiting durations from 1 to 16 are equivalent to zero.
Durations of 17 to 32 will resume after two frames, meaning 17 to 31 are equivalent to 32 and so on and so forth *sniffs*. For short waits, it may be worth considering just how many individual frames you're attempting to skip before resumption.
Durations of 17 to 32 will resume after two frames, meaning 17 to 31 are equivalent to 32 and so on and so forth *sniffs*. For short waits, it may be worth considering just how many individual frames you're attempting to skip before resumption.
Please correct me if I'm wrong, but if frame for any reason takes more than usual time to render, the actual amount of frames waited might be shorter.
.
Some other notes:
- Dink Smallwood version 1.08 screen update will always take at least 12 ms/frame (~83 FPS).
- The game has 'soft-cap' of 68 ms per frame (~15 FPS); if the frame takes longer, the game will behave as if it had taken 68 ms.
- Sprite animations will always advance at most one animation-frame per screen-frame, so animation speed is effectively capped to game speed.
- Sprite's timing does not affect the execution of the scripts
- If you want to wait until some sprite has finished its animation, the most reliable way to do it is to wait until sp_seq(&sprite, -1) returns 0, like this:
Please correct me if I'm wrong, but if frame for any reason takes more than usual time to render, the actual amount of frames waited might be shorter.
.
Some other notes:
- Dink Smallwood version 1.08 screen update will always take at least 12 ms/frame (~83 FPS).
- The game has 'soft-cap' of 68 ms per frame (~15 FPS); if the frame takes longer, the game will behave as if it had taken 68 ms.
- Sprite animations will always advance at most one animation-frame per screen-frame, so animation speed is effectively capped to game speed.
- Sprite's timing does not affect the execution of the scripts
- If you want to wait until some sprite has finished its animation, the most reliable way to do it is to wait until sp_seq(&sprite, -1) returns 0, like this:
int &seq; loop: &seq = sp_seq(¤t_sprite, -1); if(&seq != 0) { wait(1); goto loop; } //Sprite done animating
Wait,
I just tested and at least on YeOldeDink, wait(0) seems to always wait at least 2 frames.
Or more specificially, I made a script that runs this code:
and there were two calls to update_frame between each time the loop was run.
Is this YOD issue or does this also happen on vanilla Dink and HD?
I just tested and at least on YeOldeDink, wait(0) seems to always wait at least 2 frames.
Or more specificially, I made a script that runs this code:
loop: &seq = sp_seq(¤t_sprite, -1); &frame = sp_frame(¤t_sprite, -1); &pseq = sp_pseq(¤t_sprite, -1); &pframe = sp_pframe(¤t_sprite, -1); debug("seq &seq frame &frame pseq &pseq pframe &pframe"); wait(1); goto loop;
and there were two calls to update_frame between each time the loop was run.
Is this YOD issue or does this also happen on vanilla Dink and HD?
> the actual amount of frames waited might be shorter.
Indeed it is for yedink and other old dinks. However I believe that Seth has messed with the timing system on his end so that the frame-draw time is explicitly locked, so it doesn't apply for most users.
> and there were two calls to update_frame between each time the loop was run.
It appears that when a script callback is added to the callback list by running wait() etc it is added with no "timer" value (i.e. the time to wait) which is only calculated a frame later. This is the same for all engines!
You can see this in action in yedink while it's looping by opening the callbacks window and then setting it to pause on every frame, then stepping through by unpausing. The timer value will flip-flop between zero when it hits the wait, and then a game tick some time in the future.
I did a little test by adding the line
to scripting_add_callback() so that when a wait is triggered and the callback added to the list, it sets the initial timer value meaning it gets run on the next frame, rather than waiting to calculate a value, which ends up running things as one would expect them to, and you get a debug line each pframe:
This means I was wrong in the top post somewhat, but it is odd that it's not like this by default.
Also what did redink1 do to the emojis?
Indeed it is for yedink and other old dinks. However I believe that Seth has messed with the timing system on his end so that the frame-draw time is explicitly locked, so it doesn't apply for most users.
> and there were two calls to update_frame between each time the loop was run.
It appears that when a script callback is added to the callback list by running wait() etc it is added with no "timer" value (i.e. the time to wait) which is only calculated a frame later. This is the same for all engines!
You can see this in action in yedink while it's looping by opening the callbacks window and then setting it to pause on every frame, then stepping through by unpausing. The timer value will flip-flop between zero when it hits the wait, and then a game tick some time in the future.
I did a little test by adding the line
callback[k].timer = game_GetTicks() + callback[k].min;
to scripting_add_callback() so that when a wait is triggered and the callback added to the list, it sets the initial timer value meaning it gets run on the next frame, rather than waiting to calculate a value, which ends up running things as one would expect them to, and you get a debug line each pframe:
? [Scripting] scripting_resume_script(3) ?[Lua] get sprite value frame from sprite 2 ?[Lua] get sprite value pframe from sprite 2 frame: 18 pframe: 18 ? [Scripting] scripting_kill_callbacks(3) ?? Callback added to 1. ? Called script 3 from callback 1. Difference of 98ms ? [Scripting] scripting_resume_script(3) ?[Lua] get sprite value frame from sprite 2 ?[Lua] get sprite value pframe from sprite 2 frame: 19 pframe: 19 ? [Scripting] scripting_kill_callbacks(3) ?? Callback added to 1. ? Called script 3 from callback 1. Difference of 16ms ? [Scripting] scripting_resume_script(3) ?[Lua] get sprite value frame from sprite 2 ?[Lua] get sprite value pframe from sprite 2 frame: 20 pframe: 20 ? [Scripting] scripting_kill_callbacks(3) ?? Callback added to 1. ? Called script 3 from callback 1. Difference of 14ms ? [Scripting] scripting_resume_script(3) ?[Lua] get sprite value frame from sprite 2 ?[Lua] get sprite value pframe from sprite 2 frame: 21 pframe: 21 ? [Scripting] scripting_kill_callbacks(3) ?? Callback added to 1. ? Called script 3 from callback 1. Difference of 15ms ? [Scripting] scripting_resume_script(3) ?[Lua] get sprite value frame from sprite 2 ?[Lua] get sprite value pframe from sprite 2 frame: 22 pframe: 22 ? [Scripting] scripting_kill_callbacks(3) ?? Callback added to 1. ? Called script 3 from callback 1. Difference of 16ms ? [Scripting] scripting_resume_script(3) ?[Lua] get sprite value frame from sprite 2 ?[Lua] get sprite value pframe from sprite 2 frame: 23 pframe: 23 ? [Scripting] scripting_kill_callbacks(3) ?? Callback added to 1. ? Called script 3 from callback 1. Difference of 14ms ? [Scripting] scripting_resume_script(3) ?[Lua] get sprite value frame from sprite 2 ?[Lua] get sprite value pframe from sprite 2 frame: 24 pframe: 24 ? [Scripting] scripting_kill_callbacks(3) ?? Callback added to 1. ? Called script 3 from callback 1. Difference of 16ms ? [Scripting] scripting_resume_script(3) ?[Lua] get sprite value frame from sprite 2 ?[Lua] get sprite value pframe from sprite 2 frame: 25 pframe: 25 ? [Scripting] scripting_kill_callbacks(3) ?? Callback added to 1. ? Called script 3 from callback 1. Difference of 15ms ? [Scripting] scripting_resume_script(3) ?[Lua] get sprite value frame from sprite 2 ?[Lua] get sprite value pframe from sprite 2 frame: 26 pframe: 26
This means I was wrong in the top post somewhat, but it is odd that it's not like this by default.
Also what did redink1 do to the emojis?
I figured out a way to make a one frame wait!
All you have to do is to use move_stop with a sprite with a low enough timing and end position that is on opposing direction than the movement.
Here's a modified version of the animation loop from the previous message:
Since the sprite does not need to move to reach the target, the move_stop returns immediately on the next time the sprite's brain activates, which (due to low sp_timing) is going to be the next frame. The sprite's speed and base walk do not matter and its position will not be changed.
If you don't want to mess with ¤t_sprite's timing you can create another sprite and use it for sp_timing and move_stop functions.
All you have to do is to use move_stop with a sprite with a low enough timing and end position that is on opposing direction than the movement.
Here's a modified version of the animation loop from the previous message:
int &seq; //Timing must be between 1 and 15, so the sprite movement is checked every frame. sp_timing(¤t_sprite, 1); loop: &seq = sp_seq(¤t_sprite, -1); if(&seq != 0) { move_stop(¤t_sprite, 2, -2147483648, 1); goto loop; } //Sprite done animating
Since the sprite does not need to move to reach the target, the move_stop returns immediately on the next time the sprite's brain activates, which (due to low sp_timing) is going to be the next frame. The sprite's speed and base walk do not matter and its position will not be changed.
If you don't want to mess with ¤t_sprite's timing you can create another sprite and use it for sp_timing and move_stop functions.
Very nice find! It also bypasses the callback system, along with the rest of the yielders such as activate_bow and say_stop which saves a slot.
I tried this loop to check if sp_seq() was 0 yet on a brain 12 sprite, but it didn't seem to work for me. It is an explosion sprite that I want as a transition to a full screen picture.
In fact the sprite 12 stopped playing so for now I'm going back to just guesstimating the wait(x) amount. YOD speed is faster than DinkHD for me, so I end up trying to find the sweet spot between the two.
In fact the sprite 12 stopped playing so for now I'm going back to just guesstimating the wait(x) amount. YOD speed is faster than DinkHD for me, so I end up trying to find the sweet spot between the two.
The brain 12 sprite will delete itself after it reaches the target size, and sp_seq will return -1 for deleted sprites, so you should check if the seq > 0.
Also make sure that the script is not attached to the explosion itself, or it will be killed with the explosion.
Try this:
edit: had the check wrong: <= instead of >
Also make sure that the script is not attached to the explosion itself, or it will be killed with the explosion.
Try this:
int &seq; sp_timing(¤t_sprite, 1); loop: &seq = sp_seq(&explosion, -1); if(&seq > 0) { move_stop(¤t_sprite, 2, -2147483648, 1); goto loop; } //Sprite done animating
edit: had the check wrong: <= instead of >
> YOD speed is faster than DinkHD for me
Interesting, as I would have assumed the opposite. The scale brain calculates how many pixels to grow or shrink by per frame using an algorithm one may see here, in which "num" is the amount of pixels changed, calculated by dividing base_timing by 4 and then multiplying by 5.
Traditionally, base_timing is calculated by averaging the frame rate, and one may see its value in yedink by going to the "Variables" window and clicking on timers under the engine tab. At 60 frames per second, it should be hovering at around 5 or 6. Base_timing is the value I refer to above in regards to Seth locking the speed in the RTDink source, in which it's stuck at 7 without any FPS calculation applied.
Therefore, in Yedink (and 1.08 etc) it should be changing the sprite's size by 6 or 7 pixels per frame, whereas in RTDink i'd assume it'd be 9. The scale brain is also the only brain that interacts with the timing system in this way.
Interesting, as I would have assumed the opposite. The scale brain calculates how many pixels to grow or shrink by per frame using an algorithm one may see here, in which "num" is the amount of pixels changed, calculated by dividing base_timing by 4 and then multiplying by 5.
Traditionally, base_timing is calculated by averaging the frame rate, and one may see its value in yedink by going to the "Variables" window and clicking on timers under the engine tab. At 60 frames per second, it should be hovering at around 5 or 6. Base_timing is the value I refer to above in regards to Seth locking the speed in the RTDink source, in which it's stuck at 7 without any FPS calculation applied.
Therefore, in Yedink (and 1.08 etc) it should be changing the sprite's size by 6 or 7 pixels per frame, whereas in RTDink i'd assume it'd be 9. The scale brain is also the only brain that interacts with the timing system in this way.
No, the magic of Seseler's discovery is that it's possible to use move_stop to create a one-frame wait without having to use the wait system at all.
Wait() will always wait at least 2 frames, so there might be a frame after explosion ending and script continuing; while move_stop (with suitably low sp_timing) will allow the script to make a check every frame.
So wait will work, but there might be a one frame between the explosion ending and the script detecting it.
Edit: actually there might also be one frame delay before move_stop-using script detects explosion ending, if the detector sprite and the explosion are on wrong order on sprite list... and it also requires that the detection script is attached to the sprite. So it might make sense to just use wait just for the simplicity.
So wait will work, but there might be a one frame between the explosion ending and the script detecting it.
Edit: actually there might also be one frame delay before move_stop-using script detects explosion ending, if the detector sprite and the explosion are on wrong order on sprite list... and it also requires that the detection script is attached to the sprite. So it might make sense to just use wait just for the simplicity.
Hmm, I tried it, but unfortunately it didn't wait any time at all...
My script is somewhat long with a few things happening, but it is meant to be a sequential series of events...
==========================================
Okay this seems to work for me
My script is somewhat long with a few things happening, but it is meant to be a sequential series of events...
==========================================
Okay this seems to work for me
//explosion sounds and smoke to fill the screen int &sbomb = create_sprite(320, 240, 12, 434, 1); sp_brain(&sbomb, 12); sp_brain_parm(&sbomb, 800); sp_noclip(&sbomb, 1); sp_seq(&sbomb, 434); sp_que(&sbomb, 2000); sp_timing(&sbomb, 0); playsound(56, 44100, 0, 0, 0); playsound(57, 44100, 0, 0, 0); stopmidi(); int &seq; sp_timing(¤t_sprite, 1); loop: &seq = sp_seq(&sbomb, -1); if(&seq > 0) { wait(1); goto loop; }