CU_MDX_Framework.zip (63 KB, 2,866 hits)
CUnitFramework.zip (101.1 KB, 12,582 hits)
inside the project (not solution) directory.
When working with your own projects, it is extremely helpful to put together a framework that you can reuse again and again in future projects. This saves both time and energy since you don’t have to write the same code that you have already written half a dozen times. Using the knowledge from the previous tutorials, I will introduce you to my C-Unit Framework, which we can use to easily create_ new applications.
The idea behind any framework is to hide the code that is common to every application using the framework. This includes window creation, DirectX initialization, fullscreen toggles, etc. Since all that code remains the same in every application, we shouldn’t have to fuss with it let alone see it. Instead, we will modify an application-specific class whose methods will be called by the framework whenever necessary.
To begin with, our framework will need to support 2 basic types of resource methods: resource creation methods and resource disposal methods. The resource creation and disposal methods will need to account for which memory pool the resource is created in. For our purposes, we will categorize a resource as being either a Pool.Managed resource or a Pool.Default resource. Managed resources are backed up by system memory and do not need to be recreated unless the device is destroyed. Default resources, on the other hand, are not backed up so default resources need to be recreated whenever the device is reset. Keeping all this in mind, we will write Managed resource creation and disposal methods (OnCreateDevice and OnDestroyDevice) and Default resource creation and disposal methods (OnResetDevice and OnLostDevice). We will also add functions to handle application initialization, frame updates and frame renders, key presses, etc. (I recommend reading MSDN’s page on managing resources before continuing).
The C-Unit Framework supports the use of stack-based states. This allows for the implementation of multiple game-states and/or screens. Each node of the stack will inherit from the abstract GameState class:
/// <summary>A game state.</summary> /// <remarks>Contains a method for each stage of execution.</remarks> public abstract class GameState { public abstract void OnCreateDevice( Device device ); public abstract void OnResetDevice( Device device ); public abstract void OnLostDevice(); public abstract void OnDestroyDevice(); public abstract void OnUpdateFrame( Device device, float elapsedTime ); public abstract void OnRenderFrame( Device device, float elapsedTime ); public abstract void OnKeyboard( List<Keys> keyState, char pressedChar, int pressedKey, float elapsedTime ); public abstract void OnMouse( Point position, int xDelta, int yDelta, int zDelta, bool[] buttons, float elapsedTime ); public abstract void OnControl( int controlID, object data ); public abstract bool DoneWithState { get; } }
The GameState class contains the resource methods described above as well as a method for each stage of execution. The parameters of each method will be supplied by the Framework, so all we need to do to implement our own state is create_ a new class that inherits from GameState and then implement all the methods. Each GameState instance will be stored on a stack maintained by the StateManager class:
using System.Windows.Forms;
using System.Drawing;
using System.Collections.Generic;
using Microsoft.DirectX.Direct3D;
namespace CUnit
{
{
/// <summary>Creates a new StateManager</summary>
public StateManager() 
}
/// <summary>Pushes a State onto the stack.</summary>
/// <param name=”state”>New State.</param>
public void Push( GameState state ) 
}
/// <summary>Pops a State off the stack.</summary>
/// <returns>The State popped or null if the stack is empty.</returns>
public GameState Pop() 
{
m_stack.Peek().OnLostDevice();
m_stack.Peek().OnDestroyDevice();
return m_stack.Pop();
}
return null;
}
/// <summary>Peeks the top of the stack.</summary>
/// <returns>The State at the top of the Stack or null if the stack is empty.</returns>
public GameState Peek() 
{
}
return null;
}
/// <summary>Call when the Device is created.</summary>
/// <param name=”device”>D3D Device</param>
public void OnCreateDevice( Device device ) 
{
}
}
/// <summary>Call when the Device is reset.</summary>
/// <param name=”device”>D3D Device</param>
public void OnResetDevice( Device device ) 
{
}
}
/// <summary>Call when the Device is lost.</summary>
public void OnLostDevice() 
{
}
}
/// <summary>Call when the Device is destroyed.</summary>
public void OnDestroyDevice() 
{
}
}
/// <summary>Updates the frame prior to rendering.</summary>
/// <param name=”device”>D3D Device</param>
/// <param name=”elapsedTime”>Time since last frame</param>
public void OnUpdateFrame( Device device, float elapsedTime ) 
{
// Check if the State is finished
if ( m_stack.Peek().DoneWithState )
{
}
}
}
/// <summary>Renders the current frame of the current state.</summary>
/// <param name=”device”>D3D Device</param>
/// <param name=”elapsedTime”>Time since last frame</param>
public void OnRenderFrame( Device device, float elapsedTime ) 
{
}
}
/// <summary>Keyboard handler.</summary>
/// <param name=”pressedKeys”>List of pressed keys.</param>
/// <param name=”pressedChar”>Character read from keyboard.</param>
/// <param name=”pressedKey”>Keycode read from keyboard.</param>
/// <param name=”elapsedTime”>Time since last frame</param>
public void OnKeyboard( List<Keys> pressedKeys, char pressedChar, int pressedKey, float elapsedTime ) 
{
}
}
/// <summary>Mouse handler.</summary>
/// <param name=”position”>Mouse position in client coordinates</param>
/// <param name=”xDelta”>X-axis delta.</param>
/// <param name=”yDelta”>Y-axis delta.</param>
/// <param name=”zDelta”>Wheel delta.</param>
/// <param name=”buttons”>Mouse button state.</param>
/// <param name=”elapsedTime”>Time since last frame</param>
public void OnMouse( Point position, int xDelta, int yDelta, int zDelta, bool[] buttons, float elapsedTime ) 
{
}
}
/// <summary>GUI handler.</summary>
/// <param name=”controlID”>Control ID</param>
/// <param name=”data”>Control data</param>
public void OnControl( int controlID, object data ) 
{
}
}
}
}
The StateManager class manipulates a stack of GameStates and processes whatever GameState is on the top of the stack. For the resource manipulation methods, StateManager iterates through all the GameStates on the stack in order to keep all the resources on the stack valid.
Notice the GameState class has a DoneWithState property. This property is used to tell StateManager that it can pop the state off the stack (in StateManager.OnUpdateFrame). For example, if you have a menu system, when the user is down selecting items, you would set DoneWithState to true, and the menu would be popped off the stack. Of course, if you want to manually pop a state off the stack, you are free to do so. Just override the DoneWithState property and return false to prevent StateManager from popping the state automatically.
The main class of execution, GameApp, implements GameState and is the first GameState to be placed on the Framework’s stack:
{
public class GameApp : GameState
{
private const string Instructions = “Esc: Quit”;
private string m_instructionDisplay = “F1: View instructions”;
public enum ControlID { Fullscreen, Options };
private float m_fps = 0f;
private Gui.GuiManager m_gui = null;
private BitmapFont m_bFont = null;
/// <summary>Default constructor.</summary>
public GameApp( Framework framework ) 
m_framework.PushState( this );
}
/// <summary>Initialize application-specific resources, states here.</summary>
/// <returns>True on success, false on failure</returns>
private bool Initialize() 
return true;
}
/// <summary>
/// This event will be fired immediately after the Direct3D device has been
/// created, which will happen during application initialization. This is the best
/// location to create_ Pool.Managed resources since these resources need to be
/// reloaded whenever the device is destroyed. Resources created
/// here should be released in the OnDetroyDevice callback.
/// </summary>
/// <param name=”device”>The Direct3D device</param>
public override void OnCreateDevice( Device device ) 
{
}
m_gui = new Gui.GuiManager( “CUnit.xml”, new Gui.GuiManager.ControlDelegate( OnControl ) );
m_gui.CreateButton( (int)ControlID.Fullscreen, 0, new PointF( (float)BackBufferWidth - 120f, 10f ), new SizeF( 110f, 30f ), “Toggle Fullscreen”, 15f, new ColorValue( 0, 0, 0 ) );
m_gui.CreateButton( (int)ControlID.Options, 0, new PointF( (float)BackBufferWidth - 120f, 50f ), new SizeF( 110f, 30f ), “Options”, 15f, new ColorValue( 0, 0, 0 ) );
m_gui.OnCreateDevice( device );
if ( m_bFont != null )
{
}
}
/// <summary>
/// This event will be fired immediately after the Direct3D device has been
/// reset, which will happen after a lost device scenario, a window resize, and a
/// fullscreen toggle. This is the best location to create_ Pool.Default resources
/// since these resources need to be reloaded whenever the device is reset. Resources
/// created here should be released in the OnLostDevice callback.
/// </summary>
/// <param name=”device”>The Direct3D device</param>
public override void OnResetDevice( Device device ) 
{
}
if ( m_bFont != null )
{
}
// Set render states
device.RenderState.Lighting = false;
device.RenderState.FillMode = m_framework.FillMode;
// Keep buttons at upper right corner
m_gui.SetPosition( (int)ControlID.Fullscreen, new PointF( (float)BackBufferWidth - 120f, 10f ) );
m_gui.SetPosition( (int)ControlID.Options, new PointF( (float)BackBufferWidth - 120f, 50f ) );
}
/// <summary>
/// This function will be called after the Direct3D device has
/// entered a lost state and before Device.Reset() is called. Resources created
/// in the OnResetDevice callback should be released here, which generally includes all
/// Pool.Default resources.
/// </summary>
public override void OnLostDevice() 
{
}
if ( m_bFont != null )
{
}
}
/// <summary>
/// This callback function will be called immediately after the Direct3D device has
/// been destroyed, which generally happens as a result of application termination.
/// Resources created in the OnCreateDevice callback should be released here, which
/// generally includes all Pool.Managed resources.
/// </summary>
public override void OnDestroyDevice() 
{
}
if ( m_bFont != null )
{
}
}
/// <summary>Updates a frame prior to rendering.</summary>
/// <param name=”device”>The Direct3D device</param>
/// <param name=”elapsedTime”>Time elapsed since last frame</param>
public override void OnUpdateFrame( Device device, float elapsedTime ) 
}
/// <summary>Renders the current frame.</summary>
/// <param name=”device”>The Direct3D device</param>
/// <param name=”elapsedTime”>Time elapsed since last frame</param>
public override void OnRenderFrame( Device device, float elapsedTime ) 
device.BeginScene();
m_gui.Render( device );
// Only need to rebuild the text when the FPS updates
if ( m_fps != m_framework.FPS )
{
BuildText();
}
m_bFont.Render( device );
device.EndScene();
device.Present();
}
/// <summary>Builds all the BitmapFont strings.</summary>
private void BuildText() 
m_bFont.AddString( “FPS: “ + m_fps.ToString( “f2″ ), new RectangleF( 5f, 5f, (float)BackBufferWidth, 100f ), BitmapFont.Align.Left, 16f, ColorValue.FromColor( Color.Red ), true );
m_bFont.AddString( m_instructionDisplay, new RectangleF( 5f, 20f, 500f, 500f ), BitmapFont.Align.Left, 16f, ColorValue.FromColor( Color.White ), true );
}
/// <summary>Keyboard event handler</summary>
/// <param name=”pressedKeys”>List of pressed keys</param>
/// <param name=”pressedChar”>Unicode character read from Windows Form</param>
/// <param name=”pressedKey”>Pressed key from Form used for repeatable keys</param>
/// <param name=”elapsedTime”>Time since last frame</param>
public override void OnKeyboard( List<Keys> pressedKeys, char pressedChar, int pressedKey, float elapsedTime ) 
{
}
foreach ( Keys k in pressedKeys )
{
{
break;
case Keys.F1:
m_instructionDisplay = ( m_instructionDisplay == Instructions ) ? “F1: View instructions” : Instructions;
BuildText();
break;
}
}
}
/// <summary>Mouse event handler</summary>
/// <param name=”position”>Mouse position in client coordinates</param>
/// <param name=”xDelta”>X-axis delta</param>
/// <param name=”yDelta”>Y-axis delta</param>
/// <param name=”zDelta”>Mouse wheel delta</param>
/// <param name=”buttons”>Mouse buttons</param>
public override void OnMouse( Point position, int xDelta, int yDelta, int zDelta, bool[] buttons, float elapsedTime ) 
{
}
}
/// <summary>Gui Control handler</summary>
/// <param name=”controlID”>Control ID</param>
/// <param name=”data”>Control data</param>
public override void OnControl( int controlID, object data ) 
{
break;
case (int)ControlID.Options:
break;
}
}
/// <summary>
/// Inherited method from GameState. Determines whether
/// the StateManager should pop this state.
/// </summary>
public override bool DoneWithState 
}
/// <summary>The main entry point for the application.</summary>
[STAThread]
static void Main() 
{
try
{
framework.Initialize( true, 640, 480, “Creating a Framework” );
framework.Run();
}
catch ( Exception e )
{
System.Windows.Forms.MessageBox.Show( e.ToString(), “Error” );
return;
}
}
}
/// <summary>Gets the Device’s current back buffer height</summary>
public int BackBufferHeight 
}
/// <summary>Gets the Device’s current back buffer width</summary>
public int BackBufferWidth 
}
}
}
The GameApp class implements all the methods of GameState. For now, you can ignore the code that is already in the methods (the gui code and bitmap font code) since we will go over it in a later tutorial. I’m just showing you how to implement a GameState. Every time we create_ a new program, we just need to edit this file. Some code that should look familiar is in the OnRenderFrame method. It has the Device rendering methods that we learned about in the earlier tutorials. Remember, all of these methods will be called automatically by the Framework (with the exception of Main, or course) and the provided parameters should provide us with an easy means of creating new applications.
So what class maintains StateManager and supplies all the parameters? That would be the Framework class:
{
{
private Timer m_timer = null;
private StateManager m_gameStates = null;
private FrameworkState m_state;
private DeviceSettings m_newSettings = null;
public Framework() 
// Set default values
m_state = new FrameworkState();
m_graphics = new Graphics();
m_timer = new Timer();
m_state.WindowLocation = new Point( 0, 0 );
m_state.WindowSize = new Size( 640, 480 );
m_state.FillMode = FillMode.Solid;
m_mousePosition = new System.Drawing.Point(
MousePosition.X - this.ClientRectangle.X, MousePosition.Y - this.ClientRectangle.Y );
this.Closed += new EventHandler( Framework_Closed );
m_gameStates = new StateManager();
}
/// <summary>Initialize the Framework.</summary>
/// <param name=”windowed”>True for window mode, false for fullscreen mode.</param>
/// <param name=”width”>Window width</param>
/// <param name=”height”>Window height</param>
/// <param name=”title”>Text to display in the title bar.</param>
/// <returns>True on success, false on failure</returns>
public void Initialize( bool windowed, int width, int height, string title ) 
this.ClientSize = m_state.WindowSize;
this.Text = title;
this.Show();
// Initialize Direct3D
m_graphics.Initialize( windowed, this, width, height );
// Set device events
m_graphics.Device.DeviceLost += new System.EventHandler( this.OnLostDevice );
m_graphics.Device.DeviceReset += new System.EventHandler( this.OnResetDevice );
m_graphics.Device.Disposing += new System.EventHandler( this.OnDestroyDevice );
// Create resources
OnCreateDevice();
OnResetDevice( this, null );
Pause( false, false );
m_state.Initialized = true;
}
/// <summary>Runs the main loop.</summary>
public void Run() 
Application.Run();
}
/// <summary>Called after the device is created. Create Pool.Managed resources here.</summary>
private void OnCreateDevice() 
}
/// <summary>Called after the device is reset. Create Pool.Default resources here.</summary>
/// <param name=”sender”>Sending object</param>
/// <param name=”args”>Event arguments</param>
private void OnResetDevice( object sender, EventArgs args ) 
}
/// <summary>Called when the device is lost. Release Pool.Default resources here.</summary>
/// <param name=”sender”>Sending object</param>
/// <param name=”args”>Event arguments</param>
private void OnLostDevice( object sender, EventArgs args ) 
}
/// <summary>Called after the device is disposed. Release Pool.Managed resources here.</summary>
/// <param name=”sender”>Sending object</param>
/// <param name=”args”>Event arguments</param>
private void OnDestroyDevice( object sender, EventArgs args ) 
}
/// <summary>Update the current frame.</summary>
private void OnUpdateFrame() 
{
}
m_timer.Update();
// Process mouse event
if ( m_numPressedButtons > 0 || m_xDelta != 0 || m_yDelta != 0 || m_zDelta != 0 )
{
m_xDelta = 0;
m_yDelta = 0;
m_zDelta = 0;
// Need this or else app won’t be able to process mouse releases
if ( NoButtonsDown )
{
}
}
// Process keyboard event
if ( m_pressedKeys.Count > 0 )
{
m_gameStates.OnKeyboard( m_pressedKeys, m_pressedChar, m_pressedKey, m_timer.ElapsedTime );
// Application may be enumerating through pressedKeys, so we should
// remove newly locked keys after the application enumerates.
if ( oldLockCount != m_keyLock.Count )
{
{
}
}
m_pressedChar = char.MinValue;
m_pressedKey = 0;
}
m_gameStates.OnUpdateFrame( m_graphics.Device, m_timer.ElapsedTime );
// New settings from DeviceOptionsDisplay
if ( m_newSettings != null )
{
m_newSettings = null;
}
}
/// <summary>Render the current frame.</summary>
private void OnRenderFrame() 
m_graphics.Device == null || m_graphics.Device.IsDisposed || m_state.FormClosing ||
m_state.DeviceLost )
{
}
try
{
}
catch ( DeviceLostException )
{
System.Threading.Thread.Sleep( 50 );
if ( !m_state.DeviceLost )
{
m_state.DeviceLost = true;
}
}
}
/// <summary>Resets the device with new settings or creates a new device.</summary>
/// <param name=”newSettings”>New Device settings</param>
public void ChangeDevice( DeviceSettings newSettings ) 
m_state.DisableResize = true;
DeviceSettings oldSettings = m_graphics.CurrentSettings;
if ( newSettings.PresentParameters.IsWindowed )
{
}
else
{
}
m_graphics.Windowed = newSettings.PresentParameters.IsWindowed;
// Set new window style
if ( m_graphics.Windowed )
{
this.FormBorderStyle = FormBorderStyle.Sizable;
this.TopMost = false;
}
else
{
this.FormBorderStyle = FormBorderStyle.None;
// Save the current location/client size
m_state.WindowLocation = this.Location;
m_state.WindowSize = this.ClientSize;
}
// If AdapterOrdinal, DeviceType, and BehaviorFlags are the same, we can just do a Reset().
// If they’ve changed, we need to do a complete device tear down/rebuild.
if ( ( oldSettings.AdapterOrdinal == newSettings.AdapterOrdinal ) &&
( oldSettings.DeviceType == newSettings.DeviceType ) &&
( oldSettings.BehaviorFlags == newSettings.BehaviorFlags ) )
{
m_graphics.Reset();
}
else
{
m_graphics.ChangeDevice( newSettings );
// Set device events
m_graphics.Device.DeviceLost += new System.EventHandler( this.OnLostDevice );
m_graphics.Device.DeviceReset += new System.EventHandler( this.OnResetDevice );
m_graphics.Device.Disposing += new System.EventHandler( this.OnDestroyDevice );
// Create resources
OnCreateDevice();
OnResetDevice( this, null );
}
if ( m_graphics.Windowed )
{
// Restore the window size/location
this.Location = m_state.WindowLocation;
this.ClientSize = m_state.WindowSize;
}
m_state.DisableResize = false;
Pause( false, false );
}
/// <summary>Toggles between window and fullscreen mode</summary>
public void ToggleFullscreen() 
m_graphics.Windowed = !m_graphics.Windowed;
if ( m_graphics.Windowed )
{
}
else
{
}
}
/// <summary>Pushes a new state onto the frameworks State stack.</summary>
/// <param name=”state”>New state</param>
public void PushState( GameState state ) 
}
/// <summary>Pauses or unpauses rendering and the timer.</summary>
/// <param name=”pauseRendering”>True to pause rendering, false to unpause rendering.</param>
/// <param name=”pauseTimer”>True to pause the timer, false to unpause the timer.</param>
private void Pause( bool pauseRendering, bool pauseTimer ) 
{
}
else
{
}
if ( pauseTimer )
{
}
else
{
}
m_state.RenderingPaused = ( m_state.RenderingPausedCount > 0 );
m_state.TimerPaused = ( m_state.TimerPausedCount > 0 );
if ( m_state.TimerPaused && m_timer != null )
{
}
else if ( !m_state.TimerPaused && m_timer != null )
{
}
}
/// <summary>Window resize event. Reset the device, update_ and render the frame.</summary>
/// <param name=”e”>Event arguments</param>
protected override void OnResize( EventArgs e ) 
( this.WindowState == FormWindowState.Minimized )
|| m_graphics == null || m_state.DeviceLost || !m_state.Initialized )
{
}
base.OnResize( e );
try
{
}
catch ( DeviceLostException )
{
{
Pause( true, true );
}
return;
}
OnUpdateFrame();
OnRenderFrame();
}
/// <summary>Application idle event. Updates and renders frames.</summary>
/// <param name=”sender”>Sending object</param>
/// <param name=”e”>Event arguments</param>
private void OnApplicationIdle( object sender, EventArgs e ) 
{
}
while ( AppStillIdle )
{
this.WindowState != FormWindowState.Minimized && m_state.Initialized )
{
OnRenderFrame();
}
else if ( m_state.DeviceLost )
{
}
}
}
/// <summary>Tries to regain a lost device.</summary>
private void RegainLostDevice() 
if ( !m_graphics.Device.CheckCooperativeLevel() )
{
if ( code == ResultCode.DeviceLost )
{
// So wait until it can be reset.
System.Threading.Thread.Sleep( 50 );
return;
}
try
{
m_graphics.Reset();
m_state.DeviceLost = false;
m_state.DisableResize = false;
Pause( false, false );
}
catch ( DeviceLostException )
{
System.Threading.Thread.Sleep( 50 );
}
}
}
/// <summary>Called when the Form is closed</summary>
/// <param name=”sender”>Sending object</param>
/// <param name=”e”>Event arguments</param>
private void Framework_Closed( object sender, EventArgs e ) 
Application.Exit();
}
/// <summary>Returns whether the application is currently idle.</summary>
private bool AppStillIdle 
{
return !NativeMethods.PeekMessage( out msg, IntPtr.Zero, 0, 0, 0 );
}
}
/// <summary>Gets the Framework’s Graphics object</summary>
public Graphics Graphics 
}
/// <summary>Gets and Sets new settings for the Device</summary>
public DeviceSettings NewSettings 
set { m_newSettings = value; }
}
/// <summary>Returns the size of the current backbuffer.</summary>
public Size DisplaySize 
{
{
{
}
else
{
}
}
return this.ClientSize;
}
}
/// <summary>Gets and sets the fillmode.</summary>
public FillMode FillMode 
{
}
set
{
if ( m_graphics.Device != null )
{
}
}
}
/// <summary>Gets the framerate.</summary>
public float FPS 
}
/// <summary>Gets the current settings of the device.</summary>
public DeviceSettings CurrentSettings 
{
{
}
return null;
}
}
}
}
The Framework class is the main workhorse of the C-Unit Framework. It maintains a StateManager instance and supplies it with the parameters used to call all the methods of GameState. In the Framework class, you will see methods that look familiar (OnCreateDevice, OnLostDevice, etc.), methods that look foreign, and methods that are self-explanatory. For all the code that looks foreign, such as the timer code, lost device code, graphics code, and device settings code, you can ignore for now since we’ll be going over it in later tutorials.
The Framework class maintains the execution flow of the application and contains Tom Miller’s render loop that we created in the first tutorial. You can probably understand the code by reading through it so here is a rough diagram of the Framework’s execution:
The C-Unit Framework also provides all the mouse and keyboard input data you might need to use through a partial class of Framework:
{
{
private List<Keys> m_pressedKeys = new List<Keys>();
private List<Keys> m_keyLock = new List<Keys>();
private char m_pressedChar = char.MinValue;
private int m_pressedKey = 0;
// Mouse
private System.Drawing.Point m_mousePosition;
private int m_xDelta = 0;
private int m_yDelta = 0;
private int m_zDelta = 0;
private bool[] m_mouseButtons = new bool[3];
private int m_numPressedButtons = 0;
/// <summary>Mouse move event.</summary>
/// <param name=”e”>Event arguments</param>
protected override void OnMouseMove( MouseEventArgs e ) 
m_yDelta = m_mousePosition.Y - e.Location.Y;
m_mousePosition = e.Location;
}
/// <summary>Mouse wheel event.</summary>
/// <param name=”e”>Event arguments</param>
protected override void OnMouseWheel( MouseEventArgs e ) 
}
/// <summary>Mouse down event.</summary>
/// <param name=”e”>Event arguments</param>
protected override void OnMouseDown( MouseEventArgs e ) 
switch ( e.Button )
{
break;
case MouseButtons.Middle:
break;
case MouseButtons.Right:
break;
}
}
/// <summary>Mouse up event.</summary>
/// <param name=”e”>Event arguments</param>
protected override void OnMouseUp( MouseEventArgs e ) 
{
break;
case MouseButtons.Middle:
break;
case MouseButtons.Right:
break;
}
}
/// <summary>Returns true if the main mouse buttons are all down.</summary>
private bool NoButtonsDown 
}
/// <summary>Key down event.</summary>
/// <param name=”e”>Event arguments</param>
protected override void OnKeyDown( KeyEventArgs e ) 
{
}
// Store key value for repeatable non-character keys in GUI EditBox
// such as backspace, arrows, and delete
m_pressedKey = e.KeyValue;
}
/// <summary>Key press event.</summary>
/// <param name=”e”>Event arguments</param>
protected override void OnKeyPress( KeyPressEventArgs e ) 
m_pressedChar = e.KeyChar;
}
/// <summary>Key up event.</summary>
/// <param name=”e”>Event arguments</param>
protected override void OnKeyUp( KeyEventArgs e ) 
m_keyLock.Remove( e.KeyCode );
}
/// <summary>Locks a key so it is only read once per key down.</summary>
/// <param name=”key”>Key to lock</param>
public void LockKey( Keys key ) 
}
}
}
Using the Windows Forms input events, I simply store all the input data in some simple packages to be used by the GameStates. Most of this code is pretty straight forward so I shouldn’t need to explain much. The only extra feature is the LockKey method. The LockKey method allows you to lock a key so it is only processed once per key down. This is useful because in many games, when we hit a key, we only want an action to execute once.
In the Framework class, there is an instance of FrameworkState. FrameworkState just holds some simple state information about the Framework:
namespace CUnit
{
{
public bool DeviceLost;
public bool FormClosing;
public bool DisableResize;
public bool DeviceOptionsShowing;
public bool RenderingPaused;
public int RenderingPausedCount;
public int TimerPausedCount;
public bool TimerPaused;
public Point WindowLocation;
public Size WindowSize;
public FillMode FillMode;
}
}
The FrameworkState struct is basically a container for a bunch of state variables. Putting them in a struct just makes the Framework class less cluttered and give me some nice use for Intellisense
There are two different ways we can use the stack maintained by StateManager. The first way is to call Framework.PushState, which will add a GameState onto the Framework’s stack as shown in this code snippet from GameApp:
public GameApp( Framework framework )
{
m_framework.PushState( this );
}
/// <summary>Gui Control handler</summary>
/// <param name=”controlID”>Control ID</param>
/// <param name=”data”>Control data</param>
public override void OnControl( int controlID, object data )
{
{
break;
}
}
In the above code, we’re pushing the main state, GameApp, onto the Framework’s stack in the GameApp constructor. Actually, this initial push is required in order for the Framework to have anything on its stack. You can also see that in OnControl, which is called when we interact with the C-Unit Framework’s GUI, we push the DeviceOptionsDialog onto the stack. The DeviceOptionsDialog is used to configure the Direct3D Device, but we’ll learn how to do that later. When the DeviceOptionsDialog is pushed on the stack, all processing goes to the DeviceOptionsDialog class, which inherits from GameState. When we’re done with the DeviceOptionsDialog, it is popped from the stack and execution returns to GameApp, which is the next node on the stack.
The other way to use the stack in StateManager is to give the GameApp class its own instance of StateManager. This allows you to manually maintain your own application’s game state stack. You would just need to put StateManager’s method calls in the corresponding methods of GameApp.
Now that we have a Framework to build applications with, we can continue on with the Managed DirectX learning madness.