Scrolling sign function (and non-blocking Wait() query)

Started by AdamM, Fri 18/04/2008 01:03:59

Previous topic - Next topic

AdamM

Check me out, I've decided to upgrade to the 'Technical Forum'!

I've written my very first supercool proper function, called ScrollSign.

function ScrollSign(String message, int scrollfromx, int scollfromy);

Utilizing SSH's SpriteFont module with a font of my own invention named 'Scrolling', it produces text (message) that gradually originates at co-ordinates scrollfromx and scrollfromy and scrolls left for a pre-determined amount of pixels (136, that being the size of the scrolling signs I have drawn) before disappearing.

There are three sections enclosed by while() loops; the first one gradually scrolls the text until it is fully visible, the second moves the text along the sign, and the third one swallows it at the left edge.

(It still needs work; I don't think it will work with text longer than 136 pixels, it will overlap the sign etc.)

This is the first time I've done this sort of thing so I'm sure the scripting is very sloppy, feel free to suggest improvements.

I have a problem which I will elaborate on at the end of the script.

Code: ags
function ScrollSign(String message, int scrollfromx, int scollfromy) {

RawSaveScreen();
DynamicSprite* ScrollingSign=Scrolling.TextOnSprite(message);
int pixelsright;
int a;
int b;
pixelsright=ScrollingSign.Width-1;
a=(ScrollingSign.Width-pixelsright);
b=a;
int pixelsleft;
int xleft=scrollfromx;
int xcompare=(scrollfromx-136);
//136

while (pixelsright!=0) {
 DynamicSprite* ScrollingCrop=Scrolling.TextOnSprite(message);             //make a DynamicSprite of the message
 a=(ScrollingSign.Width-pixelsright);                 //work out how many pixels to remove from the right
 b=a;
 ScrollingCrop.Crop(0, 0, b, ScrollingCrop.Height);                //crop them
 RawDrawImage(xleft,scollfromy,ScrollingCrop.Graphic);      //draw it
 ScrollingCrop.Delete();
 Wait(2);                                           //keep it there for a bit
 RawRestoreScreen();                                        //erase it
 RawSaveScreen();
 xleft--;                                                           //move it left
 pixelsright--;                                                 //reveal another pixel's worth of message
 }

while (xleft!=xcompare) {                               //do the same thing but without revealing pixels
 DynamicSprite* ScrollingCrop=Scrolling.TextOnSprite(message);
 RawDrawImage(xleft,scollfromy,ScrollingCrop.Graphic);
 Wait(2);
 RawRestoreScreen();
 RawSaveScreen();
 xleft--;
 ScrollingCrop.Delete();
 }

int c;
int d;
c=ScrollingSign.Width;
d=c;

while (d!=0) {                            //do the same thing but with removing pixels from the left side
 DynamicSprite* ScrollingCrop=Scrolling.TextOnSprite(message);
 a=(ScrollingSign.Width-pixelsleft);
 b=a;
 ScrollingCrop.Crop(pixelsleft, 0, b, ScrollingCrop.Height);
 RawDrawImage(xleft,scollfromy,ScrollingCrop.Graphic);
 Wait(2);
 RawRestoreScreen();
 RawSaveScreen();
 pixelsleft++;
 d--;
 ScrollingCrop.Delete();
 }
}


The main problem I can't find a solution to is that due to my usage of Wait() in the while() loops, this is a blocking function, which was not my intention at all: I want it to run in the background. is there a non-blocking alternative I can use instead of Wait() to keep the action running? I tried a suggestion by SSH in another thread, which was to use an variable check like so:

Code: ags

int count;

while (pixelsright!=0) {
 DynamicSprite* ScrollingCrop=Scrolling.TextOnSprite(message);
 a=(ScrollingSign.Width-pixelsright);
 b=a;
 ScrollingCrop.Crop(0, 0, b, ScrollingCrop.Height);
 RawDrawImage(xleft,scollfromy,ScrollingCrop.Graphic);
 ScrollingCrop.Delete();

 count++;

  if (count==20) {                         // if message has stayed on screen for twenty loops

  RawRestoreScreen();                 // get rid of it
  RawSaveScreen();
  xleft--;
  pixelsright--;

  }

 }


But this just resulted in the game hanging for ages and then revealing that it had bled the message all over my sign somehow.

Is there a way to fix this?

SSH

To make stuff like this non-blocking, you need to set everything up in a function, then set some kind of variable. Then, you check for that variable in repeatedly_execute and if it is set, you do the stuff where you need the Waits.

Code: ags


int signScrolling;
int signTimer;
int pixelsright;
int a;
int b;
int pixelsleft;
int xleft;
int xcompare;

function ScrollSign(String message, int scrollfromx, int scollfromy) {

  RawSaveScreen();
  DynamicSprite* ScrollingSign=Scrolling.TextOnSprite(message);
  pixelsright=ScrollingSign.Width-1;
  a=(ScrollingSign.Width-pixelsright);
  b=a;
  xleft=scrollfromx;
  xcompare=(scrollfromx-136);
  signScrolling=2;
  signTimer=signScrolling;
}

function repeatedly_execute () {
  if (signScrolling) {
    signTimer--;
    if (signTimer==0) {
      while (pixelsright!=0) {
        RawRestoreScreen();                                        //erase it
        RawSaveScreen();
        DynamicSprite* ScrollingCrop=Scrolling.TextOnSprite(message);
        a=(ScrollingSign.Width-pixelsright); 
        b=a;
        ScrollingCrop.Crop(0, 0, b, ScrollingCrop.Height);                //crop them
        RawDrawImage(xleft,scollfromy,ScrollingCrop.Graphic);      //draw it
        ScrollingCrop.Delete();
        xleft--;                                                           //move it left
        pixelsright--;                                                 //reveal another pixel's worth of message
        signTimer=signScrolling;
      }


ETC.

However, wouldn't it be easier to put the sprite on an overlay or GUI and then just move the overlayor GUI around?
12

GarageGothic

This:

Code: ags
RawRestoreScreen();                                        //erase it
RawSaveScreen();


seems quite redundant (you're saving the exact same screen that you already have stored). Considering how slow an operation RawSaveScreen() is, it should generally be kept out of repeatedly_execute if at all possible. Overall, functions like these - including many of SSH's modules, but I know he's been updating them - would benefit greatly from the new DrawingSurface commands in AGS 3.0+.

AdamM

Quote from: SSH on Fri 18/04/2008 10:00:31
To make stuff like this non-blocking, you need to set everything up in a function, then set some kind of variable. Then, you check for that variable in repeatedly_execute and if it is set, you do the stuff where you need the Waits.

I tried your suggestion with the code you supplied, but I haven't even been able to compile yet: First I had to move repeatedly execute after ScrollSign, and declare a global String for the message that both functions can use; then I realised the other while() loops would be run simultaeneously, so I had to put in other variables like signTimer2, signTimer3; and even then I don't know how to get them to count down individually.

Code: ags
    
  if (signScrolling) {
    signTimer1--;
    signTimer2--;
    signTimer3--;

    if (signTimer1==0) {
      while (pixelsright!=0) {
       DynamicSprite* ScrollingCrop=Scrolling.TextOnSprite(message);
       a=(ScrollingSign.Width-pixelsright);
       b=a;
       ScrollingCrop.Crop(0, 0, b, ScrollingCrop.Height);
       RawDrawImage(xleft,scollfromy,ScrollingCrop.Graphic);
       ScrollingCrop.Delete();
       RawRestoreScreen();
       xleft--;
       pixelsright--;
       signTimer1=signScrolling;
      }


  if (pixelsright==0 && signTimer2==0) {
      signTimer1=-1;
      signTimer2=signScrolling;
      }
  
  if (signTimer2==0) {
      while (xleft!=xcompare) {
       DynamicSprite* ScrollingCrop=Scrolling.TextOnSprite(message);
       RawDrawImage(xleft,scollfromy,ScrollingCrop.Graphic);
       Wait(2);
       RawRestoreScreen();
       xleft--;
       ScrollingCrop.Delete();
       signTimer2=signScrolling;
      }


Or something like that, I don't know. That still wouldn't work. You missed this problem 'cause you cut the rest off with 'ETC.'

Quote
However, wouldn't it be easier to put the sprite on an overlay or GUI and then just move the overlayor GUI around?

Would it be easier? Would it require less scripting? I don't know how to do that.

Quote from: GarageGothic on Fri 18/04/2008 12:22:08
This:

Code: ags
RawRestoreScreen();                                        //erase it
RawSaveScreen();


seems quite redundant (you're saving the exact same screen that you already have stored).

I know, I put in the extra SaveScreen because it didn't seem to work otherwise (like, it seemed like it was restoring it once and then discarding it), but I took them out just now and it works fine, so whatever.

QuoteOverall, functions like these - including many of SSH's modules, but I know he's been updating them - would benefit greatly from the new DrawingSurface commands in AGS 3.0+.

I don't know how to do that.


SSH

Well, I don't have time to rewrite your whole script, I was  trying to give you the idea, hence the etc. etc.

Also, are you saying your stuff now works? It's not clear. What is wrong if it doesn't?
12

AdamM

Fair enough, it's just that the idea only works without the context of the rest of the script, there's a whole load of other stuff that breaks it.

Ok, this is now my mess of a function. It now uses about half of the global integers in my game. It should work but it doesn't (the game doesn't hang, but it doesn't show me any scrolling words either). This is fast becoming more trouble than it seems worth.

Code: ags


int signScrolling;
int signTimer1;
int signTimer2;
int signTimer3;
int pixelsright;
int a;
int b;
int pixelsleft;
int xleft;
int xcompare;
int c;
int d;
int scrollfromx2;
int scrollfromy2;

...

function ScrollSign(String message, int scrollfromx, int scrollfromy) {

...

signmessage=message;
scrollfromx2=scrollfromx;
scrollfromy2=scrollfromy;
signScrolling=2;
signTimer1=signScrolling;
signTimer2=-1;
signTimer3=-1;

...

}

...

function repeatedly_execute() {

  if (signScrolling) {
    if (signTimer1!=0 && signTimer1!=-1) {
        signTimer1--;
      }
    if (signTimer2!=0 && signTimer2!=-1) {
        signTimer2--;
      }
    if (signTimer3!=0 && signTimer3!=-1) {
        signTimer3--;
      }
    
    if (signTimer1==0) {
      while (pixelsright!=0) {
       DynamicSprite* ScrollingCrop=Scrolling.TextOnSprite(signmessage);
       a=(ScrollingCrop.Width-pixelsright);
       b=a;
       ScrollingCrop.Crop(0, 0, b, ScrollingCrop.Height);
       RawDrawImage(xleft,scrollfromy2,ScrollingCrop.Graphic);
       ScrollingCrop.Delete();
       RawRestoreScreen();
       xleft--;
       pixelsright--;
       signTimer1=signScrolling;
      }
    }
      if (pixelsright==0 && signTimer2==-1) {
      signTimer1=-1;
      signTimer2=signScrolling;
      pixelsright=-1;
      }
    
    if (signTimer2==0) {
      while (xleft!=xcompare) {
       DynamicSprite* ScrollingCrop=Scrolling.TextOnSprite(signmessage);
       RawDrawImage(xleft,scrollfromy2,ScrollingCrop.Graphic);
       RawRestoreScreen();
       xleft--;
       ScrollingCrop.Delete();
       signTimer2=signScrolling;
      }
    }
      if (xleft==xcompare && signTimer3==-1) {
      signTimer2=-2;
      signTimer3=signScrolling;
      xcompare=-2;
      }
    
    
    if (signTimer3==0) {
      while (d!=0) {
       DynamicSprite* ScrollingCrop=Scrolling.TextOnSprite(signmessage);
       a=(ScrollingCrop.Width-pixelsleft);
       b=a;
       ScrollingCrop.Crop(pixelsleft, 0, b, ScrollingCrop.Height);
       RawDrawImage(xleft,scrollfromy2,ScrollingCrop.Graphic);
       RawRestoreScreen();
       pixelsleft++;
       d--;
       ScrollingCrop.Delete();
       signTimer3=signScrolling;
      }
    }
  }

AdamM

I've suddenly realised the fundamental mistakes I've made in the above script and am working to correct them now.

AdamM

WHAT THE HELL IS GOING ON?

Code: ags

function repeatedly_execute () {

    if (signTimer1!=0 && signTimer1!=-1) {      //decrease signTimer1 until 0
        signTimer1--;
      }
    if (signTimer2!=0 && signTimer2!=-1) {
        signTimer2--;
      }
    if (signTimer3!=0 && signTimer3!=-1) {
        signTimer3--;
      }

    if (pixelsright!=0 && signTimer1!=1) {       //when signTimer1 is not 1
       DynamicSprite* ScrollingCrop=Scrolling.TextOnSprite(signmessage);
       a=(ScrollingCrop.Width-pixelsright);
       b=a;
       ScrollingCrop.Crop(0, 0, b, ScrollingCrop.Height);
       RawDrawImage(xleft,scrollfromy2,ScrollingCrop.Graphic);   //create, crop, draw the message repeatedly
       ScrollingCrop.Delete();
     }
     
     if (signTimer1==1) {                 //when signTimer1 counts down to one
       RawRestoreScreen();              //restore the screen to what you saved it as when you called ScrollSign
       xleft--;
       pixelsright--;
       signTimer1=signScrolling;
     }


That bloody bit at the end isn't restoring the bloody screen! The if conditional IS getting run; but for whatever reason it's not deleting what I've drawn onto it.

I HATE THIS.

Pumaman

When are you calling RawSaveScreen? Try using the debugger to put a breakpoint on the RawSaveScreen and RawRestoreScreen lines, and make sure they're being called in the correct order.

AdamM

Surely this isn't important enough for you to get involved, CJ. :)

I'm calling RawSaveScreen in the function ScrollSign, which is in the global script and called from the room script. Like SSH said, it sets up all the initial variables and then passes everything to repeatedly_execute. RawSaveScreen is only called once in this entire process, at the very beginning of ScrollSign, before I've drawn anything at all onto the background. RawRestoreScreen is called multiple times in repeatedly_execute afterwards.

I'm not sure what you mean by breakpoints, but I keep sprinkling Displays everywhere to make sure I know what's being called, and everything definitely seems to be working as it should. I've even commented out the rest of repeatedly_execute. AGS just seems to be ignoring the RawRestoreScreen (although it obviously isn't really and it must be due to some stupid mistake I've made in the scripting that once solved I will have embarassed myself once again), because when I run the function the words just bleed across my sign.

VK Dan

Regarding breakpoints, take a look at the Debugging page in the manual. They'll let you step through your code one line at a time. Very helpful for debugging.

AdamM

It took me five minutes of pressing F9 and F11 in confusion in the script editor before I realised with a sinking feeling you were probably talking about 3.0. I use 2.72. I hate being forced to upgrade...

subspark


Gilbert

Subspark, please think before you post, if you don't have something worthy to post just don't post.

Thank you.

AdamM

On the contrary I think subspark's mocking of my choice to not upgrade my software the millisecond a new version is released was a valuable contribution and I invite him to make similar comments in future preferably with his face within striking range of my fist.

ANYWAY. I have finally upgraded my function with DrawingSurface commands (the only bits of my game 3.0 didn't convert automatically, natch). I have declared 'scrollsurface' to draw on and 'scrollbackup' to restore the screen at the top of my global script. This is the relevant two-thirds in repeatedly_execute:

Code: ags

  if (signScrolling) {

//Count down the timers
    if (signTimer1!=0 && signTimer1!=-1) {
        signTimer1--;
      }
    if (signTimer2!=0 && signTimer2!=-1 && signTimer2!=-2) {
        signTimer2--;
      }
    if (signTimer3!=0 && signTimer3!=-1) {
        signTimer3--;
      }


//Repeatedly draw the sign
    if (pixelsright!=0 && signTimer1!=0) {
       scrollsurface = Room.GetDrawingSurfaceForBackground();
       DynamicSprite* ScrollingCrop=Scrolling.TextOnSprite(signmessage);
       a=(ScrollingCrop.Width-pixelsright);
       b=a;
       ScrollingCrop.Crop(0, 0, b, ScrollingCrop.Height);
       scrollsurface.DrawImage(xleft,scrollfromy2,ScrollingCrop.Graphic);
       ScrollingCrop.Delete();
       scrollsurface.Release();
       signTimer1--;
     }


//Restore the screen when timer runs out     
     if (signTimer1==0) {
       scrollsurface = Room.GetDrawingSurfaceForBackground();
       scrollsurface.DrawSurface(scrollbackup);
       scrollsurface.Release();
       xleft--;
       pixelsright--;
       signTimer1=signScrolling;
     }

//Transition to the next stage
     if (pixelsright==0 && signTimer1!=-1) {
       signTimer1=-1;
       signTimer2=signScrolling;
     }

//Repeatedly draw the sign
    if (signTimer2!=-2 && xleft!=xcompare) {
       scrollsurface = Room.GetDrawingSurfaceForBackground();
       DynamicSprite* ScrollingCrop=Scrolling.TextOnSprite(signmessage);
       scrollsurface.DrawImage(xleft,scrollfromy2,ScrollingCrop.Graphic);
       ScrollingCrop.Delete();
       scrollsurface.Release();
    }
//Restore the screen when timer runs out  
     if (signTimer2==0) {
       scrollsurface = Room.GetDrawingSurfaceForBackground();
       scrollsurface.DrawSurface(scrollbackup);
       scrollsurface.Release();
       xleft--;
       signTimer2=signScrolling;
       }


Frustratingly, it works just like I want it to, with a small snag, in that it produces a flickering effect. This is probably due to the DrawingSurface commands taking up processing and memory (and CJ will probably be in here in a minute to confirm that is the case). Strangely, the second stage of the function - see the comments in the script - flickers less rapidly than the first. Eagle-eyed viewers will note that the only difference between the first and second stages is that there is no cropping going on in the second stage. Why this would make the flickering effect slower I cannot fathom.

While flickering is not unrealistic for an electronic display, I'd like it to work how I originally visualised it, and also get to the bottom of this maddening problem anyway. So any suggestions?

subspark

Loosen up guys. I was actually just sighing in a light hearted manner at your comment about being forced to upgrade. I thought it was rather funny. Not that any of us are really forced to do anything around here.  :)
Why would I mock sombody for not choosing to upgrade to the minite release of AGS?

Cheers,
Paul.

Pumaman

Quote from: AdamM on Mon 21/04/2008 05:24:19
Frustratingly, it works just like I want it to, with a small snag, in that it produces a flickering effect.

What exactly is flickering? Do you keep seeing the background through the drawn on images? If so you've probably got some dodgy logic that's restoring the screen when it shouldn't be. Try using the breakpoints feature of 3.0 to run through the game and check which of your drawing commands are being called in which order.

AdamM

Yes, I see it as you described, CJ. However stepping through the script with the debugger is showing that everything is getting called when it should be. The speed (int signScrolling) is set to 5, so the text is getting drawn repeatedly (yet to the viewer appears static) four times, and then once the timer hits 0, the screen is restored once, and the timer is reset. While stepping through the script I noticed these DrawingSurface functions don't update the screen until the very end of the global script. Logically this would mean the viewer would see words on the sign 4/5 of the time and a blank sign 1/5 of the time, but that was the case with the old functions RawDrawImage and RawRestoreScreen in the past and they didn't cause any flickering. Did the old functions update instantly, and if so, is it the reason for this?

I noticed what was causing the speed discrepancy (an extra signTimer1--, as you can see above, making the timer tick down twice as fast), so that's been eliminated, but the flickering remains...

Pumaman

So what is it that you're seeing flickering? Having the words there 4/5 of the time and not the other 1/5 would seem to create a flickering effect -- I guess I just don't really understand what you're wanting to achieve and what's actually happening.

Perhaps you could upload a demo to demonstrate?

AdamM

Very well.

http://www.2shared.com/file/3187192/42f53535/scrolldemo.html

Download link on that site is hidden in the bottom right corner, by the way.

Just run the compiled exe to experience the flickering. Press the left mouse button to run a Display() command and block the script, thus pausing the scroll. Depending on when you block, the sign will either have completely opaque words or be blank. Hopefully you'll understand what I mean by flickering now - what I want to achieve is to lose it.

SMF spam blocked by CleanTalk