Sgt. Conker We are "absolutely fine"

18Sep/102

Article: Vacant Skies – Action RPG Tutorial Series

by Aaron T Foley

Welcome to the Vacant Skies – Action RPG Tutorial Series. My name is Aaron Foley and I’ll be your host throughout this series. The primary purpose of the series is to document a route on how to make a complete action RPG using XNA and various resources throughout the internet. There are so many amazing resources out there now for XNA that it is pointless to keep reinventing the wheel when writing a game. So I’m going to collect various tutorials, code, and libraries and utilize them all to create a game. So let’s get this show on the road.

First off we need to determine some of our base resources we will be using. Most will already have these, but if you don’t there are links that can direct you to where you can get them. I’ll be linking to the Express editions of Visual C#, but will be using Visual Studio for the tutorials. I also will be using XNA Game Studio 3.1 for now until XNA Game Studio 4.0 comes out. Once 4.0 comes out, these will be switched over to using XNA 4.0 and Visual Studio 2010.

Resources

Once we have all these installed, we can begin.

Download: Part1_Resources.zip

To start we will need to create a new project using the Windows Game (3.1) template.

I like to use my Dropbox for storing my projects, but you can store them where ever you feel like. So once that is created we will have our base application.

Next we will need to add another project to our solution. This will be our helper library and will store code and classes we will use throughout our game. So add a new project to our solution and select Windows Game Library (3.1) from our Templates and name it.

Now in our main application we need to add the library reference to references and in the content references.

So now we have our base project ready. So let’s get our hands dirty.

For our first resource we need some way of managing what state our game is in. There is a wonderful sample on the XNA Creators Club site that handles this great. So let’s go get that and implement it into our game.

Game State Management Sample

To start we will be using the graphics and content from the sample as filler graphics until we get a chance to work on some more. So copy the content from the sample over to our main application. I broke them out into their own folders (i.e. Textures and Fonts) to start some basic organization of our content.

So our first task is to add the ScreenManager component to our library. So create a folder in our library called Screens and add in the three class files GameScreen.cs, and ScreenManager.cs. These will need some tweaking to work so let’s see what we have.

First go through and change the namespaces in each file to the corresponding namespaces of your library (i.e. VacantSkies.Lib.Screens). I also created a new class file and moved the ScreenState enumeration over too it, but that’s more of a personal preference and is not necessary.

Next we will create another folder in our Library and name it Input. Here we will copy over the InputState class file and change its namespace to what we need. (i.e. VacantSkies.Lib.Input)

So now if we try to compile we will probably get a couple errors stating that some namespaces couldn’t be found. So you can either add those in by hand, or resolve them however you choose. Once those are resolved they should compile.

Now let’s add in our title screen. In our main application add a new folder called Screens. Next add in all the screens from the Game State Management sample. Once again go through and change all our namespaces over and resolve any namespaces that are missing.

Next in our constructor for our game application add in the following lines of code.

   	  public Game1()
        {
            graphics = new GraphicsDeviceManager(this)
            {
                PreferredBackBufferWidth = 1280,
                PreferredBackBufferHeight = 720
            };

            Content.RootDirectory = "Content";

            screenManager = new ScreenManager(this);

            Components.Add(screenManager);

            screenManager.AddScreen(new BackgroundScreen(), null);
            screenManager.AddScreen(new MainMenuScreen(), null);
        }

Make sure you change the paths to our content also in ScreenManager, BackgroundScreen, GameplayScreen, and MessageBoxScreen to reflect any folder changes in our Content folder. Once that is complete you can run your application and should get a fully navigable menu system.

It isn’t anything spectacular yet, but we can edit it to whatever we need now.

So let’s start doing some changes to our game screens now. First let’s make the title screen really show our game instead of the filler graphics. So add in the following to our textures content, VacantSkies-Title, and VacantSkies-TitleText.

First lets open BackgroundScreen. This screen will sit behind all of our other screens, so we want it to be something rather basic instead of the sample background. So in the LoadContent change the backgroundTexture to “Textures\\gradient”.

Next lets open up our MainMenuScreen. There are going to be some significant changes need to be made here. So before we dive into this lets determine what we want our main menu to actually reflect.

  • Main Menu
    • New Story
    • Continue Story
    • Options
    • Credits
    • Exit

So you can see there is some changes we need to make to the menu. So in the constructor of our MainMenuScreen add in some new menu entries and change other entries to reflect this menu structure. So our constructor should look like this.

   	public MainMenuScreen() : base("")
        {
            // Create our menu entries.
            MenuEntry playGameMenuEntry = new MenuEntry("New Game");
            MenuEntry continueGameMenuEntry = new MenuEntry("Continue Story");
            MenuEntry optionsMenuEntry = new MenuEntry("Options");
            MenuEntry creditsMenuEntry = new MenuEntry("Credits");
            MenuEntry exitMenuEntry = new MenuEntry("Exit");

            // Hook up menu event handlers.
            playGameMenuEntry.Selected += PlayGameMenuEntrySelected;
            continueGameMenuEntry.Selected += ContinueGameMenuEntrySelected;
            optionsMenuEntry.Selected += OptionsMenuEntrySelected;
            creditsMenuEntry.Selected += CreditsGameMenuEntrySelected;
            exitMenuEntry.Selected += OnCancel;

            // Add entries to the menu.
            MenuEntries.Add(playGameMenuEntry);
            MenuEntries.Add(continueGameMenuEntry);
            MenuEntries.Add(optionsMenuEntry);
            MenuEntries.Add(creditsMenuEntry);
            MenuEntries.Add(exitMenuEntry);

		MenuPosition = new Vector2(100, 300);
        }

And the two new event hooks should look like this for now.

  /// <summary>
        /// Event handler for when the Continue Game menu entry is selected.
        /// </summary>
        void ContinueGameMenuEntrySelected(object sender, PlayerIndexEventArgs e)
        {

        }
	  /// <summary>
        /// Event handler for when the Credits menu entry is selected.
        /// </summary>
        void CreditsGameMenuEntrySelected(object sender, PlayerIndexEventArgs e)
        {

        }

Now in order to continue we need to change some things in MenuScreen. First add in a property Vector2 MenuPosition {get; set;} This will allow us to move our menu to wherever we choose on the screen. Now in the constructor of MenuScreen set MenuPosition = new Vector2(100, 150) and then change our draw methods to use MenuPosition instead of position.
Now jump back over to our MainMenuScreen and add in the following fields.

        ContentManager content;
        Texture2D background;
        Texture2D title;

Then add in the LoadContent method.

    public override void LoadContent()
        {
            if (content == null) content = new ContentManager(ScreenManager.Game.Services, "Content");
            background = content.Load<Texture2D>("Textures\\VacantSkies-Title");
            title = content.Load<Texture2D>("Textures\\VacantSkies-TitleText");
        }
Then finally our Draw method.
    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);
            byte fade = TransitionAlpha;

            Color transitionColor = new Color(fade, fade, fade);

            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);

                spriteBatch.Draw(background, fullscreen, transitionColor);
                spriteBatch.Draw(title, new Vector2(100f, 50f), transitionColor);

            spriteBatch.End();

            base.Draw(gameTime);
        }

So now when we run it, our game is starting to take on shape.

Now that we have our base menu in place, let’s change the other menu screens to what we need. Let’s work with the Options screen first. There are not going to be a bunch of options to set, but this leaves it open to you if you want to add in some more options for your game. There are going to be two options that can be set, Main Volume and SFX Volume. These will be stored in a static class that will contain global information for the game, and when the player saves, it will save to that players save slot.
So in our library let’s add a new class called Settings. This will be a static class so we have access to it throughout our library.

	public static class Settings
	{
		public static int MainVolume = 75;
		public static int SFXVolume = 75;
	}

Now let’s go take a look at the OptionsMenuScreen. Most of the sample code here can be gotten rid of, but lets first add in some backgrounds for screens other than the Title Screen. We will add in another texture called Background001 to our Content folder in our main application. Than we will create our fields and add in our LoadContent method to load the background.
Our new fields:


		ContentManager content;
		Texture2D background;

Our LoadContent method:

#region Load Content

		public override void LoadContent()
		{
			if (content == null) content = new ContentManager(ScreenManager.Game.Services, "Content");
			background = content.Load<Texture2D>("Textures\\Background001");
		}

		#endregion

Then our Draw method:

	#region Drawing

		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);
            byte fade = TransitionAlpha;

            Color transitionColor = new Color(fade, fade, fade);

            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);

                spriteBatch.Draw(background, fullscreen, transitionColor);

            spriteBatch.End();

            base.Draw(gameTime);
        }

        #endregion

So now, if you run our application and go to the options menu you will see a nice wood background.

Next, we are going to add another tweak to the MenuScreen to allow us to set the position of the Menu Title, so if we need to we can move it to wherever we wish. We are also going to have it default to center horizontally on the screen. So open MenuScreen.cs and add in the property

public Vector2 TitlePosition { get; set; }

Then in our constructor set TitlePosition to whatever. The original default is TitlePosition = new Vector2(426, 80); Now in our Draw method change our code to use the new TitlePosition, and then add a new method to center the title when we load the screen.

	public void CenterTitle()
		{
			if (ScreenManager != null)
			{
				TitlePosition = new Vector2((ScreenManager.GraphicsDevice.Viewport.Width / 2) - (ScreenManager.Font.MeasureString(menuTitle).X / 2), 80);
			}
		}

Now we can go back to our OptionsMenuScreen.LoadContent and add the line CenterTitle() so our title centers when we load the screen.
So next we need to create our menu items for Main Volume and SFX Volume. Lets create two new MenuEntries for our menu items in the constructor, and then hook them in. So this is what our OptionsMenuScreen constructor should now look like.

  public OptionsMenuScreen() : base("Options")
        {
			// Create our menu entries.
			mainVolume = new MenuEntry(string.Empty);
			sfxVolume = new MenuEntry(string.Empty);

            SetMenuEntryText();

            MenuEntry backMenuEntry = new MenuEntry("Back");

            // Hook up menu event handlers.
			mainVolume.Selected += MainVolumeEntrySelected;
			sfxVolume.Selected += SFXVolumeEntrySelected;
            backMenuEntry.Selected += OnCancel;

            // Add entries to the menu.
			MenuEntries.Add(mainVolume);
			MenuEntries.Add(sfxVolume);
            MenuEntries.Add(backMenuEntry);
        }

As you can see we created two more fields mainVolume, and sfxVolume, and added a new method SetMenuEntryText().

/// <summary>
        /// Fills in the latest values for the options screen menu text.
        /// </summary>
        void SetMenuEntryText()
        {
			mainVolume.Text = String.Format("Main Volume: {0} %", Settings.MainVolume);
			sfxVolume.Text = String.Format("SFX Volume: {0} %", Settings.SFXVolume);
        }

Then we need to setup our event hooks for when the items are selected.

/// <summary>
		/// Event handler for when the Main Volume menu entry is selected.
		/// </summary>
		void MainVolumeEntrySelected(object sender, PlayerIndexEventArgs e)
		{
			Settings.MainVolume += 5;
			if (Settings.MainVolume > 100) Settings.MainVolume = 0;

			SetMenuEntryText();
		}

		/// <summary>
		/// Event handler for when the SFX Volume menu entry is selected.
		/// </summary>
		void SFXVolumeEntrySelected(object sender, PlayerIndexEventArgs e)
		{
			Settings.SFXVolume += 5;
			if (Settings.SFXVolume > 100) Settings.SFXVolume = 0;

			SetMenuEntryText();
		}

So now, when we run it we should have something like this.

When we select each item, the volume increases 5% and will wrap around to 0%. In addition, it saves it to our Settings class.
Next, we will add in a Credits screen. This will be straightforward and will only have one menu item. So on our Screens folder add a new class and name it CreditsScreen.

    class CreditsScreen: MenuScreen
    {
        #region Fields

		ContentManager content;
		Texture2D background;

        #endregion

        #region Initialization

        /// <summary>
        /// Constructor.
        /// </summary>
		public CreditsScreen() : base("Credits")
        {
			MenuEntry backMenuEntry = new MenuEntry("Back");
            backMenuEntry.Selected += OnCancel;
            MenuEntries.Add(backMenuEntry);
        }

        #endregion

		#region Load Content

		public override void LoadContent()
		{
			if (content == null) content = new ContentManager(ScreenManager.Game.Services, "Content");
			background = content.Load<Texture2D>("Textures\\Background001");
			CenterTitle();
			if (ScreenManager != null)
			{
				MenuPosition = new Vector2(100f, ScreenManager.GraphicsDevice.Viewport.Height - (ScreenManager.GraphicsDevice.Viewport.Height * 0.1f));
			}
		}

		#endregion

       	#region Drawing

		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);
            byte fade = TransitionAlpha;

            Color transitionColor = new Color(fade, fade, fade);

			Vector2 position = new Vector2(100, 150);

			float transitionOffset = (float)Math.Pow(TransitionPosition, 2);

			if (ScreenState == ScreenState.TransitionOn)
				position.X -= transitionOffset * 256;
			else
				position.X += transitionOffset * 512;

            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);

                spriteBatch.Draw(background, fullscreen, transitionColor);

				spriteBatch.DrawString(ScreenManager.Font, "[ Design / Concept / Programming / Art ] - Aaron T Foley", position, Color.White);
				spriteBatch.DrawString(ScreenManager.Font, "[ Design / Testing ] - Ginger Foley", position + new Vector2(0f, ScreenManager.Font.LineSpacing * 2), Color.White);
				spriteBatch.DrawString(ScreenManager.Font, "[ Music ] - Neil Lynn", position + new Vector2(0f, ScreenManager.Font.LineSpacing * 4), Color.White);

            spriteBatch.End();

            base.Draw(gameTime);
        }

        #endregion
    }

As you can see there is nothing special about this screen, but it can be molded into whatever you want.
Our next section will deal with our Continue Story screen and loading from storage.
So our first thing we need to do is create the new screen ContinueGameScreen. Once again, this will be roughly the same as the Credits screen and the Options screen.

	class ContinueGameScreen : MenuScreen
    {
        #region Fields

		ContentManager content;
		Texture2D background;

        #endregion

        #region Initialization

        /// <summary>
        /// Constructor.
        /// </summary>
		public ContinueGameScreen() : base("Continue Game")
        {
			MenuEntry backMenuEntry = new MenuEntry("Back");
            backMenuEntry.Selected += OnCancel;
            MenuEntries.Add(backMenuEntry);
        }

        #endregion

		#region Load Content

		public override void LoadContent()
		{
			if (content == null) content = new ContentManager(ScreenManager.Game.Services, "Content");
			background = content.Load<Texture2D>("Textures\\Background001");
			CenterTitle();
		}

		#endregion

        #region Handle Input

        #endregion

		#region Drawing

		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);
            byte fade = TransitionAlpha;

            Color transitionColor = new Color(fade, fade, fade);

            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);

                spriteBatch.Draw(background, fullscreen, transitionColor);

            spriteBatch.End();

            base.Draw(gameTime);
        }

        #endregion
    }

So this is pretty basic, but we will flesh it out. Also don’t forget to add in your event hook in your MainMenuScreen to transition to the continue screen.

/// <summary>
        /// Event handler for when the Continue Game menu entry is selected.
        /// </summary>
        void ContinueGameMenuEntrySelected(object sender, PlayerIndexEventArgs e)
        {
			ScreenManager.AddScreen(new ContinueGameScreen(), e.PlayerIndex);
        }

Now that we have the screen loading, let’s get even more dirty. We will be using a library to handle the storage device for us. It is a nice library called EasyStorage.

EasyStorage

So download this and in the DLL’s to our solution. I created a new folder in my solution called EasyStorage and added in the DLL and resource files it uses there. Then I added the reference to it.

Now we need to determine what we want to see on our Continue Game screen.

My original concept will use 5 slots that display some information about the save that’s currently within that slot. So we need to create a SaveGameDescription that holds the information we want to display in each slot.

[Serializable]
	public class SaveGameDescription
	{
		public string Filename { get; set; }
		public string PlayerName { get; set; }
		public string PlayerLevel { get; set; }
		public string PlayerClass { get; set; }
		public string Region { get; set; }
		public string Description { get; set; }

		public SaveGameDescription()
		{
			Filename = "";
			PlayerName = "";
			PlayerLevel = "0";
			PlayerClass = "Unknown";
			Region = "Unknown";
			Description = "Empty";
		}
	}

Now that we have defined what our SaveGameDescriptions will be, we need somewhere to store them globally. So we will create a static class called Global that will store data we will need throughout the game such as this.

	public static class Global
	{

		public static List<SaveGameDescription> SaveGameDescriptions { get; set; }

		static Global()
		{
			SaveGameDescriptions = new List<SaveGameDescription>();
		}
	}

So now we have some data to work with to create our 5 slots for our continue screen. So back in our continue game screen we will modify our screen to handle the five slots.
I’m wanting to use the MenuEntry system to do this, but each item has a default of the Fonts Line Spacing to space the items out on the screen. So we will have to modify MenuScreen again to have an adjustable LineSpacing.
So in MenuScreen.cs add the property,

public float LineSpacing { get; set; }

and in the constructor set it to 0. This will be the amount each item will space in addition to the default Font.LineSpacing. Then in the Draw method modify the following line.

position.Y += menuEntry.GetHeight(this) + LineSpacing;

So now we can set our additional line spacing in our screens to whatever we choose.
So here is the final ContinueGameScreen.cs. Right now it doesn’t actually load any save data, but it provides the shell for doing it. It does load save descriptions, and if one doesn’t exist it creates a blank one so it can display information on the slot.

	class ContinueGameScreen : MenuScreen
    {
        #region Fields

		ISaveDevice saveDevice;

		ContentManager content;
		Texture2D background;

		MenuEntry slot1;
        MenuEntry slot2;
        MenuEntry slot3;
        MenuEntry slot4;
        MenuEntry slot5;

		private SaveGameDescription loadedSaveGameDescription;
		private SaveGameDescription savedSaveGameDescription;

		public string StorageContainerName = "Vacant Skies";

		private readonly XmlSerializer serializer = new XmlSerializer(typeof(SaveGameDescription));

        #endregion

		#region Initialization

		/// <summary>
        /// Constructor.
        /// </summary>
		public ContinueGameScreen() : base("Continue Game")
        {
			MenuEntry backMenuEntry = new MenuEntry("Back");
            backMenuEntry.Selected += OnCancel;

			slot1 = new MenuEntry("Slot 1");
			slot2 = new MenuEntry("Slot 2");
			slot3 = new MenuEntry("Slot 3");
			slot4 = new MenuEntry("Slot 4");
			slot5 = new MenuEntry("Slot 5");

			SetMenuText();

			slot1.Selected += Slot1Selected;
			slot2.Selected += Slot2Selected;
			slot3.Selected += Slot3Selected;
			slot4.Selected += Slot4Selected;
			slot5.Selected += Slot5Selected;

			MenuEntries.Add(slot1);
			MenuEntries.Add(slot2);
			MenuEntries.Add(slot3);
			MenuEntries.Add(slot4);
			MenuEntries.Add(slot5);
			MenuEntries.Add(backMenuEntry);
        }

        #endregion

		#region Load Content

		public override void LoadContent()
		{
			if (content == null) content = new ContentManager(ScreenManager.Game.Services, "Content");
			background = content.Load<Texture2D>("Textures\\Background001");
			CenterTitle();
			LineSpacing = ScreenManager.Font.LineSpacing * 1.25f;

			LoadStorageDevice();
		}

		#endregion

        #region Handle Input

		void Slot1Selected(object sender, PlayerIndexEventArgs e)
		{
			SetMenuText();
		}

		void Slot2Selected(object sender, PlayerIndexEventArgs e)
		{
			SetMenuText();
		}

		void Slot3Selected(object sender, PlayerIndexEventArgs e)
		{
			SetMenuText();
		}

		void Slot4Selected(object sender, PlayerIndexEventArgs e)
		{
			SetMenuText();
		}

		void Slot5Selected(object sender, PlayerIndexEventArgs e)
		{
			SetMenuText();
		}

        #endregion

		#region Drawing

		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);
            byte fade = TransitionAlpha;

            Color transitionColor = new Color(fade, fade, fade);

            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);

                spriteBatch.Draw(background, fullscreen, transitionColor);

            spriteBatch.End();

            base.Draw(gameTime);
        }

		#endregion

		void SetMenuText()
		{
			if (Global.SaveGameDescriptions.Count > 1)
			{
				SaveGameDescription save = Global.SaveGameDescriptions[0];
				slot1.Text = string.Format("Slot 1 | Name: {0} Class: {1} Level: {2} Region: {3}\r\n       | {4}", save.PlayerName, save.PlayerClass, save.PlayerLevel, save.Region, save.Description);
			}
			if (Global.SaveGameDescriptions.Count > 2)
			{
				SaveGameDescription save = Global.SaveGameDescriptions[1];
				slot2.Text = string.Format("Slot 2 | Name: {0} Class: {1} Level: {2} Region: {3}\r\n       | {4}", save.PlayerName, save.PlayerClass, save.PlayerLevel, save.Region, save.Description);
			}
			if (Global.SaveGameDescriptions.Count > 3)
			{
				SaveGameDescription save = Global.SaveGameDescriptions[2];
				slot3.Text = string.Format("Slot 3 | Name: {0} Class: {1} Level: {2} Region: {3}\r\n       | {4}", save.PlayerName, save.PlayerClass, save.PlayerLevel, save.Region, save.Description);
			}
			if (Global.SaveGameDescriptions.Count > 4)
			{
				SaveGameDescription save = Global.SaveGameDescriptions[3];
				slot4.Text = string.Format("Slot 4 | Name: {0} Class: {1} Level: {2} Region: {3}\r\n       | {4}", save.PlayerName, save.PlayerClass, save.PlayerLevel, save.Region, save.Description);
			}
			if (Global.SaveGameDescriptions.Count > 1)
			{
				SaveGameDescription save = Global.SaveGameDescriptions[4];
				slot5.Text = string.Format("Slot 5 | Name: {0} Class: {1} Level: {2} Region: {3}\r\n       | {4}", save.PlayerName, save.PlayerClass, save.PlayerLevel, save.Region, save.Description);
			}
		}

		void LoadStorageDevice()
		{
#if WINDOWS
			saveDevice = new PCSaveDevice(StorageContainerName);
			ReadSaves();
#else
			// add the GamerServicesComponent
			Components.Add(new GamerServicesComponent(this));

			// create and add our SaveDevice
			SharedSaveDevice sharedSaveDevice = new SharedSaveDevice(StorageContainerName);
			Components.Add(sharedSaveDevice);

			// hook an event for when the device is selected to run our test
			sharedSaveDevice.DeviceSelected += (s, e) => ReadSaves();

			// hook two event handlers to force the user to choose a new device if they cancel the
			// device selector or if they disconnect the storage device after selecting it
			sharedSaveDevice.DeviceSelectorCanceled += (s, e) => e.Response = SaveDeviceEventResponse.Force;
			sharedSaveDevice.DeviceDisconnected += (s, e) => e.Response = SaveDeviceEventResponse.Force;

			// prompt for a device on the first Update we can
			sharedSaveDevice.PromptForDevice();

			// make sure we hold on to the device
			saveDevice = sharedSaveDevice;
#endif

			SetMenuText();
		}

		private void ReadSaves()
		{
			Global.SaveGameDescriptions.Clear();
			for (int i = 1; i <= 5; i++)
			{
				string filename = String.Format("SaveSlot{0}-Description.xml", i);
				if (!saveDevice.FileExists(filename))
				{
					Trace.WriteLine(String.Format("Failed to find file {0}.", filename));
					savedSaveGameDescription = new SaveGameDescription();
					if (!saveDevice.Save(filename, SerializeSave))
					{
						Trace.WriteLine(string.Format("Failed to save file {0}.", filename));
					}
				}
				else
				{
					if (!saveDevice.Load(filename, DeserializeSave))
					{
						Trace.WriteLine(String.Format("Failed to load file {0}.", filename));
					}
					Global.SaveGameDescriptions.Add(loadedSaveGameDescription);
				}
			}
		}

		private void DeserializeSave(Stream stream)
		{
			loadedSaveGameDescription = serializer.Deserialize(stream) as SaveGameDescription;
			Trace.WriteLine("Save Game Description Loaded: " + loadedSaveGameDescription);
		}

		private void SerializeSave(Stream stream)
		{
			Trace.WriteLine("Game Description Saved: " + savedSaveGameDescription);
			serializer.Serialize(stream, savedSaveGameDescription);
		}
    }

So now if you run our application you should see something like this in the continue screen.

So this concludes part one of the Action RPG Tutorial Series. In our next part we will be diving into creating the game world and populating it.

Download: Part1_Source.zip

About Absolutely Fine Tutorial Contest

Look out for the complete list of entries in our tutorial contest! Coming soon (it's being built step by step)!
Comments (2) Trackbacks (0)
  1. Great Tutorial!
    Since XNA 4.0 is out it may be a good idea to remove the recompile stuff since they removed this option. (I´m really missing this feature :( )
    If I did not overread it, you dont tell us the tint colors you use for the day night circle. I´ve to admit that I´m unable to find some good looking colors ;)

  2. Uh… Stupid Comment, the light values are in the code…


Leave a comment


*

No trackbacks yet.