티스토리 뷰

이번 시간에 다뤄볼 내용은 작년 9월에 있었던 Build Windows Conference에서 언급된 내용입니다. 이때 MS ATG부서의 PM Chales Cox 씨가 나와서 자신이 아주 단기간에 게임을 만들어보겠다고 합니다. 아마 그때 사람들은 이말을 믿었을까요? 그런데 그게 현실로 나타났네요.
 
원본은 여기서 확인할 수 있고, 자료는 없습니다. 뭐든 자신이 직접 만들고 결과를 보는게 낫겠지요.
그전에 윈도폰에서 게임이 어떻게 돌아가는지에 대한 언급이 필요한 것 같네요.


윈도우폰에서의 게임이란 어떻게 보면 잘 차려진 밥상위의 먹음직스런 반찬일 수 있습니다. MS는 이미 Xbox라는 플렛폼을 통해서 콘솔게임에 대한 노하우가 갖춰진 상태였고, 여기에 윈도우폰 게임의 환경을 구축하기 위해서 Cross Platform 체제를 형성했습니다. 즉 Xbox에서 하던 게임을 윈폰에서 할 수 있는 그런 환경을 미래에 구축하겠다는 거지요, 가까운 예로는 kinect Animal이나 사진에 나와있는 Full House Poker도 Xbox나 윈폰에서 같이 즐길 수 있는 게임 중 하나이지요.


개발 활경은 다음과 같이 Silverlight와 XNA의 Interop을 통해서 구현됩니다. 당연히 이를 개발하는 언어는 c#/.net이 되겠네요.


 XNA Studio의 전체적인 루프입니다. XNA는 어떻게 보면 Visual studio에서 게임에 관해 아주 잘 만들어진 템플릿입니다. 기존 코드가 전체적인 개체를 통합적으로 관리했던 것에 비해서 XNA 프로젝트는 각 개체에 대한 관리가 효율적으로 이뤄집니다. 여기서 게임에 관한 주요 동작을 구현하는 곳이 바로 Update와 Draw 메서드 입니다. 여기서 게임의 AI나 그래픽의 형성 등이 이뤄지는 곳이지요. 

 
보통 이런식으로 이미지가 폰상에 호출됩니다. 이때 이미지를 사용하기 위해서는 사전에 spriteBatch 라는 변수를 호출하고 그에 대한 Player라는 컨텐츠를 불러오는 형식이 됩니다. 물론 이에 대한 것을 폰상의 화면에 그려주는 과정은 앞에서도 언급한 것처럼 Draw 메서드에서 이뤄집니다.

자 이제 본격적으로 프로젝트를 만들어보겠습니다. XNA game Studio를 통해서 MyBuildShooter라는 프로젝트를 생성합니다.


우선 기존과 다른게 없는 프로젝트가 형성되는데 우리가 맨 처음으로 봐야 할 곳은 바로 Solution Explorer입니다.
여기서 모든 개체들의 관리가 이뤄집니다. 만약 이미지를 삽입해야 된다고 하면 기존에는 그냥 Image폴더를 만들고 통합적으로 삽입했던 것에 비해서 XNA에서는 Content 항목이 따로 있습니다. 여기다가 삽입을 하면 되지요.

이번 프로젝트의 목적은 shooting 게임이니까 몇가지 예제파일을 삽입해보도록 하겠습니다.


(위 파일들은 모두 오픈 소스입니다.)

이걸 압축을 푸시면 이미지 파일 2개와 음원 파일 하나가 있습니다. 이를 Content에 Add-Existing Item항목을 통해서 삽입해줍니다.


 
이렇게 하면 Content 항목에 개체가 삽입된 것을 확인할 수 있습니다. 각각 이름을 바꿔봐야겠네요. 이 파일 이름을 토대로 코드에서 불러오는 것이기에 파일이름은 간단한게 좋겠지요.
 


자 이제 본격적인 코드 작성 시간입니다. 변수로 Player의 이미지와 Player의 위치를 지정해줄 PlayerPosition이 필요합니다.


이 이미지의 호출은 LoadContent에서 이뤄집니다. 아까 선언한 PlayerTexture에 Content를 Load해야 합니다.


Cox씨 말로는 이 툴 자체가 유기적으로 되어 있어서 파일명만 써놓으면 포멧에 상관없이 호출을 해올 수 있다고 하네요. 물론 기존 방식대로 파일의 경로를 지정해줘도 파일을 Load할 수 있습니다.

폰 상에 player 이미지를 호출하기 위해서는 앞에서도 말씀드렸지만 Draw라는 곳에서 이뤄집니다. 


위와 같이 그리는 작업을 합니다. spriteBatch는 한마디로 그리는 펜이라고 보시면 됩니다. 폰이 이미지를 그릴 때 가상의 펜이 동작을 시작하면서 해당 개체를 그대로 그리게 하지요. 물론 마치고 나서는 동작을 멈추게 하는 작업이 필요할 거고요.여기까지 해보고 실행하면 다음 결과가 나옵니다.



기존 이미지가 조금 크지요. 이 부분은 나중에 크기 조절하면서 수정해보겠습니다. 우선은 지금 PlayerPosition이 지정되지 않았기 때문에 기본적인 값인 (0 0)으로 설정된 것을 확인할 수 있습니다. 지금 위 이미지는 정지된 화면처럼 보이지만 사실은 초당 30프레임의 속도로 계속 그려지고 있는 겁니다. 이렇게 뿌려지고 있기 때문에 이미지의 이동이 자연스럽게 보이는 겁니다.

여기서 다시 개념 설명이 들어가야겠네요.


XNA에서 주로 삽입할 수 있는 2D 개체는 SpriteBatch와 SpriteFont입니다. 여기서 조금 특이한건 SpriteFont라는 건데요. SpriteFont도 하나의 이미지입니다. 기존 WPF 환경에서 Font 삽입은 TextBlock을 삽입해서 이뤄졌지만 XNA에서는 하나의 이미지로써 처리됩니다. 이로써 얻는 장점은 SpriteFont는 벡터 이미지로 그려지기 때문에 아무리 늘려도 폰트가 깨지지 않는다는 겁니다. 물론 하나의 이미지이기에 다른 작업을 더 할 수 있고요.
 또 하나의 장점은 3D를 그리는 것도 가능하다는 겁니다. 다양한 작업을 제공합니다.

위에서 언급된 특징을 통해서 XNA는 사용자가 조금더 쉽고 빠르게 애니메이션을 구축할 수 있는 환경을 제공했습니다.


XNA에서 제공하는 3D 효과는 총 5개입니다. 물론 일반적인 3D 툴이 제공하는 기능보다는 적긴 하지만 그래도 기본적인 게임을 개발하는데 있어서 부족함이 없어보입니다. 다만 cox씨가 언급하는 단점으로는 Shader에 대한 지원이 미미하다는 겁니다. 이 점은 고려하셔야 할 부분인 것 같습니다.

이제 터치에 대한 코드를 분석해보고자 합니다.


터치는 다음과 같이 이뤄집니다. 그냥 자유롭게 움직이는 것을 구현하고 싶으면 FreeDrag라는 속성을 사용하면 됩니다.

코드에서도 그대로 적용해보겠습니다.


Initialize 메서드에 이렇게 형성함으로써 자유롭게 이동할 수 있는 여지를 만들었습니다. 이제 다음으로는 Update를 건들여야 하겠지요.

 
 Gesture가 들어왔을때 그 gesture가 FreeDrag의 형태로 띌때 다음 조건문이 실행될겁니다. 위의 예시에 나온대로 PlayerPostion을 Delta에 매핑하면 됩니다.


다음이 실행결과입니다.
 


 
마우스를 누른 상태에서 잘 움직이네요. 


유의를 해야될 점은 GestureSample은 터치 포인트를 2점에 한해서만 사용할 수 있습니다. 따라서 손가락 두개로만 할 수있는 작업들이 포함되겠지요. 그 외의 터치포인트를 쓰려면 GetState라는 메서드를 사용해야 합니다.이는 터치 포인트를 4개까지 인식할 수 있습니다. 만약 GestureSample을 쓴다면 drag나 tap pinch에 관한 알고리즘을 작성할 필요가 없겠지만 GetState를 쓴다면 각가의 포인트에 대한 코드를 일일이 작성해줘야 합니다.
 물론 다른 OS의 폰에서도 개발에 적용되는 거지만  7.1 망고버전부터 자이로센서와 모션 API의 사용이 가능해졌습니다.

다음으로 해야 될건 enemy의 움직임이겠지요? 기본적인 틀은 다음과 같습니다.


역시 변수로 적의 위치에 대한 항목이 지정이 되어야 하겠고 그 이동점이 sin함수의 형태를 띄는 것을 확인할 수 있습니다.


 앞에서 언급한 것 이외로 필요한 것이 적의 초기 위치와 이동할 때 속도입니다. 초기 위치가 중요한 것은 처음 시작할 때부터 게임 종료가 된다면 이는 적절하지 않은 거겠지요. 그리고 적은 계속 움직여야 하니까 그에 대한 속도도 중요한 것이고요. 참고로 Cox씨는 스피트가 8.0f라고 한 것에 대해서 Magic Number라고 하네요. 

역시 Player를 호출한 것과 마찬가지로 LoadContent부분에서도 적의 이미지를 불러오는 과정이 필요합니다. 그리고 이부분에서는 적의 초기위치에 대한 설정도 필요합니다. 



적의 위치를 초기위치로 설정하면 그 위치에 처음 생성되겠지요.


 Update에서는 적의 위치가 실시간으로 어떻게 변화할 것인지에 대한 정의가 필요합니다. 앞에서 말한대로 sin곡선을 이용해서 y값이 변화하는 것을 적용시켰습니다. 그리고 화면에서 안보이면 다시 나타나야 하기 때문에 초기화 시키는 과정도 필요하고요. 이제 동작을 설정했으니 Draw에서 그려야 합니다. 이 역시 Player 그리는 과정과 동일합니다.
 

 실행결과는 다음과 같습니다.


적이 사인곡선을 이루면서 그려지는게 보이지요.

다음으로 할 작업은 충돌 및 총알에 관한 이벤트 형성입니다. Update에서 계속해서 다루겠습니다.


충돌에 대한 이벤트를 만들려면 그 이미지 자체가 충돌의 개체가 되는 것이 아니라 그 이미지에 해당하는 사각형을 만들어서 서로가 부딪쳤을 때의 상황을 형성하게 됩니다. 충돌 이벤트는 다음과 같은 예시로 드는게 좋을 거 같네요.


이렇게 해놓는다면 적을 지정한 사각형이 Player의 Rect에 부딪친다면 다시 원래위치로 돌아가게끔 하는 것이죠. 이것에 대한 결과는 또 다음과 같이 나오겠네요.


다음으로 나오는 이야기가 바로 이동 패턴입니다.


뭔가의 충돌을 할때는 BoundingBox나 Sphere Rectangle을 이용해서 사용하는데 이번 프로젝트에서 우리가 사용한 것은 Rectangle이었지요. 여기에 뭔가 충돌할 때 쓰는 InterSects 라던가 아니면 뭔가 포함되는 Contains 메서드를 사용한다면 조금더 효과적인 이벤트를 줄 수 있겠지요. 추가적으로 균일적인 이동 패턴을 막기 위해서는 항상 Random이라는 개념이 포함되어 있어야 합니다.

다음으로 할 내용은 바로 소리 삽입입니다.


소리도 하나의 개체입니다. 따라서 Content의 Load 하는 과정이 필요한데 기존에는 Texture2D로 했던 부분을 여기서는 SoundEffect로 처리했습니다. 이때문에 변수 지정할 때도 이런 과정이 필요하겠지요.


Demo로 제공된 파일은 총 소리가 나는 거니까 Gun이라는 이름의 SoundEffect가 들어갔습니다. 이에 대한 내용은 다른 개체와 마찬가지로 LoadContent에서 이뤄져야 합니다.


그러고 나서 아까 만든 충돌했을 때의 이벤트에 이 SoundEffect를 실행시키면 되겠지요.


결과는 역시 예상한 것과 같습니다.


조금 안어울리긴 하지만 그래도 적당한 사운드 소스를 찾는다면 왠만한 효과도 낼 수 있을 듯 합니다.
여기까지가 XNA로 간단한 게임을 만드는 과정입니다. 
다음으로 해볼 부분은 XNA와 Silverlight를 동시에 사용하는 겁니다. 이른바 SLXNA라고 하지요.


이를 이용하면 확장성에서 더 큰 이점을 가질 수 있습니다. WPF 기반을 사용하기 때문에 Silverlight관련 Control을 사용할 수 있고 실질적인 게임을 사용할 때는 XNA로 switching해서 사용할 수 있습니다. 하지만 이를 이용할 때 돌아가는 방식이 조금 바뀝니다.


전에는 Update와Draw의 반복으로 게임이 진행되었지만 Silverlight로 전환되면서 Initialize 과정이 사라집니다. 전반적인 메서드의 이름도 다 바뀝니다. 뭐 그래도 거진 비슷한 과정을 거칩니다. 다만 Silverlight 의 개념으로 페이지가 나타나기 때문에 Navigate라는 방식을 써야 합니다. 
그럼 이전에 만든 XNA 게임을 Silverlight로 바꿔보겠습니다.

게임 프로젝트 생성시 다음과 같이 하고 이름을 MyBuildShooterInSLXNA로 하겠습니다.


Solution Explorer를 보면 왠지 뭔가가 짬뽕된 듯한 느낌을 받습니다.


여기서 위에서 언급한 Integration을 해주면 됩니다. LoadContent에 있던 내용을 OnNavigatedTo로 옮겨주는 식으로 해주면 됩니다. 참고로 우리가 수정하는 코드파일은 GamePage.xaml.cs 파일입니다.

그리고 일부 오류가 나는 분이 계실텐데요. Using 구문과 Reference 파일을 정리해줘야 합니다. 이상하게 새로 만든 프로젝트의 라이브러리 파일이 Reference로 삽입되어 있는데 이 부분을 삭제하시고 Using 지시자 속에 XNA 관련 네임스페이스를 기존 XNA로만 만들었을때 생성되는 것으로 대체해야 합니다.


이렇게 해야 soundEffect관련 항목 오류가 발생하지 않습니다. 나머지 항목은 그대로 옮기면 됩니다. 그 후에 Content라고 되어 있는 부분을 전부 contentManager로 바꿔줘야 합니다. 또한 기존에 GraphicsDevice로 정의되어 있는 부분을 다음과 같이 바꿔줘야 합니다.

(참 위의 using 지시자속에 System.Windows.Shape 항목을 빼야 합니다. 이게 Xna.Framework와 충돌이 일어납니다. 이 부분을 정리하지 않으면 Rectangle 생성시 오류가 발생합니다.


OnUpdate도 똑같습니다.


OnDraw 메서드도 똑같습니다.


이번에는 배경색을 파란색으로 바꿔봤습니다.

----------------------------------------------------------------------------------------------------------
그런데 이부분 부터 오류가 납니다. 구글링 결과 버그인 거 같습니다. 앞에서 SLXNA에서도 Content 항목이 따로 있다고 말씀드렸는데 이 프로젝트 자체에서 Content를 인식하지 않습니다. 임의로 할려면 Main Root에 그 Content를 삽입하면 되는데 이러면 굳이 SLXNA로 만들 필요가 없는거지요. 혹자의 말로는 프로젝트 이름을 다르게 해서 하면 정상적으로 된다고 하는 버그 인것 같습니다. 이에 대한 부분은 조금 확인해봐야 할 듯합니다.

어쨌든 XNA로 간단한 게임을 만들어보면서 게임이 어떻게 형성되는지를 확인해보았습니다.SLXNA가 제대로 잘 된다면 딱히 UI 부분을 어떻게 하지 않더라고 Controls 등으로 처리 가능한 부분도 있을 것이고요.

댓글