안녕하세요. 너만바라보면 입니다.

저번시간에는 퍼즐 게임의 UI, 게임 로직, 멀티터치 지원을 구현해 보았습니다. 이번시간에는 애니메이션 효과와 IsolateStorage를 이용하여 게임상태를 유지하는 것을 구현해 보도록 하겠습니다.

 

우리가 예전예전 시간에 윈도우폰7의 애니메이션 효과를 구현했던 적이 있었습니다. 지금쯤은 잘 기억이 안나시겠지만 말이죠? 그때 애니메이션 효과를 구현하기 위해서 작성했던 것이 바로 스토리 보드입니다. 우리가 스토리 보드에서는 타임라인을 지정할 수 있었죠?

이제 이것을 여기에 써먹어 볼 것입니다. 그런데 우리는 Blend로 작업하지 않고 Visual Studio에서 각각의 선택지점을 지정해 여기서 중요 속성 변경을 할 것입니다.

 

<phone:PhoneApplicationPage.Resources>

        <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>

        <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>

    </phone:PhoneApplicationPage.Resources>

 

PuzzlePage.xaml 파일을 여신후 xaml코드로 작성해 볼 것입니다. 위와 같이 xaml 코드에서도 작성해 주셔도 무방합니다. 물론 Blend를 이용해서도 가능하죠.

조금 설명을 드리자면 사용자가 이겼을 경우에 이 애니메이션 효과가 작동하게 되는데요. 애니메이션이 퍼즐 해답을 포함한 이미지를 중간 축을 중심으로 회전하고 이때 축하메시지가 점차 사라지는 모양이 나오게 됩니다. 게임에 이겼을 때 말고 사용자가 게임을 다시 시작할 때 발생하는 전환에 대한 스토리보드도 함께 작성되었습니다. 이 스토리보드의 이름은 ResetWinTransition 입니다.

 

이렇게 스토리보드만 작성해 놓는다고 만사 형통일까요? 아닙니다. 이 스토리보드는 단지 행위에 불과합니다. 이를 연결시켜줘야 겠죠? 게임이 끝나고 나서 이겼을 때 이 스토리보드가 실행되도록 행위를 지정해 줘야 합니다. 그런데 우리는 이미 저번시간에 PuzzlePage에 지정해 주었습니다.

this.game.GameOver += delegate

            {

                this.WinTransition.Begin();

 

바로 위와 같은 코드인데요. 게임이 끝났을 경우 WinTrasition을 시작하라는 것이죠?

 1.PNG

자 이제 애니메이션 효과도 완성되었습니다. 실행시켰을 경우 처음 Start 버튼을 누르시고 난 뒤의 화면입니다.

 

 

 2.PNG

퍼즐을 다 맞추고 나서 풀기를 누르면 이미지가 회전하면서 Congratulation! 이라는 문자가 사라질듯 하면서 나타나는 것을 보실 수 있습니다.

 

이제 우리가 만들어 볼 것은 Isolate Storage를 사용하여 게임상태를 유지하는 것을 구현해 보겠습니다. 쉽게 말해서 Isolate Storage라는 것은 제가 애플리케이션에서 이용하는 정보를 로컬 저장소에 저장하고 불러올 수 있는 그런 장소를 말합니다.

우리가 흔히 메모장을 통해 메모를 작성할때나 게임의 경우 현재 스테이지를 저장하고 다음에 다시 게임을 시작했을 때 전에 했던 스테이지를 이어서 하게끔 할 경우 등에서 Isolate Storage가 사용이 됩니다.

 3.PNG

우선 솔루션 탐색기에서 프로젝트를 클릭한뒤 마우스 오른쪽 버튼을 누르셔서 Add Reference를 실행해 줍니다. 우리에겐 이제 낯설지 않은 화면입니다. System.Servicemodel.Web을 선택하고 오케이 버튼을 눌러줍니다. 그 다음 IsolatedStorageHelper.cs 파일을 추가해 줍니다. 이 파일은 현제 게시물에 첨부 파일에 있습니다.

이정도 하면 이제 IsolatedStorage를 사용할 수 있는 환경을 설정된 것입니다.

설정만 했다고 사용이 가능할까요? 우선 IsolatedStorage를 사용할 수 있는 UI부터 구성해 봅시다. 불러오기 버튼과 저장 버튼 그리고 이 저장소를 비울 수 있는 클린 버튼을 만들어 봅시다.

버튼 만드는 것쯤이야 이제 식은죽 먹기이죠?

 

<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" >

                <Button x:Name="LoadButton" Content="Load" Margin="10" Click="LoadButton_Click" />

                <Button x:Name="SaveButton" Content="Save" Margin="10" Click="SaveButton_Click" />

                <Button x:Name="ClearStorageButton" Content="Clear" Margin="10" Click="ClearStorageButton_Click" />

            </StackPanel>

 


4.PNG

소스코드와 캡쳐된 화면을 참고하세요.

이제 무엇을 하면 될까요? 감이 잡히지 않으세요? 각 버튼들에 대한 이벤트를 설정해 줘야 겠죠?

private void LoadButton_Click(object sender, RoutedEventArgs e)

        {

            var gameState = IsolatedStorageHelper.GetObject<PuzzleState>("PuzzleState");

            if (gameState == null)

            {

                MessageBox.Show("Sorry, no game state found.", "Oops!", MessageBoxButton.OK);

            }

            else

            {

                // set game state

                this.game.SetState(gameState);

            }

        }

 

        private void SaveButton_Click(object sender, RoutedEventArgs e)

        {

            // save game state

            PuzzleState gameState = this.game.GetState();

            IsolatedStorageHelper.SaveObject("PuzzleState", gameState);

        }

 

        private void ClearStorageButton_Click(object sender, RoutedEventArgs e)

        {

            // remove state and image

            IsolatedStorageHelper.DeleteObject("PuzzleState");

        }

 

 

위의 코드와 같이 각각의 버튼에 대한 이벤트를 추가하였습니다. 그런데 중요한건 IsolatedStorageHelper 가 대부분의 역할을 수행하죠? 이것에 대한 설정도 빼놓지 말아야 합니다.

IsolatedStorageHelper.cs 파일을 클릭해서 코드를 수정해 봅시다.

public static T GetObject<T>(string key)

        {

            if (IsolatedStorageSettings.ApplicationSettings.Contains(key))

            {

                string serializedObject = IsolatedStorageSettings.ApplicationSettings[key].ToString();

                return Deserialize<T>(serializedObject);

            }

 

            return default(T);

        }

 

GetObject<T> 메서드에 대해서 알아봅시다. 이 메서드는 키가 주어진 Isolate Stroage에서 개체를 가져오는 역할을 합니다. 말그대로 Load 해오는 것이죠. IsolatedStorageSettings 클래스를 사용합니다. 우선 무엇인가가 저장이 되있어야 불러오겠죠? 아무것도 없으면 불러올것도 없으니까요.

 

public static void SaveObject<T>(string key, T objectToSave)

        {

            string serializedObject = Serialize(objectToSave);

            IsolatedStorageSettings.ApplicationSettings[key] = serializedObject;

        }

 

SaveObject<T> 메서드는 키가 주어진 Isolate Storage에 개체를 저장합니다. 그러면 저장소에서 개체를 다시 가져올 올 때 이 키를 사용할 수 있습니다.

 

public static void DeleteObject(string key)

        {

            IsolatedStorageSettings.ApplicationSettings.Remove(key);

        }

 

그 다음은 DeleteObject를 이용하여 개체를 제거하는 것을 구현하였습니다.

 

이제 실행시켜 보겠습니다.

 

시작 버튼을 눌러서 게임을 시작해 봅시다.

 

 

5.PNG

Isolated Storage 기능이 잘 구현되는가 확인해 보기 위해 Your Moves를 잘 체크해 주세요. 우린 이 상태를 저장시켜 놓고 로드가 잘 되는지 확인해 볼 것입니다. Save 버튼을 눌러보겠습니다.

 

6.PNG

10번을 이동한 다음 로드 버튼을 눌러보겠습니다.

7.PNG

로드 버튼을 누르면 위의 화면과 같이 저장되었던 것이 나타납니다

우리가 3강 동안 만든 애플리케이션의 완성 파일은 첨부파일에 같이 첨부해서 올려놓도록 하겠습니다. 참고하시기 바랍니다.

 

우리는 지난 3강동안 퍼즐 게임을 만드는 어플리케이션을 구현해 보았습니다. 사실 이 정도의 애플리케이션으로는 상용 애플리케이션으로써 Store에 올려서 배포하기에는 무리가 따르기는 합니다. 하지만 이렇게 여러분께서 직접 애플리케이션을 벌 것 아니더라도 한두개씩 만들어 보시고 또한 Microsoft 가 여러분들을 위해 공개한 애플리케이션의 소스를 탐구해 보시면서 더욱 학습하신다면 여러분들도 언젠가는 상용 애플리케이션을 개발 할 수 있는 훌륭한 개발자가 되어있을 것이라 굳게 믿습니다.

 

우리의 강의는 이것이 끝이 아닙니다. 마지막 강의에서 뵙겠습니다.

정말 수고하셨습니다.





안녕하세요.윈도우폰에 관심이 많은 너만바라보면 입니다.