Dissolving sprites (and other fun)

Started by Scavenger, Thu 26/08/2010 04:41:48

Previous topic - Next topic

Scavenger


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.)

Code: ags

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.

Dualnames

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.
Worked on Strangeland, Primordia, Hob's Barrow, The Cat Lady, Mage's Initiation, Until I Have You, Downfall, Hunie Pop, and every game in the Wadjet Eye Games catalogue (porting)

tzachs

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:

Code: ags

char speed = 30;
int totalPixels = sprite.GetTotalPixels();
int pixelsChanged = 0;
while (pixelsChanged < totalPixels)
{
   pixelsChanged += sprite.Dissolve(speed);
   Wait(1);
   object[0].Graphic = sprite.Graphic;
}

Calin Leafshade

I recommend using the AGSBlend plugin, then you can fade out each pixel with the PutAlpha function :D

GarageGothic

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 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.

Calin Leafshade

#5
How about this idea.

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

Code: ags

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:

Code: ags

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.

Monsieur OUXX

Quote from: GarageGothic on Thu 26/08/2010 13:20:48
A different approach could be to use a shuffle algorithm to make the dissolve speed consistent.

+1.
 

GarageGothic

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
Code: ags

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:

Code: ags
int ran = i + Random(200-i);

Calin Leafshade

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.

abstauber


SMF spam blocked by CleanTalk