This looks like a good candidate for two patterns:
MVC and
MVVM
I would structure my code in an MVC way for the main game (e.g. your form 2), and use MVVM for the resource management screens (e.g your form1).
The M (model) for both would be the same.
The V (views) would be different, because you're displaying the game in one form and a resource management screen in the other.
The C (controller) and VM (ViewModel) would be very different.
The controller would direct the flow of the game.
The view model essentially allows you to plug your model into the view in a clean fashion.
I'll try and give an example, taking a vertical slice of your game. It'll use Winforms, rather than WPF, but the ideas can be applied to either, and the only difference is in the view layer.
Starting with a VM:
using ExampleModel;
namespace ViewModels
{
public class ArmyViewModel
{
private ArmyModel model;
public ArmyViewModel(ArmyModel model)
{
this.model = model;
}
public void UpgradeSoldiers()
{
foreach (SoldierModel soldier in model.Soldiers)
{
soldier.Upgrade();
}
}
}
}
This is a VM for the ArmyModel, which models an army in your game. (I'm only modelling the ability to upgrade your soldiers, there would be far more code in the final version - whatever logic you need to make your game make sense.) As you can see the VM has a method that allows your army to upgrade each of its soldiers. This works by iterating over the soldiers in the model that the VM contains and calling the Upgrade method on each soldier.
In the layer above the VM (the view) you would have a class that looks something like this:
using System;
using System.Windows.Forms;
using ExampleModel;
using ViewModels;
namespace ExampleApp
{
public partial class Form1 : Form
{
private ArmyViewModel viewModel;
public Form1(GameModel model)
{
viewModel = new ArmyViewModel(model.PlayerArmy);
InitializeComponent();
}
private void btnUpgrade_Click(object sender, EventArgs e)
{
viewModel.UpgradeSoldiers();
}
}
}
This represents the form that will allow the player to upgrade her soldiers. You can see in the constructor that you need to pass in an instance of the GameModel class, so that the form can instantiate its ArmyViewModel. (I'm not 100% sure this is the best way to do this, but it is sufficient for this example.)
The form also has an event handler, that handles when the "Upgrade the soldiers" button is clicked. As you can see, this calls the UpgradeSoldiers method that the ArmyModel has.
For actually playing the game, you have the controller class, which might look like this:
using ExampleModel;
namespace ExampleControllers
{
public enum GameState { stopped, paused, running };
public class GameController
{
GameModel model;
GameState currentState = GameState.stopped;
public GameController(GameModel model)
{
this.model = model;
}
/*
* methods for controlling the flow of the game would go below
*/
}
}
As you can see in the constructor, the GameController constructor take a game model, and stores it for future reference (in all the flow control methods, etc.).
Then in your form2, you would have code like this:
using System.Windows.Forms;
using ExampleControllers;
using ExampleModel;
namespace ExampleApp
{
public partial class Form2 : Form
{
GameController controller;
public Form2(GameModel model )
{
controller = new GameController(model);
InitializeComponent();
}
/*
* code to handle whatever events take place on this form (e.g. menu clicks) should go below here
*/
}
}
So you can see that creating this form creates the controller using an
already created model of the game.
Tying the two forms together is the Program class, which you'll find in the main project. It kicks off the application.
The one I have looks like this:
using System;
using System.Windows.Forms;
using ExampleModel;
namespace ExampleApp
{
static class Program
{
static private GameModel model = new GameModel();
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1(model));
}
}
}
Ass you can see the class creates an instance of Form1 and displays it. Most of this code is generated by visual studio, but I added in the GameModel private member, and the passing of it into the constructor for Form1.
To get the same set of soldiers that are held in the model into Form2 you can make Form2 be a sort of singleton, so you would change the class like this:
using System;
using System.Windows.Forms;
using ExampleModel;
namespace ExampleApp
{
static class Program
{
static private GameModel model = new GameModel();
static private private Form2 form2;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
form2 = new Form(model);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1(model));
}
}
}
In the above I've added in 2 lines of code, one declaring form2 and the other instantiating it, passing in the model as it does. this means that forms2 and the instance of Form1 have the same model, so the soldiers are the same. so anything you do to the soldiers in form2 will apply to the soldiers in the instance of Form1.
For completeness I'll show you the skeletons of the model classes:
namespace ExampleModel
{
public class GameModel
{
public ArmyModel PlayerArmy
{
get;
private set;
}
public GameModel()
{
PlayerArmy = new ArmyModel();
}
}
}
using System.Collections.Generic;
namespace ExampleModel
{
public class ArmyModel
{
private readonly List<SoldierModel> soldiers = new List<SoldierModel>();
public IEnumerable<SoldierModel> Soldiers
{
get{return soldiers;}
}
}
}
namespace ExampleModel
{
public class SoldierModel
{
public void Upgrade()
{
//whatever this does
}
}
}
The interesting part is in the ArmyModel: it is using a List<SoldierModel> to hold hall the soldiers in an army. The List<T> generic class implements the IEnumerable<T> generic interface which allows developers to iterate over the structure in a foreach loop and use it in other fun things like Linq statements. You'll notice that the Soldiers property only exposes the IEnumerable<T> interface. This is so that someone accessing the list of soldiers can
only iterate over it. It is important to know how much functionality you want to expose to others when designing your classes.
For example you could change the above to look like this:
public List<SoldierModel> Soldiers
{
get{return soldiers;}
}
and that would allow anyone accessing the property to have full access to the list, which would allow adding and removing elements, as well as all the other public methods of the List<T> class. This can be undesirable if you want strict control over how soldiers are added and removed from the army.
So, the short answer to your question is to store the soldiers for your player in a list of soldiers that is passed to both forms.
Edited by Matt Ellen, 27 December 2010 - 05:19 PM.