이번 시간에는 애플리케이션 로직을 프로그래밍 한다. 로직에는 퍼즐 보드를 초기화 하고 애플리케이션의 리소스에서 이미지를 읽고 사용자 인터페이스로 부터 이벤트에 대한 핸들러를 생성한다.
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 |