Falling snow

To code along with this project, open the Snowflakes project. The project contains a snowflake object with both Scale and Rotate transforms. The MainPage.xaml file contains the LayoutRoot Canvas with a black/gray gradient fill, and a storyboard timer called Snowfall. This project has a lot going on, so let's get started!

When run, the completed project will create a number of snowflakes that are positioned randomly about the application. Each flake will be scaled randomly, have a randomly generated y velocity, and a random transparency. The snowflakes will fall slowly, drifting back and forth. When a snowflake reaches the bottom of the application, it will be placed back at the top, in order to keep a continuous snowfall going.

1. Start by coding up the Snowflake.xaml.es file. We'll start off with a variable to maintain the flake's position. Next are four variables related to the drifting motion of the flake. These should look similar to our previous examples: Angle, Range, Position (or point of origin), and Speed. Next comes a variable for the speed at which a flake will fall. Finally, we have a public variable used to hold the height of the main application, as well as a variable that will be used to generate random numbers.

private Point Position;

private double DriftAngle; private double DriftRange; private double DriftPosition; private double DriftSpeed = .1;

private double Speed;

public double AppHeight;

private Random Rand = new RandomQ;

2. This time, we're going to modify the Snowflake() constructor in order to pass variables as each flake is initialized by the main application. Normally, the code looks like this:

public SnowflakeQ {

InitializeComponent();

We want to change ours to accept three double values. Our main application will generate random numbers for the position and opacity and pass them to the Snowflake object as it is instantiated.

public Snowflake(double Left, double Top, double Opacity) {

InitializeComponent();

The Snowflake object will also do some self-configuration. The code shown in the following few steps is placed after the InitializeComponentQ method.

3. The first line of the following code generates a random number between 0 and 5 for the flake. If the number generated is less than 1, then it is made 1. This will ensure that all of the flakes in the application actually fall.

4. Next, the point of origin for the flake's drift is assigned using the left position passed when the flake was instantiated. In addition, a range of up to 50 pixels is generated, and a random starting angle for the drift is selected. The random angle is important—this adds some variance to where in the drift motion each flake starts. Without it, all of the snowflakes would drift in unison, which would not look very natural.

DriftPosition = Left; DriftRange = Rand.Next(50); DriftAngle = Rand.Next(270);

5. Next, a scale value between .25 and 1 is generated for the flake. Once the scale value has been generated, it is assigned to both the x and y scale values for the flake in order to keep the scaling symmetrical.

ScaleFlake.ScaleX = ScaleFlake.ScaleY =

6. The last bit of code in the Snowflake() constructor positions the flake, sets the opacity, and stores the position for when the flake is moving:

Canvas.SetLeft(this, Left); Canvas.SetTop(thiSj Top); this.Opacity = Opacity; Position.X = Left; Position.Y = Top;

7. Now, we need to add a function that is called from the main code in order to move each flake when needed. This is a public method called MoveFlake() that is placed after the SnowflakeQ constructor. The function begins by updating the x and y Position variables for the snowflake. The Y variable simply moves the flake down the screen. The X variable calculates the horizontal oscillation value that causes the snowflake to drift. Following this is a test to see if the flake has moved below the bottom of the application. If so, the flake is moved to the top of the application. Finally, the actual position of the flake is updated, and the angle used to determine the drift oscillation is incremented. This is a fairly simple bit of code that does a whole lot of work for us.

public void MoveFlake() {

Position.Y += Speed;

Position.X = DriftPosition + Math.Cos(DriftAngle) * DriftRange;

Position.Y = -this.Height;

Canvas.SetLeft(thiSj Position.X); Canvas.SetTop(thiSj Position.Y);

DriftAngle += DriftSpeed;

8. Now that the snowflake code-behind is all set, move to the MainPage.xaml.es file. This is where we'll go about generating our snowflakes and getting them moving.

9. Before the MainPage() constructor, start by declaring three Lists. In C#, a List is similar to an Array, only it provides some methods that save time and code later (as you will see). The first List will contain all of our snowflake objects. The second List will be used to hold the starting x and y positions of each snowflake, and the third will hold the opacity values generated for each flake:

private List<Snowflake> Flake; private List<Point> XYStart; private List<double> OpacityValue;

10. After the Lists are declared, declare an integer to give us control over how many flakes will be on the screen, and a random number generator for use in initializing the flakes:

private int MaxFlakes = 250; private Random Rand = new Random();

11. With the variable declarations in place, move to the MainPage() constructor. The following code comes after the InitializeComponentQ method. We'll start by initializing our Lists according to the length specified by the MaxFlakes variable:

Flake = new List<Snowflake>(MaxFlakes); XYStart = new List<Point>(MaxFlakes); OpacityValue = new List<double>(MaxFlakes);

12. Next, add a loop to generate the necessary number of x and y starting positions and opacity values:

Point newPoint = new Point(Rand.Next((int)LayoutRoot.Width),

Rand.Next((int)LayoutRoot.Height)); XYStart.Add(newPoint); OpacityValue.Add(Rand.NextDoubleQ);

13. With that in place, we'll call the InitFlakes() function (which we'll code up momentarily), add an event handler for the Completed event on the Snowfall storyboard, and start the storyboard:

InitFlakesQ;

Snowfall.Completed += new EventHandler(Snowfall_Completed); Snowfall.Begin();

14. The InitFlakes() function referenced previously is shown in the following listing. Place this code after the MainPage() constructor. The sole purpose of this function is to create snow-flakes based on the values that were just generated for starting positions and opacity values. This is done by once again running a quick loop.

The code instantiates a new flake using the corresponding x and y starting positions and opacity. The newly instantiated flake is then added to the List of flakes. The flake instance has its public appHeight variable assigned in order to track the application height, and is then added to the LayoutRoot Canvas:

private void InitFlakesQ {

Snowflake flake = new Snowflake(XYStart[i].X,

XYStart[i].Yj OpacityValue[i]);

Flake.Add(flake);

flake.AppHeight = LayoutRoot.Height; LayoutRoot.Children.Add(flake);

15. The last bit of code to add before compiling and running the application is the event handler code for the Completed event on the Snowfall storyboard. This code is very straightforward— for every flake in the Flake List, the MoveFlake() method is called. All of the flakes will have their on-screen positions updated, and then the Snowfall timer will be restarted.

private void Snowfall_Completed(object sender, EventArgs e) {

foreach (Snowflake flake in Flake) {

flake.MoveFlake();

Snowfall.BeginQ;

When the application runs, the snowflake object is instantiated 250 times randomly around the screen. Each flake is randomly scaled and drifts randomly, as shown in Figure 6-25.

Snowfall Animation
Figure 6-25. The application creates flakes that drift as they fall.

We can add a little polish to the application by making each flake spin as it falls. This can be accomplished with just three lines of code inside the Snowflake.xaml.es file.

16. Add a variable declaration to hold the spin value for each flake: private double Spin;

17. Inside the SnowflakeQ constructor, add the following line of code that generates a random spin speed between 0 and 5. It's OK if some of the flakes do not spin as they fall.

18. The final line of code goes into the public MoveFlake() method, and simply increments the rotational angle of the flake by the generated Spin value:

RotateFlake.Angle += Spin;

Now, the flakes will spin a bit as they are falling, which makes the application a little more interesting to look at.

0 0

Post a comment

  • Receive news updates via email from this site