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 |
#region File Description
//-----------------------------------------------------------------------------
// GameScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using Microsoft.Xna.Framework;
#endregion
namespace AIDemos
{
/// <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
{
#region Properties
/// <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;
/// <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 255 (fully active, no transition) to 0 (transitioned
/// fully off to nothing).
/// </summary>
public byte TransitionAlpha
{
get { return (byte)(255 - TransitionPosition * 255); }
}
/// <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;
#endregion
#region Initialization
/// <summary>
/// Load graphics content for the screen.
/// </summary>
public virtual void LoadContent() { }
/// <summary>
/// Unload content for the screen.
/// </summary>
public virtual void UnloadContent() { }
#endregion
#region Update and Draw
/// <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(InputState input) { }
/// <summary>
/// This is called when the screen should draw itself.
/// </summary>
public virtual void Draw(GameTime gameTime) { }
#endregion
#region Public Methods
/// <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;
}
}
#endregion
}
} |