우선 전에 언급했다시피 WP7의 라이브러리는 WP7 Framework 아래에 Silverlight, XNA로 나누어진다. 전에 내용을 유심히 읽어봤거나 관심이 있다면 알겠지만 XNA가 게임 개발을 하는데 필요한 주요 클래스 라이브러리이다. WP7에 탑재된 XNA 라이브러리 목록들은 다음과 같다.
명칭 |
객체의 위치 |
설명 |
Input |
Microsofot.Xna.Framework.Input |
XBOX 360 컨트롤러 장치, 키보드, 마우스의 입력을 받을 수 있는 함수와 클래스 제공 |
Media |
Microsofot.Xna.Framework.Media |
음악과 앨범, 재생 목록, 사진 등의 자료 재생 및 접근과 관련된 클래스를 제공 |
Content |
Microsofot.Xna.Framework.Content |
콘텐츠 파이프라인을 통해 게임에서 사용하는 자원들을 각 파일 포맷에 따라 관리하고 유지하는데 필요한 클래스 제공 |
GameServices |
Microsofot.Xna.Framework.GameServices |
XBOX LIVE와 관련된 기능을 담당. LIVE 관련 GUI포함되어있다. 게이머의 데이터와 직접 통신하거나 선택을 반영할 수 있는 API 제공 |
Graphics |
Microsofot.Xna.Framework.Graphics |
3D 오브젝트를 표시하는 하드웨어 가속 기능과 렌더링을 포함하는 낮은 수준의 응용 프로그램 인터페이스를 제공 |
Audio |
Microsofot.Xna.Framework.Audio |
XACT로 만든 프로젝트 및 콘텐츠 오디오 파일을 재생하고 낮은 수준의 인터페이스 방식에서 조작할 수 있는 클래스 제공 |
Touch |
Microsofot.Xna.Framework.Input.Touch |
터치 기반 입력 장치를 활성화하는 클래스를 제공 |
Network |
Microsofot.Xna.Framework.Net |
XNA 프레임워크 게임상에서 XBOX Live 멀티 플레이어 지원 및 네트워킹을 구현하는 클래스를 제공 |
Storage |
Microsofot.Xna.Framework.Storage |
저장장소의 파일 입출력을 담당 |
VS 2010에 많은 템플릿이 있는데, 우리가 개발할 WP7에 관련된 템플릿은 Windows Phone Game과 Windows Phone Game Library이다. Content Pipeline Extension Library 템플릿은 기본적으로 제공되는 콘텐츠 파이프라인이 지원하지 않는 형식의 파일을 사용하거나 콘텐츠 정보를 임의로 변경하고 싶은 경우 같은 것을 위한 기본틀을 제공한다.
한 번 Windows Phone Game 템플릿을 생성해보자. 역시나 메뉴에서 Device를 Windows Phone 7 Emulator로 바꾸고 실행해보자.
그러면 아무것도 없는 마치 9x계열 커널을 처음 깔았을 때나 보일법한 새파란 화면이 보인다.. 만약 실행이 안된다면 이전에 설명했던 최저사양을 만족하지 못하는 PC임을 알 수 있을 것이다.
간단히 소스를 살펴보자.
Program.CS
C#에서 WF를 해본 사람이라면 느낌이 오겠지만 Program.cs는 작업하는데 그리 중요한 파일이 아니다 (물론 중요하지만 별 내용이 없다……… 나도 여기다가 별 짓을 다해봤기 때문에 중요하지 않다곤 말 할 수 없겠다^^; )
그냥 단지 이 프로그램을 구동하기 위한 파일이다.
1: using System;
2:
3: namespace HelloXNA4
4: {
5: #if WINDOWS || XBOX
6: static class Program
7: {
8: /// <summary>
9: /// The main entry point for the application.
10: /// </summary>
11: static void Main(string[] args)
12: {
13: using (Game1 game = new Game1())
14: {
15: game.Run();
16: }
17: }
18: }
19: #endif
20: }
진짜 별 것 없다.
Game.cs
나는 초기화나 이런 것을 굉장히 중요하다고 생각하는 사람이다. 자세히 알아볼 가치가 있다.
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using Microsoft.Xna.Framework;
5: using Microsoft.Xna.Framework.Audio;
6: using Microsoft.Xna.Framework.Content;
7: using Microsoft.Xna.Framework.GamerServices;
8: using Microsoft.Xna.Framework.Graphics;
9: using Microsoft.Xna.Framework.Input;
10: using Microsoft.Xna.Framework.Input.Touch;
11: using Microsoft.Xna.Framework.Media;
12:
13: namespace HelloXNA4
14: {
15: /// <summary>
16: /// This is the main type for your game
17: /// </summary>
18: public class Game1 : Microsoft.Xna.Framework.Game
19: {
20: GraphicsDeviceManager graphics;
21: SpriteBatch spriteBatch;
22:
23: public Game1()
24: {
25: graphics = new GraphicsDeviceManager(this);
26: Content.RootDirectory = "Content";
27:
28: // Frame rate is 30 fps by default for Windows Phone.
29: TargetElapsedTime = TimeSpan.FromTicks(333333);
30: }
31:
32: /// <summary>
33: /// Allows the game to perform any initialization it needs to before starting to run.
34: /// This is where it can query for any required services and load any non-graphic
35: /// related content. Calling base.Initialize will enumerate through any components
36: /// and initialize them as well.
37: /// </summary>
38: protected override void Initialize()
39: {
40: // TODO: Add your initialization logic here
41:
42: base.Initialize();
43: }
44:
45: /// <summary>
46: /// LoadContent will be called once per game and is the place to load
47: /// all of your content.
48: /// </summary>
49: protected override void LoadContent()
50: {
51: // Create a new SpriteBatch, which can be used to draw textures.
52: spriteBatch = new SpriteBatch(GraphicsDevice);
53:
54: // TODO: use this.Content to load your game content here
55: }
56:
57: /// <summary>
58: /// UnloadContent will be called once per game and is the place to unload
59: /// all content.
60: /// </summary>
61: protected override void UnloadContent()
62: {
63: // TODO: Unload any non ContentManager content here
64: }
65:
66: /// <summary>
67: /// Allows the game to run logic such as updating the world,
68: /// checking for collisions, gathering input, and playing audio.
69: /// </summary>
70: /// <param name="gameTime">Provides a snapshot of timing values.</param>
71: protected override void Update(GameTime gameTime)
72: {
73: // Allows the game to exit
74: if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
75: this.Exit();
76:
77: // TODO: Add your update logic here
78:
79: base.Update(gameTime);
80: }
81:
82: /// <summary>
83: /// This is called when the game should draw itself.
84: /// </summary>
85: /// <param name="gameTime">Provides a snapshot of timing values.</param>
86: protected override void Draw(GameTime gameTime)
87: {
88: GraphicsDevice.Clear(Color.CornflowerBlue);
89:
90: // TODO: Add your drawing code here
91:
92: base.Draw(gameTime);
93: }
94: }
95: }
96:
간단히 각 메서드의 역할에 대해 알아보자.
Initialize는 게임이 실행 될 때 Device관련된 초기화를 해주는 함수임을 알 수 있다. Android도 처음에 상속을 받아서 Initialize를 진행하게 되는데 그것과 같은 방식임을 알 수 있다. 여기다가 나중에 첫 구동시 필요한 것들을 적으면 되겠다. base.Initialize는 WF의 InitializeComponent 메서드의 개념 정도가 되는 것 같다.
* 아 혹시 base가 뭔지 가물가물한 분들을 위해 , base는 JAVA의 super와 같은 개념이다.(즉 자식 클래스가 부모 클래스의 메서드등을 이용할 때 쓴다는 이야기)
LoadContent 메서드는 이미지, 모델, 음원등의 컨텐츠를 사용하기 위한 역할을 수행한다. 프로젝트에 추가된 컨텐츠들은 빌드시 알아서 XNB 파일로 변환되어 저장되고, 저장된 파일을 읽어들이는 역할을 한다.
Update 메서드는 WIN32API의 WM_TIMER(?) 정도의 느낌이라고 할 까. 1초에 30번 실행되는 사실상 게임에서의 동적인 모든 것들을 처리하기 위한 메서드이다.
Draw 메서드는 이미지와 모델 텍스트등을 출력하는 메서드이다. WP7의 해상도에 맞게 알아서 출력이 됨이 참 기특한 메서드이다.
UnloadContent 메서드는 LoadContent에서 로드한 컨텐츠들을 해제하는 역할을 게임 종료 또는 장치가 변경되거나 할 때 수행된다.(소멸자 정도로 생각해도 되겠다)
이것을 그래픽으로 표현하면 다음과 같이 볼 수 있다.
그럼 여기에 간단히 텍스트와 이미지를 하나씩 출력해보겠다.
Solution Explorer를 보면 최하단에 프로젝트명Contents(Content) 라는 프로젝트가 있을 것이다. 우클릭을 하여 Add->New Item을 해보자.(한글로는 추가 –> 새 항목이다. 만약 그림이 필요하다면 좀 더 IDE에 익숙해질 필요가 있겠다ㅜㅜ)
우선 글자를 추가해보자. Sprite Font를 선택하자.
생성했더니 뭔가 소스가 많이 나온다 –_- 억… 자 이것은 일단 나중에 필요할 때 자세히 알아보기로 하고 넘어가자. 사실 살펴봐도 XML파일인 것 같은데 인코딩 형식, 끌어와야 할 네임스페이스, 폰트명, 글자크기, 자간이 있는 것 같은데 UseKerning과 Style을 잘 모르겠는데 인터넷으로 알아본 바 UseKerning은 단어간 간격이고 Style은 아마 Bold Italic등을 지정하는게 아닌가 추측해본다. Character Region이야 국가코드명이 아닐까?(무책임…)
일단 이 놈을 써보자.
Game1.cs 파일로 돌아가 Game1 생성자 전에 다음과 같은 내용을 추가해보자.
뭐라고 쓰던 자유지만 본인은 이렇게 선언을 해 보았다. (text는 구지 영어 한글 구분해 본 이유는 에뮬레이터에서 한글이 잘 되나 궁금했던 것 뿐이다. 안되면……………………… 비트맵으로 완성형 한글을 써야 하나?^^; )
1: SpriteFont spriteFont;
2: String messageTextKor = "뭐임마 깝ㄴㄴ요";
3: String messageTextEng = "You don't know me? shut up boy";
그 후 LoadContent메서드에서 Load 메서드를 이용해 한번 로딩을 해보자. 아래와 같이 쓰면 된다.
1: protected override void LoadContent()
2: {
3: // Create a new SpriteBatch, which can be used to draw textures.
4: spriteBatch = new SpriteBatch(GraphicsDevice);
5:
6: // TODO: use this.Content to load your game content here
7: spriteFont = Content.Load<SpriteFont>("SpriteFont1");
8: }
소스에서 보이는 성의 없는 객체 생성… 만약 아까 새 항목에서 이름을 지었다면 큰따옴표 안에 자신이 만든 항목의 이름을 적어주면 되겠다. 그 후 Draw 메서드에 글자 출력을 넣어보자.
1: protected override void Draw(GameTime gameTime)
2: {
3: GraphicsDevice.Clear(Color.CornflowerBlue);
4:
5: // TODO: Add your drawing code here
6:
7: spriteBatch.Begin();
8:
9: spriteBatch.DrawString(spriteFont, messageTextEng, Vector2.Zero, Color.Black);
10:
11: spriteBatch.End();
12:
13: base.Draw(gameTime);
14: }
저렇게 치면 일단 글자가 나오는데 이게 가로로 놓을 때 기준인 것 같다(뭐 게임 만들 때 난 이렇게 만들꺼니 상관 없지만…)
아 그리고 한글로 바꿔보자.
아, 에러 난다. … 망했네. 라는 생각을 가져본다.
인터넷의 수많은 LocalizationPipeline.dll 파일을 써서 하는 무언가를 살펴봤는데.. 어… 난 컴파일이 안된다. 전생에 지은 죄가 많은가? 아.. 현생에도 지은 죄는 많다..ㅜㅜ 어쨌든 남따라 해도 안되는 나는 예제를 또 분석한다.
http://create.msdn.com/en-US/education/catalog/sample/localization
여기에 보면 4.0용 예제가 있는데 XBOX360용이랑 윈도우 용 밖에 없다. 그래도 한 번 해보자. .NET은 그래서 있는거니까!
우선 Content 쪽 참조를 보니
우선 using부터 살펴보면 기본 프로젝트보다 한 줄이 추가된다.
using System.Globalization;
바로 임마인데.. 임마를 일단 추가하자.
내리다보면 내가 싫어하는 템플릿-_-을 쓰는 함수가 하나 있다. LoadLocalizedAsset이라는 함수인데 쭈욱 훑어보면 현재 프레임웍에서 인지하고 있는 언어를 언어의 정보를 얻어와서 매칭을 시켜주는 것 같다.
그리고 Draw함수를 살펴보자.
아.. 뭐야 안되잖아.
XBOX용 예제에는 Strings 라는 클래스인지 네임스페이스가 있는 것 같은데 WP에는 없다. 참조가 다른건지 뭔지 아 지친다. 그래서 인터넷에서 본 걸 따라해봐야겠다고 생각했다.
안된 이유는 Pipeline(WP에서는 Content)의 bin/Windows Phone/Debug 폴더에 dll 파일을 넣어야 된다는 것이었다. 일단 이것을 넣고..
http://blog.naver.com/windoxpxp/10101609237
이 링크를 참조했다.
뭐 텍스트를 직접 입력해도 되는데 사용할 한글 문자가 Resx파일에 있어야 한다. –_-.. 나중에 유니코드 홈페이지 들어가서 한번 긁어야겠단 생각이 들었다.
와 2시간간의 삽질을 끝내고 한글을 출력하니 너무 행복하다 ^^;; 이제 이미지를 넣어보자.
자 그 전에 해결할 것이 화면 해상도를 알아야한다.
에뮬을 실행하면 자동으로 눕혀서(480*800)에서 나오는데, 이게 가로로 될 수도 있는데 그런 것을 처리해야한다.
우선 이미지 표시되는 형식을 보면 너무나도 익숙하게도 Back Buffer와 Front Buffer가 있다. 이것을 Flip하면서 그려주는 형태인데… 즉 Back Buffer에 다 그리고 Front Buffer로 그림을 옮긴단 이야기가 된다.
우선 가로로 한다는 가정하에 버퍼 크기를 설정한다. 이것은 생성자에서 처리를 해주면 된다.
생성자를 다음과 같이 수정하자.
1: public Game1()
2: {
3: graphics = new GraphicsDeviceManager(this);
4: Content.RootDirectory = "Content";
5:
6: // Frame rate is 30 fps by default for Windows Phone.
7: TargetElapsedTime = TimeSpan.FromTicks(333333);
8:
9: graphics.PreferredBackBufferWidth = 480;
10: graphics.PreferredBackBufferHeight = 800;
11: }
graphics는 GraphicsDeviceManager 클래스고 그 속성에 후면 버퍼의 가로 세로 길이를 정할 수 있다. 원하는 값을 넣으면 바뀌게 된다. 물론 WP7에 자동 하드웨어 스케일링 기능이 있어서 화면을 알아서 늘리거나 줄여서 표기가 되고 프로그램의 성능에는 영향을 미치지 않는다(하드웨어가 지원하므로)
프로그램의 화면 회전을 고정시킬 필요가 있는데, 그럴땐 GraphicsDevice 클래스의 SupportedOrientations 속성을 사용하면 된다.
DisplayOrientation 열거형 멤버를 참조해보자.
1: public enum DisplayOrientation
2: {
3: Default = 0,
4: LandscapeLeft = 1,
5: LandscapeRight = 2,
6: Portrait = 4,
7: }
이렇게 4개로 구성되어있는데, Default는 그냥 세운 상태, LandscapeLeft와 right는 좌, 우로 가로로 둔 상태, Portrait는 뒤집어 둔 상태라고 생각하면 된다. 나는 넓은 화면만 지원할 것이므로 다음과 같이 작성해본다.
graphics.SupportedOrientations = DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight;
화면이 어느 쪽으로 돌아가는 보통 긴 축이 X로 표기되지만, Portrait 형을 포함할 경우 짧은 축이 X축을 갖게 된다. 이 점 유의하자.
풀스크린 모드를 이용할 수 있는데 위의 통신사나 핸드폰의 상태를 표기하는 부분을 가리게 된다. GraphicsDevice의 IsFullScreen 옵션을 이용하면 된다.
자.. 기본적인 상황은 이 쯤으로 알아두고 이제 정말 그림을 띄워보자.
지원 포멧은 겁나 많다. 일반적인건 다 된다고 생각하면 된다. 물론 3ds 같은건 안된다^^; 그 이외에도 등록이 가능하나 역시나 작업을 좀 해야 한다.
추가한 이미지의 속성을 좀 보면 다음과 같다.
AssetName – 이름 정도라고 생각하면 된다.
Build Action – 굳이 바꾸지 않았으면 한다. 책에 적혀있는 건데 리소스가 많이 필요한 프로그램은 미리 컨버팅을 해두면 컴파일 시간을 줄일 수 있다고 한다.
Xontent Importer는 파일 형식이다. 이것도 굳이 건드릴 필요는 없을 것 같다.
Content Processor 처리 방식을 선택하는 건데, 이 또한 지금은 굳이 건드릴 필요가 없지 싶다.
Color Key Color가 있는데 선택한 색상을 투명으로 처리하는 것이다. 컬러키라는 개념에 익숙한 분들은 잘 알 것이라 믿는다.
Color Key Enable은 컬러키로 설정한 색상을 투명 처리할 것인가 말 것인가 이다.
Generate Mipmaps 밉맵 생셩 여부를 결정하는데 사용된다.
Permutiply Alpha – 알파 값을 색상에 곱하여 저장하는 방식으로 색상 정보를 압축할 때 쓰인다
Resize to Power of Two - 2의 승수로 파일의 가로 세로 길이를 조정할 것인지를 묻는다. True로 바꿀 경우 알아서 커지기 때문에 애초에 2의 승수로 만들던지 False로 쓰기를 추천한다.
Texture Format – XNB로 변경할 때 어떤 방식으로 저장할지를 결정한다. NoChange는 그대로, Color는 XNA 색상 형식으로 저장, DxtCompressed는 DX의 텍스쳐 형식으로 저장을 한다. 크기가 많이 작아지나, 2의 승수여야만 한다.
출력 디렉토리로 복사 – 이름 그대로.
파일 이름 – 콘텐츠 파이프라인에 등록된 리소스 파일의 이름을 결정한다.
자… 이제 이미지를 로드해보자.
우선 생성자 위에 또 선언을 해보자.
Texture2D texture;
라고..
그리고 생성자를 수정하자. 나는 아까 내가 원하는 것들을 적절히 조합했고 풀스크린 옵션은 기본으로 쓰기로 해서 안 설정했다.
1: public Game1()
2: {
3: graphics = new GraphicsDeviceManager(this);
4: Content.RootDirectory = "Content";
5:
6: // Frame rate is 30 fps by default for Windows Phone.
7: TargetElapsedTime = TimeSpan.FromTicks(333333);
8:
9: graphics.PreferredBackBufferWidth = 480;
10: graphics.PreferredBackBufferHeight = 800;
11:
12: graphics.SupportedOrientations = DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight;
13: }
그 후, LoadContent부분을 수정한다.
texture = Content.Load<Texture2D>("Leva");
이 문구를 수정하자. 나는 이미지의 AssatName이 Leva라서 저렇게 해놨다. 왜 저런 이름인지는 곧 알게 됨.
그 후 Draw 함수에 아까 글자 출력을 넣던 위치에 다음 줄을 넣어보자.
spriteBatch.Draw(texture, new Rectangle(0, 0, 480, 800), Color.White);
그리고 이쯤이면 다들 눈치챘겠지만 Content.Load 함수는 템플릿.. 그렇다. C#에서는 제네릭 형식으로 콘텐츠 파이프라인이 지원하는 형식이면 모두다 넣을 수 있다.
그래서 난 성공했다.
다음엔 스프라이트에 대해 알아보도록 하겠다.