Adventure Game Studio

AGS Support => Advanced Technical Forum => Topic started by: hedgefield on Mon 14/03/2011 12:56:55

Title: rawdraw surface woes [SOLVED]
Post by: hedgefield on Mon 14/03/2011 12:56:55
Hey fellas,

I'm trying to make a visual timer countdown bar using rawdrawrectangle, but I'm running into a few issues.

Firstly, the code I have now (in the global script rep_ex):

int timer = 320;
int timergoal = 320;
int timerspeed = 2;
export timergoal, timerspeed;
function repeatedly_execute() {
 while (timergoal > timer) {
   DynamicSprite* sprite = DynamicSprite.Create(640, 480);
   DrawingSurface *surface = sprite.GetDrawingSurface();
   surface.DrawingColor = 15;
   surface.DrawRectangle(timer, 380, timergoal, 390);
   Wait(1);
   surface.Clear();
   surface.Release();
   sprite.Delete();
   timergoal -= timerspeed;
 }
}


It's structured like this:
- int timer is the left end of the bar in room coordinates
- int timergoal is the right end of the bar (the same by default)
- int timerspeed handles how fast it counts down (so essentially the actual timer part of this contraption)
Once an action updates timergoal to be higher than it's default (say 480), the while check kicks in which draws a white rectangle between timer and timergoal, which gradually becomes smaller as timergoal get closer to timer again.

Now my issues:
I began by rawdrawing the bar on the background, which yielded two problems:
- if I called surface.clear it would erase the entire background, not just the bar
- if I didn't use surface.clear, you would not be able to see the progress since the previous rectangle would never be removed.
So I tried to create a seperate blank drawing surface the size of the screen and draw on THAT (the code above), but now I cannot see the bar at all. The script executes fine but it never shows up.

This is my first real foray into using rawdraw stuff, so maybe I don't understand too well how this works, but what am I doing wrong?


Oh and one additional thing, I'm using Wait(1) now, but I'd like this thing to be non-blocking so I can do other stuff at the same time. That'd probably come down to timers, but I'm not sure where to put one here so it doesn't cause an infinite loop.
Title: Re: rawdraw surface woes
Post by: Gilbert on Mon 14/03/2011 13:00:34
You need to assign the (dynamic) sprite to either an object, a character, an overlay or even a GUI for it to appear. Have you done that yet?
Title: Re: rawdraw surface woes
Post by: Calin Leafshade on Mon 14/03/2011 13:01:09
You create a new sprite alright but you dont actually put it anywhere.

You need to create the sprite (and keep it in scope), draw on the sprite and then assign the sprite to a gui or something.
Title: Re: rawdraw surface woes
Post by: hedgefield on Mon 14/03/2011 13:11:48
Ha! Yes that works, thanks. I had a feeling it would be something inane like that :) I wrongfully assumed the newly created sprite would be on the screen, not in some sort of limbo waiting to be assigned.
Alright well now I only have to figure out the non-blockingness of it then.
Title: Re: rawdraw surface woes [halfway solved]
Post by: Calin Leafshade on Mon 14/03/2011 13:23:10
you're really halfway there with the non blocking.

Just treat rep_ex like a while loop that happens every game loop

like this:



int counter; // keep this in global scope
bool countdown;

function repeatedly_execute(){

if (countdown) counter --; // decrement the counter if the countdown is active.

//add your drawing code here.

}


problem solved.
Title: Re: rawdraw surface woes [halfway solved]
Post by: hedgefield on Mon 14/03/2011 14:58:48
I'm not exactly sure how that factors in, but in my current implementation attempt of it it does nothing (where it even froze the game up completely a tiny edit ago).

int timerleft = 320;
int timerright = 320;
int timergoal = 320;
int timerspeed = 2;
export timerleft, timerright, timergoal, timerspeed;

int counter = 2;
bool countdown = false;

function repeatedly_execute() {
 
  while ((timergoal > timerleft)&&(timergoal < timerright)) {
    if (countdown) counter--;
    DynamicSprite* sprite = DynamicSprite.Create(640, 480);
    DrawingSurface *surface = sprite.GetDrawingSurface();
    gCountdown.BackgroundGraphic = sprite.Graphic;
    surface.DrawingColor = 15;
    if (counter > 0) {
      surface.DrawRectangle(timerleft, 420, timergoal, 430);
      surface.DrawRectangle(timergoal, 420, timerright, 430);
      countdown = true;
    }
    else {
      countdown = false;
      surface.Clear();
      surface.Release();
      gCountdown.BackgroundGraphic = 0;
      sprite.Delete();
      timerleft += timerspeed;
      timerright -= timerspeed;
      counter = 2;
    }
  }
}

*I added a second bar btw so the timer shrinks down into the center from both sides now.

The main purpose of the Wait was to keep the bar visible until it clears off, otherwise it loops through the whole thing in one frame.

For completeness, this is what the trigger code is:

  timerleft=timergoal-160;
  timerright=timergoal+160;
Title: Re: rawdraw surface woes [halfway solved]
Post by: TomatoesInTheHead on Mon 14/03/2011 17:04:03
The repeatedly_execute function itself is like a non-blocking (infinite) while loop (already including a Wait(1)), so you can remove your while loop and turn it just into an if statement.
Title: Re: rawdraw surface woes [halfway solved]
Post by: hedgefield on Mon 14/03/2011 19:28:29
Well sure but the point is that it has to pause halfway through the script, not at the end, to give the bar enough time to render before I clear it again. Otherwise it goes from full to null in the blink of an eye.

And turning the while statement into an if statement - though it works - makes my cursor and description label flicker constantly while the bar redraws.
Title: Re: rawdraw surface woes [halfway solved]
Post by: Calin Leafshade on Mon 14/03/2011 19:47:37
no the point we're making is that you dont need to loop through. You dont need 'while' at all because rep_ex *is* a loop.

Just treat rep_ex as if it's a loop and dont include a wait() statement.

Title: Re: rawdraw surface woes [halfway solved]
Post by: Khris on Mon 14/03/2011 19:50:48
If you have a DrawingSurface that is cleared, then drawn to every game loop, the being clear part is what's going to be not noticeable, trust us.

You're still doing it wrong, having a while loop within rep_exe.

int timer;
int timerspeed = 2;

int counter;

DynamicSprite*sprite;

function repeatedly_execute() {
 
 if (timer > 0) {

   if (counter == 0) {

     timer--;

     // create sprite if necessary
     if (sprite == null) {
       sprite = DynamicSprite.Create(640, 480);
       gCountdown.BackgroundGraphic = sprite.Graphic;
     }

     // update sprite graphic
     DrawingSurface*surface = sprite.GetDrawingSurface();
     surface.Clear();
     surface.DrawingColor = 15;
     surface.DrawRectangle(320 - timer, 420, 320 + timer, 430);
     surface.Release();

     counter = timerspeed;
   }
   else counter--;
 }
 else {

   if (sprite != null) sprite.Delete();
 }
}


Not tested.

Just set timer to 160 to start it.

Edit: added check for sprite being != null.
Title: Re: rawdraw surface woes [halfway solved]
Post by: hedgefield on Mon 14/03/2011 20:30:40
Ahh you do it the other way around, clear first then draw. That's smart. Well I still can't wrap my head around it fully, but it works!

int timer;
int timerspeed = 2;
int countdown = 0;
int counter;
DynamicSprite*sprite;
export timer, timerspeed, countdown;

function repeatedly_execute() {
  if ((timer > 0)&&(countdown == 1)) {
    if (counter == 0) {
      timer -= timerspeed;
      sprite = DynamicSprite.Create(640, 480);
      gCountdown.BackgroundGraphic = sprite.Graphic;
      DrawingSurface*surface = sprite.GetDrawingSurface();
      surface.Clear();
      surface.DrawingColor = 15;
      surface.DrawRectangle(320 - timer, 420, 320 + timer, 430);
      surface.Release();
      counter = 1;
    }
    else counter--;
  }
  else if ((timer <= 0)&&(countdown == 1)) {
    countdown = 2;
  }
  if (countdown == 2) {
    cEgo.Say("Balls.");
    countdown = 0;
  }
}


Only thing I changed is the elsed sprite.Delete which gave me a null-referenced error so I did away with that whole conditional. And I changed timer--; to timer -= timerspeed; so that the timerspeed variable actually has an impact on the speed of the timer bar.
The added countdown int is to track when the timer actually finishes (not unimportant) so it can process what should happen next.

The trigger thus becomes:

  timer = 160;
  countdown = 1;
  timerspeed = 2; //optional



Thanks a million for the help guys! Abstract logic is not my strongest suit..
Title: Re: rawdraw surface woes [SOLVED]
Post by: Khris on Mon 14/03/2011 20:57:00
I did it like this:

timer is decremented every time counter is zero, and if that's the case, counter is reset to timerspeed.
Thus you should already have been able to adjust the speed by changing timerspeed with my code.

And instead of using an additional countdown variable, checking for timer being == 0 or not should do the trick.
Title: Re: rawdraw surface woes [SOLVED]
Post by: hedgefield on Mon 14/03/2011 21:59:33
Hm ok well maybe I did it wrong then but when I modified the timerspeed in your script I observed only a minimal difference and I had to actually reduce the value to get it to go faster. And from 2 down there's not a whole lot of digits left ;) Presumably this had to do with the fact that there the timerspeed variable is linked to counter (the delay between the rendering loop) and not to timer (the distance between the sides and the center of the rectangle).

And in fact I see you're right that I don't need the very last countdown == 2 bit, but I do need the double conditional above it, because once the timer runs out it won't idle, it will keep repeating the section under timer <= 0 until the next time it is called upon. That's why I'm keeping it contained with the countdown variable. It basically represents the scope of the timer - when the countdown is activated (1), it could either be counting down (timer > 0) or processing the action triggered when it has finished counting down (timer <= 0), and then shutting the countdown off again (0).

...
 else if ((timer <= 0)&&(countdown == 1)) {
   
   cEgo.Say("Balls.");
   countdown = 0;
 }


I could wrap both timer checks inside one countdown check too actually... but this works just as well.
Title: Re: rawdraw surface woes [SOLVED]
Post by: Khris on Mon 14/03/2011 23:18:21
Right, to trigger an event once after the countdown runs out, you need an additional variable.

Regarding the timing you're also correct; I didn't think about the actual speed too much. Another way to increase the speed would have been to change the timer--; line into timer -= 2; etc.

Ideally, the function is built in a way that you provide the amount of seconds and that's translated into pixels per loop (using a float of course).
Title: Re: rawdraw surface woes [SOLVED]
Post by: hedgefield on Mon 14/03/2011 23:27:16
True, that would be best-case scenario. I'll have to fiddle with it some more, use it in some real situations to see how it handles. I already found I had to add IsGamePaused as a third conditional since pausing the game only halts movement and animations. But at least for now it works great, thanks to your code :)