Intermission #4 – Moving to Windows Phone
No need to guess what this little section is about. Games on Windows Phone 7.
For now this is going to be a side project to the main tutorial since XNA 4.0 and the Windows Phone 7 kit hasn’t been fully released yet. So on with the show.
Source updated for Final combined update project for GS 4.0 project here on Codeplex (Windows and WP7)
Getting started
For the Windows Phone side of this tutorial you are going to need the Windows Phone Development Kit (The April refresh is the latest), this will download and install everything you need including:
- Visual Studio 2010 Express for Windows Phone
- XNA Studio 4.0
- Windows Phone 7 Emulator
- Silverlight for Windows Phone 7
Get them installed and started and you see the great new Visual studio 2010 interface now completely re-written in WPF.
New project
Now exactly the same as before in this earlier post, you need to set up a new project, except this time you need to browse to the XNA Game Studio 4.0 section and select the “Windows Phone Game (4.0)” project.
I suggest calling the project “XNAStarTrooper2D_Phone7”, select an appropriate folder and click OK, then you will be presented with a nice clean empty Phone solution.
First thing you should notice is that in a new phone solution the content and code projects are already split up for you, bear this in mind for the next step.
Now copy over the Engine Folder from your current Startrooper game to the code project and then copy over the class files beginning with StarTrooper (StarTrooperGame, StarTrooperSprites and StarTrooperBackground).
Finally copy over the Pictures, Music and Sounds folders from the content project in the original project and paste them into the Content Project of the Phone Game (making sure to select the content project and not the code project!!)
You should now have a solution made up like the following:
Updates for Windows Phone 7
Now straight off the bat, if you build the project there will be several errors logged, fear no though as this is to be expected.
Now with any upgrade project, I’ve always found it easier to do the conversion myself as you can ensure you know exactly what changes are required, to that first we need to update the default “Game1.cs” class with our engine and setup code from the StarTrooperGame.CS
This is broken down into the following steps:
- Copy the InternalUpdate and LoadResources functions to the end of the Game1.cs
- Copy the base attributes
- Copy the constructor updates
- Apply the update from the previous post for the graphics buffer.
- Finally copy the additional code from the update and draw functions
What you should end up with is the following.
1 |
<pre style="margin: 0em; width: 100%; font-family: consolas,"Courier New",courier,monospace; font-size: 12px; background-color: rgb(251, 251, 251);"> 1: <span style="color: rgb(0, 0, 255);">using</span> System; |
1 |
2: <span style="color: rgb(0, 0, 255);">using</span> System.Collections.Generic; |
1 |
3: <span style="color: rgb(0, 0, 255);">using</span> System.Linq; |
1 |
4: <span style="color: rgb(0, 0, 255);">using</span> Microsoft.Xna.Framework; |
1 |
5: <span style="color: rgb(0, 0, 255);">using</span> Microsoft.Xna.Framework.Audio; |
1 |
6: <span style="color: rgb(0, 0, 255);">using</span> Microsoft.Xna.Framework.Content; |
1 |
7: <span style="color: rgb(0, 0, 255);">using</span> Microsoft.Xna.Framework.GamerServices; |
1 |
8: <span style="color: rgb(0, 0, 255);">using</span> Microsoft.Xna.Framework.Graphics; |
1 |
9: <span style="color: rgb(0, 0, 255);">using</span> Microsoft.Xna.Framework.Input; |
1 |
10: <span style="color: rgb(0, 0, 255);">using</span> Microsoft.Xna.Framework.Input.Touch; |
1 |
11: <span style="color: rgb(0, 0, 255);">using</span> Microsoft.Xna.Framework.Media; |
1 |
12: |
1 |
13: <span style="color: rgb(0, 0, 255);">namespace</span> XNAStarTrooper2D_Phone7 |
1 |
14: { |
1 |
15: <span style="color: rgb(128, 128, 128);">/// <summary></span> |
1 |
16: <span style="color: rgb(128, 128, 128);">/// This is the main type for your game</span> |
1 |
17: <span style="color: rgb(128, 128, 128);">/// </summary></span> |
1 |
18: <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">class</span> Game1 : Microsoft.Xna.Framework.Game |
1 |
19: { |
1 |
20: <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">static</span> GraphicsDeviceManager graphics; |
1 |
21: <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">static</span> SpriteBatch spriteBatch; |
1 |
22: |
1 |
23: <span style="color: rgb(0, 128, 0);">//Game management related properties</span> |
1 |
24: <span style="color: rgb(0, 0, 255);">static</span> List<Sprite> m_Sprites = <span style="color: rgb(0, 0, 255);">new</span> List<Sprite>(); |
1 |
25: <span style="color: rgb(0, 0, 255);">static</span> List<Sprite> m_ZOrderedSprites = <span style="color: rgb(0, 0, 255);">new</span> List<Sprite>(); |
1 |
26: <span style="color: rgb(0, 0, 255);">static</span> List<Sprite> m_DeletedSprites = <span style="color: rgb(0, 0, 255);">new</span> List<Sprite>(); |
1 |
27: <span style="color: rgb(0, 0, 255);">static</span> List<Sprite> m_AddedSprites = <span style="color: rgb(0, 0, 255);">new</span> List<Sprite>(); |
1 |
28: |
1 |
29: <span style="color: rgb(0, 0, 255);">static</span> List<Text2D> m_Text2Ds = <span style="color: rgb(0, 0, 255);">new</span> List<Text2D>(); |
1 |
30: <span style="color: rgb(0, 0, 255);">static</span> List<Text2D> m_DeletedText2Ds = <span style="color: rgb(0, 0, 255);">new</span> List<Text2D>(); |
1 |
31: <span style="color: rgb(0, 0, 255);">static</span> List<Text2D> m_AddedText2Ds = <span style="color: rgb(0, 0, 255);">new</span> List<Text2D>(); |
1 |
32: |
1 |
33: |
1 |
34: |
1 |
35: <span style="color: rgb(0, 0, 255);">static</span> List<SoundEffect> m_Sounds = <span style="color: rgb(0, 0, 255);">new</span> List<SoundEffect>(); |
1 |
36: <span style="color: rgb(0, 0, 255);">static</span> List<SoundEffectInstance> m_Music = <span style="color: rgb(0, 0, 255);">new</span> List<SoundEffectInstance>(); |
1 |
37: |
1 |
38: |
1 |
39: |
1 |
40: <span style="color: rgb(0, 0, 255);">private</span> <span style="color: rgb(0, 0, 255);">const</span> <span style="color: rgb(0, 0, 255);">int</span> TargetFrameRate = 60; |
1 |
41: <span style="color: rgb(0, 0, 255);">private</span> <span style="color: rgb(0, 0, 255);">const</span> <span style="color: rgb(0, 0, 255);">int</span> m_BackBufferWidth = 480; |
1 |
42: <span style="color: rgb(0, 0, 255);">private</span> <span style="color: rgb(0, 0, 255);">const</span> <span style="color: rgb(0, 0, 255);">int</span> m_BackBufferHeight = 800; |
1 |
43: |
1 |
44: <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">static</span> Trooper Trooper; |
1 |
45: <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">static</span> Condor Condor; |
1 |
46: |
1 |
47: |
1 |
48: Random m_Random = <span style="color: rgb(0, 0, 255);">new</span> Random(); |
1 |
49: |
1 |
50: |
1 |
51: <span style="color: rgb(0, 0, 255);">public</span> Game1() |
1 |
52: { |
1 |
53: graphics = <span style="color: rgb(0, 0, 255);">new</span> GraphicsDeviceManager(<span style="color: rgb(0, 0, 255);">this</span>); |
1 |
54: Content.RootDirectory = "<span style="color: rgb(139, 0, 0);">Content</span>"; |
1 |
55: |
1 |
56: <span style="color: rgb(0, 128, 0);">// Frame rate is 30 fps by default for Windows Phone.</span> |
1 |
57: TargetElapsedTime = TimeSpan.FromTicks(333333); |
1 |
58: |
1 |
59: <span style="color: rgb(0, 128, 0);">// Pre-autoscale settings.</span> |
1 |
60: graphics.PreferredBackBufferWidth = m_BackBufferWidth; |
1 |
61: graphics.PreferredBackBufferHeight = m_BackBufferHeight; |
1 |
62: } |
1 |
63: |
1 |
64: <span style="color: rgb(128, 128, 128);">/// <summary></span> |
1 |
65: <span style="color: rgb(128, 128, 128);">/// Allows the game to perform any initialization it needs to before starting to run.</span> |
1 |
66: <span style="color: rgb(128, 128, 128);">/// This is where it can query for any required services and load any non-graphic</span> |
1 |
67: <span style="color: rgb(128, 128, 128);">/// related content. Calling base.Initialize will enumerate through any components</span> |
1 |
68: <span style="color: rgb(128, 128, 128);">/// and initialize them as well.</span> |
1 |
69: <span style="color: rgb(128, 128, 128);">/// </summary></span> |
1 |
70: <span style="color: rgb(0, 0, 255);">protected</span> <span style="color: rgb(0, 0, 255);">override</span> <span style="color: rgb(0, 0, 255);">void</span> Initialize() |
1 |
71: { |
1 |
72: <span style="color: rgb(0, 128, 0);">// TODO: Add your initialization logic here</span> |
1 |
73: |
1 |
74: <span style="color: rgb(0, 0, 255);">base</span>.Initialize(); |
1 |
75: } |
1 |
76: |
1 |
77: <span style="color: rgb(128, 128, 128);">/// <summary></span> |
1 |
78: <span style="color: rgb(128, 128, 128);">/// LoadContent will be called once per game and is the place to load</span> |
1 |
79: <span style="color: rgb(128, 128, 128);">/// all of your content.</span> |
1 |
80: <span style="color: rgb(128, 128, 128);">/// </summary></span> |
1 |
81: <span style="color: rgb(0, 0, 255);">protected</span> <span style="color: rgb(0, 0, 255);">override</span> <span style="color: rgb(0, 0, 255);">void</span> LoadContent() |
1 |
82: { |
1 |
83: <span style="color: rgb(0, 128, 0);">// Create a new SpriteBatch, which can be used to draw textures.</span> |
1 |
84: spriteBatch = <span style="color: rgb(0, 0, 255);">new</span> SpriteBatch(GraphicsDevice); |
1 |
85: |
1 |
86: <span style="color: rgb(0, 128, 0);">// TODO: use this.Content to load your game content here</span> |
1 |
87: |
1 |
88: LoadResources(); |
1 |
89: |
1 |
90: } |
1 |
91: |
1 |
92: <span style="color: rgb(128, 128, 128);">/// <summary></span> |
1 |
93: <span style="color: rgb(128, 128, 128);">/// UnloadContent will be called once per game and is the place to unload</span> |
1 |
94: <span style="color: rgb(128, 128, 128);">/// all content.</span> |
1 |
95: <span style="color: rgb(128, 128, 128);">/// </summary></span> |
1 |
96: <span style="color: rgb(0, 0, 255);">protected</span> <span style="color: rgb(0, 0, 255);">override</span> <span style="color: rgb(0, 0, 255);">void</span> UnloadContent() |
1 |
97: { |
1 |
98: <span style="color: rgb(0, 128, 0);">// TODO: Unload any non ContentManager content here</span> |
1 |
99: } |
1 |
100: |
1 |
101: <span style="color: rgb(128, 128, 128);">/// <summary></span> |
1 |
102: <span style="color: rgb(128, 128, 128);">/// Allows the game to run logic such as updating the world,</span> |
1 |
103: <span style="color: rgb(128, 128, 128);">/// checking for collisions, gathering input, and playing audio.</span> |
1 |
104: <span style="color: rgb(128, 128, 128);">/// </summary></span> |
1 |
105: <span style="color: rgb(128, 128, 128);">/// <param name="gameTime">Provides a snapshot of timing values.</param></span> |
1 |
106: <span style="color: rgb(0, 0, 255);">protected</span> <span style="color: rgb(0, 0, 255);">override</span> <span style="color: rgb(0, 0, 255);">void</span> Update(GameTime gameTime) |
1 |
107: { |
1 |
108: <span style="color: rgb(0, 128, 0);">// Allows the game to exit</span> |
1 |
109: <span style="color: rgb(0, 0, 255);">if</span> (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) |
1 |
110: <span style="color: rgb(0, 0, 255);">this</span>.Exit(); |
1 |
111: |
1 |
112: <span style="color: rgb(0, 128, 0);">// TODO: Add your update logic here</span> |
1 |
113: InternalUpdate(); <span style="color: rgb(0, 128, 0);">// <- Added Sprite update function here</span> |
1 |
114: |
1 |
115: <span style="color: rgb(0, 0, 255);">base</span>.Update(gameTime); |
1 |
116: } |
1 |
117: |
1 |
118: <span style="color: rgb(128, 128, 128);">/// <summary></span> |
1 |
119: <span style="color: rgb(128, 128, 128);">/// This is called when the game should draw itself.</span> |
1 |
120: <span style="color: rgb(128, 128, 128);">/// </summary></span> |
1 |
121: <span style="color: rgb(128, 128, 128);">/// <param name="gameTime">Provides a snapshot of timing values.</param></span> |
1 |
122: <span style="color: rgb(0, 0, 255);">protected</span> <span style="color: rgb(0, 0, 255);">override</span> <span style="color: rgb(0, 0, 255);">void</span> Draw(GameTime gameTime) |
1 |
123: { |
1 |
124: GraphicsDevice.Clear(Color.CornflowerBlue); |
1 |
125: |
1 |
126: <span style="color: rgb(0, 128, 0);">// TODO: Add your drawing code here</span> |
1 |
127: m_ZOrderedSprites.Sort(Sprite.ComparisonZOrder);<span style="color: rgb(0, 128, 0);">// <- Added Sprite draw code here</span> |
1 |
128: |
1 |
129: <span style="color: rgb(0, 0, 255);">foreach</span> (Sprite s <span style="color: rgb(0, 0, 255);">in</span> m_ZOrderedSprites) |
1 |
130: s.Draw(gameTime, spriteBatch); |
1 |
131: |
1 |
132: <span style="color: rgb(0, 0, 255);">foreach</span> (Text2D t <span style="color: rgb(0, 0, 255);">in</span> m_Text2Ds) |
1 |
133: t.Draw(gameTime, spriteBatch); |
1 |
134: |
1 |
135: <span style="color: rgb(0, 0, 255);">base</span>.Draw(gameTime); |
1 |
136: } |
1 |
137: |
1 |
138: <span style="color: rgb(0, 0, 255);">void</span> InternalUpdate() |
1 |
139: { |
1 |
140: <span style="color: rgb(0, 0, 255);">foreach</span> (Sprite s <span style="color: rgb(0, 0, 255);">in</span> m_AddedSprites) |
1 |
141: { |
1 |
142: m_Sprites.Add(s); |
1 |
143: m_ZOrderedSprites.Add(s); |
1 |
144: } |
1 |
145: m_AddedSprites.Clear(); |
1 |
146: |
1 |
147: <span style="color: rgb(0, 0, 255);">foreach</span> (Sprite s <span style="color: rgb(0, 0, 255);">in</span> m_DeletedSprites) |
1 |
148: { |
1 |
149: m_Sprites.Remove(s); |
1 |
150: m_ZOrderedSprites.Remove(s); |
1 |
151: } |
1 |
152: m_DeletedSprites.Clear(); |
1 |
153: |
1 |
154: <span style="color: rgb(0, 0, 255);">foreach</span> (Sprite s <span style="color: rgb(0, 0, 255);">in</span> m_Sprites) |
1 |
155: s.InternalUpdate(); |
1 |
156: |
1 |
157: <span style="color: rgb(0, 0, 255);">foreach</span> (Sprite s <span style="color: rgb(0, 0, 255);">in</span> m_Sprites) |
1 |
158: s.Update(); |
1 |
159: |
1 |
160: |
1 |
161: <span style="color: rgb(0, 0, 255);">foreach</span> (Text2D t <span style="color: rgb(0, 0, 255);">in</span> m_AddedText2Ds) |
1 |
162: m_Text2Ds.Add(t); |
1 |
163: m_AddedText2Ds.Clear(); |
1 |
164: |
1 |
165: <span style="color: rgb(0, 0, 255);">foreach</span> (Text2D t <span style="color: rgb(0, 0, 255);">in</span> m_DeletedText2Ds) |
1 |
166: m_Text2Ds.Remove(t); |
1 |
167: m_DeletedText2Ds.Clear(); |
1 |
168: |
1 |
169: <span style="color: rgb(0, 0, 255);">foreach</span> (Text2D t <span style="color: rgb(0, 0, 255);">in</span> m_Text2Ds) |
1 |
170: t.InternalUpdate(); |
1 |
171: |
1 |
172: } |
1 |
173: |
1 |
174: <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">void</span> LoadResources() |
1 |
175: { |
1 |
176: |
1 |
177: #region Background |
1 |
178: <span style="color: rgb(0, 128, 0);">//Type the code here to add the background to the game.</span> |
1 |
179: |
1 |
180: Texture2D background = Content.Load<Texture2D>(@"<span style="color: rgb(139, 0, 0);">Pictures\background</span>"); |
1 |
181: |
1 |
182: Background bg = <span style="color: rgb(0, 0, 255);">new</span> Background(background); |
1 |
183: |
1 |
184: bg.Position = <span style="color: rgb(0, 0, 255);">new</span> Vector2(0, m_BackBufferHeight / 2); |
1 |
185: bg.ScaleX = BackBufferWidth / (<span style="color: rgb(0, 0, 255);">float</span>)background.Width; |
1 |
186: bg.ScaleY = BackBufferHeight / (<span style="color: rgb(0, 0, 255);">float</span>)background.Height; |
1 |
187: bg.ZOrder = 10; |
1 |
188: |
1 |
189: m_AddedSprites.Add(bg); |
1 |
190: |
1 |
191: Background bg2 = (Background)bg.Clone(); |
1 |
192: bg2.Position = <span style="color: rgb(0, 0, 255);">new</span> Vector2(0, -BackBufferHeight / 2); |
1 |
193: bg2.ScaleX = BackBufferWidth / background.Width; |
1 |
194: bg2.ScaleY = BackBufferHeight / background.Height; |
1 |
195: bg2.ZOrder = 10; |
1 |
196: |
1 |
197: m_AddedSprites.Add(bg2); |
1 |
198: |
1 |
199: #endregion |
1 |
200: |
1 |
201: #region Trooper |
1 |
202: <span style="color: rgb(0, 128, 0);">//Type the code here to add the Trooper sprite.</span> |
1 |
203: Trooper trooper = <span style="color: rgb(0, 0, 255);">new</span> Trooper(Content.Load<Texture2D>(@"<span style="color: rgb(139, 0, 0);">Pictures\TrooperSpritesheet</span>"), 6, <span style="color: rgb(0, 0, 255);">true</span>); |
1 |
204: trooper.Position = <span style="color: rgb(0, 0, 255);">new</span> Vector2(BackBufferWidth / 2, BackBufferHeight - 50); |
1 |
205: m_AddedSprites.Add(trooper); |
1 |
206: |
1 |
207: Trooper = trooper; |
1 |
208: #endregion |
1 |
209: |
1 |
210: #region Condor |
1 |
211: <span style="color: rgb(0, 128, 0);">//Type the code here to add the Condor sprite.</span> |
1 |
212: Condor condor = <span style="color: rgb(0, 0, 255);">new</span> Condor(); |
1 |
213: |
1 |
214: Animation condorAnimation = <span style="color: rgb(0, 0, 255);">new</span> Animation(Content.Load<Texture2D>(@"<span style="color: rgb(139, 0, 0);">Pictures\CondorSpritesheet</span>"), 4); |
1 |
215: |
1 |
216: condorAnimation.Play(); |
1 |
217: condorAnimation.Loop = <span style="color: rgb(0, 0, 255);">true</span>; |
1 |
218: |
1 |
219: <span style="color: rgb(0, 0, 255);">int</span>[] ExplosionDelay = { 4, 3, 4 }; |
1 |
220: Animation condorExplosion = <span style="color: rgb(0, 0, 255);">new</span> Animation(Content.Load<Texture2D>(@"<span style="color: rgb(139, 0, 0);">Pictures\CondorExplosionSpritesheet</span>"), 3, ExplosionDelay); |
1 |
221: |
1 |
222: condorExplosion.Play(); |
1 |
223: |
1 |
224: condor.AddAnimation(condorAnimation); |
1 |
225: condor.AddAnimation(condorExplosion); |
1 |
226: Condor = condor; |
1 |
227: |
1 |
228: #endregion |
1 |
229: } |
1 |
230: |
1 |
231: #region Properties |
1 |
232: <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">static</span> <span style="color: rgb(0, 0, 255);">float</span> BackBufferWidth { <span style="color: rgb(0, 0, 255);">get</span> { <span style="color: rgb(0, 0, 255);">return</span> (<span style="color: rgb(0, 0, 255);">float</span>)m_BackBufferWidth ;} } |
1 |
233: <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">static</span> <span style="color: rgb(0, 0, 255);">float</span> BackBufferHeight { <span style="color: rgb(0, 0, 255);">get</span> { <span style="color: rgb(0, 0, 255);">return</span> (<span style="color: rgb(0, 0, 255);">float</span>)m_BackBufferHeight ;} } |
1 |
234: #endregion |
1 |
235: |
1 |
236: } |
1 |
237: } |
You may note that I’ve left the Graphics backbuffer values to the default for windows phones, this is better to leave this for now since the phone project is separate.
Last job to ensure the project is cleaned up and tidy. Delete the original “StarTrooperGame.cs” file and then Rename “Game1.cs” to “StarTrooperGame.cs”.
This will keep the main tutorial and the Windows Phone project in line with code changes.
Final Changes
If you compile the project now you will notice that there are still errors, this is down to the “ICloneable” interface not being available for Windows Phone Projects, not to fear though as the project can live without it (The ICloneable interface just gives a standard implementation for Cloning objects)
SO we need to update the Sprite and Animation classes and remove their iCloneable interface references, from:
1 |
<pre style="margin: 0em; width: 100%; font-family: consolas,"Courier New",courier,monospace; font-size: 12px; background-color: rgb(251, 251, 251);"> 1: <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">class</span> Sprite : ICloneable |
1 |
2: |
1 |
3: <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">class</span> Animation : iCloneable |
To:
1 |
<pre style="margin: 0em; width: 100%; font-family: consolas,"Courier New",courier,monospace; font-size: 12px; background-color: rgb(251, 251, 251);"> 1: <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">class</span> Sprite |
1 |
2: |
1 |
3: <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">class</span> Animation |
And that’s it, if you run the project you should now see the game running happily in the Windows Phone Emulator.
Final code for this intermission can be found here.