 |
Create a Platformer Starter Kit, compile it and exit, we’re doing this is so that we have access to the content and code. |
 |
Extract the Network Game State Manager to a new directory. |
 |
Open the solution file from the Network Game State Manager folder you just created. |
 |
Expand the content sub project. |
 |
Add 5 new sub folders called :- Fonts, Graphics, Levels, Sounds, StateManager. |
 |
In the Graphics folder add 4 new sub folders called Backgrounds, Overlays, Sprites & Tiles. |
 |
Move the gamefont.spritefont & menufont.spritefont into the Fonts folder from the root of the content sub project. |
 |
Move the remaining files from the root of the content sub project into the StateManager folder. |
 |
Open an Explorer window at the SharedContent folder for the Platformer project you created in step 1 then open the Sounds folder. |
 |
Select all the WMA files and drag them over to the Sounds folder in your Solution Explorer, When you drop them on this folder they will be added to the solution. |
 |
Now in the Explorer window navigate up two directories and then into the HighResolutionContent Folder where you will find Backgrounds, Fonts, Levels, Overlays, Sprites & Tiles folders. |
 |
Open each of these directories and copy their contents into their corresponding folders in the Solution Explorer. |
 |
Add a new sub folder to the NetworkStateManagementWindows solution called GameClasses. |
 |
Go to your Explorer window, move back up from the Graphics folder to the main solution directory, here you need to select the following files :- Animation.cs, AnimationPlayer.cs, Circle.cs, Enemy.cs, Gem.cs, Level.cs, Player.cs, RectangleExtensions.cs, Tile.cs only. |
 |
Drag and drop these files on to the GameClasses folder, and they will be added to your solution. |
 |
Open up each of the files you just added and change the namespace line from
or what ever you called your temp solution back in step one, to
namespace NetworkStateManagement
Why? So we dont have to add a using directive to the game file. VS2008 gives you a real easy way to do this see picture on left. |
|
Expand Screens folder and open GamePlayScreen.cs ready for editing. |
|
Remove the following lines from The fields region :-
Vector2 playerPosition = new Vector2(100, 100);
Vector2 enemyPosition = new Vector2(100, 100);
Random random = new Random();
|
|
And then add the following lines to the Fields region:-
// Global content.
private SpriteFont hudFont;
private Texture2D winOverlay;
private Texture2D loseOverlay;
private Texture2D diedOverlay;
// Meta-level game state.
private int levelIndex = -1;
private Level level;
private bool wasContinuePressed;
// When the time remaining is less than the warning time, it blinks on the hud
private static readonly TimeSpan WarningTime = TimeSpan.FromSeconds(30);
private const Buttons ContinueButton = Buttons.A;
|
|
Replace the LoadContent method with this :-
public override void LoadContent()
{
if (content == null)
content = new ContentManager(ScreenManager.Game.Services, "Content");
// Load fonts
hudFont = content.Load<SpriteFont>("Fonts/Hud");
// Load overlay textures
winOverlay = content.Load<Texture2D>("Graphics/Overlays/you_win");
loseOverlay = content.Load<Texture2D>("Graphics/Overlays/you_lose");
diedOverlay = content.Load<Texture2D>("Graphics/Overlays/you_died");
MediaPlayer.IsRepeating = true;
MediaPlayer.Play(content.Load<Song>("Sounds/Music"));
LoadNextLevel();
// 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();
}
|
|
Replace the Update method with this :-
public override void Update(GameTime gameTime, bool otherScreenHasFocus,
bool coveredByOtherScreen)
{
base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
if (IsActive)
{
level.Update(gameTime);
}
#region Network Update
// If we are in a network game, check if we should return to the lobby.
if ((networkSession != null) && !IsExiting)
{
if (networkSession.SessionState == NetworkSessionState.Lobby)
{
LoadingScreen.Load(ScreenManager, true, null,
new BackgroundScreen(),
new LobbyScreen(networkSession));
}
}
#endregion
}
|
|
Replace HandlePlayerInput method with :-
bool HandlePlayerInput(InputState input, PlayerIndex playerIndex)
{
// Look up inputs for the specified player profile.
KeyboardState keyboardState = input.CurrentKeyboardStates[(int)playerIndex];
GamePadState gamePadState = input.CurrentGamePadStates[(int)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[(int)playerIndex];
if (input.IsPauseGame(playerIndex) || gamePadDisconnected)
{
ScreenManager.AddScreen(new PauseMenuScreen(networkSession), playerIndex);
return false;
}
bool continuePressed = gamePadState.IsButtonDown(ContinueButton) ||
keyboardState.IsKeyDown(Keys.Space) ||
keyboardState.IsKeyDown(Keys.Up) ||
keyboardState.IsKeyDown(Keys.W);
// Perform the appropriate action to advance the game and
// to get the player back to playing.
if (!wasContinuePressed && continuePressed)
{
if (!level.Player.IsAlive)
{
level.StartNewLife();
}
else if (level.TimeRemaining == TimeSpan.Zero)
{
if (level.ReachedExit)
LoadNextLevel();
else
ReloadCurrentLevel();
}
}
wasContinuePressed = continuePressed;
return true;
}
[edit]A gotcha has been fixed with the continuePressed check, when you are using the keyboard for input, thanks to DarkChief for finding that one [/edit]
|
|
Finaly replace the Draw method with this :-
public override void Draw(GameTime gameTime)
{
//Platformer code
ScreenManager.SpriteBatch.Begin();
level.Draw(gameTime, ScreenManager.SpriteBatch);
DrawHud();
ScreenManager.SpriteBatch.End();
// If the game is transitioning on or off, fade it out to black.
if (TransitionPosition > 0)
ScreenManager.FadeBackBufferToBlack(255 - TransitionAlpha);
}
|
|
Next you need to add some code to handle the HUD and Level re/loading copy the following code block in after the last method in the class :-
#region Hud and Load Level
private void DrawHud()
{
Rectangle titleSafeArea = ScreenManager.GraphicsDevice.Viewport.TitleSafeArea;
Vector2 hudLocation = new Vector2(titleSafeArea.X, titleSafeArea.Y);
Vector2 center = new Vector2(titleSafeArea.X + titleSafeArea.Width / 2.0f,
titleSafeArea.Y + titleSafeArea.Height / 2.0f);
#region Draw time remaining.
// Uses modulo division to cause blinking when the player is running out of time.
string timeString = "TIME: " + level.TimeRemaining.Minutes.ToString("00") +
":" + level.TimeRemaining.Seconds.ToString("00");
Color timeColor;
if (level.TimeRemaining > WarningTime ||
level.ReachedExit ||
(int)level.TimeRemaining.TotalSeconds % 2 == 0)
{
timeColor = Color.Yellow;
}
else
{
timeColor = Color.Red;
}
DrawShadowedString(hudFont, timeString, hudLocation, timeColor);
#endregion
#region Draw score
float timeHeight = hudFont.MeasureString(timeString).Y;
DrawShadowedString(hudFont,
"SCORE: " + level.Score.ToString(),
hudLocation + new Vector2(0.0f, timeHeight * 1.2f),
Color.Yellow);
#endregion
#region Determine the status overlay message to show.
Texture2D status = null;
if (level.TimeRemaining == TimeSpan.Zero)
{
if (level.ReachedExit)
{
status = winOverlay;
}
else
{
status = loseOverlay;
}
}
else if (!level.Player.IsAlive)
{
status = diedOverlay;
}
if (status != null)
{
// Draw status message.
Vector2 statusSize = new Vector2(status.Width, status.Height);
ScreenManager.SpriteBatch.Draw(status, center - statusSize / 2, Color.White);
}
#endregion
}
private void DrawShadowedString(SpriteFont font, string value, Vector2 position,
Color color)
{
ScreenManager.SpriteBatch.DrawString(font,
value,
position + new Vector2(1.0f, 1.0f),
Color.Black);
ScreenManager.SpriteBatch.DrawString(font, value, position, color);
}
private void LoadNextLevel()
{
// Find the path of the next level.
string levelPath;
// Loop here so we can try again when we can't find a level.
while (true)
{
// Try to find the next level. They are sequentially numbered txt files.
levelPath = String.Format("Levels/{0}.txt", ++levelIndex);
levelPath = Path.Combine(StorageContainer.TitleLocation,
"Content/" + levelPath);
if (File.Exists(levelPath))
break;
// If there isn't even a level 0, something has gone wrong.
if (levelIndex == 0)
throw new Exception("No levels found.");
// Whenever we can't find a level, start over again at 0.
levelIndex = -1;
}
// Unloads the content for the current level before loading the next one.
//if (level != null)
// level.Dispose();
// Load the level.
level = new Level(content, levelPath);
}
private void ReloadCurrentLevel()
{
--levelIndex;
LoadNextLevel();
}
#endregion
|
|
Next you need to jump back to the top of the file and add some using statements :-
using Microsoft.Xna.Framework.Media;
using System.IO;
using Microsoft.Xna.Framework.Storage;
|
|
Expand GameClasses folder and open Level.cs ready for editing. |
|
Find the Constructor(in the Loading region-Why?) and change the first parameter type from
public Level(IServiceProvider serviceProvider, string path)
to
public Level(ContentManager serviceProvider, string path)
|
|
and the line just under it from
content = new ContentManager(serviceProvider, "Content");
to
content = serviceProvider;
|
 |
Expand the Levels folder in the content sub project, Select all three text files and change the Build Action property to None and the Copyto Output Directory to Copy if newer. |
 |
Press Ctrl+F to get the Find dialog box up, and enter “Content.Load<Texture2D>” in to the Find what: box and select “Entire Solution” from the Look in: dropdown & make sure the Match Case option is off. For each item you find alter the method parameter to point at it’s new location EG :-
texture = Level.Content.Load<Texture2D>("Sprites/Gem");
Becomes
texture = Level.Content.Load<Texture2D>("Graphics/Sprites/Gem");
Sounds & Fonts should be unaffected….
[Edit]My mistake the line in ScreenManager.cs that loads in it’s font, near line 115, needs updating from “menufont” to “fonts/menufont” thanks to Rives for pointing that one out [/Edit] |
|
Open Game.cs ready for editing from the solution root. |
|
Go to the constructor method and find the lines that set up the screen resolution :-
graphics.PreferredBackBufferWidth = 1067;
graphics.PreferredBackBufferHeight = 600;
And change them both to the following values :-
graphics.PreferredBackBufferWidth = 1280;
graphics.PreferredBackBufferHeight = 720;
|
|
Now Compile and run the solution by pressing F5. |
|
Job Done. |