ae-gamestatemanagement

ae-gamestatemanagement Commit Details


Date:2016-03-27 18:10:24 (8 years 8 months ago)
Author:Natalie Adams
Branch:master
Commit:81df8416172a5398d46e110af278e4550fc05c1d
Message:Initial commit

Changes:

File differences

.gitignore
1
2
3
*.user
/obj
/bin
.gitmodules
1
2
3
[submodule "FNA"]
path = FNA
url = git://srchub.org/fna-workbench.git
BackgroundScreen.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#region File Description
//-----------------------------------------------------------------------------
// BackgroundScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using GameStateManagement;
#endregion
namespace GameStateManagement
{
/// <summary>
/// The background screen sits behind all the other menu screens.
/// It draws a background image that remains fixed in place regardless
/// of whatever transitions the screens on top of it may be doing.
/// </summary>
public class BackgroundScreen : GameScreen
{
#region Fields
ContentManager content;
Texture2D backgroundTexture;
#endregion
#region Initialization
/// <summary>
/// Constructor.
/// </summary>
public BackgroundScreen()
{
TransitionOnTime = TimeSpan.FromSeconds(0.5);
TransitionOffTime = TimeSpan.FromSeconds(0.5);
}
/// <summary>
/// Loads graphics content for this screen. The background texture is quite
/// big, so we use our own local ContentManager to load it. This allows us
/// to unload before going from the menus into the game itself, wheras if we
/// used the shared ContentManager provided by the Game class, the content
/// would remain loaded forever.
/// </summary>
public override void Activate(bool instancePreserved)
{
if (!instancePreserved)
{
if (content == null)
content = new ContentManager(ScreenManager.Game.Services, "Content");
backgroundTexture = content.Load<Texture2D>("background");
}
}
/// <summary>
/// Unloads graphics content for this screen.
/// </summary>
public override void Unload()
{
content.Unload();
}
#endregion
#region Update and Draw
/// <summary>
/// Updates the background screen. Unlike most screens, this should not
/// transition off even if it has been covered by another screen: it is
/// supposed to be covered, after all! This overload forces the
/// coveredByOtherScreen parameter to false in order to stop the base
/// Update method wanting to transition off.
/// </summary>
public override void Update(GameTime gameTime, bool otherScreenHasFocus,
bool coveredByOtherScreen)
{
base.Update(gameTime, otherScreenHasFocus, false);
}
/// <summary>
/// Draws the background screen.
/// </summary>
public override void Draw(GameTime gameTime)
{
SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
Rectangle fullscreen = new Rectangle(0, 0, viewport.Width, viewport.Height);
spriteBatch.Begin();
spriteBatch.Draw(backgroundTexture, fullscreen,
new Color(TransitionAlpha, TransitionAlpha, TransitionAlpha));
spriteBatch.End();
}
#endregion
}
}
Button.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#region File Description
//-----------------------------------------------------------------------------
// Button.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
using System;
using GameStateManagement;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace GameStateManagement
{
/// <summary>
/// A special button that handles toggling between "On" and "Off"
/// </summary>
class BooleanButton : Button
{
private string option;
private bool value;
/// <summary>
/// Creates a new BooleanButton.
/// </summary>
/// <param name="option">The string text to display for the option.</param>
/// <param name="value">The initial value of the button.</param>
public BooleanButton(string option, bool value)
: base(option)
{
this.option = option;
this.value = value;
GenerateText();
}
protected override void OnTapped()
{
// When tapped we need to toggle the value and regenerate the text
value = !value;
GenerateText();
base.OnTapped();
}
/// <summary>
/// Helper that generates the actual Text value the base class uses for drawing.
/// </summary>
private void GenerateText()
{
Text = string.Format("{0}: {1}", option, value ? "On" : "Off");
}
}
/// <summary>
/// Represents a touchable button.
/// </summary>
class Button
{
/// <summary>
/// The text displayed in the button.
/// </summary>
public string Text = "Button";
/// <summary>
/// The position of the top-left corner of the button.
/// </summary>
public Vector2 Position = Vector2.Zero;
/// <summary>
/// The size of the button.
/// </summary>
public Vector2 Size = new Vector2(250, 75);
/// <summary>
/// The thickness of the border drawn for the button.
/// </summary>
public int BorderThickness = 4;
/// <summary>
/// The color of the button border.
/// </summary>
public Color BorderColor = new Color(200, 200, 200);
/// <summary>
/// The color of the button background.
/// </summary>
public Color FillColor = new Color(100, 100, 100) * .75f;
/// <summary>
/// The color of the text.
/// </summary>
public Color TextColor = Color.White;
/// <summary>
/// The opacity of the button.
/// </summary>
public float Alpha = 0f;
/// <summary>
/// Invoked when the button is tapped.
/// </summary>
public event EventHandler<EventArgs> Tapped;
/// <summary>
/// Creates a new Button.
/// </summary>
/// <param name="text">The text to display in the button.</param>
public Button(string text)
{
Text = text;
}
/// <summary>
/// Invokes the Tapped event and allows subclasses to perform actions when tapped.
/// </summary>
protected virtual void OnTapped()
{
if (Tapped != null)
Tapped(this, EventArgs.Empty);
}
/// <summary>
/// Passes a tap location to the button for handling.
/// </summary>
/// <param name="tap">The location of the tap.</param>
/// <returns>True if the button was tapped, false otherwise.</returns>
public bool HandleTap(Vector2 tap)
{
if (tap.X >= Position.X &&
tap.Y >= Position.Y &&
tap.X <= Position.X + Size.X &&
tap.Y <= Position.Y + Size.Y)
{
OnTapped();
return true;
}
return false;
}
/// <summary>
/// Draws the button
/// </summary>
/// <param name="screen">The screen drawing the button</param>
public void Draw(GameScreen screen)
{
// Grab some common items from the ScreenManager
SpriteBatch spriteBatch = screen.ScreenManager.SpriteBatch;
SpriteFont font = screen.ScreenManager.Font;
Texture2D blank = screen.ScreenManager.BlankTexture;
// Compute the button's rectangle
Rectangle r = new Rectangle(
(int)Position.X,
(int)Position.Y,
(int)Size.X,
(int)Size.Y);
// Fill the button
spriteBatch.Draw(blank, r, FillColor * Alpha);
// Draw the border
spriteBatch.Draw(
blank,
new Rectangle(r.Left, r.Top, r.Width, BorderThickness),
BorderColor * Alpha);
spriteBatch.Draw(
blank,
new Rectangle(r.Left, r.Top, BorderThickness, r.Height),
BorderColor * Alpha);
spriteBatch.Draw(
blank,
new Rectangle(r.Right - BorderThickness, r.Top, BorderThickness, r.Height),
BorderColor * Alpha);
spriteBatch.Draw(
blank,
new Rectangle(r.Left, r.Bottom - BorderThickness, r.Width, BorderThickness),
BorderColor * Alpha);
// Draw the text centered in the button
Vector2 textSize = font.MeasureString(Text);
Vector2 textPosition = new Vector2(r.Center.X, r.Center.Y) - textSize / 2f;
textPosition.X = (int)textPosition.X;
textPosition.Y = (int)textPosition.Y;
spriteBatch.DrawString(font, Text, textPosition, TextColor * Alpha);
}
}
}
FNA
1
Subproject commit 4e35338d6c3dfb85752b4b94c6ced3223c8b35f5
GameScreen.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
#region File Description
//-----------------------------------------------------------------------------
// GameScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
using System;
using System.IO;
using Microsoft.Xna.Framework;
// TODO: Add Touch API to FNA
//using Microsoft.Xna.Framework.Input.Touch;
namespace GameStateManagement
{
/// <summary>
/// Enum describes the screen transition state.
/// </summary>
public enum ScreenState
{
TransitionOn,
Active,
TransitionOff,
Hidden,
}
/// <summary>
/// A screen is a single layer that has update and draw logic, and which
/// can be combined with other layers to build up a complex menu system.
/// For instance the main menu, the options menu, the "are you sure you
/// want to quit" message box, and the main game itself are all implemented
/// as screens.
/// </summary>
public abstract class GameScreen
{
public GameScreen()
{
HasVirtualStick = false;
HasCursor = false;
}
public bool HasCursor = false;
/// <summary>
/// Normally when one screen is brought up over the top of another,
/// the first screen will transition off to make room for the new
/// one. This property indicates whether the screen is only a small
/// popup, in which case screens underneath it do not need to bother
/// transitioning off.
/// </summary>
public bool IsPopup
{
get { return isPopup; }
protected set { isPopup = value; }
}
bool isPopup = false;
public bool HasVirtualStick { get; set; }
/// <summary>
/// Indicates how long the screen takes to
/// transition on when it is activated.
/// </summary>
public TimeSpan TransitionOnTime
{
get { return transitionOnTime; }
protected set { transitionOnTime = value; }
}
TimeSpan transitionOnTime = TimeSpan.Zero;
/// <summary>
/// Indicates how long the screen takes to
/// transition off when it is deactivated.
/// </summary>
public TimeSpan TransitionOffTime
{
get { return transitionOffTime; }
protected set { transitionOffTime = value; }
}
TimeSpan transitionOffTime = TimeSpan.Zero;
/// <summary>
/// Gets the current position of the screen transition, ranging
/// from zero (fully active, no transition) to one (transitioned
/// fully off to nothing).
/// </summary>
public float TransitionPosition
{
get { return transitionPosition; }
protected set { transitionPosition = value; }
}
float transitionPosition = 1;
/// <summary>
/// Gets the current alpha of the screen transition, ranging
/// from 1 (fully active, no transition) to 0 (transitioned
/// fully off to nothing).
/// </summary>
public float TransitionAlpha
{
get { return 1f - TransitionPosition; }
}
/// <summary>
/// Gets the current screen transition state.
/// </summary>
public ScreenState ScreenState
{
get { return screenState; }
protected set { screenState = value; }
}
ScreenState screenState = ScreenState.TransitionOn;
/// <summary>
/// There are two possible reasons why a screen might be transitioning
/// off. It could be temporarily going away to make room for another
/// screen that is on top of it, or it could be going away for good.
/// This property indicates whether the screen is exiting for real:
/// if set, the screen will automatically remove itself as soon as the
/// transition finishes.
/// </summary>
public bool IsExiting
{
get { return isExiting; }
protected internal set { isExiting = value; }
}
bool isExiting = false;
/// <summary>
/// Checks whether this screen is active and can respond to user input.
/// </summary>
public bool IsActive
{
get
{
return !otherScreenHasFocus &&
(screenState == ScreenState.TransitionOn ||
screenState == ScreenState.Active);
}
}
bool otherScreenHasFocus;
/// <summary>
/// Gets the manager that this screen belongs to.
/// </summary>
public ScreenManager ScreenManager
{
get { return screenManager; }
internal set { screenManager = value; }
}
ScreenManager screenManager;
/// <summary>
/// Gets the index of the player who is currently controlling this screen,
/// or null if it is accepting input from any player. This is used to lock
/// the game to a specific player profile. The main menu responds to input
/// from any connected gamepad, but whichever player makes a selection from
/// this menu is given control over all subsequent screens, so other gamepads
/// are inactive until the controlling player returns to the main menu.
/// </summary>
public PlayerIndex? ControllingPlayer
{
get { return controllingPlayer; }
internal set { controllingPlayer = value; }
}
PlayerIndex? controllingPlayer;
/// <summary>
/// Gets the gestures the screen is interested in. Screens should be as specific
/// as possible with gestures to increase the accuracy of the gesture engine.
/// For example, most menus only need Tap or perhaps Tap and VerticalDrag to operate.
/// These gestures are handled by the ScreenManager when screens change and
/// all gestures are placed in the InputState passed to the HandleInput method.
/// </summary>
/*public GestureType EnabledGestures
{
get { return enabledGestures; }
protected set
{
enabledGestures = value;
// the screen manager handles this during screen changes, but
// if this screen is active and the gesture types are changing,
// we have to update the TouchPanel ourself.
if (ScreenState == ScreenState.Active)
{
TouchPanel.EnabledGestures = value;
}
}
}
GestureType enabledGestures = GestureType.None;*/
/// <summary>
/// Gets whether or not this screen is serializable. If this is true,
/// the screen will be recorded into the screen manager's state and
/// its Serialize and Deserialize methods will be called as appropriate.
/// If this is false, the screen will be ignored during serialization.
/// By default, all screens are assumed to be serializable.
/// </summary>
public bool IsSerializable
{
get { return isSerializable; }
protected set { isSerializable = value; }
}
bool isSerializable = true;
/// <summary>
/// Activates the screen. Called when the screen is added to the screen manager or if the game resumes
/// from being paused or tombstoned.
/// </summary>
/// <param name="instancePreserved">
/// True if the game was preserved during deactivation, false if the screen is just being added or if the game was tombstoned.
/// On Xbox and Windows this will always be false.
/// </param>
public virtual void Activate(bool instancePreserved) { }
/// <summary>
/// Deactivates the screen. Called when the game is being deactivated due to pausing or tombstoning.
/// </summary>
public virtual void Deactivate() { }
/// <summary>
/// Unload content for the screen. Called when the screen is removed from the screen manager.
/// </summary>
public virtual void Unload() { }
/// <summary>
/// Allows the screen to run logic, such as updating the transition position.
/// Unlike HandleInput, this method is called regardless of whether the screen
/// is active, hidden, or in the middle of a transition.
/// </summary>
public virtual void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
{
this.otherScreenHasFocus = otherScreenHasFocus;
if (isExiting)
{
// If the screen is going away to die, it should transition off.
screenState = ScreenState.TransitionOff;
if (!UpdateTransition(gameTime, transitionOffTime, 1))
{
// When the transition finishes, remove the screen.
ScreenManager.RemoveScreen(this);
}
}
else if (coveredByOtherScreen)
{
// If the screen is covered by another, it should transition off.
if (UpdateTransition(gameTime, transitionOffTime, 1))
{
// Still busy transitioning.
screenState = ScreenState.TransitionOff;
}
else
{
// Transition finished!
screenState = ScreenState.Hidden;
}
}
else
{
// Otherwise the screen should transition on and become active.
if (UpdateTransition(gameTime, transitionOnTime, -1))
{
// Still busy transitioning.
screenState = ScreenState.TransitionOn;
}
else
{
// Transition finished!
screenState = ScreenState.Active;
}
}
}
/// <summary>
/// Helper for updating the screen transition position.
/// </summary>
bool UpdateTransition(GameTime gameTime, TimeSpan time, int direction)
{
// How much should we move by?
float transitionDelta;
if (time == TimeSpan.Zero)
transitionDelta = 1;
else
transitionDelta = (float)(gameTime.ElapsedGameTime.TotalMilliseconds / time.TotalMilliseconds);
// Update the transition position.
transitionPosition += transitionDelta * direction;
// Did we reach the end of the transition?
if (((direction < 0) && (transitionPosition <= 0)) ||
((direction > 0) && (transitionPosition >= 1)))
{
transitionPosition = MathHelper.Clamp(transitionPosition, 0, 1);
return false;
}
// Otherwise we are still busy transitioning.
return true;
}
/// <summary>
/// Allows the screen to handle user input. Unlike Update, this method
/// is only called when the screen is active, and not when some other
/// screen has taken the focus.
/// </summary>
public virtual void HandleInput(GameTime gameTime, InputState input) { }
/// <summary>
/// This is called when the screen should draw itself.
/// </summary>
public virtual void Draw(GameTime gameTime) { }
/// <summary>
/// Tells the screen to go away. Unlike ScreenManager.RemoveScreen, which
/// instantly kills the screen, this method respects the transition timings
/// and will give the screen a chance to gradually transition off.
/// </summary>
public void ExitScreen()
{
if (TransitionOffTime == TimeSpan.Zero)
{
// If the screen has a zero transition time, remove it immediately.
ScreenManager.RemoveScreen(this);
}
else
{
// Otherwise flag that it should transition off and then exit.
isExiting = true;
}
}
}
}
GameStateManagement.csproj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A1A96363-C163-4A2A-8F31-D84D80C4C0D7}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>GameStateManagement</RootNamespace>
<AssemblyName>GameStateManagement</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="BackgroundScreen.cs" />
<Compile Include="Button.cs" />
<Compile Include="GameplayScreen.cs" />
<Compile Include="GameScreen.cs" />
<Compile Include="InputAction.cs" />
<Compile Include="InputState.cs" />
<Compile Include="IScreenFactory.cs" />
<Compile Include="LoadingScreen.cs" />
<Compile Include="MainMenuScreen.cs" />
<Compile Include="MenuEntry.cs" />
<Compile Include="MenuScreen.cs" />
<Compile Include="MessageBoxScreen.cs" />
<Compile Include="OptionsMenuScreen.cs" />
<Compile Include="PauseMenuScreen.cs" />
<Compile Include="PhoneMainMenuScreen.cs" />
<Compile Include="PhoneMenuScreen.cs" />
<Compile Include="PhonePauseScreen.cs" />
<Compile Include="PlayerIndexEventArgs.cs" />
<Compile Include="ScreenManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="FNA\FNA.csproj">
<Project>{35253ce1-c864-4cd3-8249-4d1319748e8f}</Project>
<Name>FNA</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
GameStateManagement.sln
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.24720.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameStateManagement", "GameStateManagement.csproj", "{A1A96363-C163-4A2A-8F31-D84D80C4C0D7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FNA", "FNA\FNA.csproj", "{35253CE1-C864-4CD3-8249-4D1319748E8F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A1A96363-C163-4A2A-8F31-D84D80C4C0D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A1A96363-C163-4A2A-8F31-D84D80C4C0D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1A96363-C163-4A2A-8F31-D84D80C4C0D7}.Debug|x86.ActiveCfg = Debug|Any CPU
{A1A96363-C163-4A2A-8F31-D84D80C4C0D7}.Debug|x86.Build.0 = Debug|Any CPU
{A1A96363-C163-4A2A-8F31-D84D80C4C0D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1A96363-C163-4A2A-8F31-D84D80C4C0D7}.Release|Any CPU.Build.0 = Release|Any CPU
{A1A96363-C163-4A2A-8F31-D84D80C4C0D7}.Release|x86.ActiveCfg = Release|Any CPU
{A1A96363-C163-4A2A-8F31-D84D80C4C0D7}.Release|x86.Build.0 = Release|Any CPU
{35253CE1-C864-4CD3-8249-4D1319748E8F}.Debug|Any CPU.ActiveCfg = Debug|x86
{35253CE1-C864-4CD3-8249-4D1319748E8F}.Debug|x86.ActiveCfg = Debug|x86
{35253CE1-C864-4CD3-8249-4D1319748E8F}.Debug|x86.Build.0 = Debug|x86
{35253CE1-C864-4CD3-8249-4D1319748E8F}.Release|Any CPU.ActiveCfg = Release|x86
{35253CE1-C864-4CD3-8249-4D1319748E8F}.Release|x86.ActiveCfg = Release|x86
{35253CE1-C864-4CD3-8249-4D1319748E8F}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
GameplayScreen.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
#region File Description
//-----------------------------------------------------------------------------
// GameplayScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using GameStateManagement;
#endregion
namespace GameStateManagementSample
{
/// <summary>
/// This screen implements the actual game logic. It is just a
/// placeholder to get the idea across: you'll probably want to
/// put some more interesting gameplay in here!
/// </summary>
class GameplayScreen : GameScreen
{
#region Fields
ContentManager content;
SpriteFont gameFont;
Vector2 playerPosition = new Vector2(100, 100);
Vector2 enemyPosition = new Vector2(100, 100);
Random random = new Random();
float pauseAlpha;
InputAction pauseAction;
#endregion
#region Initialization
/// <summary>
/// Constructor.
/// </summary>
public GameplayScreen()
{
TransitionOnTime = TimeSpan.FromSeconds(1.5);
TransitionOffTime = TimeSpan.FromSeconds(0.5);
pauseAction = new InputAction(
new Buttons[] { Buttons.Start, Buttons.Back },
new Keys[] { Keys.Escape },
true);
}
/// <summary>
/// Load graphics content for the game.
/// </summary>
public override void Activate(bool instancePreserved)
{
if (!instancePreserved)
{
if (content == null)
content = new ContentManager(ScreenManager.Game.Services, "Content");
gameFont = content.Load<SpriteFont>("gamefont");
// A real game would probably have more content than this sample, so
// it would take longer to load. We simulate that by delaying for a
// while, giving you a chance to admire the beautiful loading screen.
Thread.Sleep(1000);
// once the load has finished, we use ResetElapsedTime to tell the game's
// timing mechanism that we have just finished a very long frame, and that
// it should not try to catch up.
ScreenManager.Game.ResetElapsedTime();
}
#if WINDOWS_PHONE
if (Microsoft.Phone.Shell.PhoneApplicationService.Current.State.ContainsKey("PlayerPosition"))
{
playerPosition = (Vector2)Microsoft.Phone.Shell.PhoneApplicationService.Current.State["PlayerPosition"];
enemyPosition = (Vector2)Microsoft.Phone.Shell.PhoneApplicationService.Current.State["EnemyPosition"];
}
#endif
}
public override void Deactivate()
{
#if WINDOWS_PHONE
Microsoft.Phone.Shell.PhoneApplicationService.Current.State["PlayerPosition"] = playerPosition;
Microsoft.Phone.Shell.PhoneApplicationService.Current.State["EnemyPosition"] = enemyPosition;
#endif
base.Deactivate();
}
/// <summary>
/// Unload graphics content used by the game.
/// </summary>
public override void Unload()
{
content.Unload();
#if WINDOWS_PHONE
Microsoft.Phone.Shell.PhoneApplicationService.Current.State.Remove("PlayerPosition");
Microsoft.Phone.Shell.PhoneApplicationService.Current.State.Remove("EnemyPosition");
#endif
}
#endregion
#region Update and Draw
/// <summary>
/// Updates the state of the game. This method checks the GameScreen.IsActive
/// property, so the game will stop updating when the pause menu is active,
/// or if you tab away to a different application.
/// </summary>
public override void Update(GameTime gameTime, bool otherScreenHasFocus,
bool coveredByOtherScreen)
{
base.Update(gameTime, otherScreenHasFocus, false);
// Gradually fade in or out depending on whether we are covered by the pause screen.
if (coveredByOtherScreen)
pauseAlpha = Math.Min(pauseAlpha + 1f / 32, 1);
else
pauseAlpha = Math.Max(pauseAlpha - 1f / 32, 0);
if (IsActive)
{
// Apply some random jitter to make the enemy move around.
const float randomization = 10;
enemyPosition.X += (float)(random.NextDouble() - 0.5) * randomization;
enemyPosition.Y += (float)(random.NextDouble() - 0.5) * randomization;
// Apply a stabilizing force to stop the enemy moving off the screen.
Vector2 targetPosition = new Vector2(
ScreenManager.GraphicsDevice.Viewport.Width / 2 - gameFont.MeasureString("Insert Gameplay Here").X / 2,
200);
enemyPosition = Vector2.Lerp(enemyPosition, targetPosition, 0.05f);
// TODO: this game isn't very fun! You could probably improve
// it by inserting something more interesting in this space :-)
}
}
/// <summary>
/// Lets the game respond to player input. Unlike the Update method,
/// this will only be called when the gameplay screen is active.
/// </summary>
public override void HandleInput(GameTime gameTime, InputState input)
{
if (input == null)
throw new ArgumentNullException("input");
// Look up inputs for the active player profile.
int playerIndex = (int)ControllingPlayer.Value;
KeyboardState keyboardState = input.CurrentKeyboardStates[playerIndex];
GamePadState gamePadState = input.CurrentGamePadStates[playerIndex];
// The game pauses either if the user presses the pause button, or if
// they unplug the active gamepad. This requires us to keep track of
// whether a gamepad was ever plugged in, because we don't want to pause
// on PC if they are playing with a keyboard and have no gamepad at all!
bool gamePadDisconnected = !gamePadState.IsConnected &&
input.GamePadWasConnected[playerIndex];
PlayerIndex player;
if (pauseAction.Evaluate(input, ControllingPlayer, out player) || gamePadDisconnected)
{
#if WINDOWS_PHONE
ScreenManager.AddScreen(new PhonePauseScreen(), ControllingPlayer);
#else
ScreenManager.AddScreen(new PauseMenuScreen(), ControllingPlayer);
#endif
}
else
{
// Otherwise move the player position.
Vector2 movement = Vector2.Zero;
if (keyboardState.IsKeyDown(Keys.Left))
movement.X--;
if (keyboardState.IsKeyDown(Keys.Right))
movement.X++;
if (keyboardState.IsKeyDown(Keys.Up))
movement.Y--;
if (keyboardState.IsKeyDown(Keys.Down))
movement.Y++;
Vector2 thumbstick = gamePadState.ThumbSticks.Left;
movement.X += thumbstick.X;
movement.Y -= thumbstick.Y;
/*if (input.TouchState.Count > 0)
{
Vector2 touchPosition = input.TouchState[0].Position;
Vector2 direction = touchPosition - playerPosition;
direction.Normalize();
movement += direction;
}*/
if (movement.Length() > 1)
movement.Normalize();
playerPosition += movement * 8f;
}
}
/// <summary>
/// Draws the gameplay screen.
/// </summary>
public override void Draw(GameTime gameTime)
{
// This game has a blue background. Why? Because!
ScreenManager.GraphicsDevice.Clear(ClearOptions.Target,
Color.CornflowerBlue, 0, 0);
// Our player and enemy are both actually just text strings.
SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
spriteBatch.Begin();
spriteBatch.DrawString(gameFont, "// TODO", playerPosition, Color.Green);
spriteBatch.DrawString(gameFont, "Insert Gameplay Here",
enemyPosition, Color.DarkRed);
spriteBatch.End();
// If the game is transitioning on or off, fade it out to black.
if (TransitionPosition > 0 || pauseAlpha > 0)
{
float alpha = MathHelper.Lerp(1f - TransitionAlpha, 1f, pauseAlpha / 2);
ScreenManager.FadeBackBufferToBlack(alpha);
}
}
#endregion
}
}
IScreenFactory.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#region File Description
//-----------------------------------------------------------------------------
// IScreenFactory.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
using System;
namespace GameStateManagement
{
/// <summary>
/// Defines an object that can create a screen when given its type.
///
/// The ScreenManager attempts to handle tombstoning on Windows Phone by creating an XML
/// document that has a list of the screens currently in the manager. When the game is
/// reactivated, the ScreenManager needs to create instances of those screens. However
/// since there is no restriction that a particular GameScreen subclass has a parameterless
/// constructor, there is no way the ScreenManager alone could create those instances.
///
/// IScreenFactory fills this gap by providing an interface the game should implement to
/// act as a translation from type to instance. The ScreenManager locates the IScreenFactory
/// from the Game.Services collection and passes each screen type to the factory, expecting
/// to get the correct GameScreen out.
///
/// If your game screens all have parameterless constructors, the minimal implementation of
/// this interface would look like this:
///
/// return Activator.CreateInstance(screenType) as GameScreen;
///
/// If you have screens with constructors that take arguments, you will need to ensure that
/// you can read these arguments from storage or generate new ones, then construct the screen
/// based on the type.
///
/// The ScreenFactory type in the sample game has the minimal implementation along with some
/// extra comments showing a potentially more complex example of how to implement IScreenFactory.
/// </summary>
public interface IScreenFactory
{
/// <summary>
/// Creates a GameScreen from the given type.
/// </summary>
/// <param name="screenType">The type of screen to create.</param>
/// <returns>The newly created screen.</returns>
GameScreen CreateScreen(Type screenType);
}
}
InputAction.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#region File Description
//-----------------------------------------------------------------------------
// InputAction.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
namespace GameStateManagement
{
/// <summary>
/// Defines an action that is designated by some set of buttons and/or keys.
///
/// The way actions work is that you define a set of buttons and keys that trigger the action. You can
/// then evaluate the action against an InputState which will test to see if any of the buttons or keys
/// are pressed by a player. You can also set a flag that indicates if the action only occurs once when
/// the buttons/keys are first pressed or whether the action should occur each frame.
///
/// Using this InputAction class means that you can configure new actions based on keys and buttons
/// without having to directly modify the InputState type. This means more customization by your games
/// without having to change the core classes of Game State Management.
/// </summary>
public class InputAction
{
private readonly Buttons[] buttons;
private readonly Keys[] keys;
private readonly bool newPressOnly;
// These delegate types map to the methods on InputState. We use these to simplify the evalute method
// by allowing us to map the appropriate delegates and invoke them, rather than having two separate code paths.
private delegate bool ButtonPress(Buttons button, PlayerIndex? controllingPlayer, out PlayerIndex player);
private delegate bool KeyPress(Keys key, PlayerIndex? controllingPlayer, out PlayerIndex player);
/// <summary>
/// Initializes a new InputAction.
/// </summary>
/// <param name="buttons">An array of buttons that can trigger the action.</param>
/// <param name="keys">An array of keys that can trigger the action.</param>
/// <param name="newPressOnly">Whether the action only occurs on the first press of one of the buttons/keys,
/// false if it occurs each frame one of the buttons/keys is down.</param>
public InputAction(Buttons[] buttons, Keys[] keys, bool newPressOnly)
{
// Store the buttons and keys. If the arrays are null, we create a 0 length array so we don't
// have to do null checks in the Evaluate method
this.buttons = buttons != null ? buttons.Clone() as Buttons[] : new Buttons[0];
this.keys = keys != null ? keys.Clone() as Keys[] : new Keys[0];
this.newPressOnly = newPressOnly;
}
/// <summary>
/// Evaluates the action against a given InputState.
/// </summary>
/// <param name="state">The InputState to test for the action.</param>
/// <param name="controllingPlayer">The player to test, or null to allow any player.</param>
/// <param name="player">If controllingPlayer is null, this is the player that performed the action.</param>
/// <returns>True if the action occurred, false otherwise.</returns>
public bool Evaluate(InputState state, PlayerIndex? controllingPlayer, out PlayerIndex player)
{
// Figure out which delegate methods to map from the state which takes care of our "newPressOnly" logic
ButtonPress buttonTest;
KeyPress keyTest;
if (newPressOnly)
{
buttonTest = state.IsNewButtonPress;
keyTest = state.IsNewKeyPress;
}
else
{
buttonTest = state.IsButtonPressed;
keyTest = state.IsKeyPressed;
}
// Now we simply need to invoke the appropriate methods for each button and key in our collections
foreach (Buttons button in buttons)
{
if (buttonTest(button, controllingPlayer, out player))
return true;
}
foreach (Keys key in keys)
{
if (keyTest(key, controllingPlayer, out player))
return true;
}
// If we got here, the action is not matched
player = PlayerIndex.One;
return false;
}
}
}
InputState.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
#region File Description
//-----------------------------------------------------------------------------
// InputState.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;
//using Microsoft.Xna.Framework.Input.Touch;
//using FarseerPhysics.SamplesFramework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Linq;
namespace GameStateManagement
{
/// <summary>
/// an enum of all available mouse buttons.
/// </summary>
public enum MouseButtons
{
LeftButton,
MiddleButton,
RightButton,
ExtraButton1,
ExtraButton2
}
/// <summary>
/// Helper for reading input from keyboard, gamepad, and touch input. This class
/// tracks both the current and previous state of the input devices, and implements
/// query methods for high level input actions such as "move up through the menu"
/// or "pause the game".
/// </summary>
public class InputState
{
public const int MaxInputs = 4;
public readonly KeyboardState[] CurrentKeyboardStates;
public readonly GamePadState[] CurrentGamePadStates;
public readonly KeyboardState[] LastKeyboardStates;
public readonly GamePadState[] LastGamePadStates;
public readonly bool[] GamePadWasConnected;
/*
* Needed for virtual stick on WP7
* -- Nathan Adams [adamsna@datanethost.net] - 4/12/2012
*/
private GamePadState _currentVirtualState;
private GamePadState _lastVirtualState;
private bool _handleVirtualStick;
/*
* I didn't create an array for the virtual stick because there will only be one
* -- Nathan Adams [adamsna@datanethost.net] - 4/12/2012
*/
/*
* Adding variables for the cursor
* -- Nathan Adams [adamsna@datanethost.net] - 4/15/2012
*
*/
private MouseState _currentMouseState;
private MouseState _lastMouseState;
private Vector2 _cursor;
private bool _cursorIsValid;
private bool _cursorIsVisible;
private bool _cursorMoved;
private Texture2D _cursorSprite;
#if WINDOWS_PHONE
private VirtualStick _phoneStick;
private VirtualButton _phoneA;
private VirtualButton _phoneB;
#endif
//public TouchCollection TouchState;
//public readonly List<GestureSample> Gestures = new List<GestureSample>();
private ScreenManager _manager;
private Viewport _viewport;
/// <summary>
/// Constructs a new input state.
/// </summary>
public InputState(ScreenManager manager)
{
_manager = manager;
CurrentKeyboardStates = new KeyboardState[MaxInputs];
CurrentGamePadStates = new GamePadState[MaxInputs];
LastKeyboardStates = new KeyboardState[MaxInputs];
LastGamePadStates = new GamePadState[MaxInputs];
GamePadWasConnected = new bool[MaxInputs];
_currentVirtualState = new GamePadState();
_lastVirtualState = new GamePadState();
_cursorIsVisible = false;
_cursorMoved = false;
#if WINDOWS_PHONE
_cursorIsValid = false;
#else
_cursorIsValid = true;
#endif
_cursor = Vector2.Zero;
_handleVirtualStick = false;
}
public MouseState MouseState
{
get { return _currentMouseState; }
}
public GamePadState VirtualState
{
get { return _currentVirtualState; }
}
public MouseState PreviousMouseState
{
get { return _lastMouseState; }
}
public GamePadState PreviousVirtualState
{
get { return _lastVirtualState; }
}
public bool ShowCursor
{
get { return _cursorIsVisible && _cursorIsValid; }
set { _cursorIsVisible = value; }
}
public bool EnableVirtualStick
{
get { return _handleVirtualStick; }
set { _handleVirtualStick = value; }
}
public Vector2 Cursor
{
get { return _cursor; }
}
public bool IsCursorMoved
{
get { return _cursorMoved; }
}
public bool IsCursorValid
{
get { return _cursorIsValid; }
}
public void LoadContent()
{
ContentManager man = new ContentManager(_manager.Game.Services, "Content");
//_cursorSprite = new Sprite(man.Load<Texture2D>("Common/cursor"));
_cursorSprite = man.Load<Texture2D>("Common/cursor");
#if WINDOWS_PHONE
// virtual stick content
_phoneStick = new VirtualStick(man.Load<Texture2D>("Common/socket"),
man.Load<Texture2D>("Common/stick"), new Vector2(80f, 400f));
Texture2D temp = man.Load<Texture2D>("Common/buttons");
_phoneA = new VirtualButton(temp, new Vector2(695f, 380f), new Rectangle(0, 0, 40, 40), new Rectangle(0, 40, 40, 40));
_phoneB = new VirtualButton(temp, new Vector2(745f, 360f), new Rectangle(40, 0, 40, 40), new Rectangle(40, 40, 40, 40));
#endif
_viewport = _manager.GraphicsDevice.Viewport;
}
private GamePadState HandleVirtualStickWin()
{
Vector2 _leftStick = Vector2.Zero;
List<Buttons> _buttons = new List<Buttons>();
PlayerIndex pout;
if (IsNewKeyPress(Keys.A, PlayerIndex.One, out pout))
{
_leftStick.X -= 1f;
}
if (IsNewKeyPress(Keys.S, PlayerIndex.One, out pout))
{
_leftStick.Y -= 1f;
}
if (IsNewKeyPress(Keys.D, PlayerIndex.One, out pout))
{
_leftStick.X += 1f;
}
if (IsNewKeyPress(Keys.W, PlayerIndex.One, out pout))
{
_leftStick.Y += 1f;
}
if (IsNewKeyPress(Keys.Space, PlayerIndex.One, out pout))
{
_buttons.Add(Buttons.A);
}
if (IsNewKeyPress(Keys.LeftControl, PlayerIndex.One, out pout))
{
_buttons.Add(Buttons.B);
}
if (_leftStick != Vector2.Zero)
{
_leftStick.Normalize();
}
return new GamePadState(_leftStick, Vector2.Zero, 0f, 0f, _buttons.ToArray());
}
private GamePadState HandleVirtualStickWP7()
{
List<Buttons> _buttons = new List<Buttons>();
Vector2 _stick = Vector2.Zero;
#if WINDOWS_PHONE
_phoneA.Pressed = false;
_phoneB.Pressed = false;
TouchCollection touchLocations = TouchPanel.GetState();
foreach (TouchLocation touchLocation in touchLocations)
{
_phoneA.Update(touchLocation);
_phoneB.Update(touchLocation);
_phoneStick.Update(touchLocation);
}
if (_phoneA.Pressed)
{
_buttons.Add(Buttons.A);
}
if (_phoneB.Pressed)
{
_buttons.Add(Buttons.B);
}
_stick = _phoneStick.StickPosition;
#endif
return new GamePadState(_stick, Vector2.Zero, 0f, 0f, _buttons.ToArray());
}
public void Draw()
{
if (_cursorIsVisible && _cursorIsValid)
{
_manager.SpriteBatch.Begin();
_manager.SpriteBatch.Draw(_cursorSprite, _cursor, null, Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0f);
_manager.SpriteBatch.End();
}
#if WINDOWS_PHONE
if (_handleVirtualStick)
{
_manager.SpriteBatch.Begin();
_phoneA.Draw(_manager.SpriteBatch);
_phoneB.Draw(_manager.SpriteBatch);
_phoneStick.Draw(_manager.SpriteBatch);
_manager.SpriteBatch.End();
}
#endif
}
/// <summary>
/// Reads the latest state user input.
/// </summary>
public void Update(GameTime gameTime)
{
_lastMouseState = _currentMouseState;
if (_handleVirtualStick)
{
_lastVirtualState = _currentVirtualState;
}
_currentMouseState = Mouse.GetState();
if (_handleVirtualStick)
{
#if XBOX
_currentVirtualState= GamePad.GetState(PlayerIndex.One);
#elif WINDOWS
if (GamePad.GetState(PlayerIndex.One).IsConnected)
{
_currentVirtualState = GamePad.GetState(PlayerIndex.One);
}
else
{
_currentVirtualState = HandleVirtualStickWin();
}
#elif WINDOWS_PHONE
_currentVirtualState = HandleVirtualStickWP7();
#endif
}
for (int i = 0; i < MaxInputs; i++)
{
LastKeyboardStates[i] = CurrentKeyboardStates[i];
LastGamePadStates[i] = CurrentGamePadStates[i];
CurrentKeyboardStates[i] = Keyboard.GetState();
CurrentGamePadStates[i] = GamePad.GetState((PlayerIndex)i);
// Keep track of whether a gamepad has ever been
// connected, so we can detect if it is unplugged.
if (CurrentGamePadStates[i].IsConnected)
{
GamePadWasConnected[i] = true;
}
}
// Get the raw touch state from the TouchPanel
//TouchState = TouchPanel.GetState();
// Read in any detected gestures into our list for the screens to later process
//Gestures.Clear();
/*while (TouchPanel.IsGestureAvailable)
{
//System.Diagnostics.Debugger.Break();
Gestures.Add(TouchPanel.ReadGesture());
}*/
//System.Diagnostics.Debugger.Break();
// Update cursor
Vector2 oldCursor = _cursor;
if (CurrentGamePadStates[0].IsConnected && CurrentGamePadStates[0].ThumbSticks.Left != Vector2.Zero)
{
Vector2 temp = CurrentGamePadStates[0].ThumbSticks.Left;
_cursor += temp * new Vector2(300f, -300f) * (float)gameTime.ElapsedGameTime.TotalSeconds;
Mouse.SetPosition((int)_cursor.X, (int)_cursor.Y);
}
else
{
_cursor.X = _currentMouseState.X;
_cursor.Y = _currentMouseState.Y;
}
//if (this.IsNewKeyPress(Keys.P, PlayerIndex.One, out p))
// Console.WriteLine(_cursor.ToString());
_cursor.X = MathHelper.Clamp(_cursor.X, 0f, _viewport.Width);
_cursor.Y = MathHelper.Clamp(_cursor.Y, 0f, _viewport.Height);
//if (this.IsNewKeyPress(Keys.P, PlayerIndex.One, out p))
// Console.WriteLine(_cursor.ToString());
if (_cursorIsValid && oldCursor != _cursor)
{
_cursorMoved = true;
}
else
{
_cursorMoved = false;
}
#if WINDOWS
if (_viewport.Bounds.Contains(_currentMouseState.X, _currentMouseState.Y))
{
_cursorIsValid = true;
}
else
{
_cursorIsValid = false;
}
#elif WINDOWS_PHONE
if (_currentMouseState.LeftButton == ButtonState.Pressed)
{
_cursorIsValid = true;
}
else
{
_cursorIsValid = false;
}
#endif
//if (this.IsNewKeyPress(Keys.P, PlayerIndex.One, out p))
// Console.WriteLine(_viewport.ToString());
}
/// <summary>
/// Helper for checking if a key was pressed during this update. The
/// controllingPlayer parameter specifies which player to read input for.
/// If this is null, it will accept input from any player. When a keypress
/// is detected, the output playerIndex reports which player pressed it.
/// </summary>
public bool IsKeyPressed(Keys key, PlayerIndex? controllingPlayer, out PlayerIndex playerIndex)
{
if (controllingPlayer.HasValue)
{
// Read input from the specified player.
playerIndex = controllingPlayer.Value;
int i = (int)playerIndex;
return CurrentKeyboardStates[i].IsKeyDown(key);
}
else
{
// Accept input from any player.
return (IsKeyPressed(key, PlayerIndex.One, out playerIndex) ||
IsKeyPressed(key, PlayerIndex.Two, out playerIndex) ||
IsKeyPressed(key, PlayerIndex.Three, out playerIndex) ||
IsKeyPressed(key, PlayerIndex.Four, out playerIndex));
}
}
/// <summary>
/// Helper for checking if a button was pressed during this update.
/// The controllingPlayer parameter specifies which player to read input for.
/// If this is null, it will accept input from any player. When a button press
/// is detected, the output playerIndex reports which player pressed it.
/// </summary>
public bool IsButtonPressed(Buttons button, PlayerIndex? controllingPlayer, out PlayerIndex playerIndex)
{
if (controllingPlayer.HasValue)
{
// Read input from the specified player.
playerIndex = controllingPlayer.Value;
int i = (int)playerIndex;
return CurrentGamePadStates[i].IsButtonDown(button);
}
else
{
// Accept input from any player.
return (IsButtonPressed(button, PlayerIndex.One, out playerIndex) ||
IsButtonPressed(button, PlayerIndex.Two, out playerIndex) ||
IsButtonPressed(button, PlayerIndex.Three, out playerIndex) ||
IsButtonPressed(button, PlayerIndex.Four, out playerIndex));
}
}
/// <summary>
/// Helper for checking if a key was newly pressed during this update. The
/// controllingPlayer parameter specifies which player to read input for.
/// If this is null, it will accept input from any player. When a keypress
/// is detected, the output playerIndex reports which player pressed it.
/// </summary>
public bool IsNewKeyPress(Keys key, PlayerIndex? controllingPlayer, out PlayerIndex playerIndex)
{
if (controllingPlayer.HasValue)
{
// Read input from the specified player.
playerIndex = controllingPlayer.Value;
int i = (int)playerIndex;
return (CurrentKeyboardStates[i].IsKeyDown(key) &&
LastKeyboardStates[i].IsKeyUp(key));
}
else
{
// Accept input from any player.
return (IsNewKeyPress(key, PlayerIndex.One, out playerIndex) ||
IsNewKeyPress(key, PlayerIndex.Two, out playerIndex) ||
IsNewKeyPress(key, PlayerIndex.Three, out playerIndex) ||
IsNewKeyPress(key, PlayerIndex.Four, out playerIndex));
}
}
public bool IsNewKeyRelease(Keys key, PlayerIndex? controllingPlayer, out PlayerIndex playerIndex)
{
if (controllingPlayer.HasValue)
{
// Read input from the specified player.
playerIndex = controllingPlayer.Value;
int i = (int)playerIndex;
return (CurrentKeyboardStates[i].IsKeyUp(key) &&
LastKeyboardStates[i].IsKeyDown(key));
}
else
{
// Accept input from any player.
return (IsNewKeyRelease(key, PlayerIndex.One, out playerIndex) ||
IsNewKeyRelease(key, PlayerIndex.Two, out playerIndex) ||
IsNewKeyRelease(key, PlayerIndex.Three, out playerIndex) ||
IsNewKeyRelease(key, PlayerIndex.Four, out playerIndex));
}
}
/// <summary>
/// Helper for checking if a button was newly pressed during this update.
/// The controllingPlayer parameter specifies which player to read input for.
/// If this is null, it will accept input from any player. When a button press
/// is detected, the output playerIndex reports which player pressed it.
/// </summary>
public bool IsNewButtonPress(Buttons button, PlayerIndex? controllingPlayer, out PlayerIndex playerIndex)
{
if (controllingPlayer.HasValue)
{
// Read input from the specified player.
playerIndex = controllingPlayer.Value;
int i = (int)playerIndex;
return (CurrentGamePadStates[i].IsButtonDown(button) &&
LastGamePadStates[i].IsButtonUp(button));
}
else
{
// Accept input from any player.
return (IsNewButtonPress(button, PlayerIndex.One, out playerIndex) ||
IsNewButtonPress(button, PlayerIndex.Two, out playerIndex) ||
IsNewButtonPress(button, PlayerIndex.Three, out playerIndex) ||
IsNewButtonPress(button, PlayerIndex.Four, out playerIndex));
}
}
public bool IsNewButtonRelease(Buttons button, PlayerIndex? controllingPlayer, out PlayerIndex playerIndex)
{
if (controllingPlayer.HasValue)
{
// Read input from the specified player.
playerIndex = controllingPlayer.Value;
int i = (int)playerIndex;
return (CurrentGamePadStates[i].IsButtonUp(button) &&
LastGamePadStates[i].IsButtonDown(button));
}
else
{
// Accept input from any player.
return (IsNewButtonRelease(button, PlayerIndex.One, out playerIndex) ||
IsNewButtonRelease(button, PlayerIndex.Two, out playerIndex) ||
IsNewButtonRelease(button, PlayerIndex.Three, out playerIndex) ||
IsNewButtonRelease(button, PlayerIndex.Four, out playerIndex));
}
}
public bool IsNewVirtualButtonPress(Buttons button)
{
return (_lastVirtualState.IsButtonUp(button) &&
_currentVirtualState.IsButtonDown(button));
}
public bool IsNewVirtualButtonRelease(Buttons button)
{
return (_lastVirtualState.IsButtonDown(button) &&
_currentVirtualState.IsButtonUp(button));
}
/// <summary>
/// Helper for checking if a mouse button was newly pressed during this update.
/// </summary>
public bool IsNewMouseButtonPress(MouseButtons button)
{
switch (button)
{
case MouseButtons.LeftButton:
return (_currentMouseState.LeftButton == ButtonState.Pressed &&
_lastMouseState.LeftButton == ButtonState.Released);
case MouseButtons.RightButton:
return (_currentMouseState.RightButton == ButtonState.Pressed &&
_lastMouseState.RightButton == ButtonState.Released);
case MouseButtons.MiddleButton:
return (_currentMouseState.MiddleButton == ButtonState.Pressed &&
_lastMouseState.MiddleButton == ButtonState.Released);
case MouseButtons.ExtraButton1:
return (_currentMouseState.XButton1 == ButtonState.Pressed &&
_lastMouseState.XButton1 == ButtonState.Released);
case MouseButtons.ExtraButton2:
return (_currentMouseState.XButton2 == ButtonState.Pressed &&
_lastMouseState.XButton2 == ButtonState.Released);
default:
return false;
}
}
/// <summary>
/// Checks if the requested mouse button is released.
/// </summary>
/// <param name="button">The button.</param>
public bool IsNewMouseButtonRelease(MouseButtons button)
{
switch (button)
{
case MouseButtons.LeftButton:
return (_lastMouseState.LeftButton == ButtonState.Pressed &&
_currentMouseState.LeftButton == ButtonState.Released);
case MouseButtons.RightButton:
return (_lastMouseState.RightButton == ButtonState.Pressed &&
_currentMouseState.RightButton == ButtonState.Released);
case MouseButtons.MiddleButton:
return (_lastMouseState.MiddleButton == ButtonState.Pressed &&
_currentMouseState.MiddleButton == ButtonState.Released);
case MouseButtons.ExtraButton1:
return (_lastMouseState.XButton1 == ButtonState.Pressed &&
_currentMouseState.XButton1 == ButtonState.Released);
case MouseButtons.ExtraButton2:
return (_lastMouseState.XButton2 == ButtonState.Pressed &&
_currentMouseState.XButton2 == ButtonState.Released);
default:
return false;
}
}
}
}
LoadingScreen.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#region File Description
//-----------------------------------------------------------------------------
// LoadingScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using GameStateManagement;
#endregion
namespace GameStateManagement
{
/// <summary>
/// The loading screen coordinates transitions between the menu system and the
/// game itself. Normally one screen will transition off at the same time as
/// the next screen is transitioning on, but for larger transitions that can
/// take a longer time to load their data, we want the menu system to be entirely
/// gone before we start loading the game. This is done as follows:
///
/// - Tell all the existing screens to transition off.
/// - Activate a loading screen, which will transition on at the same time.
/// - The loading screen watches the state of the previous screens.
/// - When it sees they have finished transitioning off, it activates the real
/// next screen, which may take a long time to load its data. The loading
/// screen will be the only thing displayed while this load is taking place.
/// </summary>
class LoadingScreen : GameScreen
{
#region Fields
bool loadingIsSlow;
bool otherScreensAreGone;
GameScreen[] screensToLoad;
#endregion
#region Initialization
/// <summary>
/// The constructor is private: loading screens should
/// be activated via the static Load method instead.
/// </summary>
private LoadingScreen(ScreenManager screenManager, bool loadingIsSlow,
GameScreen[] screensToLoad)
{
this.loadingIsSlow = loadingIsSlow;
this.screensToLoad = screensToLoad;
TransitionOnTime = TimeSpan.FromSeconds(0.5);
}
/// <summary>
/// Activates the loading screen.
/// </summary>
public static void Load(ScreenManager screenManager, bool loadingIsSlow,
PlayerIndex? controllingPlayer,
params GameScreen[] screensToLoad)
{
// Tell all the current screens to transition off.
foreach (GameScreen screen in screenManager.GetScreens())
screen.ExitScreen();
// Create and activate the loading screen.
LoadingScreen loadingScreen = new LoadingScreen(screenManager,
loadingIsSlow,
screensToLoad);
screenManager.AddScreen(loadingScreen, controllingPlayer);
}
#endregion
#region Update and Draw
/// <summary>
/// Updates the loading screen.
/// </summary>
public override void Update(GameTime gameTime, bool otherScreenHasFocus,
bool coveredByOtherScreen)
{
base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
// If all the previous screens have finished transitioning
// off, it is time to actually perform the load.
if (otherScreensAreGone)
{
ScreenManager.RemoveScreen(this);
foreach (GameScreen screen in screensToLoad)
{
if (screen != null)
{
ScreenManager.AddScreen(screen, ControllingPlayer);
}
}
// Once the load has finished, we use ResetElapsedTime to tell
// the game timing mechanism that we have just finished a very
// long frame, and that it should not try to catch up.
ScreenManager.Game.ResetElapsedTime();
}
}
/// <summary>
/// Draws the loading screen.
/// </summary>
public override void Draw(GameTime gameTime)
{
// If we are the only active screen, that means all the previous screens
// must have finished transitioning off. We check for this in the Draw
// method, rather than in Update, because it isn't enough just for the
// screens to be gone: in order for the transition to look good we must
// have actually drawn a frame without them before we perform the load.
if ((ScreenState == ScreenState.Active) &&
(ScreenManager.GetScreens().Length == 1))
{
otherScreensAreGone = true;
}
// The gameplay screen takes a while to load, so we display a loading
// message while that is going on, but the menus load very quickly, and
// it would look silly if we flashed this up for just a fraction of a
// second while returning from the game to the menus. This parameter
// tells us how long the loading is going to take, so we know whether
// to bother drawing the message.
if (loadingIsSlow)
{
SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
SpriteFont font = ScreenManager.Font;
const string message = "Loading...";
// Center the text in the viewport.
Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
Vector2 viewportSize = new Vector2(viewport.Width, viewport.Height);
Vector2 textSize = font.MeasureString(message);
Vector2 textPosition = (viewportSize - textSize) / 2;
Color color = Color.White * TransitionAlpha;
// Draw the text.
spriteBatch.Begin();
spriteBatch.DrawString(font, message, textPosition, color);
spriteBatch.End();
}
}
#endregion
}
}
MainMenuScreen.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#region File Description
//-----------------------------------------------------------------------------
// MainMenuScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using Microsoft.Xna.Framework;
using GameStateManagementSample;
#endregion
namespace GameStateManagement
{
/// <summary>
/// The main menu screen is the first thing displayed when the game starts up.
/// </summary>
class MainMenuScreen : MenuScreen
{
#region Initialization
/// <summary>
/// Constructor fills in the menu contents.
/// </summary>
public MainMenuScreen()
: base("Main Menu")
{
// Create our menu entries.
MenuEntry playGameMenuEntry = new MenuEntry("Play Game");
MenuEntry optionsMenuEntry = new MenuEntry("Options");
MenuEntry exitMenuEntry = new MenuEntry("Exit");
// Hook up menu event handlers.
playGameMenuEntry.Selected += PlayGameMenuEntrySelected;
optionsMenuEntry.Selected += OptionsMenuEntrySelected;
exitMenuEntry.Selected += OnCancel;
// Add entries to the menu.
MenuEntries.Add(playGameMenuEntry);
MenuEntries.Add(optionsMenuEntry);
MenuEntries.Add(exitMenuEntry);
}
#endregion
#region Handle Input
/// <summary>
/// Event handler for when the Play Game menu entry is selected.
/// </summary>
void PlayGameMenuEntrySelected(object sender, PlayerIndexEventArgs e)
{
LoadingScreen.Load(ScreenManager, true, e.PlayerIndex,
new GameplayScreen());
}
/// <summary>
/// Event handler for when the Options menu entry is selected.
/// </summary>
void OptionsMenuEntrySelected(object sender, PlayerIndexEventArgs e)
{
ScreenManager.AddScreen(new OptionsMenuScreen(), e.PlayerIndex);
}
/// <summary>
/// When the user cancels the main menu, ask if they want to exit the sample.
/// </summary>
protected override void OnCancel(PlayerIndex playerIndex)
{
const string message = "Are you sure you want to exit this sample?";
MessageBoxScreen confirmExitMessageBox = new MessageBoxScreen(message);
confirmExitMessageBox.Accepted += ConfirmExitMessageBoxAccepted;
ScreenManager.AddScreen(confirmExitMessageBox, playerIndex);
}
/// <summary>
/// Event handler for when the user selects ok on the "are you sure
/// you want to exit" message box.
/// </summary>
void ConfirmExitMessageBoxAccepted(object sender, PlayerIndexEventArgs e)
{
ScreenManager.Game.Exit();
}
#endregion
}
}
MenuEntry.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#region File Description
//-----------------------------------------------------------------------------
// MenuEntry.cs
//
// XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using GameStateManagement;
#endregion
namespace GameStateManagement
{
/// <summary>
/// Helper class represents a single entry in a MenuScreen. By default this
/// just draws the entry text string, but it can be customized to display menu
/// entries in different ways. This also provides an event that will be raised
/// when the menu entry is selected.
/// </summary>
public class MenuEntry
{
#region Fields
/// <summary>
/// The text rendered for this entry.
/// </summary>
string text;
/// <summary>
/// Tracks a fading selection effect on the entry.
/// </summary>
/// <remarks>
/// The entries transition out of the selection effect when they are deselected.
/// </remarks>
float selectionFade;
/// <summary>
/// The position at which the entry is drawn. This is set by the MenuScreen
/// each frame in Update.
/// </summary>
Vector2 position;
#endregion
#region Properties
/// <summary>
/// Gets or sets the text of this menu entry.
/// </summary>
public string Text
{
get { return text; }
set { text = value; }
}
/// <summary>
/// Gets or sets the position at which to draw this menu entry.
/// </summary>
public Vector2 Position
{
get { return position; }
set { position = value; }
}
#endregion
#region Events
/// <summary>
/// Event raised when the menu entry is selected.
/// </summary>
public event EventHandler<PlayerIndexEventArgs> Selected;
/// <summary>
/// Method for raising the Selected event.
/// </summary>
protected internal virtual void OnSelectEntry(PlayerIndex playerIndex)
{
if (Selected != null)
Selected(this, new PlayerIndexEventArgs(playerIndex));
}
#endregion
#region Initialization
/// <summary>
/// Constructs a new menu entry with the specified text.
/// </summary>
public MenuEntry(string text)
{
this.text = text;
}
#endregion
#region Update and Draw
/// <summary>
/// Updates the menu entry.
/// </summary>
public virtual void Update(MenuScreen screen, bool isSelected, GameTime gameTime)
{
// there is no such thing as a selected item on Windows Phone, so we always
// force isSelected to be false
#if WINDOWS_PHONE
isSelected = false;
#endif
// When the menu selection changes, entries gradually fade between
// their selected and deselected appearance, rather than instantly
// popping to the new state.
float fadeSpeed = (float)gameTime.ElapsedGameTime.TotalSeconds * 4;
if (isSelected)
selectionFade = Math.Min(selectionFade + fadeSpeed, 1);
else
selectionFade = Math.Max(selectionFade - fadeSpeed, 0);
}
/// <summary>
/// Draws the menu entry. This can be overridden to customize the appearance.
/// </summary>
public virtual void Draw(MenuScreen screen, bool isSelected, GameTime gameTime)
{
// there is no such thing as a selected item on Windows Phone, so we always
// force isSelected to be false
#if WINDOWS_PHONE
isSelected = false;
#endif
// Draw the selected entry in yellow, otherwise white.
Color color = isSelected ? Color.Yellow : Color.White;
// Pulsate the size of the selected menu entry.
double time = gameTime.TotalGameTime.TotalSeconds;
float pulsate = (float)Math.Sin(time * 6) + 1;
float scale = 1 + pulsate * 0.05f * selectionFade;
// Modify the alpha to fade text out during transitions.
color *= screen.TransitionAlpha;
// Draw text, centered on the middle of each line.
ScreenManager screenManager = screen.ScreenManager;
SpriteBatch spriteBatch = screenManager.SpriteBatch;
SpriteFont font = screenManager.Font;
Vector2 origin = new Vector2(0, font.LineSpacing / 2);
spriteBatch.DrawString(font, text, position, color, 0,
origin, scale, SpriteEffects.None, 0);
}
/// <summary>
/// Queries how much space this menu entry requires.
/// </summary>
public virtual int GetHeight(MenuScreen screen)
{
return screen.ScreenManager.Font.LineSpacing;
}
/// <summary>
/// Queries how wide the entry is, used for centering on the screen.
/// </summary>
public virtual int GetWidth(MenuScreen screen)
{
return (int)screen.ScreenManager.Font.MeasureString(Text).X;
}
#endregion
}
}
MenuScreen.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
#region File Description
//-----------------------------------------------------------------------------
// MenuScreen.cs
//
// XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
//using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Input;
//using FarseerPhysics.SamplesFramework;
#endregion
namespace GameStateManagement
{
/// <summary>
/// Base class for screens that contain a menu of options. The user can
/// move up and down to select an entry, or cancel to back out of the screen.
/// </summary>
public class MenuScreen : GameScreen
{
#region Fields
// the number of pixels to pad above and below menu entries for touch input
const int menuEntryPadding = 10;
private List<MenuEntry> menuEntries = new List<MenuEntry>();
int selectedEntry = 0;
string menuTitle;
InputAction menuUp;
InputAction menuDown;
InputAction menuSelect;
InputAction menuCancel;
#endregion
#region Properties
/// <summary>
/// Gets the list of menu entries, so derived classes can add
/// or change the menu contents.
/// </summary>
protected IList<MenuEntry> MenuEntries
{
get { return menuEntries; }
}
#endregion
#region Initialization
/// <summary>
/// Constructor.
/// </summary>
public MenuScreen(string menuTitle)
{
this.menuTitle = menuTitle;
// menus generally only need Tap for menu selection
//EnabledGestures = GestureType.Tap;
TransitionOnTime = TimeSpan.FromSeconds(0.5);
TransitionOffTime = TimeSpan.FromSeconds(0.5);
menuUp = new InputAction(
new Buttons[] { Buttons.DPadUp, Buttons.LeftThumbstickUp },
new Keys[] { Keys.Up },
true);
menuDown = new InputAction(
new Buttons[] { Buttons.DPadDown, Buttons.LeftThumbstickDown },
new Keys[] { Keys.Down },
true);
menuSelect = new InputAction(
new Buttons[] { Buttons.A, Buttons.Start },
new Keys[] { Keys.Enter, Keys.Space },
true);
menuCancel = new InputAction(
new Buttons[] { Buttons.B, Buttons.Back },
new Keys[] { Keys.Escape },
true);
}
#endregion
public void AddMenuItem(string name)
{
menuEntries.Add(new MenuEntry(name));
}
#region Handle Input
/// <summary>
/// Allows the screen to create the hit bounds for a particular menu entry.
/// </summary>
protected virtual Rectangle GetMenuEntryHitBounds(MenuEntry entry)
{
// the hit bounds are the entire width of the screen, and the height of the entry
// with some additional padding above and below.
return new Rectangle(
0,
(int)entry.Position.Y - menuEntryPadding,
ScreenManager.GraphicsDevice.Viewport.Width,
entry.GetHeight(this) + (menuEntryPadding * 2));
}
/// <summary>
/// Responds to user input, changing the selected entry and accepting
/// or cancelling the menu.
/// </summary>
public override void HandleInput(GameTime gameTime, InputState input)
{
// For input tests we pass in our ControllingPlayer, which may
// either be null (to accept input from any player) or a specific index.
// If we pass a null controlling player, the InputState helper returns to
// us which player actually provided the input. We pass that through to
// OnSelectEntry and OnCancel, so they can tell which player triggered them.
#if WINDOWS || XBOX360
PlayerIndex playerIndex;
// Move to the previous menu entry?
if (menuUp.Evaluate(input, ControllingPlayer, out playerIndex))
{
selectedEntry--;
if (selectedEntry < 0)
selectedEntry = menuEntries.Count - 1;
}
// Move to the next menu entry?
if (menuDown.Evaluate(input, ControllingPlayer, out playerIndex))
{
selectedEntry++;
if (selectedEntry >= menuEntries.Count)
selectedEntry = 0;
}
if (menuSelect.Evaluate(input, ControllingPlayer, out playerIndex))
{
OnSelectEntry(selectedEntry, playerIndex);
}
else if (menuCancel.Evaluate(input, ControllingPlayer, out playerIndex))
{
OnCancel(playerIndex);
}
#endif
#if WINDOWS_PHONE
//selectedEntry = 1;
PlayerIndex player;
if (input.IsNewButtonPress(Buttons.Back, ControllingPlayer, out player))
{
OnCancel(player);
}
// look for any taps that occurred and select any entries that were tapped
foreach (GestureSample gesture in input.Gestures)
{
//System.Diagnostics.Debugger.Break();
if (gesture.GestureType == GestureType.Tap)
{
// convert the position to a Point that we can test against a Rectangle
Point tapLocation = new Point((int)gesture.Position.X, (int)gesture.Position.Y);
// iterate the entries to see if any were tapped
for (int i = 0; i < menuEntries.Count; i++)
{
MenuEntry menuEntry = menuEntries[i];
if (GetMenuEntryHitBounds(menuEntry).Contains(tapLocation))
{
// select the entry. since gestures are only available on Windows Phone,
// we can safely pass PlayerIndex.One to all entries since there is only
// one player on Windows Phone.
OnSelectEntry(i, PlayerIndex.One);
}
}
}
}
#endif
}
/// <summary>
/// Handler for when the user has chosen a menu entry.
/// </summary>
protected virtual void OnSelectEntry(int entryIndex, PlayerIndex playerIndex)
{
menuEntries[entryIndex].OnSelectEntry(playerIndex);
}
/// <summary>
/// Handler for when the user has cancelled the menu.
/// </summary>
protected virtual void OnCancel(PlayerIndex playerIndex)
{
ExitScreen();
}
/// <summary>
/// Helper overload makes it easy to use OnCancel as a MenuEntry event handler.
/// </summary>
protected void OnCancel(object sender, PlayerIndexEventArgs e)
{
OnCancel(e.PlayerIndex);
}
#endregion
#region Update and Draw
/// <summary>
/// Allows the screen the chance to position the menu entries. By default
/// all menu entries are lined up in a vertical list, centered on the screen.
/// </summary>
protected virtual void UpdateMenuEntryLocations()
{
// Make the menu slide into place during transitions, using a
// power curve to make things look more interesting (this makes
// the movement slow down as it nears the end).
float transitionOffset = (float)Math.Pow(TransitionPosition, 2);
// start at Y = 175; each X value is generated per entry
Vector2 position = new Vector2(0f, 175f);
// update each menu entry's location in turn
for (int i = 0; i < menuEntries.Count; i++)
{
MenuEntry menuEntry = menuEntries[i];
// each entry is to be centered horizontally
position.X = ScreenManager.GraphicsDevice.Viewport.Width / 2 - menuEntry.GetWidth(this) / 2;
if (ScreenState == ScreenState.TransitionOn)
position.X -= transitionOffset * 256;
else
position.X += transitionOffset * 512;
// set the entry's position
menuEntry.Position = position;
// move down for the next entry the size of this entry
position.Y += menuEntry.GetHeight(this);
}
}
/// <summary>
/// Updates the menu.
/// </summary>
public override void Update(GameTime gameTime, bool otherScreenHasFocus,
bool coveredByOtherScreen)
{
base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
// Update each nested MenuEntry object.
for (int i = 0; i < menuEntries.Count; i++)
{
bool isSelected = IsActive && (i == selectedEntry);
menuEntries[i].Update(this, isSelected, gameTime);
}
}
/// <summary>
/// Draws the menu.
/// </summary>
public override void Draw(GameTime gameTime)
{
// make sure our entries are in the right place before we draw them
UpdateMenuEntryLocations();
GraphicsDevice graphics = ScreenManager.GraphicsDevice;
SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
SpriteFont font = ScreenManager.Font;
spriteBatch.Begin();
// Draw each menu entry in turn.
for (int i = 0; i < menuEntries.Count; i++)
{
MenuEntry menuEntry = menuEntries[i];
bool isSelected = IsActive && (i == selectedEntry);
menuEntry.Draw(this, isSelected, gameTime);
}
// Make the menu slide into place during transitions, using a
// power curve to make things look more interesting (this makes
// the movement slow down as it nears the end).
float transitionOffset = (float)Math.Pow(TransitionPosition, 2);
// Draw the menu title centered on the screen
Vector2 titlePosition = new Vector2(graphics.Viewport.Width / 2, 80);
Vector2 titleOrigin = font.MeasureString(menuTitle) / 2;
Color titleColor = new Color(192, 192, 192) * TransitionAlpha;
float titleScale = 1.25f;
titlePosition.Y -= transitionOffset * 100;
spriteBatch.DrawString(font, menuTitle, titlePosition, titleColor, 0,
titleOrigin, titleScale, SpriteEffects.None, 0);
spriteBatch.End();
}
#endregion
}
}
MessageBoxScreen.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#region File Description
//-----------------------------------------------------------------------------
// MessageBoxScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using GameStateManagement;
#endregion
namespace GameStateManagement
{
/// <summary>
/// A popup message box screen, used to display "are you sure?"
/// confirmation messages.
/// </summary>
class MessageBoxScreen : GameScreen
{
#region Fields
string message;
Texture2D gradientTexture;
InputAction menuSelect;
InputAction menuCancel;
#endregion
#region Events
public event EventHandler<PlayerIndexEventArgs> Accepted;
public event EventHandler<PlayerIndexEventArgs> Cancelled;
#endregion
#region Initialization
/// <summary>
/// Constructor automatically includes the standard "A=ok, B=cancel"
/// usage text prompt.
/// </summary>
public MessageBoxScreen(string message)
: this(message, true)
{ }
/// <summary>
/// Constructor lets the caller specify whether to include the standard
/// "A=ok, B=cancel" usage text prompt.
/// </summary>
public MessageBoxScreen(string message, bool includeUsageText)
{
const string usageText = "\nA button, Space, Enter = ok" +
"\nB button, Esc = cancel";
if (includeUsageText)
this.message = message + usageText;
else
this.message = message;
IsPopup = true;
TransitionOnTime = TimeSpan.FromSeconds(0.2);
TransitionOffTime = TimeSpan.FromSeconds(0.2);
menuSelect = new InputAction(
new Buttons[] { Buttons.A, Buttons.Start },
new Keys[] { Keys.Space, Keys.Enter },
true);
menuCancel = new InputAction(
new Buttons[] { Buttons.B, Buttons.Back },
new Keys[] { Keys.Escape, Keys.Back },
true);
}
/// <summary>
/// Loads graphics content for this screen. This uses the shared ContentManager
/// provided by the Game class, so the content will remain loaded forever.
/// Whenever a subsequent MessageBoxScreen tries to load this same content,
/// it will just get back another reference to the already loaded data.
/// </summary>
public override void Activate(bool instancePreserved)
{
if (!instancePreserved)
{
ContentManager content = ScreenManager.Game.Content;
gradientTexture = content.Load<Texture2D>("gradient");
}
}
#endregion
#region Handle Input
/// <summary>
/// Responds to user input, accepting or cancelling the message box.
/// </summary>
public override void HandleInput(GameTime gameTime, InputState input)
{
PlayerIndex playerIndex;
// We pass in our ControllingPlayer, which may either be null (to
// accept input from any player) or a specific index. If we pass a null
// controlling player, the InputState helper returns to us which player
// actually provided the input. We pass that through to our Accepted and
// Cancelled events, so they can tell which player triggered them.
if (menuSelect.Evaluate(input, ControllingPlayer, out playerIndex))
{
// Raise the accepted event, then exit the message box.
if (Accepted != null)
Accepted(this, new PlayerIndexEventArgs(playerIndex));
ExitScreen();
}
else if (menuCancel.Evaluate(input, ControllingPlayer, out playerIndex))
{
// Raise the cancelled event, then exit the message box.
if (Cancelled != null)
Cancelled(this, new PlayerIndexEventArgs(playerIndex));
ExitScreen();
}
}
#endregion
#region Draw
/// <summary>
/// Draws the message box.
/// </summary>
public override void Draw(GameTime gameTime)
{
SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
SpriteFont font = ScreenManager.Font;
// Darken down any other screens that were drawn beneath the popup.
ScreenManager.FadeBackBufferToBlack(TransitionAlpha * 2 / 3);
// Center the message text in the viewport.
Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
Vector2 viewportSize = new Vector2(viewport.Width, viewport.Height);
Vector2 textSize = font.MeasureString(message);
Vector2 textPosition = (viewportSize - textSize) / 2;
// The background includes a border somewhat larger than the text itself.
const int hPad = 32;
const int vPad = 16;
Rectangle backgroundRectangle = new Rectangle((int)textPosition.X - hPad,
(int)textPosition.Y - vPad,
(int)textSize.X + hPad * 2,
(int)textSize.Y + vPad * 2);
// Fade the popup alpha during transitions.
Color color = Color.White * TransitionAlpha;
spriteBatch.Begin();
// Draw the background rectangle.
spriteBatch.Draw(gradientTexture, backgroundRectangle, color);
// Draw the message box text.
spriteBatch.DrawString(font, message, textPosition, color);
spriteBatch.End();
}
#endregion
}
}
OptionsMenuScreen.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#region File Description
//-----------------------------------------------------------------------------
// OptionsMenuScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using Microsoft.Xna.Framework;
#endregion
namespace GameStateManagement
{
/// <summary>
/// The options screen is brought up over the top of the main menu
/// screen, and gives the user a chance to configure the game
/// in various hopefully useful ways.
/// </summary>
class OptionsMenuScreen : MenuScreen
{
#region Fields
MenuEntry ungulateMenuEntry;
MenuEntry languageMenuEntry;
MenuEntry frobnicateMenuEntry;
MenuEntry elfMenuEntry;
enum Ungulate
{
BactrianCamel,
Dromedary,
Llama,
}
static Ungulate currentUngulate = Ungulate.Dromedary;
static string[] languages = { "C#", "French", "Deoxyribonucleic acid" };
static int currentLanguage = 0;
static bool frobnicate = true;
static int elf = 23;
#endregion
#region Initialization
/// <summary>
/// Constructor.
/// </summary>
public OptionsMenuScreen()
: base("Options")
{
// Create our menu entries.
ungulateMenuEntry = new MenuEntry(string.Empty);
languageMenuEntry = new MenuEntry(string.Empty);
frobnicateMenuEntry = new MenuEntry(string.Empty);
elfMenuEntry = new MenuEntry(string.Empty);
SetMenuEntryText();
MenuEntry back = new MenuEntry("Back");
// Hook up menu event handlers.
ungulateMenuEntry.Selected += UngulateMenuEntrySelected;
languageMenuEntry.Selected += LanguageMenuEntrySelected;
frobnicateMenuEntry.Selected += FrobnicateMenuEntrySelected;
elfMenuEntry.Selected += ElfMenuEntrySelected;
back.Selected += OnCancel;
// Add entries to the menu.
MenuEntries.Add(ungulateMenuEntry);
MenuEntries.Add(languageMenuEntry);
MenuEntries.Add(frobnicateMenuEntry);
MenuEntries.Add(elfMenuEntry);
MenuEntries.Add(back);
}
/// <summary>
/// Fills in the latest values for the options screen menu text.
/// </summary>
void SetMenuEntryText()
{
ungulateMenuEntry.Text = "Preferred ungulate: " + currentUngulate;
languageMenuEntry.Text = "Language: " + languages[currentLanguage];
frobnicateMenuEntry.Text = "Frobnicate: " + (frobnicate ? "on" : "off");
elfMenuEntry.Text = "elf: " + elf;
}
#endregion
#region Handle Input
/// <summary>
/// Event handler for when the Ungulate menu entry is selected.
/// </summary>
void UngulateMenuEntrySelected(object sender, PlayerIndexEventArgs e)
{
currentUngulate++;
if (currentUngulate > Ungulate.Llama)
currentUngulate = 0;
SetMenuEntryText();
}
/// <summary>
/// Event handler for when the Language menu entry is selected.
/// </summary>
void LanguageMenuEntrySelected(object sender, PlayerIndexEventArgs e)
{
currentLanguage = (currentLanguage + 1) % languages.Length;
SetMenuEntryText();
}
/// <summary>
/// Event handler for when the Frobnicate menu entry is selected.
/// </summary>
void FrobnicateMenuEntrySelected(object sender, PlayerIndexEventArgs e)
{
frobnicate = !frobnicate;
SetMenuEntryText();
}
/// <summary>
/// Event handler for when the Elf menu entry is selected.
/// </summary>
void ElfMenuEntrySelected(object sender, PlayerIndexEventArgs e)
{
elf++;
SetMenuEntryText();
}
#endregion
}
}
PauseMenuScreen.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#region File Description
//-----------------------------------------------------------------------------
// PauseMenuScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using Microsoft.Xna.Framework;
#endregion
namespace GameStateManagement
{
/// <summary>
/// The pause menu comes up over the top of the game,
/// giving the player options to resume or quit.
/// </summary>
class PauseMenuScreen : MenuScreen
{
#region Initialization
/// <summary>
/// Constructor.
/// </summary>
public PauseMenuScreen()
: base("Paused")
{
// Create our menu entries.
MenuEntry resumeGameMenuEntry = new MenuEntry("Resume Game");
MenuEntry quitGameMenuEntry = new MenuEntry("Quit Game");
// Hook up menu event handlers.
resumeGameMenuEntry.Selected += OnCancel;
quitGameMenuEntry.Selected += QuitGameMenuEntrySelected;
// Add entries to the menu.
MenuEntries.Add(resumeGameMenuEntry);
MenuEntries.Add(quitGameMenuEntry);
}
#endregion
#region Handle Input
/// <summary>
/// Event handler for when the Quit Game menu entry is selected.
/// </summary>
void QuitGameMenuEntrySelected(object sender, PlayerIndexEventArgs e)
{
const string message = "Are you sure you want to quit this game?";
MessageBoxScreen confirmQuitMessageBox = new MessageBoxScreen(message);
confirmQuitMessageBox.Accepted += ConfirmQuitMessageBoxAccepted;
ScreenManager.AddScreen(confirmQuitMessageBox, ControllingPlayer);
}
/// <summary>
/// Event handler for when the user selects ok on the "are you sure
/// you want to quit" message box. This uses the loading screen to
/// transition from the game back to the main menu screen.
/// </summary>
void ConfirmQuitMessageBoxAccepted(object sender, PlayerIndexEventArgs e)
{
LoadingScreen.Load(ScreenManager, false, null, new BackgroundScreen(),
new MainMenuScreen());
}
#endregion
}
}
PhoneMainMenuScreen.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#region File Description
//-----------------------------------------------------------------------------
// PhoneMainMenuScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
using System;
using GameStateManagement;
using GameStateManagementSample;
using Microsoft.Xna.Framework;
namespace GameStateManagement
{
class PhoneMainMenuScreen : PhoneMenuScreen
{
public PhoneMainMenuScreen()
: base("Main Menu")
{
// Create a button to start the game
Button playButton = new Button("Play");
playButton.Tapped += playButton_Tapped;
MenuButtons.Add(playButton);
// Create two buttons to toggle sound effects and music. This sample just shows one way
// of making and using these buttons; it doesn't actually have sound effects or music
BooleanButton sfxButton = new BooleanButton("Sound Effects", true);
sfxButton.Tapped += sfxButton_Tapped;
MenuButtons.Add(sfxButton);
BooleanButton musicButton = new BooleanButton("Music", true);
musicButton.Tapped += musicButton_Tapped;
MenuButtons.Add(musicButton);
}
void playButton_Tapped(object sender, EventArgs e)
{
// When the "Play" button is tapped, we load the GameplayScreen
LoadingScreen.Load(ScreenManager, true, PlayerIndex.One, new GameplayScreen());
}
void sfxButton_Tapped(object sender, EventArgs e)
{
BooleanButton button = sender as BooleanButton;
// In a real game, you'd want to store away the value of
// the button to turn off sounds here. :)
}
void musicButton_Tapped(object sender, EventArgs e)
{
BooleanButton button = sender as BooleanButton;
// In a real game, you'd want to store away the value of
// the button to turn off music here. :)
}
protected override void OnCancel()
{
ScreenManager.Game.Exit();
base.OnCancel();
}
}
}
PhoneMenuScreen.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#region File Description
//-----------------------------------------------------------------------------
// PhoneMenuScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
using System;
using System.Collections.Generic;
using GameStateManagement;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
//using Microsoft.Xna.Framework.Input.Touch;
namespace GameStateManagement
{
/// <summary>
/// Provides a basic base screen for menus on Windows Phone leveraging the Button class.
/// </summary>
class PhoneMenuScreen : GameScreen
{
List<Button> menuButtons = new List<Button>();
string menuTitle;
InputAction menuCancel;
/// <summary>
/// Gets the list of buttons, so derived classes can add or change the menu contents.
/// </summary>
protected IList<Button> MenuButtons
{
get { return menuButtons; }
}
/// <summary>
/// Creates the PhoneMenuScreen with a particular title.
/// </summary>
/// <param name="title">The title of the screen</param>
public PhoneMenuScreen(string title)
{
menuTitle = title;
TransitionOnTime = TimeSpan.FromSeconds(0.5);
TransitionOffTime = TimeSpan.FromSeconds(0.5);
// Create the menuCancel action
menuCancel = new InputAction(new Buttons[] { Buttons.Back }, null, true);
// We need tap gestures to hit the buttons
//EnabledGestures = GestureType.Tap;
}
public override void Activate(bool instancePreserved)
{
// When the screen is activated, we have a valid ScreenManager so we can arrange
// our buttons on the screen
float y = 140f;
float center = ScreenManager.GraphicsDevice.Viewport.Bounds.Center.X;
for (int i = 0; i < MenuButtons.Count; i++)
{
Button b = MenuButtons[i];
b.Position = new Vector2(center - b.Size.X / 2, y);
y += b.Size.Y * 1.5f;
}
base.Activate(instancePreserved);
}
public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
{
// Update opacity of the buttons
foreach (Button b in menuButtons)
{
b.Alpha = TransitionAlpha;
}
base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
}
/// <summary>
/// An overrideable method called whenever the menuCancel action is triggered
/// </summary>
protected virtual void OnCancel() { }
public override void HandleInput(GameTime gameTime, InputState input)
{
// Test for the menuCancel action
PlayerIndex player;
if (menuCancel.Evaluate(input, ControllingPlayer, out player))
{
OnCancel();
}
// Read in our gestures
/*foreach (GestureSample gesture in input.Gestures)
{
// If we have a tap
if (gesture.GestureType == GestureType.Tap)
{
// Test the tap against the buttons until one of the buttons handles the tap
foreach (Button b in menuButtons)
{
if (b.HandleTap(gesture.Position))
break;
}
}
}*/
base.HandleInput(gameTime, input);
}
public override void Draw(GameTime gameTime)
{
GraphicsDevice graphics = ScreenManager.GraphicsDevice;
SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
SpriteFont font = ScreenManager.Font;
spriteBatch.Begin();
// Draw all of the buttons
foreach (Button b in menuButtons)
b.Draw(this);
// Make the menu slide into place during transitions, using a
// power curve to make things look more interesting (this makes
// the movement slow down as it nears the end).
float transitionOffset = (float)Math.Pow(TransitionPosition, 2);
// Draw the menu title centered on the screen
Vector2 titlePosition = new Vector2(graphics.Viewport.Width / 2, 80);
Vector2 titleOrigin = font.MeasureString(menuTitle) / 2;
Color titleColor = new Color(192, 192, 192) * TransitionAlpha;
float titleScale = 1.25f;
titlePosition.Y -= transitionOffset * 100;
spriteBatch.DrawString(font, menuTitle, titlePosition, titleColor, 0,
titleOrigin, titleScale, SpriteEffects.None, 0);
spriteBatch.End();
base.Draw(gameTime);
}
}
}
PhonePauseScreen.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#region File Description
//-----------------------------------------------------------------------------
// PhonePauseScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
using System;
namespace GameStateManagement
{
/// <summary>
/// A basic pause screen for Windows Phone
/// </summary>
class PhonePauseScreen : PhoneMenuScreen
{
public PhonePauseScreen()
: base("Paused")
{
// Create the "Resume" and "Exit" buttons for the screen
Button resumeButton = new Button("Resume");
resumeButton.Tapped += resumeButton_Tapped;
MenuButtons.Add(resumeButton);
Button exitButton = new Button("Exit");
exitButton.Tapped += exitButton_Tapped;
MenuButtons.Add(exitButton);
}
/// <summary>
/// The "Resume" button handler just calls the OnCancel method so that
/// pressing the "Resume" button is the same as pressing the hardware back button.
/// </summary>
void resumeButton_Tapped(object sender, EventArgs e)
{
OnCancel();
}
/// <summary>
/// The "Exit" button handler uses the LoadingScreen to take the user out to the main menu.
/// </summary>
void exitButton_Tapped(object sender, EventArgs e)
{
LoadingScreen.Load(ScreenManager, false, null, new BackgroundScreen(),
new PhoneMainMenuScreen());
}
protected override void OnCancel()
{
ExitScreen();
base.OnCancel();
}
}
}
PlayerIndexEventArgs.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#region File Description
//-----------------------------------------------------------------------------
// PlayerIndexEventArgs.cs
//
// XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using Microsoft.Xna.Framework;
#endregion
namespace GameStateManagement
{
/// <summary>
/// Custom event argument which includes the index of the player who
/// triggered the event. This is used by the MenuEntry.Selected event.
/// </summary>
public class PlayerIndexEventArgs : EventArgs
{
/// <summary>
/// Constructor.
/// </summary>
public PlayerIndexEventArgs(PlayerIndex playerIndex)
{
this.playerIndex = playerIndex;
}
/// <summary>
/// Gets the index of the player who triggered this event.
/// </summary>
public PlayerIndex PlayerIndex
{
get { return playerIndex; }
}
PlayerIndex playerIndex;
}
}
Properties/AssemblyInfo.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("GameStateManagement")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("GameStateManagement")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("a1a96363-c163-4a2a-8f31-d84d80c4c0d7")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
ScreenManager.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
#region File Description
//-----------------------------------------------------------------------------
// ScreenManager.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Diagnostics;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
//using Microsoft.Xna.Framework.Input.Touch;
using System.IO;
using System.IO.IsolatedStorage;
using System.Xml.Linq;
//using FarseerPhysics.SamplesFramework;
#endregion
namespace GameStateManagement
{
/// <summary>
/// The screen manager is a component which manages one or more GameScreen
/// instances. It maintains a stack of screens, calls their Update and Draw
/// methods at the appropriate times, and automatically routes input to the
/// topmost active screen.
/// </summary>
public class ScreenManager : DrawableGameComponent
{
#region Fields
private const string StateFilename = "ScreenManagerState.xml";
List<GameScreen> screens = new List<GameScreen>();
List<GameScreen> tempScreensList = new List<GameScreen>();
InputState input;
SpriteBatch spriteBatch;
SpriteFont font;
Texture2D blankTexture;
bool isInitialized;
bool traceEnabled;
/// <summary>
/// Contains all the fonts avaliable for use.
/// </summary>
//private SpriteFonts _spriteFonts;
#endregion
#region Properties
public InputState InputState
{
get { return input; }
private set { input = value; }
}
/*public SpriteFonts Fonts
{
get { return _spriteFonts; }
}*/
/// <summary>
/// A default SpriteBatch shared by all the screens. This saves
/// each screen having to bother creating their own local instance.
/// </summary>
public SpriteBatch SpriteBatch
{
get { return spriteBatch; }
}
/// <summary>
/// A default font shared by all the screens. This saves
/// each screen having to bother loading their own local copy.
/// </summary>
public SpriteFont Font
{
get { return font; }
}
/// <summary>
/// If true, the manager prints out a list of all the screens
/// each time it is updated. This can be useful for making sure
/// everything is being added and removed at the right times.
/// </summary>
public bool TraceEnabled
{
get { return traceEnabled; }
set { traceEnabled = value; }
}
/// <summary>
/// Gets a blank texture that can be used by the screens.
/// </summary>
public Texture2D BlankTexture
{
get { return blankTexture; }
}
#endregion
#region Initialization
/// <summary>
/// Constructs a new screen manager component.
/// </summary>
public ScreenManager(Game game)
: base(game)
{
// we must set EnabledGestures before we can query for them, but
// we don't assume the game wants to read them.
//TouchPanel.EnabledGestures = GestureType.None;
this.input = new InputState(this);
}
/// <summary>
/// Initializes the screen manager component.
/// </summary>
public override void Initialize()
{
base.Initialize();
isInitialized = true;
}
/// <summary>
/// Load your graphics content.
/// </summary>
protected override void LoadContent()
{
// Load content belonging to the screen manager.
ContentManager content = new ContentManager(this.Game.Services, "Content/Fonts");
//_spriteFonts = new SpriteFonts(content);
spriteBatch = new SpriteBatch(GraphicsDevice);
font = content.Load<SpriteFont>("menufont");
blankTexture = Game.Content.Load<Texture2D>("Materials/blank");
input.LoadContent();
// Tell each of the screens to load their content.
foreach (GameScreen screen in screens)
{
screen.Activate(false);
}
}
/// <summary>
/// Unload your graphics content.
/// </summary>
protected override void UnloadContent()
{
// Tell each of the screens to unload their content.
foreach (GameScreen screen in screens)
{
screen.Unload();
}
}
#endregion
#region Update and Draw
/// <summary>
/// Allows each screen to run logic.
/// </summary>
public override void Update(GameTime gameTime)
{
// Read the keyboard and gamepad.
input.Update(gameTime);
// Make a copy of the master screen list, to avoid confusion if
// the process of updating one screen adds or removes others.
tempScreensList.Clear();
foreach (GameScreen screen in screens)
tempScreensList.Add(screen);
bool otherScreenHasFocus = !Game.IsActive;
bool coveredByOtherScreen = false;
// Loop as long as there are screens waiting to be updated.
while (tempScreensList.Count > 0)
{
// Pop the topmost screen off the waiting list.
GameScreen screen = tempScreensList[tempScreensList.Count - 1];
tempScreensList.RemoveAt(tempScreensList.Count - 1);
// Update the screen.
screen.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
if (screen.ScreenState == ScreenState.TransitionOn ||
screen.ScreenState == ScreenState.Active)
{
// If this is the first active screen we came across,
// give it a chance to handle input.
if (!otherScreenHasFocus)
{
input.ShowCursor = screen.HasCursor;
input.EnableVirtualStick = screen.HasVirtualStick;
screen.HandleInput(gameTime, input);
otherScreenHasFocus = true;
}
// If this is an active non-popup, inform any subsequent
// screens that they are covered by it.
if (!screen.IsPopup)
coveredByOtherScreen = true;
}
}
// Print debug trace?
if (traceEnabled)
TraceScreens();
}
/// <summary>
/// Prints a list of all the screens, for debugging.
/// </summary>
void TraceScreens()
{
List<string> screenNames = new List<string>();
foreach (GameScreen screen in screens)
screenNames.Add(screen.GetType().Name);
Debug.WriteLine(string.Join(", ", screenNames.ToArray()));
}
/// <summary>
/// Tells each screen to draw itself.
/// </summary>
public override void Draw(GameTime gameTime)
{
foreach (GameScreen screen in screens)
{
if (screen.ScreenState == ScreenState.Hidden)
continue;
screen.Draw(gameTime);
}
input.Draw();
}
#endregion
#region Public Methods
/// <summary>
/// Adds a new screen to the screen manager.
/// </summary>
public void AddScreen(GameScreen screen, PlayerIndex? controllingPlayer)
{
screen.ControllingPlayer = controllingPlayer;
screen.ScreenManager = this;
screen.IsExiting = false;
// If we have a graphics device, tell the screen to load content.
if (isInitialized)
{
screen.Activate(false);
}
screens.Add(screen);
// update the TouchPanel to respond to gestures this screen is interested in
//TouchPanel.EnabledGestures = screen.EnabledGestures;
}
/// <summary>
/// Adds a new screen to the screen manager with a default PlayerIndex of one
/// </summary>
public void AddScreen(GameScreen screen)
{
screen.ControllingPlayer = PlayerIndex.One;
screen.ScreenManager = this;
screen.IsExiting = false;
// If we have a graphics device, tell the screen to load content.
if (isInitialized)
{
screen.Activate(false);
}
screens.Add(screen);
// update the TouchPanel to respond to gestures this screen is interested in
//TouchPanel.EnabledGestures = screen.EnabledGestures;
}
/// <summary>
/// Removes a screen from the screen manager. You should normally
/// use GameScreen.ExitScreen instead of calling this directly, so
/// the screen can gradually transition off rather than just being
/// instantly removed.
/// </summary>
public void RemoveScreen(GameScreen screen)
{
// If we have a graphics device, tell the screen to unload content.
if (isInitialized)
{
screen.Unload();
}
screens.Remove(screen);
tempScreensList.Remove(screen);
// if there is a screen still in the manager, update TouchPanel
// to respond to gestures that screen is interested in.
/*if (screens.Count > 0)
{
TouchPanel.EnabledGestures = screens[screens.Count - 1].EnabledGestures;
}*/
}
/// <summary>
/// Expose an array holding all the screens. We return a copy rather
/// than the real master list, because screens should only ever be added
/// or removed using the AddScreen and RemoveScreen methods.
/// </summary>
public GameScreen[] GetScreens()
{
return screens.ToArray();
}
/// <summary>
/// Helper draws a translucent black fullscreen sprite, used for fading
/// screens in and out, and for darkening the background behind popups.
/// </summary>
public void FadeBackBufferToBlack(float alpha)
{
spriteBatch.Begin();
spriteBatch.Draw(blankTexture, GraphicsDevice.Viewport.Bounds, Color.Black * alpha);
spriteBatch.End();
}
/// <summary>
/// Informs the screen manager to serialize its state to disk.
/// </summary>
public void Deactivate()
{
#if !WINDOWS_PHONE
return;
#else
// Open up isolated storage
using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
{
// Create an XML document to hold the list of screen types currently in the stack
XDocument doc = new XDocument();
XElement root = new XElement("ScreenManager");
doc.Add(root);
// Make a copy of the master screen list, to avoid confusion if
// the process of deactivating one screen adds or removes others.
tempScreensList.Clear();
foreach (GameScreen screen in screens)
tempScreensList.Add(screen);
// Iterate the screens to store in our XML file and deactivate them
foreach (GameScreen screen in tempScreensList)
{
// Only add the screen to our XML if it is serializable
if (screen.IsSerializable)
{
// We store the screen's controlling player so we can rehydrate that value
string playerValue = screen.ControllingPlayer.HasValue
? screen.ControllingPlayer.Value.ToString()
: "";
root.Add(new XElement(
"GameScreen",
new XAttribute("Type", screen.GetType().AssemblyQualifiedName),
new XAttribute("ControllingPlayer", playerValue)));
}
// Deactivate the screen regardless of whether we serialized it
screen.Deactivate();
}
// Save the document
using (IsolatedStorageFileStream stream = storage.CreateFile(StateFilename))
{
doc.Save(stream);
}
}
#endif
}
public bool Activate(bool instancePreserved)
{
#if !WINDOWS_PHONE
return false;
#else
// If the game instance was preserved, the game wasn't dehydrated so our screens still exist.
// We just need to activate them and we're ready to go.
if (instancePreserved)
{
// Make a copy of the master screen list, to avoid confusion if
// the process of activating one screen adds or removes others.
tempScreensList.Clear();
foreach (GameScreen screen in screens)
tempScreensList.Add(screen);
foreach (GameScreen screen in tempScreensList)
screen.Activate(true);
}
// Otherwise we need to refer to our saved file and reconstruct the screens that were present
// when the game was deactivated.
else
{
// Try to get the screen factory from the services, which is required to recreate the screens
IScreenFactory screenFactory = Game.Services.GetService(typeof(IScreenFactory)) as IScreenFactory;
if (screenFactory == null)
{
throw new InvalidOperationException(
"Game.Services must contain an IScreenFactory in order to activate the ScreenManager.");
}
// Open up isolated storage
using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
{
// Check for the file; if it doesn't exist we can't restore state
if (!storage.FileExists(StateFilename))
return false;
// Read the state file so we can build up our screens
using (IsolatedStorageFileStream stream = storage.OpenFile(StateFilename, FileMode.Open))
{
XDocument doc = XDocument.Load(stream);
// Iterate the document to recreate the screen stack
foreach (XElement screenElem in doc.Root.Elements("GameScreen"))
{
// Use the factory to create the screen
Type screenType = Type.GetType(screenElem.Attribute("Type").Value);
GameScreen screen = screenFactory.CreateScreen(screenType);
// Rehydrate the controlling player for the screen
PlayerIndex? controllingPlayer = screenElem.Attribute("ControllingPlayer").Value != ""
? (PlayerIndex)Enum.Parse(typeof(PlayerIndex), screenElem.Attribute("ControllingPlayer").Value, true)
: (PlayerIndex?)null;
screen.ControllingPlayer = controllingPlayer;
// Add the screen to the screens list and activate the screen
screen.ScreenManager = this;
screens.Add(screen);
screen.Activate(false);
// update the TouchPanel to respond to gestures this screen is interested in
TouchPanel.EnabledGestures = screen.EnabledGestures;
}
}
}
}
return true;
#endif
}
#endregion
}
}

Archive Download the corresponding diff file

Branches

Number of commits:
Page rendered in 0.22340s using 14 queries.