Adventure Game Studio

AGS Support => Advanced Technical Forum => Topic started by: Scavenger on Thu 26/08/2010 04:41:48

Title: Dissolving sprites (and other fun)
Post by: Scavenger on Thu 26/08/2010 04:41:48

So I'm trying to create some graphic effects for my game that are reminiscient of the stuff you'd find on the Super NES, and one particular one I thought I'd cut my teeth on was dissolving a sprite into nothingness.

Turns out it's harder than it looks in AGS! With the following code, I can make things dissolve at an increasingly slow rate (depending on how many pixels have already been decimated), slowing down to infinity as my "Is it Done" checker slugs along. Without the checker, the code does pretty much function, but leaves behind one or two errant pixels that annoy me and make it so that I can't tell whether or not an object needs to be removed or not.

How do I speed up this code? How would you run a check on a sprite to see whether it's fully dissolved? Am I doing this completely wrong? Is there a way to do this in reverse? (I wanted to do this in code rather than with sprites because I'd be doing this on several sprites of varying size rather than just a select few.)


function Dissolve (this DynamicSprite*,  char speed)
{
  DrawingSurface *surface = this.GetDrawingSurface();
  char i=speed;
  int x;
  int y;
  int errorchecking;
  int area = surface.Width * surface.Height;
  surface.DrawingColor = 0;
  while (i)
  {
    x = Random(surface.Width);
    y = Random(surface.Height);
    if (surface.GetPixel (x, y) != 0)
    {
      surface.DrawPixel (x, y);
      i--;
    }
    else errorchecking++;
    if (errorchecking > area)
    {
      surface.Release ();
      return 1;
    }
  }
  surface.Release ();
}

function CheckDissolveComplete (this DynamicSprite*)
{
  DrawingSurface *surface = this.GetDrawingSurface();
  int x1 = surface.Width;
  int y1 = surface.Height;
  while (y1 != -1)
  {
      if (y1 == -1)
      {
        // You should never get here. This is in case whiles run one last loop when their condition is met

        surface.Release ();
        return 1;
      }
      if (surface.GetPixel (x1,y1) != 0)
      {
        surface.Release ();
        return 0;
      }
      else x1--;
      if (x1 == -1)
      {
        x1 = surface.Width;
        y1--;
      }
  }
  surface.Release ();
  return 1;
  }

//The Room code I used (object 0 is needed, uses default game)

function room_AfterFadeIn()
{
DynamicSprite *sprite = DynamicSprite.CreateFromExistingSprite(object[0].Graphic);
Display ("This is a test of the Dissolve function.");
char speed = 30;
while (sprite.CheckDissolveComplete () == 0)
{
  sprite.Dissolve (speed);
  Wait (1);
  object[0].Graphic = sprite.Graphic;
}

Display ("This has been a broadcast of the Dissolve Function.");
}


Any help would be appreciated. This kind of thing really stumps me.
Title: Re: Dissolving sprites (and other fun)
Post by: Dualnames on Thu 26/08/2010 06:15:41
Khris wrote a module for dissolving sprites which you can find here: http://www.adventuregamestudio.co.uk/yabb/index.php?topic=36031.msg472391#msg472391
Perhaps it can help you see a trick or two.
Title: Re: Dissolving sprites (and other fun)
Post by: tzachs on Thu 26/08/2010 10:09:32
There are a few optimizations I can think of.
The simplest one, would probably be this:

Have a function to run before starting the first dissolve to cound the number of pixels which are not in color 0.
Then have your Dissolve function return the number of pixels it changed.
And then you can replace the CheckDissolveComplete with a simple counter check.
The "After fadein" function will look something like:


char speed = 30;
int totalPixels = sprite.GetTotalPixels();
int pixelsChanged = 0;
while (pixelsChanged < totalPixels)
{
   pixelsChanged += sprite.Dissolve(speed);
   Wait(1);
   object[0].Graphic = sprite.Graphic;
}
Title: Re: Dissolving sprites (and other fun)
Post by: Calin Leafshade on Thu 26/08/2010 10:19:46
I recommend using the AGSBlend plugin, then you can fade out each pixel with the PutAlpha function :D
Title: Re: Dissolving sprites (and other fun)
Post by: GarageGothic on Thu 26/08/2010 13:20:48
Personally I really don't like using brute-force Randoms (at least not for any activity that runs in rep_exec), it's way too unpredictable.  A different approach could be to use a shuffle algorithm (http://en.wikipedia.org/wiki/Fisher-Yates_shuffle) to make the dissolve speed consistent.

Basically, create a dynamic array the size of your sprite (height*width). Then start from the top left corner and use the shuffle algorithm to switch array number of pixel 0 with a random pixel - delete the random pixel as you do so, proceed to next pixel, lather, rinse, repeat, by the end of it all pixels should be dissolved but in a random order.
Since you're running through the pixels in order, you don't even have to fill the array beforehand since the number of the pixel in the current place is either identical to its id in the array or has been switched and thus contains the value of an earlier pixel to delete.
Title: Re: Dissolving sprites (and other fun)
Post by: Calin Leafshade on Thu 26/08/2010 13:39:06
How about this idea.

Make a struct array which is the size of the number of pixels


struct pixel{
   int x;
   int y;
}

pixel pixels[200];

//populate the array in order.

pixels[0].x = 0;
pixels[0].y = 0;

pixels[1].x = 1;
pixels[1].y = 0;

//etc you'd obvious do this algorithmically not manually.

//then you can randomly shuffle the array

while (i < 2000){
pixel temp;
int r1 = Random(200);
temp = pixels[r1];
int r2 = Random(200);
pixels[r1] = pixels[r2];
pixels[r2] = temp;
i ++;
}

//now the array is shuffled you an just iterate through it, guaranteeing that all the pixels are got in a nice predictable time-scale.


EDIT: actually a better way of suffling the array would be to iterate through it instead of brute force randoming.

like this:


while (i < 200){
pixel temp;
temp = pixels[i];
int ran = Random(200);
pixels[i] = pixels[ran];
pixels[ran] = temp;
i ++;
}


you could also put a flag in the struct which says if the pixel has already been shuffled once.. if it has then it doesnt need to be shuffled again and can be skipped.

Although i'm not sure if the condition check is actually most costly than just moving it again anyway.
Title: Re: Dissolving sprites (and other fun)
Post by: Monsieur OUXX on Thu 26/08/2010 14:07:04
Quote from: GarageGothic on Thu 26/08/2010 13:20:48
A different approach could be to use a shuffle algorithm (http://en.wikipedia.org/wiki/Fisher-Yates_shuffle) to make the dissolve speed consistent.

+1.
Title: Re: Dissolving sprites (and other fun)
Post by: GarageGothic on Thu 26/08/2010 15:17:04
Quote from: Calin Leafshade on Thu 26/08/2010 13:39:06EDIT: actually a better way of suffling the array would be to iterate through it instead of brute force randoming.

Indeed, that was what I was trying to suggest. Also, though I'm not sure if it came across, part of my reason for proposing it was that it wouldn't require you to initialize the array, you could simply proceed the number of pixels you want to dissolve each frame and then pick up from there following game loop.

Quote
while (i < 200){
pixel temp;
temp = pixels[i];
int ran = Random(200);
pixels[i] = pixels[ran];
pixels[ran] = temp;
i ++;
}

Maybe I'm misreading your code, but should the random seed decrease every iteration? Like so:

int ran = i + Random(200-i);
Title: Re: Dissolving sprites (and other fun)
Post by: Calin Leafshade on Thu 26/08/2010 15:25:21
well yea i suppose it could do although swapping a pixel with a pixel that has already been swapped is not really much of an issue.. providing all the pixels have been swapped by the end of the iteration it doesnt really matter.

I understand that it could technically mean that a pixel could be swapped back into its original place but that chances of that are very very slim and even if it did happen you wouldnt really notice a few pixels in their original place in the sequence anyway.

also the random function should be Random(199) in this example.
Title: Re: Dissolving sprites (and other fun)
Post by: abstauber on Fri 27/08/2010 11:16:14
Here's my dirty hack of Khris' module:
http://shatten.sonores.de/wp-content/uploads/2010/08/dissolveMod.zip


Enjoy :)