PDA

View Full Version : Snow!



pkmnfrk
03-11-2009, 02:24 AM
To celebrate the fact that Winter is nearly done, I present the dynamic snow script!


const int screenHeight = 176;
const int screenWidth = 256;

ffc script Snow {
void run(int flakes, int wind) {

//I do not know how big this can be safely increased.
//However, 200 flakes should be fine for most situations
int snow[200];
flakes = Floor(flakes);
wind = Floor(wind);

if(flakes > 200) flakes = 200;

//if the wind isn't very strong, let's thin out the snowfall
//a bit. It looks much better this way.
if(Abs(wind) < 4) {
flakes -= 45 - Abs(wind) * 15;
}

//Calculating the size and position of a paralellogram of
//snow hurt my brain :(
int xmod = 0;

//And, it took me an hour to determine this block
if(wind > 0) {
xmod = wind * -160;
} else {
xmod = 0;
}

//Give each flake an initial position on screen, as
//though it's been snowing the whole time ;)
for(int i = 0; i < flakes; i++) {
snow[i] = Rand(screenWidth + Abs(wind) * 160) + xmod + 3000 + (Rand(screenHeight - 1) + 1) / 1000;
}

int x; int y;
//main loop
while(true) {

//for each snow flake...
for(int i = 0; i < flakes; i++) {
//I pack the snow flake like: x . y
x = Floor(snow[i]);
y = (snow[i] - x) * 1000;

//the 3000 is simply to prevent negative values in the snow[] array
//3000 is insufficient for wind strengths > 18
//(but, that's already stronger than any wind on earth!)
x -= 3000;

//move the snow
y += 1;
x += Rand(3) - 1;
x += wind;

//if the snow goes off the bottom, remove it and regenerate it
//also, if there's wind, clip the snow that blows off the side
if(y > screenHeight || (wind > 0 && x > screenWidth + 5) || (wind < 0 && x < -5)) {
x = Rand(screenWidth + Abs(wind) * 160) + xmod;

y = 1;
}

//draw the on-screen flakes
if(x >= 0 && x < screenWidth) Screen->Circle(6, x, y, 1, 1, 1, 0, 0, 0, true, 128); //Screen->PutPixel(6, x, y, 1, 0, 0, 0, 128);

//pack the snow again :P
snow[i] = x + 3000 + y / 1000;

}

Waitframe();
}
}
}

It's really easy: Just set the script to an invisible FFC, set D0 to the number of desired snowflakes on screen at once (current max is 200, but that's easily raised if necessary), and even add a bit of wind by setting D1 (max is +/-18, but the snow is almost horizontal at that speed!).

Let me know what you think!

ShadowTiger
03-11-2009, 08:31 AM
I got three semi-identical errors when compiling the script, all related to the function "Floor" ... Line 10, 11, and 45. Error S10: Function Floor is undeclared.


Still, thank you for the script. :)

Joe123
03-11-2009, 11:40 AM
You need to
import "std.zh" ST =)

pkmnfrk
03-11-2009, 01:27 PM
Oh come on. Surely that's implied by now...

blue_knight
03-11-2009, 01:37 PM
The script looks interesting, it seems you can extend some of the ideas to make a full parametrized 2D particle system. Anyway I do have a question: why are you using Screen->Circle() instead of Screen->PutPixel()? It looks like you used the latter initially but changed your mind. At first I thought you wanted snow flakes larger then 1 pixel - but it looks like your circle has a radius of 1... I haven't actually used PutPixel or Circle so I could just be missing something :)

pkmnfrk
03-11-2009, 02:05 PM
It looks much better with the radius 1 circle.

And, incidentally, a circle with a radius of 1 actually has a width of 2 (since, radius is the distance from the centre to the edge ;))

And, also, it seems that a radius 1 circle is actually drawn like this:


#
###
#

Finally, perhaps you could make a full particle system, but I doubt it. As it currently stands, there's no room to store any other information about a particle other than its position. In my Starfield script (http://www.armageddongames.net/forums/showpost.php?p=1209296&postcount=2), I used the same idea, but represented position in polar notation (since, the stars need to travel in a circle).

In both of these scripts, I don't have to store speed or direction (since, snow always falls down, and stars always shoot outward).

Plus, a more complicated system would be rather slow for anything more than a handful of particles.

lucas92
03-11-2009, 03:59 PM
Nice script. :)

But wouldn't it be simple to just draw falling snow combos on layer 5 instead?

pkmnfrk
03-11-2009, 04:51 PM
If you count finding and setting up a separate screen, and then setting it as layer 5, and oops, it's the wrong Palette/CSet as easier than plopping down an FFC, then yes.

However, try getting dynamic looking snow out of a combo...

Gleeok
03-12-2009, 01:08 AM
Cool. Hands down, the best snow particle ZScript in existence. Don't question it's usefulness or compare it to layers people, just try it.



It looks much better with the radius 1 circle.

And, incidentally, a circle with a radius of 1 actually has a width of 2 (since, radius is the distance from the centre to the edge ;))

Due note that circles seem to be pretty damn slow in allegro. I'd bet drawing those five pixels using putpixel would be a whole lot faster, but then you wouldn't have the option of drawing different sized circles.



Finally, perhaps you could make a full particle system, but I doubt it. As it currently stands, there's no room to store any other information about a particle other than its position.


You might like to know that Grikarugun has had a dynamic particle system in it for a little while now. :) (I've just been busy guys, sorry)



Plus, a more complicated system would be rather slow for anything more than a handful of particles.

Actually I'm reasonably sure it's faster. :tongue:

pkmnfrk
03-12-2009, 01:23 AM
Due note that circles seem to be pretty damn slow in allegro. I'd bet drawing those five pixels using putpixel would be a whole lot faster, but then you wouldn't have the option of drawing different sized circles.

Are you sure about that? I would think that the overhead of calling Screen->PutPixel() 5 times would offset any gain from not drawing a miniature circle.

Remind me to profile this at some point.



You might like to know that Grikarugun has had a dynamic particle system in it for a little while now. :) (I've just been busy guys, sorry)

I'll have to check it out. Are the scripts available?


Actually I'm reasonably sure it's faster. :tongue:

Perhaps, perhaps not. But, I would bet a million rupees that a general purpose particle system would be slower compared to one tailor made for, say, snow.

Edit, RE PutPixel vs Circle:

I just did some testing. Although I got slow down for flake quantities > 500 (or so), I discovered something else in favour of using Circle. Although I have absolutely no idea how the primitive drawing system works in ZC, I can make a guess based on this experiment.

Essentially, I built in a way to switch between 5 PutPixels and 1 Circle. At 500 flakes (thats 500 circles vs 2500 PutPixels), half the PutPixel flakes didn't show up! I would switch between the two, and watch half the snow flakes blink in and out of existence. I am of the opinion that I used up all the Primitive drawing command buffer (or something to that effect).

So. I'm sticking with Screen->Circle for now.

Gleeok
03-12-2009, 02:01 AM
Edit, RE PutPixel vs Circle:

I just did some testing. Although I got slow down for flake quantities > 500 (or so), I discovered something else in favour of using Circle. Although I have absolutely no idea how the primitive drawing system works in ZC, I can make a guess based on this experiment.

Essentially, I built in a way to switch between 5 PutPixels and 1 Circle. At 500 flakes (thats 500 circles vs 2500 PutPixels), half the PutPixel flakes didn't show up! I would switch between the two, and watch half the snow flakes blink in and out of existence. I am of the opinion that I used up all the Primitive drawing command buffer (or something to that effect).

So. I'm sticking with Screen->Circle for now.

Hmm, that's an interesting one....


Wow, there's a bug or something in there. I couldn't even profile putpixel because of it. This is what I used:



const int LOOPS = 1;

global script slot2
{
void run()
{
while(true)
{
for(int i = 0; i < LOOPS ; i++)
for(int x = 0; x < 256; x++)
for(int y = 0; y < 176; y++)
Screen->PutPixel(6, x , y, 2, 0, 0, 0, 64);
Waitframe();
}
}
}


It completely crippled the engine. I wonder why?

pkmnfrk
03-12-2009, 02:10 AM
Like I alluded to, I believe that the engine does not simply draw to the given layer when you issue a command. I think it stores the command somewhere, and then executes it when it then draws the layer.

That way, drawing to layer 2 after drawing to layer 6 works "properly".

Obviously, it can only store so many commands before it pukes.

The other thing is that ZScript is orders of magnitude slower than, say, running Allegro's code directly. The overhead of drawing individual pixels way outweighs the actual drawing time. We would get similar results drawing 256x178 rectangles.

So... In other words, draw as little as possible.

Edit: The way I would solve to "drawing to layers in the wrong order" problem would be to have 6 canvasses, onto which I draw things. Then, just before showing the frame, I would blit them all together onto the back buffer.

I don't know if this would be slower, though. It would definitely use more memory...

Gleeok
03-12-2009, 02:23 AM
You may be right but I remember around build 400 or so testing drawing functions by the thousands. I posted a bug thread in case there is something bottle necking the pixel rendering. Shoot first and sort out the details later is what pappy used to say. :P

For the meantime I'd continue to use circles and lines. lol.

ShadowTiger
03-13-2009, 02:12 PM
You need to
import "std.zh" ST =)
Oh come on. Surely that's implied by now...Pretty much the reason for me asking and wondering, is that it wasn't implied, as if I even have to wonder why it was left out, then it's not obvious. Wouldn't it be better to include the line at the beginning of the script so this sort of conversation wouldn't happen? (lol you're dealing with an idiot here.. *sigh* ) It'd be easier just for the skilled scripter to remove the line as necessary than to leave it out and have people tripping over it because they thought the person who write the script would have included everything necessary in the first place. Non-scripters wouldn't know any better, and isn't the point of writing the scripts and showcasing them that nonscripters could just pick them up and plug them in?


Otherwise, snow looks very cool. :) I don't suppose we can change the value of D1 on the fly, mid-game?

pkmnfrk
03-13-2009, 02:42 PM
Just set different FFC's to use different values. So, you can't change it one one screen, but different screens can have different values.

Joe123
03-13-2009, 03:15 PM
Seeing as all of your scripts have to be in the same file, it's not a good idea to include it at the top of every script incase it creeps into someone's file twice.

Then you're declaring all the variables in std.zh twice and obviously at that point it won't compile.

pkmnfrk
03-14-2009, 01:31 AM
As an old coworker used to say, "It's six of one, and a half-dozen of the other".

If I include it, someone's going to say "I get an error: Redefinition of LW_BOMB"
If I don't, someone's going to say "I get an error: Variable not declared: LW_BOMB"

I think that std.zh should have #ifdef guards, as in C :P


#ifndef STD_ZH
#define STD_ZH

//...

#endif

(that's a joke)

(partially)

Joe123
03-14-2009, 07:04 AM
It could be defined in the ~init script maybe

ShadowTiger
03-14-2009, 09:07 AM
I'd do it up. :blah: