Holiday Project

Screen Shot 2012-12-30 at 5.47.30 AMWhile taking some time off to enjoy the holidays, I decided to try something new.

Background
While I’ve been working with simple UI animations in Real Studio for some time, I’ve never done anything like this. I’ve seen an awesome fire animation done by my buddy Shawn Rapp, but I never had much of an interest in it personally. Well, tonight, while standing on my back porch in the slowly falling snow, I decided to give it a shot.

What It Is
Essentially, it’s a snowfall generator. You supply it with a background image (or just a color) and it draws falling snow with calculated per-flake drifting. It wasn’t overly difficult, but it was amazingly fun.

How Did You Do It?
Well, to start with, I made a Thread subclass which I named “thdSnowFall”. In in, I placed the following Event Definition:

1
Update( p as Picture )

And the following Properties:

1
2
3
4
5
6
BGColor as Color = &c000000
BGImage as Picture
Drift as Boolean = True
Flakes as Integer = 1000
Height as Integer
Width as Integer

All of these properties are computed (just so we can make them show up in the window editor propertylist).

From there, I added two methods:

1
2
3
4
5
6
7
8
9
Private Sub InitArray(intPosX() as Integer, intPosY() as Integer, intSpeed() as Integer, intSize() as Integer, ItemCount as Integer)
  dim intCycle as Integer
  for intCycle = 0 to ItemCount
    intPosX.Append( t.InRange( 0, self.Width ) )
    intPosY.Append( t.InRange( 0, self.Height ) )
    intSpeed.Append( t.InRange( 5, 25 ) )
    intSize.Append( (t.InRange( 1, 3 ) * 100 + t.InRange( 50, 100 )) / 100 )
  next
End Sub
1
2
3
4
5
6
Private Sub DoSnow(intPosX() as Integer, intPosY() as Integer, intSpeed() as Integer, intSize() as Integer, intItem as Integer)
  intPosX(intItem) = t.InRange( 0, self.Width )
  intPosY(intItem) = 0
  intSpeed(intItem) = t.InRange( 5, 25 )
  intSize(intItem) = (t.InRange( 1, 3 ) * 100 + t.InRange( 50, 100 )) / 100
End Sub

Now, to the meat and potatoes, so to speak. In the Run Event Handler, we place the following bit of code which does all the heavy lifting:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  Dim intPosX(), intPosY(), intSpeed(), intSize(), intWind As Integer
  Dim rec As Rectangle
  Dim intItem As Integer
 
  dim g as Graphics
 
  t = new Random
 
  InitArray( intPosX, intPosY, intSpeed, intSize, Flakes - 1 )
 
  while true
    if IsNull( picSnowfall ) then picSnowfall = new Picture( Width, Height )
    if picSnowfall.Width <> Width or picSnowfall.Height <> Height then picSnowfall = new Picture( Width, Height )
 
    g = picSnowfall.Graphics
 
    g.ClearRect( 0, 0, g.Width, g.Height )
 
    g.ForeColor = BGColor
    g.FillRect( 0, 0, g.Width, g.Height )
 
    if not IsNull( BGImage ) then g.DrawPicture( BGImage, 0, 0, g.Width, g.Height, 0, 0, BGImage.Width, BGImage.Height )
 
    for intItem = 0 to Flakes - 1
      if Drift then
        dim strWind as String = "0." + CStr( t.InRange( 0, 2 ) ) + CStr( t.InRange( 0, 5 ) )
        if t.InRange( 0, 1 ) = 0 then strWind = "-" + strWind
        intWind = (intSpeed(intItem) + intSize(intItem)) * CDbl( strWind )
        intPosX(intItem) = intPosX(intItem) + intWind
      end if
 
      intPosY(intItem) = intPosY(intItem) + intSpeed(intItem)
      if intPosY(intItem) > Height then
        DoSnow( intPosX, intPosY, intSpeed, intSize, intItem )
      end if
 
      g.ForeColor = rgb( 255, 255, 255, 150 )
      g.FillOval( intPosX(intItem), intPosY(intItem), intSize(intItem), intSize(intItem) )
 
    next
 
    Update( picSnowfall )
 
    me.Sleep( 50 )
  wend

From there, it’s just tossing an instance of thdSnowFall on to the window, adding an image to our project, and hooking everything up. First of all, we need to add a picture buffer property to our window:

1
picSnowfall As Picture

Next, we need to add some code to our Update event in our thdSnowFall instance:

1
2
  picSnowfall = p
  self.Refresh()

Followed by our code in the Open event of our window (where thdS1 is our thdSnowFall instance, and scene1 is our background image):

1
2
3
4
5
  thdS1.BGColor = rgb( 0, 0, 0 )
  thdS1.BGImage = scene1
  thds1.Width = me.Width
  thds1.Height = me.Height
  thdS1.Run

And, finally, the code in our paint event to draw the buffer out to the screen:

1
if not IsNull( picSnowfall ) then g.DrawPicture( picSnowfall, 0, 0, g.Width, g.Height )

Optional
If your window is resizable, you need to add the following to window’s Resized event handler:

1
2
3
4
  thdS1.Width = me.Width
  thds1.Height = me.Height
  thdS1.Kill
  thdS1.Run

That’s It!
Hit the run button, and enjoy your snowfall scene. Be sure you build for Cocoa on OS X and check App.UseGDIPlus for Windows, but other than that, you should be good. Here’s the result comparison:
snowscene-comp