Skip through custom typewriter effect

Started by kanth, Wed 08/06/2016 11:07:27

Previous topic - Next topic

kanth

Hey everyone!
First i want to thank you all for your help, i have been cruising the forum as a guest for weeks and resolved all my problems thanks to you guys.

Well until now, my limited scripting knowledge is showing.
I've got a typewriting script and it works great, but i want to be able to skip the typewriter effect and show the entire line being typed at once (on mouse click or key press):

Code: ags

    Overlay *TypedText;
    
      void CommentNARRATOR(int delay, String text){ 
                                                          
 
     int x; 
     int y;
     AudioClip *sound;
     sound = aNarrator_voice;
     String displayedLine = "";
     int i = 0;
  
  while (i < text.Length) {
    
     displayedLine = String.Format ("%s%c", displayedLine, text.Chars[i]);
     TypedText = Overlay.CreateTextual(85, 530, 555, eFontnarrator, 65535, displayedLine);
    
    if (text.Chars[i] == ' ')
       Wait(delay/2);
    else if (text.Chars[i] == '[')
       Wait(delay/2);
    else {
      if (sound)
        sound.Play ();
       Wait(delay);
        
     }

     i++;

  }
 if (TypedText.Valid) 
     WaitMouseKey(TYPEnarrator_MAXIMUM_DISPLAY_TIME * (delay * 4));
     TypedText.Remove();
     }



I tried multiple things, but nothing worked.

if (mouse.IsButtonDown(eMouseLeft))
i = text.Length;
is a start but only type the first letter?

Snarky

#1
Instead of

Code: ags
     displayedLine = String.Format ("%s%c", displayedLine, text.Chars[i]);


Try

Code: ags
     displayedLine = text.Truncate(i);


(The problem is that you were building the displayedLine character by character, so by skipping all the intermediate steps it would miss out all of the corresponding characters. Here instead we just set displayedLine directly to the first i characters of the text.)

Also, don't set i to text.Length; set it to text.Length-1. Otherwise the body of the loop won't run.

Danvzare

#2
I tested this out to see if it works, and it does.

Code: ags

    Overlay *TypedText;
    
      void CommentNARRATOR(int delay, String text){ 
                                                          
 
     int x; 
     int y;
     AudioClip *sound;
     sound = aNarrator_voice;
     String displayedLine = "";
     int i = 0;
  StartCutscene(3);
  while (i < text.Length) {
    
     displayedLine = String.Format ("%s%c", displayedLine, text.Chars[i]);
     TypedText = Overlay.CreateTextual(85, 530, 555, eFontnarrator, 65535, displayedLine);
    
    if (text.Chars[i] == ' ')
       Wait(delay/2);
    else if (text.Chars[i] == '[')
       Wait(delay/2);
    else {
      if (sound)
        sound.Play ();
       Wait(delay);
        
     }

     i++;

  }
 EndCutscene();
 if (TypedText.Valid) 
     WaitMouseKey(TYPEnarrator_MAXIMUM_DISPLAY_TIME * (delay * 4));
     TypedText.Remove();
     }



Only two lines of code, and it's done.

By the way, those lines are StartCutscene(3) and EndCutscene().
(They're on lines 12 and 32.)

kanth

Hi Snarky!

Code: ags

displayedLine = text.Truncate(i);


I loose my last letter!
(thanks for the text.Length-1 tip)


Hey Danvzare!

It works perfectly indeed, thank you! I didn't know it was "proper" to use StartCutscene/EndCutscene like that.

Snarky

Quote from: kanth on Wed 08/06/2016 12:21:15
Hi Snarky!

Code: ags

displayedLine = text.Truncate(i);


I loose my last letter!
(thanks for the text.Length-1 tip)

Oh, right. Unlike the version you had, Truncate(i) doesn't include the i'th character, so to behave the same way you should change the loop to go from 1 to text.Length, instead of from 0 to text.Length-1. (And then set the value back to text.Length in the skip event.) Like this:

Code: ags
  int i = 1;     // 1 instead of 0
  while (i <= text.Length) // Notice the equals sign
  {
    // ...
    i++;
  }


The StartCutscene()/EndCutscene() solution certainly works, but it's a bit inefficient internally. If you have background animations going on at the same time, they will jump ahead any time you skip, which you may or may not want.

kanth

Ooookay thanks i see now. Works the same!

I can skip, but i think it's messing with the waitmousekey, if i click when the line is done it's getting to the next line directly whithout the typing effect.

I've got:

Code: ags

 
     i++;

      if (mouse.IsButtonDown(eMouseLeft)){
      i = text.Length;   
      }  
  }
      if (TypedText.Valid)
     WaitMouseKey(TYPEnarrator_MAXIMUM_DISPLAY_TIME * (delay * 4));
     // if i put a Wait() here it's solving the problem but not for the first line in the dialog (which is triggered by an option)
     TypedText.Remove();
      }


I think i'm missing a stupid detail as always :/

Snarky

If you're happy with the StartCutscene()/EndCutscene() solution, just go with that.

Otherwise, off the top of my head the best solution has to do with setting a flag when you skip to the end of the typewriting (e.g. typingInterrupted = true), and treat that case separately. Depending on how robust you want to make it (i.e. what happens if the user presses the mouse button and doesn't let go?) it could get a bit complicated, though.

Khris

Try this:

Code: ags
bool MyWait(int delay) {
  int i = 0;
  bool down;
  while (i < delay) {
    Wait(1);
    down = mouse.IsButtonDown(eMouseLeft);
    if (down) i = delay;
    i++;
  }
  return down;
}

Overlay *TypedText;

void CommentNARRATOR(String text, int delay) {
  
  int x; 
  int y;
  AudioClip *sound;
  sound = aNarrator_voice;
  String displayedLine = "";
  int i = 0;
  bool skip = false;
  
  while (i < text.Length) {
    
    displayedLine = text.Truncate(i + 1);
    TypedText = Overlay.CreateTextual(85, 530, 555, eFontnarrator, 65535, displayedLine);
    
    if (text.Chars[i] == 32 || text.Chars[i] == 91) {
      skip = MyWait(delay / 2);
      if (skip) i = text.Length - 2;
    }
    else {
      if (sound != null && !skip)
        sound.Play();
      skip = MyWait(delay);  
      if (skip) i = text.Length - 2;
    }
    i++;
  }
  if (skip) {
    Game.StopAudio(eAudioTypeSound);
    while (mouse.IsButtonDown(eMouseLeft)) Wait(1);
  }
  
  if (TypedText.Valid && !skip) 
    WaitMouseKey(TYPEnarrator_MAXIMUM_DISPLAY_TIME * (delay * 4));
  TypedText.Remove();
}

kanth

Hi Khris!

I get a "Error (line 15): Parameter type does not match prototype" :/


Khris

Sorry, I completely forgot; I switched around the order of the parameters because I wanted to make delay into an optional parameter.

Look at your GlobalScript.ash for the line that says
Code: ags
import void CommentNARRATOR(int delay, String text);

Change it to
Code: ags
import void CommentNARRATOR(String text, delay = 4);  // default value, delay is now optional


However, if you already have lots of function calls in your script, you might probably switch the order in the function definition instead, otherwise you have to change all those function calls.

kanth

Okay thanks a lot! Works like a charm :) i kept the int delay, i like being able to change the speed directly.

But i still get the same problem as with Snarky's script: when i click on a finished line, it skips to the next without typing.
If i put a wait() just after the waitmousekey it's good, but when i click on a dialog option for example the next first line skip the typing.

Khris

At the very start of the function, add this:
Code: ags
  while (mouse.IsButtonDown(eMouseLeft)) Wait(1);


As for stating the delay, the key word is "optional".
By using the import line like I suggested, you can state the delay or omit it, which will set it to 4.

Code: ags
  CommentNARRATOR("Standard delay");  // uses delay = 4
  CommentNARRATOR("Slow talking", 10);

kanth

Quote
As for stating the delay, the key word is "optional".
By using the import line like I suggested, you can state the delay or omit it, which will set it to 4.

Ok yes got it, even more handy.

And yes thank you that solved it!! I don't quite understand all of it for now but i'll make sure to study it :)
Thanks for the quick reply and taking time to explain, you guys are awesome.

shaun9991

I'm sorry to dredge up this ancient post!

I'm using this CommentNARRATOR typewriter code for my next game, and was wondering how to go about rendering the typed text in a GUI label rather than as an overlay? This is because I have lighting/shadow GUI layers running in the game, and I'd like the text to be "above" this. The overlay is always drawn behind the GUI in the Z order, if that makes sense.

I've been fiddling around with no avail. Any help would be greatly appreciated :)

Many thanks,
Shaun

Code: ags
     Overlay *TypedText;

void CommentNARRATOR(String text, int delay) {
  
   while (mouse.IsButtonDown(eMouseLeft)) Wait(1);
 
  int x;
  int y;
  AudioClip *sound;
  sound = null;
  String displayedLine = "";
  int i = 0;
  bool skip = false;
 
  while (i < text.Length) {
   
    displayedLine = text.Truncate(i + 1);
    TypedText = Overlay.CreateTextual(10, 140, 300, eFontMunro2, 65535, displayedLine);
   
    if (text.Chars[i] == 32 || text.Chars[i] == 91) {
      skip = MyWait(delay / 2);
      if (skip) i = text.Length - 2;
    }
    else {
      if (sound != null && !skip)
        sound.Play();
      skip = MyWait(delay); 
      if (skip) i = text.Length - 2;
    }
    i++;
  }
  if (skip) {
    Game.StopAudio(eAudioTypeSound);
    while (mouse.IsButtonDown(eMouseLeft)) Wait(1);
  }
 
  if (TypedText.Valid && !skip)
    WaitMouseKey(delay*4);
   WaitMouseKey(4000 * (delay * 4));
   //WaitMouseKey(TYPEnarrator_MAXIMUM_DISPLAY_TIME * (delay * 4));
  TypedText.Remove();
}
 

Support Cloak and Dagger Games on Patreon: https://www.patreon.com/user?u=460039

Crimson Wizard

#14
@shaun9991, it seems simple, just replace
Code: ags

TypedText = Overlay.CreateTextual(10, 140, 300, eFontMunro2, 65535, displayedLine);

with
Code: ags

myLabel.Text = displayedLine;


Of course you'd also have to make gui visible before typewriter effect starts, and replace "TypedText.Remove();" with "gMyGui.Visible = false;" or similar.

shaun9991

That worked a charm, thank you so much CW! :)
Support Cloak and Dagger Games on Patreon: https://www.patreon.com/user?u=460039

SMF spam blocked by CleanTalk