Using timers and aSound.Play() to do a non-blocking fade-in/fade-out

Started by bx83, Thu 21/03/2019 20:48:59

Previous topic - Next topic

bx83

I'm trying to use timers as a way of achieving a non-blocking fade-in/out. This is because I want the screen to go gradually black *at the same time* as a piece of fading-out audio ending; not immediately after.
This timer/audio playing is done instead of:

cCharacter.Say("&1 Blah blah blah blah blah blah....");
FadeOut();
Wait(GetGameSpeed()*1);
FadeIn();
cCharacter.Say("&2 .....blah blah blah blah blah blah....");
FadeOut();
Wait(GetGameSpeed()*1);
FadeIn();
cCharacter.Say("&3 .....blah blah blah blah blah blah.");

...because speech is always blocking, so the screen fade-out starts after the audio finishes.
The audio itself fades-out, so for this to happen at the same time as the screen fade-out is ideal.

This is what I've come up with at the moment, which obviously doesn't work.
I've also tried SayBubble() instead of SayBackground() - but of course speech is blocking, so even timers and timer-checking is not worried about.

GlobalScript.asc:
Code: ags

function BuddhistCowIslandSpeech()
{
  game.bgspeech_stay_on_display = 1;

  gTitleCard.BackgroundGraphic=3144;       //size-of-background black graphic
  gTitleCard.Transparency=100;  //invisible
  gTitleCard.Visible=true;

  SetTimer(TIMER_BUDDHIST_FADE_IN_OUT, GetGameSpeed()*22);
  BuddhistFadeInOut=100;
  
  BuddhistFadeInOut_1=true;
  
  aBUDD64.Play(eAudioPriorityNormal, eOnce);    //This audio is the speech part to the following line - so normally it would be: cBuddhistCow.Say("&64 Mooaclah was bored.....
  cBuddhistCow.SayBackground("Mooaclah was a bored creature, with many supernatural powers, and a life many years beyond our own; seriously, he had a better chance than someone who eats fruit and vegetables and does an hour of cardio a day. Like hundreds of times. He was old. One day, Mooaclah knew he couldn't control his kingdom on videogames and pizza alone, so he created many cow-shaped vessels to store his power...");

  //no time to pause here since SayBackground is non-blocking. But... Say is blocking. So....?

  SetTimer(TIMER_BUDDHIST_FADE_IN_OUT,GetGameSpeed()*1);
  BuddhistFadeInOut_1=false;
  BuddhistFadeInOut_2=true;
	
  aBUDD66.Play(eAudioPriorityNormal, eOnce);
  cBuddhistCow.SayBackground("...and they fought, spears and chains clashing against eachother until the god Arktus was finally vanquished; he changed quickly into a mountain sized pile of dirt, and we name him now ''Moorardna'', or, ''the third mountain''. After a time, it became the second age of Agnor, and pending a great flood (caused by the introduction of a new mountain), he built a large boat and fit all of the animals on board.");
	
  aBUDD67.Play(eAudioPriorityNormal, eOnce);
  cBuddhistCow.SayBackground("Horses, cows, ducks and crows; deer and peacock and dog; and some which no longer exist, such as the monkey...");
	
  SetTimer(TIMER_BUDDHIST_FADE_IN_OUT, GetGameSpeed()*5);
  BuddhistFadeInOut_2=false;
  BuddhistFadeInOut_3=true;
  
  aBUDD69.Play(eAudioPriorityNormal, eOnce);
	cBuddhistCow.SayBackground("...and so Elargro said to the one true king, ''Escapod, bring my golden to--");
	SetTimer(TIMER_BUDDHIST_FADE_IN_OUT,GetGameSpeed()*1);
  BuddhistFadeInOut_3=false;
  BuddhistFadeInOut_4=true;
  
  gTitleCard.Visible=false;    //turn off the black GUI; everything back to normal at this point
}

.......

function repeatedly_execute()
{
  //various non-blocking fade-in/out blocks according to which bool is true:

  while(IsTimerExpired(TIMER_BUDDHIST_FADE_IN_OUT)) {
    if (BuddhistFadeInOut_1) {
      BuddhistFadeInOut-=10;
      if (BuddhistFadeInOut<0) BuddhistFadeInOut=0;
      gTitleCard.Transparency=BuddhistFadeInOut;
      SetTimer(TIMER_BUDDHIST_FADE_IN_OUT, 15);
    }
    else if (BuddhistFadeInOut_2) {
      BuddhistFadeInOut+=10;
      if (BuddhistFadeInOut>100) BuddhistFadeInOut=100;
      gTitleCard.Transparency=BuddhistFadeInOut;
      SetTimer(TIMER_BUDDHIST_FADE_IN_OUT, 5);
    }
    else if (BuddhistFadeInOut_3) {
      BuddhistFadeInOut-=10;
      if (BuddhistFadeInOut<0) BuddhistFadeInOut=0;
      gTitleCard.Transparency=BuddhistFadeInOut;
      SetTimer(TIMER_BUDDHIST_FADE_IN_OUT, 15);
    }
    else if (BuddhistFadeInOut_4) {
      BuddhistFadeInOut+=10;
      if (BuddhistFadeInOut>100) BuddhistFadeInOut=100;
      gTitleCard.Transparency=BuddhistFadeInOut;
      SetTimer(TIMER_BUDDHIST_FADE_IN_OUT, 5);
    }
  }
}


Any help anyone can give would be great ;P

bx83


eri0o

I have done something using tween module and gui for custom transition and use timer module for timer, but I never used speech.

bx83


jahnocli

In other software you could display a black rectangle covering the screen. You would start with maximum transparency, and gradually decrease the transparency until the rectangle is completely black. Would this work?
Life is a puzzle, a quest and an adventure

eri0o

You can maybe check which channels has something playing and then use tween to also lower volumes.

bx83

jahnocli This is exactly what I did. The problem is SayBackground() doesn't work like I think it will

bx83

I mean how the hell do you put a blocking event after the SayBackgrond() so it doesn't just happen instantly? Yet to make it work, you must follow the SayBackground() command with something that blocks; it doesn't occur to me what this is.

morganw

SayBackground returns a pointer to an Overlay, and the Overlay will become invalid when the speech ends.
https://www.adventuregamestudio.co.uk/manual/ags69.htm#Overlay.Valid

bx83

So if I loop checking Overlay after SayBackground, this will work, and it won't just instantly skip over SayBackground to move on to the next blocking event? How would I get a blocking event here, if not a loop?

Crimson Wizard

Quote from: bx83 on Sun 24/03/2019 18:12:53
So if I loop checking Overlay after SayBackground, this will work, and it won't just instantly skip over SayBackground to move on to the next blocking event? How would I get a blocking event here, if not a loop?

Wait(1) in a loop blocks the script while updating the game and screen.

Code: ags

Overlay *o = character.SayBackground(...);
while (o.Valid) Wait(1);


EDIT: But! I see in your posted code you have something in rep_exec. Wait() stops rep_exec too, so maybe this won't do. Need to read your code more...

EDIT2: Ok, not 100% certain, but maybe changing it to rep_exec_always will make it work.

EDIT3: Also, this line does not make sense to me:
Code: ags

while(IsTimerExpired(TIMER_BUDDHIST_FADE_IN_OUT))

maybe it should be
Code: ags

if (IsTimerExpired(TIMER_BUDDHIST_FADE_IN_OUT))

?

bx83

This is the variable that controls fading in and out in globalscript repexec. It's a timer with a #defined name.

Crimson Wizard

Quote from: bx83 on Sun 24/03/2019 22:47:25
This is the variable that controls fading in and out in globalscript repexec. It's a timer with a #defined name.

Yes, I understand it's a #defined timer id, what I mean is there's no sense to have "while" there, because it won't repeat in this loop anyway. IsTimerExpired will only return true once per started timer, and next timers will run out later during next visits to rep_exec.

For example, let's assume it gets there when first timer has expired. It goes under "while", does some fading and starts one of the next timers. Then it will check IsTimerExpired again, but it will be false because the new timer has not even run a little yet. So it will quit rep_exec until the next game update. It will never actually loop inside there.

bx83

Understood, changed while to if.

Code: ags
while (o.Valid) Wait(1);

Still won't work because this is SayBackground(), but blocking. The game will get stuck here and not update timers, not alter the transparency of the black screen, etc.
Unless, once again I've missed something. Put the timer/screen blackening in the "while (o.Valid)" loop?

Crimson Wizard

Quote from: bx83 on Mon 25/03/2019 08:36:56
Code: ags
while (o.Valid) Wait(1);

Still won't work because this is SayBackground(), but blocking. The game will get stuck here and not update timers, not alter the transparency of the black screen, etc.
Unless, once again I've missed something. Put the timer/screen blackening in the "while (o.Valid)" loop?

I feared this, seeing you have stuff in rep_exec. I would experiment changing rep_exec to rep_exec_always, but unfortunately I don't remember if timers update when there's a blocking command?

If they don't then you need to do this differently. Probably you should put everything under "if (timer expired)" in rep_exec: saybackground commands and so on (in which case you should remove Waits and rely on solely on timers).
I don't have a time to think this over better or test right now, will come back later (or maybe someone else can take a look).

Cassiebsg

Alternatively, drop the built in timer and make your own, so that you can update it manually inside your while along side the wait command...

Code: ags

while (timer<800) //or whatever value you need it to be
{
  wait(1);
  timer++;
}
There are those who believe that life here began out there...

SMF spam blocked by CleanTalk