이번 시간에는 애플리케이션 로직을 프로그래밍 한다. 로직에는 퍼즐 보드를 초기화 하고 애플리케이션의 리소스에서 이미지를 읽고 사용자 인터페이스로 부터 이벤트에 대한 핸들러를 생성한다.
1. 프로젝트에 게임의 로직을 담는 클래스를 추가한다. [솔루션 탐색기]에서 WindowsPhonePuzzle 프로젝트 노드를 오른 클릭하고 [추가]|[기존 항목]을 선택한다. [기존 항목 추가] 대화 상자에서, 설치한 랩의 [Source]폴더의 [Assets]를 찾아서 PuzzleGame.cs를 선택하고 [추가]를 클릭한다.
[그림 10] 추가된 프로젝트 파일 표시
2. 3회에서 생성한 퍼즐 페이지의 코드 숨김 파일을 열고 PuzzlePage.xaml 파일을 오른 클릭한 뒤 [코드 보기]를 선택한다.
3. PuzzlePage.xaml.cs에 다음 네임스페이스 선언을 삽입한다.
using System.Windows.Media.Imaging;
using System.Windows.Resources;
4. PuzzlePage 클래스에서 아래 강조 표시한 멤버 변수 선언을 삽입한다.
public partial class PuzzlePage : PhoneApplicationPage
{
  private const double DoubleTapSpeed = 500;
  private const int ImageSize = 435;
  private PuzzleGame game;
  private Canvas[] puzzlePieces;
  private Stream imageStream;
public PuzzlePage()
  {
    InitializeComponent();
  }
}
5. 이제 아래 코드 조각에서 강조 표시한 ImageStream 속성을 추가한다.
public partial class PuzzlePage : PhoneApplicationPage
{
  ...
  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++;
        }
      }
    }
  }
public PuzzlePage()
  {
    InitializeComponent();
  }
}
6. 아래 코드 조각에서 강조 표시한 PuzzlePage의 생성자를 업데이트 한다.
public partial class PuzzlePage : PhoneApplicationPage
{
  ...
  public PuzzlePage()
  {
    InitializeComponent();
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();
  }
}
GameStarted: 이 이벤트는 새로운 게임이 시작할 때 일어난다. 이 이벤트에 대한 핸들러는 움직인 조각의 수를 패널에 표시하고 게임을 시작하는 방법을 지시하는 범례를 숨긴 후 움직인 조각의 수를 재 설정한다.
GameOver: 이 이벤트는 퍼즐을 풀었을 때 일어난다. 이 이벤트에 대한 핸들러는 게임을 시작하는 방법을 지시하는 범례를 표시 한 후 움직인 카운트를 업데이트한다.
PieceUpdated: 이 이벤트는 조각 이동이 일어날 때 마다 발생한다. 이 이벤트에 대한 핸들러는 움직인 조각을 애니메이션 한 후 이동 카운트를 업데이트한다.
 
마지막으로 이벤트 핸들러를 구독한 후, 생성자는 InitBoard  메서드를 호출해 게임 보드를 초기화 한다.
7. 다음 PuzzlePage 클래스에서 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();
}
8. PuzzlePage 클래스에 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();
}
9. MouseLeftButtonDown 이벤트에 대한 핸들러를 추가한다. 방법은 PuzzlePage 클래스에 다음의 강조 표시한 코드를 삽입한다.
private void PuzzlePiece_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
  if (!this.game.IsPlaying)
  {
    this.game.NewGame();
  }
}
이 이벤트 핸들러는 보드 표면을 클릭해 발생하고 이미 하나가 진행 중이지 않는 한 새로운 게임을 시작한다.
10. 이제 "Solve" 버튼의 Click 이벤트에 대한 핸들러를 추가한다.
private void SolveButton_Click(object sender, RoutedEventArgs e)
{
    this.game.Reset();
    this.game.CheckWinner();
}
11. [F5]를 눌러 애플리케이션을 빌드하고 윈도우 폰 에뮬레이터로 배포한다. 잠깐 기다리면 시작화면이 표시되고 바로 [START]를 클릭한다.
[그림 11] 새 게임 시작하기
12. 에뮬레이터 윈도우에서 이미지를 터치하면 퍼즐이 시작한다. 이미지는 여러 조각으로 나눠지고 랜덤한 방식으로 보드에 재 배열된다. AnimatePiece 메서드가 적용된 애니메이션 스토리보드의 결과로 부드럽게 조각이 슬라이딩 되지 않고 순간적으로 조각들이 위치를 바꾼다. 이 메서드는 각 퍼즐의 왼쪽 및 위쪽 좌표를 움직여 변형한다.
[그림 12] 게임 시작 후 보드에서 임의로 배열된 퍼즐 조각
13. 각 조각을 선택해 드래그를 시도해 보면, 현재는 아무런 효과도 없을 것이다. 이 부분은 다음 시간에 멀티 터치를 지원하는 코드를 추가해 볼 것이다.
14. 이제 [SOLVE] 버튼을 클릭하고 보드의 조각들이 재배치 되어 원리의 이미지를 표시하는 것을 확인하자.
15. [SHIFT+F5]를 눌러 디버깅 세션을 종료한다.
'Programming > Windows Phone' 카테고리의 다른 글
| 본격적인 윈도우 폰7 시대의 서막 - Windows Phone 7.1: Mango (0) | 2011.05.26 | 
|---|---|
| 생애 첫 번째 윈도우 폰 7 애플리케이션 만들기 5회 (0) | 2011.05.01 | 
| 생애 첫 번째 윈도우 폰 7 애플리케이션 만들기 3회 (0) | 2011.03.13 | 
| 2010년 7월 TechED의 윈도우 폰7 관련 사진 몇장. (2) | 2011.03.06 | 
| 생애 첫 번째 윈도우 폰 7 애플리케이션 만들기 2회 (0) | 2011.02.24 |