본문 바로가기

Silverlight

[Sample] 눈오는 애니메이션을 만들어봅시다!


   쇼티예요.

   종전의 팁 오브 더 데이에서는,
 
   여기서 정보를 충분히 찾을 수 있는 것이 나왔기 때문에(WCF를 실버라이트에서 쓰는 법).. 생략을 했구요.

   이번 TTD에는 바로, 이것입니다!
 


  네. 바로 '눈이 오는 효과 내기'가 되겠습니다. 조금.. 포스팅이 길더라구요.

  여기에, 이분이 구현하신 데모 사이트가 있습니다.
  (http://silverlight.services.live.com/invoke/66033/Snowfall/iframe.html)
 
  또, 실행을 시켜보시면 아시겠지만 설정하는 컨트롤도 따로 준비가 되었습니다. (디자인 차암... 한숨나오는..;)
 

   
  사운드의 볼륨을 설정하고, 바람의 세기도 조절이 가능하죠. (주 : 근데 실제 실행시켜보면 여긴 안되는듯?)

  그리고 실험결과 Beta 2 보다는 RTM 버젼에서 좀더 퍼포먼스가 효율적이었습니다. 볼륨을 높이실때 만약 베타 2라면
  조심하시길 바랍니다.
  (원문 : I noticed the application was a lot less performant on a beta 2 build then on an RTM build. So, if you are 
            using beta 2 I would recommend caution when increasing the volume of the snow.
   RTM버젼을 이 분들은 쓰고 있다는 이야기인가요..)
 
  그럼. 시작해보겠습니다.
 
  1. 새 프로젝트를 생성합니다.
  2. 다음 이미지묶음을 다운로드 받습니다. (첨부파일을 다운로드받아주세요)
  3. 유저컨트롤을 하나 생성합니다(여기서는 SnowFlake.xaml) 이것을 하려면, Add->New Item...으로 ... (..아시죠?;)
  4. 이 SnowFlake.cs를 다음과 같이 코딩합니다.

<UserControl x:Class="Snowflakes.SnowFlake"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Image x:Name="Flake" Source="snowflake.png">
        <Image.RenderTransform>
            <ScaleTransform x:Name="SnowScale" ScaleX="0.25" ScaleY="0.25"></ScaleTransform>
        </Image.RenderTransform>
    </Image>
</UserControl>

     그냥 이미지 하나를 추가했고, 이 그림이 제가 원하는 것보다 좀 큰 크기였기 때문에, ScaleTransform을 사용해서
     1/4 로 줄여주었습니다.

   5. 이제 비하인드 코드(SnowFlake.xaml.cs)를 작성해보도록 하겠습니다. 여기엔 이 눈이 떨어지는 로직을
      작성할꺼예요.

     대충 다음과 같은 방법입니다.
     1) 눈이 내리는 효과를 Left, Top 좌표의 변화로 합니다. 이 효과는 50개의 방향전환 중에 하나를 랜덤하게 가집니다.
     2) 눈 부스러기의 Top 좌표가 밑에 바닥 좌표와 같아지는 때나 (이 경우에는 764겠죠) 혹은 이게 화면 밖을 벗어날 경우
         Completed = true로 둘 것입니다. 메인타이머가 돌면서 Completed가 true인 객체(눈부스러기)들은 치울것입니다.
     3) 눈 부스러기가 만들어질 때, Depth 퍼센테이지를 주어서, 랜덤하게 스케일을 키우거나, 작게 할 것입니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
 
namespace Snowflakes
{
    public partial class SnowFlake : UserControl
    {
        double _posLeft = 0.0;
        double _posTop = 0.0;
        double _floorValue = 764;
        int seed = DateTime.Now.Millisecond;
        Random _rand = new Random(DateTime.Now.Millisecond);
        private bool _completed = false;
 
        double _horzInc = 0.0;
 
        public bool Completed
        {
            get { return _completed; }
        } 
 
        public SnowFlake(double left, double top)
        {
            InitializeComponent();
 
            _posLeft = left;
            _posTop = top;
 
            this.SetValue(Canvas.LeftProperty, _posLeft);
            this.SetValue(Canvas.TopProperty, _posTop);
 
            Random rndSize = new Random(DateTime.Now.Millisecond);
            SnowScale.ScaleX = SnowScale.ScaleY = 0.05 * rndSize.Next(1, 6);
         }
          
        public void Fall(double windValue)
        {
            int value = _rand.Next(50);
 
            if (value == 25) //  1 in 50 change to change direction
            {
                if (_horzInc >= 0.2)
                    _horzInc = 0;
                else if (_horzInc <= -0.2)
                    _horzInc = 0;
                else
                {
                    value = _rand.Next(3);
                    if (value == 1) _horzInc = 0.2;
                    else if (value == 2) _horzInc = -0.2;
                    else if (value > 0) _horzInc = 0.0;
                }
            }
             
            double horzValue = _horzInc + (windValue * 0.1);
            
            _posLeft += horzValue;
 
            this.SetValue(Canvas.LeftProperty, _posLeft);
            this.SetValue(Canvas.TopProperty, _posTop++);
 
            _completed = (_posLeft >= 1019 || _posLeft <= 0 || _posTop >= _floorValue);
        }
    }
}

 
  6. 그다음에 Page.xaml.cs에서는, 이 눈 부스러기들을 제어하기 위해서 타이머를 추가합니다.
     눈 부스러기들을 움직이고 나서 Completed = true인지 체크할 것입니다.
     만약 그렇다면, 지우기 위한 배열 안에 이 객체를 추가하려구요. 부스러기가 만들어지면,
     일단은 가장 위쪽에 위치시킬 것입니다.
     그런데, 만약 여기 바람이 있다면 바람의 방향에 맞게 움직여주게끔 해야 겠지요.
 
     툴바 제어를 위한 코드까지 포함된 소스입니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
 
namespace Snowflakes
{
    public partial class Page : UserControl
    {
        List<SnowFlake> _snowFlakes = new List<SnowFlake>();
        Storyboard _snowflakeTimer = new Storyboard();
        Random _rand = new Random(DateTime.Now.Millisecond);
        int _newFlakeCount = 2;
        int _wind = 0;
 
        public Page()
        {
            InitializeComponent();
 
            _snowflakeTimer.Duration = TimeSpan.FromMilliseconds(10);
            _snowflakeTimer.Completed += new EventHandler(SnowFlakeTimer);
            _snowflakeTimer.Begin();
        }
 
        private void SnowFlakeTimer(object sender, EventArgs e)
        {
            MoveSnowFlakes();
            CreateSnowFlakes();
        }
 
        private void MoveSnowFlakes()
        {
            List<SnowFlake> _flakesToRemove = new List<SnowFlake>();
 
            foreach (SnowFlake flake in _snowFlakes)
            {
                flake.Fall(_wind);
                if (true == flake.Completed)
                    _flakesToRemove.Add(flake);
            }
            foreach (SnowFlake flake in _flakesToRemove)
            {
                _snowFlakes.Remove(flake);
                SnowCanvas.Children.Remove(flake);
            }
 
            _snowflakeTimer.Begin();
        }
     
        private void CreateSnowFlakes()
        {
            TotalCount.Text = "Total Snowflakes = " + _snowFlakes.Count;
        
            int count = _rand.Next(0, _newFlakeCount);
 
            for (int i = 0; i < count; i++)
            {
                SnowFlake flake = new SnowFlake(_rand.Next(0, 1020), 0.0);
                _snowFlakes.Add(flake);
                SnowCanvas.Children.Add(flake);
            }
            if (_wind < 0)
            {
                for (int i = 0; i < count; i++)
                {
                    SnowFlake flake = new SnowFlake(1010, _rand.Next(0, 700));
                    _snowFlakes.Add(flake);
                    SnowCanvas.Children.Add(flake);
                }
            }
            else if (_wind > 0)
            {
                for (int i = 0; i < count; i++)
                {
                    SnowFlake flake = new SnowFlake(0, _rand.Next(0, 700));
                    _snowFlakes.Add(flake);
                    SnowCanvas.Children.Add(flake);
                }
            }
        }
 
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Application.Current.Host.Content.IsFullScreen = true;
        }
 
        private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (null != Volume)
            {
                _newFlakeCount = (int)Volume.Value;
                VolumeValue.Text = _newFlakeCount.ToString();
            }
        }
 
        private void Wind_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (null != Wind)
            {
                _wind = (int)Wind.Value;
                WindValue.Text = _wind.ToString();
            }
        }
    }
}

 
  7. 그리고 마지막으로, 툴바를 포함한 나머지 디자인에 대한 Page.xaml 소스입니다.

<UserControl x:Class="Snowflakes.Page"    
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"     
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="1020" Height="768">
    <Canvas x:Name="SnowCanvas" Width="1020" Height="768" Background="Black">
        <Image Width="1020"  Source="Scene.jpg"></Image>
        <TextBlock Canvas.Left="1022" x:Name="TotalCount" Foreground="White">Total Snowflakes</TextBlock>
        <TextBlock Canvas.Left="1022" Canvas.Top="30" Foreground="White">Volume</TextBlock>
        <Slider x:Name="Volume" Value="3" ValueChanged="Slider_ValueChanged" Canvas.Left="1110"
                                Canvas.Top="30"  Minimum="0" Maximum="10" Width="100"></Slider>
        <TextBlock  Canvas.Left="1210" Canvas.Top="30" Foreground="White"
                                       x:Name="VolumeValue">2</TextBlock>
        <TextBlock Canvas.Left="1022" Canvas.Top="60" Foreground="White">Wind</TextBlock>
        <Slider x:Name="Wind" Value="0" ValueChanged="Wind_ValueChanged" Canvas.Left="1110" Canvas.Top="60"
                                                           Minimum="-50" Maximum="50" Width="100"></Slider>
        <TextBlock  Canvas.Left="1210" Canvas.Top="60" Foreground="White" x:Name="WindValue">0</TextBlock>
        <Button Canvas.Left="1022" Canvas.Top="100" Content="Full Screen" Click="Button_Click"></Button>
    </Canvas>
</UserControl>

    이제 여러분들은, 눈내리는 아름다운 광경을 보실 수 있으실꺼예요.
 --------------------------------------------------------------------------------------------------------------
   그냥 소스 하나 카피한 느낌이네요. 보시는 분들에게 도움이 되셨길..

완성된 소스도 첨부합니다.




 [출처] 실버라이트 네이버 카페