windows phone

Windows Phone Puzzle #2: 퍼즐 보드 만들기

[미리 보기] 이번 실습에서 할 것

이 글은 [Windows Phone Puzzle #1: UI, ErrorPage 만들기]에 연결되는 포스트입니다. 먼저 읽어보고 오세요!

▼ 퍼즐 게임의 보드를 만듭니다.

Windows_Phone_Puzzle_step2_67_2

▼ 퍼즐 게임을 풀었을 때 나타나는 애니메이션 코드도 붙일 예정입니다.

Puzzle Board in Visual Studio(Task 5-Verification)

 

1. 사용자 인터페이스 만들기

▼ Visual Studio 2010 Express for Windows Phone을 실행하고, 지난 번(Step 1 과정)에 만들어 놓았던 프로젝트의 이름(Begin)을 클릭합니다.

Windows_Phone_Puzzle_step2_86

▼ Solution Explorer에 있는 WindowsPhonePuzzle 솔루션 명을 마우스 오른쪽 단추를 눌러 Add –> New Item을 클릭합니다.

Windows_Phone_Puzzle_step2_01

▼ 기본 옵션인 Windows Phone Portrait Page를 그대로 선택해서 이름을 PuzzlePage.xaml로 정해 줍니다.

Windows_Phone_Puzzle_step2_02

▼ LayoutRoot 요소의 RowDefinitions 섹션을 찾고 첫 번째 행의 Height 값을 0.2*로 변경합니다.(첫 번째 행이 남은 공간의 1/6을 차지하게 됩니다)

Windows_Phone_Puzzle_step2_03

▼ LayoutRoot 그리드 내부에 있는 TitlePanel 및 ContentPanel 요소를 삭제합니다.

※ 아래 사진에서 선택된 부분을 삭제하시면 됩니다.

Windows_Phone_Puzzle_step2_04

▼ 그리고 삭제한 위치에 아래의 소스를 붙여 넣습니다.

  <StackPanel Orientation="Vertical" VerticalAlignment="Stretch">
  </StackPanel>
  <StackPanel Orientation="Vertical" VerticalAlignment="Top" Grid.Row="1">
  </StackPanel>

※ 아래 부분입니다.

Windows_Phone_Puzzle_step2_05

▼ 아래와 같이 변경됩니다.

Windows_Phone_Puzzle_step2_06

▼ 방금 붙여 넣은 코드 중 첫 번째 StackPanel 요소의 내부에 엔터를 입력합니다.

Windows_Phone_Puzzle_step2_07

▼ 그리고 StackPanel 요소 안에 아래의 소스를 삽입합니다.

    <Button x:Name="SolveButton" Content="SOLVE" Margin="10" HorizontalAlignment="Center" Click="SolveButton_Click" />
    <StackPanel x:Name="StatusPanel" Orientation="Horizontal" HorizontalAlignment="Center" Visibility="Collapsed">
      <TextBlock HorizontalAlignment="Center" Text="Your Moves: " TextWrapping="Wrap" Foreground="#FFD0D0D0" FontSize="17.333"/>
      <TextBlock x:Name="TotalMovesTextBlock" HorizontalAlignment="Center" Text="N" TextWrapping="Wrap" Foreground="#FFFFB000" FontSize="17.333"/>
    </StackPanel>

※ 아래 위치입니다.

Windows_Phone_Puzzle_step2_08

참고: 강조 표시한 XAML 코드는 눌렀을 때 퍼즐이 풀리는 버튼 및 게임 중 수행된 총 이동 수를 표시하는 상태 패널을 정의합니다.

02-YourFirstWP7App-KOR.pdf – Windows Phone 7 앱 시리즈 시즌 1

▼ 이번에는 2번째 StackPanel 요소를 확장시켜서,

Windows_Phone_Puzzle_step2_09

▼ 마찬가지로 StackPanel 요소 안에 아래의 소스를 삽입합니다.

    <Border x:Name="CongratsBorder" Height="30" Background="#FFF10DA2" HorizontalAlignment="Center" Width="443" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" Opacity="0">
      <Border.RenderTransform>
        <TransformGroup>
          <ScaleTransform/>
          <SkewTransform/>
          <RotateTransform/>
          <TranslateTransform/>
        </TransformGroup>
      </Border.RenderTransform>
      <TextBlock HorizontalAlignment="Center" Text="CONGRATULATIONS!" TextWrapping="Wrap" Foreground="White" FontSize="17.333" VerticalAlignment="Center" FontWeight="Bold"/>
    </Border>

※ 아래 위치입니다.

Windows_Phone_Puzzle_step2_10

참고: 이 Border 요소에는 퍼즐을 풀었을 때 표시되는 메시지가 포함되어 있습니다.

Border 에 RenderTransform 이 포함된 것에 주목합니다. 이 변형은 여러분이 실습 뒷부분에서 시각 효과를 주기 위해 만들게 될 애니메이션 스토리보드에 필요합니다. 이 변형이 없으면 스토리보드가 요소를 애니메이션 시킬 수 없습니다.

02-YourFirstWP7App-KOR.pdf – Windows Phone 7 앱 시리즈 시즌 1

▼ 방금 붙여 넣은 StackPanel 요소가 끝나기 전, </border> 아래에 엔터를 입력합니다.

Windows_Phone_Puzzle_step2_11

▼ 그 부분에 아래의 소스를 붙여 넣습니다.

    <Border x:Name="border" BorderThickness="3" Background="#FF262626" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="1" RenderTransformOrigin="0.5,0.5">
      <Border.RenderTransform>
        <TransformGroup>
          <ScaleTransform/>
          <SkewTransform/>
          <RotateTransform/>
          <TranslateTransform/>
        </TransformGroup>
      </Border.RenderTransform>
      <Border.BorderBrush>
        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
          <GradientStop Color="#FFF10DA2" Offset="0"/>
          <GradientStop Color="#FFEE7923" Offset="1"/>
        </LinearGradientBrush>
      </Border.BorderBrush>
      <Canvas Height="435" Width="435">
        <Image x:Name="PreviewImage" Height="435" Width="435" Opacity="0.2" />
        <Canvas x:Name="GameContainer" Width="435" Height="435" />
      </Canvas>
    </Border>
    <TextBlock x:Name="TapToContinueTextBlock" HorizontalAlignment="Center" Text="Tap the picture to start the puzzle" TextWrapping="Wrap" Foreground="#FFD0D0D0" FontSize="17.333"/>

※ 아래 위치입니다.

Windows_Phone_Puzzle_step2_12

참고: 위의 XAML 코드는 서로 혼합되는 여러 색상을 사용하는 Linear Gradient Brush 로 그려지는 Border 요소를 정의합니다. 이 요소는 여러분이 시각 효과를 나타내기 위해 애니메이션 스토리보드를 만들 때 필요한 RenderTransform 도 지정합니다.

Border 요소 내에는 하나의 Canvas 요소가 있습니다. 이 컨테이너 요소는 해당 영역에

상대적인 좌표를 이용하여 다른 하위 요소의 위치를 명시적으로 지정하는 역할을 합니다. 그 하위 요소 중에는 퍼즐 해답의 미리보기가 포함된 워터마크(PreviewImage)를 보여주는 Image 요소 및 퍼즐 조각을 유지하는 내부 Canvas(GameContainer)가 있습니다.

이 마크업에는 게임 시작 방법에 대한 설명을 표시하는 TextBlock 요소(TapToContinueTextBlock)도 포함됩니다. 이 요소는 게임이 진행되는 동안 숨겨집니다.

02-YourFirstWP7App-KOR.pdf – Windows Phone 7 앱 시리즈 시즌 1

▼ 아래와 같은 모양으로 변경됩니다.

Windows_Phone_Puzzle_step2_13

 

2. 애플리케이션 로직 프로그래밍하기

▼ Solution Explorer에 있는 WindowsPhonePuzzle 솔루션 이름 위에서 마우스 오른쪽 버튼을 눌러 Add –> Existing Item을 클릭합니다.

Windows_Phone_Puzzle_step2_15

▼ C:\WP7TrainingKitOffline\Labs\YourFirstWP7Application\Source\Assets 에 있는 PuzzleGame.cs 파일을 Add 합니다.

Windows_Phone_Puzzle_step2_16

참고: 게임의 로직은 여러분이 본 실습에 포함된 자산에서 프로젝트에 추가하는 PuzzleGame 클래스에 들어 있습니다. 이 클래스에서는 새 게임 시작, 퍼즐 조각 이동 및 게임 상태의 저장과 복원을 포함한 메서드를 제공합니다. 여러분은 이 클래스의 소스 코드를 자유롭게 살펴볼 수 있지만 코드를 세부적으로 분석하는 것은 본 실습의 범위를 벗어납니다.

02-YourFirstWP7App-KOR.pdf – Windows Phone 7 앱 시리즈 시즌 1

▼ Solution Explorer에 있는 PuzzlePage.xaml을 마우스 오른쪽 버튼으로 눌러 View Code 를 클릭합니다.

Windows_Phone_Puzzle_step2_19

▼ PuzzlePage.xaml.cs 가 열리면 아래의 네임스페이스 선언을 추가해 줍니다.

using System.IO;
using System.Windows.Media.Imaging;
using System.Windows.Resources;

※ 이 부분입니다.

Windows_Phone_Puzzle_step2_20

▼ PuzzlePage 클래스 바로 윗 부분을 확장해서,

Windows_Phone_Puzzle_step2_21

▼ 아래의 코드를 붙여 넣습니다.

  private const double DoubleTapSpeed = 500;
  private const int ImageSize = 435;
  private PuzzleGame game;
  private Canvas[] puzzlePieces;
  private Stream imageStream;

※ 이 부분입니다.

Windows_Phone_Puzzle_step2_23

▼ 이어서 아래의 소스를 방금 붙여 넣은 자리 밑에 삽입합니다.(ImageStream 속성 추가)

  public Stream ImageStream
  {
    get
    {
      return this.imageStream;
    }
    set
    {
      this.imageStream = value;
      BitmapImage bitmap = new BitmapImage();
      bitmap.SetSource(value);
      this.PreviewImage.Source = bitmap;
      int i = 0;
      int pieceSize = ImageSize / this.game.ColsAndRows;
      for (int ix = 0; ix < this.game.ColsAndRows; ix++)
      {
        for (int iy = 0; iy < this.game.ColsAndRows; iy++)
        {
          Image pieceImage = this.puzzlePieces[i].Children[0] as Image;
          pieceImage.Source = bitmap;
          i++;
        }
      }
    }
  }

※ 아래 위치입니다.

Windows_Phone_Puzzle_step2_25

참고: ImageStream 속성은 퍼즐 이미지에 사용될 스트림을 반환합니다. 이 속성을 사용하면 배경 이미지와 각각의 퍼즐 조각이 자동으로 새로 고침 됩니다. 예를 들어 전화기 카메라로 찍은 이미지 등 유효한 비트맵을 포함한 새로운 스트림으로 속성을 설정하여 퍼즐 이미지를 바꿀 수 있습니다. 본 실습에서는 애플리케이션 패키지에 리소스로 포함된 이미지를 사용합니다.

02-YourFirstWP7App-KOR.pdf – Windows Phone 7 앱 시리즈 시즌 1

▼ PuzzlePage 클래스에 대한 생성자 부분에 엔터를 입력하고,

Windows_Phone_Puzzle_step2_26

▼아래 코드를 삽입합니다.

    SupportedOrientations = SupportedPageOrientation.Portrait | SupportedPageOrientation.Landscape;
    // Puzzle Game
    this.game = new PuzzleGame(3);
    this.game.GameStarted += delegate
    {
      this.StatusPanel.Visibility = Visibility.Visible;
      this.TapToContinueTextBlock.Opacity = 0;
      this.TotalMovesTextBlock.Text = this.game.TotalMoves.ToString();
    };
    this.game.GameOver += delegate
    {
      this.TapToContinueTextBlock.Opacity = 1;
      this.StatusPanel.Visibility = Visibility.Visible;
      this.TotalMovesTextBlock.Text = this.game.TotalMoves.ToString();
    };
    this.game.PieceUpdated += delegate(object sender, PieceUpdatedEventArgs args)
    {
      int pieceSize = ImageSize / this.game.ColsAndRows;
      this.AnimatePiece(this.puzzlePieces[args.PieceId], Canvas.LeftProperty, (int)args.NewPosition.X * pieceSize);
      this.AnimatePiece(this.puzzlePieces[args.PieceId], Canvas.TopProperty, (int)args.NewPosition.Y * pieceSize);
      this.TotalMovesTextBlock.Text = this.game.TotalMoves.ToString();
    };
    this.InitBoard();

※ 아래 부분입니다.

Windows_Phone_Puzzle_step2_27

참고: 이 생성자는 게임 로직을 인스턴스화하고(PuzzleGame 클래스에 의해 캡슐화됨)

해당 이벤트를 바인딩합니다. PuzzleGame 클래스는 다음 이벤트를 정의합니다.

GameStarted: 이 이벤트는 새 게임이 시작될 때 발생합니다. 이 Event Handler 는 이동된 조각 수를 포함한 패널을 표시하고, 게임 시작 방법을 설명하는 범례를 숨기며 이동된 조각 수를 재설정합니다.

GameOver: 이 이벤트는 퍼즐이 풀렸을 때 발생합니다. 이 Event Handler 는 게임 시작

방법을 설명하는 범례를 표시하고 이동 수를 업데이트합니다.

PieceUpdated: 이 이벤트는 조각이 이동될 때마다 발생합니다. 이 Event Handler 는 이동된 조각을 애니메이션화하고 이동 수를 업데이트합니다.

마지막으로, Event Handler 가입 후 생성자는 InitBoard 메서드를 호출하여 게임 보드를 초기화합니다.

02-YourFirstWP7App-KOR.pdf – Windows Phone 7 앱 시리즈 시즌 1

▼ PuzzlePage 클래스 다음에 엔터를 입력하고, (중괄호의 위치를 잘 보세요)

Windows_Phone_Puzzle_step2_28

▼ 아래의 소스를 삽입합니다. (InitBoard 메서드)

private void InitBoard()
{
  int totalPieces = this.game.ColsAndRows * this.game.ColsAndRows;
  int pieceSize = ImageSize / this.game.ColsAndRows;
  this.puzzlePieces = new Canvas[totalPieces];
  int nx = 0;
  for (int ix = 0; ix < this.game.ColsAndRows; ix++)
  {
    for (int iy = 0; iy < this.game.ColsAndRows; iy++)
    {
      nx = (ix * this.game.ColsAndRows) + iy;
      Image image = new Image();
      image.SetValue(FrameworkElement.NameProperty, "PuzzleImage_" + nx);
      image.Height = ImageSize;
      image.Width = ImageSize;
      image.Stretch = Stretch.UniformToFill;
      RectangleGeometry r = new RectangleGeometry();
      r.Rect = new Rect((ix * pieceSize), (iy * pieceSize), pieceSize, pieceSize);
      image.Clip = r;
      image.SetValue(Canvas.TopProperty, Convert.ToDouble(iy * pieceSize * -1));
      image.SetValue(Canvas.LeftProperty, Convert.ToDouble(ix * pieceSize * -1));
      this.puzzlePieces[nx] = new Canvas();
      this.puzzlePieces[nx].SetValue(FrameworkElement.NameProperty, "PuzzlePiece_" + nx);
      this.puzzlePieces[nx].Width = pieceSize;
      this.puzzlePieces[nx].Height = pieceSize;
      this.puzzlePieces[nx].Children.Add(image);
      this.puzzlePieces[nx].MouseLeftButtonDown += this.PuzzlePiece_MouseLeftButtonDown;
      if (nx < totalPieces - 1)
      {
        this.GameContainer.Children.Add(this.puzzlePieces[nx]);
      }
    }
  }
  // Retrieve image
  StreamResourceInfo imageResource = Application.GetResourceStream(new Uri("WindowsPhonePuzzle;component/Assets/Puzzle.jpg", UriKind.Relative));
  this.ImageStream = imageResource.Stream;
  this.game.Reset();
}

※ 아래 부분입니다.

Windows_Phone_Puzzle_step2_29

참고: InitBoard 메서드는 각 조각에 하나씩 퍼즐 조각을 포함하는 Canvas 컨트롤을 만듭니다. 각 조각은 퍼즐에 사용된 전체 이미지에서 잘려진 일부입니다.

이 메서드는 또한 애플리케이션 리소스에서 퍼즐 이미지를 가져오고 결과 스트림을 포함한 ImageStream 속성을 초기화합니다.

02-YourFirstWP7App-KOR.pdf – Windows Phone 7 앱 시리즈 시즌 1

▼ 마찬가지로 방금 삽입한 코드 아래에 엔터를 입력하고,

Windows_Phone_Puzzle_step2_30

▼ 아래의 코드(AnimatePiece 메서드)를 삽입합니다.

private void AnimatePiece(DependencyObject piece, DependencyProperty dp, double newValue)
{
    Storyboard storyBoard = new Storyboard();
    Storyboard.SetTarget(storyBoard, piece);
    Storyboard.SetTargetProperty(storyBoard, new PropertyPath(dp));
    storyBoard.Children.Add(new DoubleAnimation
    {
        Duration = new Duration(TimeSpan.FromMilliseconds(200)),
        From = Convert.ToInt32(piece.GetValue(dp)),
        To = Convert.ToDouble(newValue),
        EasingFunction = new SineEase()
    });
    storyBoard.Begin();
}

※ 아래 위치입니다.

Windows_Phone_Puzzle_step2_31

참고: AnimatePiece 는 UI 에서 퍼즐 조각을 애니메이션화하기 위한 도우미 메서드(Helper Method)입니다. 이 메서드는 코드를 사용하여 스토리보드를 만들고 컨트롤의 ependencyProperty 를 업데이트하는 목적으로 이용할 수 있습니다. 이 애플리케이션에서, 도우미 메서드는 해당 위치 제어를 위해 퍼즐 조각의 Top 및 Left 속성을 업데이트하는 역할만 합니다.

02-YourFirstWP7App-KOR.pdf – Windows Phone 7 앱 시리즈 시즌 1

▼ AnimatePiece 메서드의 아래에도 엔터를 입력하고,

Windows_Phone_Puzzle_step2_32

▼ 아래 코드(MouseLeftButtonDown Event Handler)를 입력합니다.

private void PuzzlePiece_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
  if (!this.game.IsPlaying)
  {
    this.game.NewGame();
  }
}

※ 아래 위치입니다.

Windows_Phone_Puzzle_step2_33

참고: 이 Event Handler 는 보드 화면을 클릭할 때 작동하며 이미 진행 중인 게임이 없는 경우에 새 게임을 시작합니다.

02-YourFirstWP7App-KOR.pdf – Windows Phone 7 앱 시리즈 시즌 1

▼ MouseLeftButtonDown Event Handler 아래에도 엔터를 입력하고,

Windows_Phone_Puzzle_step2_34

▼ 코드(“Solve” 버튼의 Click 이벤트에 대한 Handler)를 삽입합니다.

private void SolveButton_Click(object sender, RoutedEventArgs e)
{
    this.game.Reset();
    this.game.CheckWinner();
}

※ 아래 위치입니다.

Windows_Phone_Puzzle_step2_35

참고: Solve 버튼의 Event Handler 는 게임을 강제로 재설정하여 원래 이미지를 표시하고 퍼즐을 푼 다음, PuzzleGame 클래스에서 PuzzleGame.CheckWinner 를 호출합니다. 이 메서드는 모든 조각이 올바른 위치에 있는지 확인하고 GameOver 이벤트를 발생시켜 UI 에 축하 메시지가 표시되게 합니다.

02-YourFirstWP7App-KOR.pdf – Windows Phone 7 앱 시리즈 시즌 1

▼ Start Debugging 단추를 누릅니다.

Windows_Phone_Puzzle_step2_38

▼ 스플래쉬 화면이 나오면 [START]를 누릅니다.

Windows_Phone_Puzzle_step2_40Windows_Phone_Puzzle_step2_41

▼ 이미지를 누르면 퍼즐 게임이 시작됩니다. 하지만 퍼즐 조각들을 눌러도 별다른 반응은 없네요.

Windows_Phone_Puzzle_step2_42

이미지가 여러 조각으로 분할되고 보드에 무작위로 재정렬되는 것을 확인합니다. 또한 조각이 즉시 분할되지 않고 AnimatePiece 메서드에 의해 적용된 애니메이션 스토리보드로 인해 연속적으로 미끄러지듯 분할되는 것에도 주목합니다.

이와 같은 전환을 위해 이 메서드는 각 퍼즐 조각의 왼쪽 및 위쪽 좌표를 애니메이션화합니다. 조각을 클릭하여 선택하고 끌어봅니다. 아직은 효과가 나타나지 않는 것을 알 수 있습니다. 다음 작업에서 여러분은 멀티터치에 대한 지원을 추가하여 손가락으로 보드 조각을 끌어보고 재정렬 해 봅니다.

02-YourFirstWP7App-KOR.pdf – Windows Phone 7 앱 시리즈 시즌 1

▼ [SOLVE] 버튼을 클릭하면 조각이 재결합되어 본래 모습으로 돌아옵니다.

Windows_Phone_Puzzle_step2_43

▼ Debug –> Step Debugging을 클릭합니다.

Windows_Phone_Puzzle_step2_44

 

3. 멀티터치에 대한 지원 추가하기

▼ Solution Explorer에서 PuzzlePage.xaml을 마우스 오른쪽 단추로 눌러 View Code를 클릭합니다.

Windows_Phone_Puzzle_step2_45

▼ PuzzlePage 클래스의 기존 멤버 변수 아래에 다음 코드를 삽입합니다.

  private long lastTapTicks;
  private int movingPieceId = -1;
  private int movingPieceDirection;
  private double movingPieceStartingPosition;

※ 아래 부분입니다.

Windows_Phone_Puzzle_step2_47

▼ Solution Explorer에서 PuzzlePage.xaml을 마우스 오른쪽 단추로 눌러 View Designer를 클릭합니다.

Windows_Phone_Puzzle_step2_49

▼ 디자인 보기 화면에서 에뮬레이터 이미지 바깥에 있는 빈 공간을 클릭하여 PhoneApplicationPage 요소를 선택합니다.

Windows_Phone_Puzzle_step2_50

▼ 키보드의 F4키를 눌러 Properties 탭을 클릭합니다.

Windows_Phone_Puzzle_step2_52

▼ 그리고 ManipulationStarted 이벤트를 찾아 더블클릭합니다.

Windows_Phone_Puzzle_step2_53

▼ 그러면 아래와 같이 PhoneApplicationPage_ManipulationStarted 메서드가 생성되는데요,

Windows_Phone_Puzzle_step2_54

▼ 그 안에 아래의 코드를 삽입합니다.

  if (this.game.IsPlaying && e.ManipulationContainer is Image && e.ManipulationContainer.GetValue(FrameworkElement.NameProperty).ToString().StartsWith("PuzzleImage_"))
  {
    int pieceIx = Convert.ToInt32(e.ManipulationContainer.GetValue(FrameworkElement.NameProperty).ToString().Substring(12));
    Canvas piece = this.FindName("PuzzlePiece_" + pieceIx) as Canvas;
    if (piece != null)
    {
      int totalPieces = this.game.ColsAndRows * this.game.ColsAndRows;
      for (int i = 0; i < totalPieces; i++)
      {
        if (piece == this.puzzlePieces[i] && this.game.CanMovePiece(i) > 0)
        {
          int direction = this.game.CanMovePiece(i);
          DependencyProperty axisProperty = (direction % 2 == 0) ? Canvas.LeftProperty : Canvas.TopProperty;
          this.movingPieceDirection = direction;
          this.movingPieceStartingPosition = Convert.ToDouble(piece.GetValue(axisProperty));
          this.movingPieceId = i;
          break;
        }
      }
    }
  }

※ 아래 부분입니다.

Windows_Phone_Puzzle_step2_55

참고: ManipulationStarted 이벤트는 사용자가 조작을 위해 UIElement 를 터치할 때(또는 마우스로 클릭) 발생하며 이는 MouseDown 이벤트와 유사합니다. Event Handler 는 게임이 현재 진행 중인지 확인하고 퍼즐 조각을 나타내는 이미지 요소 중 하나가 이벤트를 발생시켰는지 확인한 후 발생시키지 않았다면 이벤트를 무시합니다. 이 Event Handler 는 이 조각에 대한 해당 Canvas 요소를 찾은 후 게임 로직을 호출하여 조각을 이동시킬 수 있는지 여부를 결정합니다. 조각을 이동하려면 조각 주변에 빈 칸이 있어야 합니다. 그런 다음 선택 조각을 저장하고 이동할 수 있는 위치의 축/방향을 기록합니다.

02-YourFirstWP7App-KOR.pdf – Windows Phone 7 앱 시리즈 시즌 1

▼ Solution Explorer에서 PuzzlePage.xaml을 마우스 오른쪽 단추로 눌러 View Designer를 클릭합니다.

Windows_Phone_Puzzle_step2_56

▼ 이번에는 PhoneApplicationPage의 Properties 창에 있는 ManipulationDelta 이벤트에 대한 Handler를 만듭니다.(항목 이름을 더블 클릭)

Windows_Phone_Puzzle_step2_57

▼ PhoneApplicationPage_ManipulationDelta 메소드에 다음의 코드를 삽입합니다.

  if (this.movingPieceId > -1)
  {
    int pieceSize = ImageSize / this.game.ColsAndRows;
    Canvas movingPiece = this.puzzlePieces[this.movingPieceId];
    // validate direction
    DependencyProperty axisProperty;
    double normalizedValue;
    if (this.movingPieceDirection % 2 == 0)
    {
      axisProperty = Canvas.LeftProperty;
      normalizedValue = e.CumulativeManipulation.Translation.X;
    }
    else
    {
      axisProperty = Canvas.TopProperty;
      normalizedValue = e.CumulativeManipulation.Translation.Y;
    }
    // enforce drag constraints
    // (top or left)
    if (this.movingPieceDirection == 1 || this.movingPieceDirection == 4)
    {
      if (normalizedValue < -pieceSize)
      {
        normalizedValue = -pieceSize;
      }
      else if (normalizedValue > 0)
      {
        normalizedValue = 0;
      }
    }
    // (bottom or right)
    else if (this.movingPieceDirection == 3 || this.movingPieceDirection == 2)
    {
      if (normalizedValue > pieceSize)
      {
        normalizedValue = pieceSize;
      }
      else if (normalizedValue < 0)
      {
        normalizedValue = 0;
      }
    }
    // set position
    movingPiece.SetValue(axisProperty, normalizedValue + this.movingPieceStartingPosition);
  }

※ 아래 부분입니다. (소스가 깁니다)

Windows_Phone_Puzzle_step2_59

참고: ManipulationDelta 이벤트는 UIElement 를 조작하면서 사용자가 손가락(또는 마우스 커서)을 움직일 때 발생합니다. 이 Event Handler 는 현재 이동 중인 조각이 있는지 여부를 확인합니다. 있는 경우, 가능한 축/방향에서만 델타 값을 포착합니다. 이를 위해, 코드에서 끌기 제약 조건(Drag Constraints)을 통해 이동이 사용 가능한 경계 내에서 이루어지는지 확인해야 합니다(조각은 서로 겹칠 수 없음). 그런 다음 Handler 는 해당 축에 대한 속성을 설정하여 조각의 위치를 업데이트합니다.

02-YourFirstWP7App-KOR.pdf – Windows Phone 7 앱 시리즈 시즌 1

▼ 이전 단계(디자이너 보기 –> 속성 창에서 이벤트 생성)를 반복해서, ManipulationCompleted 이벤트를 만듭니다. 그리고 아래의 코드를 삽입합니다.

  if (this.movingPieceId > -1)
  {
    int pieceSize = ImageSize / this.game.ColsAndRows;
    Canvas movingPiece = this.puzzlePieces[this.movingPieceId];
    // validate direction
    DependencyProperty axisProperty;
    double normalizedValue;
    if (this.movingPieceDirection % 2 == 0)
    {
      axisProperty = Canvas.LeftProperty;
      normalizedValue = e.CumulativeManipulation.Translation.X;
    }
    else
    {
      axisProperty = Canvas.TopProperty;
      normalizedValue = e.CumulativeManipulation.Translation.Y;
    }
    // enforce drag constraints
    // (top or left)
    if (this.movingPieceDirection == 1 || this.movingPieceDirection == 4)
    {
      if (normalizedValue < -pieceSize)
      {
        normalizedValue = -pieceSize;
      }
      else if (normalizedValue > 0)
      {
        normalizedValue = 0;
      }
    }
    // (bottom or right)
    else if (this.movingPieceDirection == 3 || this.movingPieceDirection == 2)
    {
      if (normalizedValue > pieceSize)
      {
        normalizedValue = pieceSize;
      }
      else if (normalizedValue < 0)
      {
        normalizedValue = 0;
      }
    }
    // set position
    movingPiece.SetValue(axisProperty, normalizedValue + this.movingPieceStartingPosition);
  }

※ 아래 위치입니다.

Windows_Phone_Puzzle_step2_61

참고: ManipulationCompleted 이벤트는 UIElement 조작 후 사용자가 화면에서 손가락을 들어올리는(또는 마우스 버튼을 놓는) 경우에 발생하며, 이는 MouseUp 이벤트와 유사합니다. 이 Event Handler 는 현재 이동 중인 퍼즐 조각이 있는지 여부를 확인합니다. 있는 경우, 마지막 ManipulationCompleted 가 실행된 시간을 확인하고 시간 간격이 DoubleTapSpeed 로 지정된 값보다 적은 경우 이 이벤트를 Double Tap 이벤트(또는 두 번 클릭)로 해석하고 조각을 인접한 빈 칸으로 이동합니다. 그렇지 않은 경우, 대상 위치와 조각 사이의 오프셋 간격(offset distance)을 계산하고 조각이 인접 슬롯까지 조각 크기의 1/3 이상 이동한 경우 조각을 이 슬롯으로 완전히 이동시킵니다. 1/3 이상 이동하지 않은 경우에는 조각을 다시 원래 위치로 가져갑니다.

02-YourFirstWP7App-KOR.pdf – Windows Phone 7 앱 시리즈 시즌 1

▼ 모두 완료하셨나요? 그렇다면 PhoneApplicationPage의 Properties 창이 아래와 같이 변해 있을 겁니다.

Windows_Phone_Puzzle_step2_62

▼ 추가된 멀티터치 지원을 테스트하기 위해 Start Debugging 단추를 누릅니다.

Windows_Phone_Puzzle_step2_63

▼ 게임을 시작해서 퍼즐 조각을 마우스로 끌어 놓는 것이 가능한지 봅니다. 조각을 한 번 움직일 때 마다 Your Moves: 부분이 증가하는 것을 확인해 보세요.

Windows_Phone_Puzzle_step2_68

실험이 끝났다면 디버깅을 종료하세요.

 

4. 애니메이션 효과 만들기

Silverlight 에서 스토리보드는 애니메이션입니다. 각 스토리보드에서는 키프레임을 포함하는 타임라인을 정의합니다. 각 키프레임은 위치, 크기, 회전, 투명도 및 배경이나 전경 색상 등의 컨트롤 속성을 독립적으로 재정의 할 수 있습니다. 이러한 접근 방식에 따라 사용자는 각 프레임을 하나씩 정의하여 애니메이션을 만들 필요 없이 타임라인의 선택 지점을 제공하여 중요 속성 변경만을 표시하면 됩니다. Silverlight 는 두 연속 키프레임 사이에서 애니메이션 된 속성 값을 내장하여 중간 프레임을 생성하고 전환을 부드럽게 만듭니다.

각 스토리보드는 페이지의 Code Behind 파일에서 사용할 수 있는 메서드와 이벤트를 가진 개체입니다. 여기에는 애니메이션의 Begin, Stop 및 Pause 를 위한 메서드와 애니메이션의 재생이 완료되었을 때 발생하는 하나의 이벤트인 Completed 가 포함됩니다. 스토리보드는 XAML 코드로 작성되며 Expression Blend 를 이용하여 쉽게 만들 수 있습니다. 이 작업에서 여러분은 사용자가 퍼즐을 성공적으로 풀었을 때 실행되는 스토리보드를 만들어 봅니다. 이 애니메이션은 퍼즐 이미지를 중간 축을 중심으로 회전시키고 점차 모양이 사라지는 메시지를 표시하는 시각 효과를 나타냅니다.

02-YourFirstWP7App-KOR.pdf – Windows Phone 7 앱 시리즈 시즌 1

▼ PuzzlePage.xaml의 상단에 Resources 섹션을 페이지에 삽입합니다.

  <phone:PhoneApplicationPage.Resources>
  </phone:PhoneApplicationPage.Resources>

※ 아래 부분입니다.

Windows_Phone_Puzzle_step2_72

참고: 리소스를 이용하면 일반적으로 정의되는 개체와 값을 재사용하는 것이 간단해집니다. 컨트롤 템플릿, 스타일, 브러시, 색상 및 애니메이션 스토리보드 등 일반적 항목에 대한 정의를 만들고 이를 리소스 사전에 보관해둘 수 있습니다. 리소스 사전은 XAML 및 코드에서 모두 사용할 수 있는 키 지정 사전(keyed dictionary)입니다. 여러분은 여러분의 애플리케이션 구조의 여러 범위에서 리소스 사전을 만들 수 있기 때문에 페이지 수준에서, 또는 애플리케이션 리소스로 리소스를 정의할 수 있습니다. 이 작업에서는 페이지의 리소스를 사용하여 애니메이션 스토리보드에 대한 정의를

저장합니다.

02-YourFirstWP7App-KOR.pdf – Windows Phone 7 앱 시리즈 시즌 1

▼ 그리고 Resources 섹션을 확장시켜서,

Windows_Phone_Puzzle_step2_73

▼ 사용자가 퍼즐을 풀었을 때 발생하는 전환에 대한 애니메이션 스토리보드(WinTransition)를 삽입합니다.

  <Storyboard x:Name="WinTransition">
    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PreviewImage" Storyboard.TargetProperty="(UIElement.Opacity)">
      <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0.2"/>
      <EasingDoubleKeyFrame KeyTime="00:00:00.7000000" Value="1"/>
    </DoubleAnimationUsingKeyFrames>
    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="border" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
      <EasingDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
      <EasingDoubleKeyFrame KeyTime="00:00:00.7000000" Value="-1">
        <EasingDoubleKeyFrame.EasingFunction>
          <CubicEase EasingMode="EaseInOut"/>
        </EasingDoubleKeyFrame.EasingFunction>
      </EasingDoubleKeyFrame>
      <EasingDoubleKeyFrame KeyTime="00:00:01.7000000" Value="1">
        <EasingDoubleKeyFrame.EasingFunction>
          <CubicEase EasingMode="EaseInOut"/>
        </EasingDoubleKeyFrame.EasingFunction>
      </EasingDoubleKeyFrame>
    </DoubleAnimationUsingKeyFrames>
    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="CongratsBorder" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
      <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
      <EasingDoubleKeyFrame KeyTime="00:00:00.7000000" Value="-1">
        <EasingDoubleKeyFrame.EasingFunction>
          <CubicEase EasingMode="EaseInOut"/>
        </EasingDoubleKeyFrame.EasingFunction>
      </EasingDoubleKeyFrame>
      <EasingDoubleKeyFrame KeyTime="00:00:01.7000000" Value="1">
        <EasingDoubleKeyFrame.EasingFunction>
          <CubicEase EasingMode="EaseInOut"/>
        </EasingDoubleKeyFrame.EasingFunction>
      </EasingDoubleKeyFrame>
    </DoubleAnimationUsingKeyFrames>
    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="CongratsBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
      <EasingDoubleKeyFrame KeyTime="00:00:01.2000000" Value="0"/>
      <EasingDoubleKeyFrame KeyTime="00:00:01.3000000" Value="0"/>
      <EasingDoubleKeyFrame KeyTime="00:00:01.4000000" Value="1"/>
    </DoubleAnimationUsingKeyFrames>
  </Storyboard>

※ 아래 위치입니다.

Windows_Phone_Puzzle_step2_74

참고: 사용자가 게임을 이긴 경우 WinTransition 스토리보드가 실행됩니다. 애니메이션이 퍼즐 해답을 포함한 이미지를 중간 축을 중심으로 회전시키고, 점차 모양이 사라지는 축하 메시지가 포함된 이미지 주변에 프레임을 표시합니다. Expression Blend 와 같은 도구를 사용하여 디자이너에서 속성을 시각적으로 애니메이션화하고 스토리보드 정의에 필요한 XAML 코드를 생성할 수 있습니다. Expression Blend 를 사용하여 애니메이션을 만드는 방법에 대한 설명은 Hello Phone 실습을 참조하십시오.

02-YourFirstWP7App-KOR.pdf – Windows Phone 7 앱 시리즈 시즌 1

▼ 방금 삽입한 코드의 아래에 엔터를 입력하고,

Windows_Phone_Puzzle_step2_75

▼ 사용자가 게임을 다시 시작할 때마다 발생하는 전환에 대한 스토리보드(ResetWinTransition)를 삽입합니다.

  <Storyboard x:Name="ResetWinTransition">
    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="CongratsBorder" Storyboard.TargetProperty="(UIElement.Opacity)" Duration="00:00:00.0010000">
      <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
    </DoubleAnimationUsingKeyFrames>
    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PreviewImage" Storyboard.TargetProperty="(UIElement.Opacity)" Duration="00:00:00.0010000">
      <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0.20000000298023224"/>
    </DoubleAnimationUsingKeyFrames>
    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="GameContainer" Storyboard.TargetProperty="(UIElement.Opacity)">
      <EasingDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
    </DoubleAnimationUsingKeyFrames>
  </Storyboard>

※ 아래 위치입니다.

Windows_Phone_Puzzle_step2_76

참고: ResetWinTransition 스토리보드는 축하 메시지를 숨기고 퍼즐 이미지 투명도를 원래 값으로 복원합니다.

02-YourFirstWP7App-KOR.pdf – Windows Phone 7 앱 시리즈 시즌 1

▼ Solution Explorer에서 PuzzlePage.xaml을 마우스 오른쪽 단추로 눌러 View Code를 클릭합니다.

Windows_Phone_Puzzle_step2_77

▼ 키보드의 Ctrl + F 를 눌러서 Find and Replace 창이 나오면 Find what: 란에 GameOver를 입력하고 [Find Next] 단추를 누릅니다.

Windows_Phone_Puzzle_step2_79

▼ 그리고 메서드 시작 부분에 아래의 코드를 삽입합니다.

    this.WinTransition.Begin();

※ 아래 부분입니다.

Windows_Phone_Puzzle_step2_80

참고: 게임이 끝나면 삽입된 코드가 스토리보드를 실행하여 완료된 퍼즐 이미지와 축하 메시지에 대한 시각 효과를 제공합니다.

02-YourFirstWP7App-KOR.pdf – Windows Phone 7 앱 시리즈 시즌 1

▼ 이번에도 Ctrl + F 를 눌러서 GameStarted 를 입력하고, [Find Next] 단추를 클릭합니다.

Windows_Phone_Puzzle_step2_81

▼ 메소드 시작 부분에 아래의 코드를 삽입합니다.

    this.ResetWinTransition.Begin();

※ 아래 부분입니다.

Windows_Phone_Puzzle_step2_82

참고: 삽입된 코드는 게임이 시작될 때 ResetWinTransition 애니메이션 스토리보드를 시작합니다. 이 스토리보드는 배경 이미지의 상태를 재설정하고 축하 메시지를 숨깁니다.

02-YourFirstWP7App-KOR.pdf – Windows Phone 7 앱 시리즈 시즌 1

▼ 변경 사항을 테스트하기 위해, Start Debugging 단추를 클릭합니다.

Windows_Phone_Puzzle_step2_83

▼ 퍼즐을 풀어보세요. 퍼즐을 풀고 나서 나타나는 화면(애니메이션)을 보고자 모든 퍼즐을 풀지 않아도 됩니다. [SOLVE] 단추를 누르면 퍼즐을 다 푼 것과 동일한 효과가 나타납니다. 윙크

Windows_Phone_Puzzle_step2_84Windows_Phone_Puzzle_step2_85

▼ 퍼즐 게임을 풀었을 때 나타나는 애니메이션입니다. 게임이 끝나면 애니메이션이 나타나고 보드가 중간 축을 중심으로 회전하면서 보드 위의 “CONGRATULATIONS” 텍스트를 포함한 박스가 점차 사라지는 것을 볼 수 있습니다.

참고: [SOLVE] 단추를 누르면 퍼즐을 다 푼 것과 동일한 효과가 나타납니다.

Puzzle Board in Visual Studio(Task 5-Verification)

소스 다운로드: (1.1MB)

다음 강좌에서는 isolate storage를 이용한 게임 상태 유지에 대해서 실습해 보도록 하겠습니다. 수고하셨습니다.

덧. 이번 포스트에서 다룬 실습 내용들은 모두 Microsoft에서 제공하는 Windows Phone 7 Training Course에 의한 것입니다. 한글로 된 자료 또한 제공되는데요, 해당 PDF문서(02-YourFirstWP7App-KOR.pdf, 1.64MB)는 Windows Phone 7 앱 시리즈에서 제공되고 있으니 많은 이용 바랍니다.

 

이전 강좌, 들어 보셨나요? 먼저 확인하세요.

HelloPhone:

Windows Phone Puzzle:

Archives