dicksonlaw583 / GMTwerk2

Callback-based timing and tweening library for GameMaker Studio 2.3+
MIT License
15 stars 2 forks source link

[Itinary Actor] the set method of an action isn't set before its time is reached #3

Closed Manukineko closed 3 years ago

Manukineko commented 3 years ago

Create Event

sprite = 0
mugshotSprite[0][0] = spr_mugshot_BG1
mugshotSprite[0][1] = spr_mugshot1
mugshotSprite[1][0] = spr_mugshot_BG2
mugshotSprite[1][1] = spr_mugshot2

step event

if keyboard_check_pressed(vk_left){
sprite++
if sprite > 1 sprite = 0
  Itinerary(0,[
      [250, function(){Tween(InstanceVar("bgx").set(-200), 0, 500, ["type", te_cubic_out]); }],
      [500, function(){Tween(InstanceVar("mgx").set(-200), 0, 500, ["type", te_cubic_out]); }]
  ]);
}

Draw event

draw_sprite(mugshotSprite[sprite][0],0,x+bgx,y);
draw_sprite(mugshotSprite[sprite][1],0,x+mgx,y);

When Left is pressed, the next sprite is first set to the actual value of bgx and mgx, and will be set to the value define in the set method only when the timer in the ItinaryActor will reach the given time of each action. For short :

  1. from 0 to 250, both next mugshot BG and mugshot are set to 0, they replace the actual sprites with no tweening
  2. at 250, bgx is set to -200. the mugshot BG disappear.
  3. from 250 to 500, the BG sprite perform the tween.
  4. at 500, mgx is set to -200. Mugshot disappear.
  5. from 500 and during the whole Action's duration, Mugshot perform the tween.

setMethodInItinaryActorBug

Actually, I'm not sure if it's a bug or if it works just as intended.

dicksonlaw583 commented 3 years ago

This is expected behaviour. Each itinerary moment is hidden behind an anonymous function so that the code will not execute ahead of time. In your code, from the second run on, there is 250ms worth of time where bgx is still at its final value from the previous run, and 500ms worth of time where mgx is doing the same. The offsets need to be set ahead of time, and care must also be taken to avoid having two itineraries active at the same time both trying to work on bgx and mgx.

Create:

sprite = 0;
animationDone = true;
mugshotSprite = [
    [sprite_mugshot_BG1, spr_mugshot1],
    [sprite_mugshot_BG2, spr_mugshot2],
];
bgx = -200;
mgx = -200;

Step:

if (keyboard_check_pressed(vk_left) && animationDone) {
    sprite = (sprite+1) mod array_length(mugshotSprite);
    animationDone = false;
    bgx = -200;
    mgx = -200;
    Itinerary(0, [
        [250, function() { Tween(InstanceVar("bgx"), 0, 500, ["type", te_cubic_out]); }],
        [500, function() { Tween(InstanceVar("mgx"), 0, 500, ["type", te_cubic_out]); }],
        [1000, function() { animationDone = true; }],
    ]);
}
Manukineko commented 3 years ago

I got it. So the set method doesn't pre-set the value when it is called in an Itinary, because Moment time is like a "delay". I think it may be worth to update the documentation on that particular case.

By the way, I have a question :

dicksonlaw583 commented 3 years ago

It has nothing to do with the set method in selectors. function() { ... } is only a declaration, none of the code inside runs until it actually gets called, which in the case of itinerary actors is when the moment time is reached. It is the same reason why code in script functions should not be expected to run unless it is being called.

By the same logic, the solution to your question is to hide those actions behind an anonymous function:

Create:

animateBgxMgx = function() {
    animationDone = false;
    bgx = -200;
    mgx = -200;
    Itinerary(0, [
        [250, function() { Tween(InstanceVar("bgx"), 0, 500, ["type", te_cubic_out]); }],
        [500, function() { Tween(InstanceVar("mgx"), 0, 500, ["type", te_cubic_out]); }],
        [1000, function() { animationDone = true; }],
    ]);
};

Then call it like animateBgxMgx(); or Delay(500, animateBgxMgx); later.