Site Sections

Friday, September 28, 2007

Resident Evil Revisited

Man, sometimes I love digging up a classic and playing through the experience one more time, especially when it is one of my favorite games of all times. In this case, it is two of my favorite games of all times, only because it was remade and so well done. Thats right, Resident Evil for the GameCube. I started playing it again the other night (I haven't seen the new movie yet and had watched the first two in preparation for tonights viewing). I wanted to show my girlfriend the intro to the GameCube version because she has never seen a Resident Evil game before. She thought it was good graphics for the system (which I would have to agree for its time) and that the overall feel of the environments were genuinely creepy.

I haven't played the remake all the way through, when I first got it I had been playing Super Smash Bros. and Zelda Windwaker like crazy and didn't really have time to play through a game that I had beat for the playstation when I was like 12. Yea it was pretty and supposedly it had additional content, whatever right? Playing through it again I can just not believe how much they improved on the classic. It is so much spookier than the original, yet all of the great original content is there in some form or the other.

For those of you out there that can't stand playing games that are more than a week old (and I know you exist - I use to be one of you) go back and play this game again, it is well worth your trouble in a time when we are all still waiting for spore...

Tuesday, September 25, 2007

GUI Manager Tutorial Pt. 2

So today we are going to talk about how to extend the abstract class of the GUIComponent that I showed you last time.

Remember, there are three important functions that need to be extended. These are the LoadGUIComponents, UpdateComponent, and Draw

Lets first discuss the type of component that we are going to make. We will be starting off with something simple that doesn't update to keep this tutorial hopefully short. Lets start with some simple GUI prototyping (placing test objects on the screen to get a feel for how the GUI will look). This way, we can spend less time trying to re-shuffle GUI elements around the screen later when we realize that our idea doesn't look good or fit and we have already spent a lot of time developing animation sequences/functionality/etc...

Lets start with a simple health bar, I whipped this image up really fast, it is in a .png format so the corners are transparent. This will be transparent in the game because if we look back at our GUIComponent class where we perform the draw loop, we can see that we have set up the SpriteBatch.Begin() function with the parameter SpriteBlendMode.AlphaBlend:



Not so exciting I know, but it took like 5 minutes, maybe I'll replace it later with something fancy. In the mean time, lets look at how to put this fine piece of artwork on the screen.

Lets add this image to the content directory of our game under a folder called textures for now. It can be moved later if you do not like it, or if you have some special hierarchy set up for each component in your GUI.

Here is our extended class of the GUIComponent, lets call it TestHUD:

using HMEngine3D.HMPostProcessors;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;

namespace HMDemo.Content.TestObjects
{
/**
* TestHud
* Test Hud object for testing gui components on the HUD.
* It is an implementation of the GUIComponent Abstract class.
* It contains no interaction or animation.
*/
public class TestHud : GUIComponent
{

private string mAsset; // the name of the texture file to use
private Texture2D mTexture; // the texture to use.

/**
* Constructor
*
* Parameters:
* aAsset - The prototype image to draw to the screen.
* aPosition - The X,Y position to draw the object.
* aEffect - The Shader to draw the object with.
*/
public TestHud(string aAsset, Vector2 aPosition, string aEffect)
: base(aPosition, aEffect)
{
mAsset = aAsset;
}

/**
* LoadGUIComponents
* LoadGUIComponents is an overwritten method of the abstract class
* GUIComponent. It must be implemented by this extending class to
* load component specific GUI element assets.
*
* Parameters:
* aDevice - The GraphicsDevice load the graphics to.
* aLoader - The ContentManager to load with.
*/
protected override void LoadGUIComponents(GraphicsDevice aDevice,
ContentManager aLoader)
{
// load the texture asset
mTexture = aLoader.Load(mAsset);
}

/**
* UpdateComponent
* UpdateComponent is an overwritten method of the abstract class
* GUIComponent. It must be implemented by this extending class to
* update the component specific GUI elements.
* It currently does nothing in this class as this is only for
* rapid prototype testing.
*
* Parameters:
* aElapsedTime - The elapsed time since the last update.
*/
public override void UpdateComponent(GameTime aElapsedTime)
{
// do nothing yet
}

/**
* Draw
* Draw is an overwritten method of the abstract class
* GUIComponent. It must be implemented by this extending class to
* draw the component specific GUI elements.
*/
protected override void Draw()
{
mSpriteBatch.Draw(mTexture, mPosition, Color.White);
}
}
}


Ok, so now that we have a TestHud class, we can create one of these objects and pass our asset (the ugly health bar image) to the constructor as the asset, along with a position for it to display on the screen and an effect which should be a simple pixel shader (or more complex if you have some sort of special effect in mind).

We do this in our main initialization block of our code. In my case, I have a DemoGame class that has a Main method as the launch point of my application, this DemoGame contains an instance of my Engine code (or Game class as it is called by Microsoft). In the Main function, I set up all of my game objects, this is also where we are going to set up our HUD objects and register them with the manager.

Oh wait, we haven't put our GUIManager anywhere yet... that could be a problem when we go to register our HUD objects right? Well lets see where we want to put that. Since we so conveniently have a copy of our extended Game object near by, we should probably place it somewhere in there.

In your Game class, place a member variable at the top called:

protected GUIManager mGUIManager; // the gui manager

I know I am so original.

We then want to add some accessor methods to this class:

public GUIManager GUIManager
{
get
{
return mGUIManager;
}
}


And now we add some method calls in some key places to instantiate, initialize, update and draw:

In our constructor add:

mGUIManager = new GUIManager();

In our LoadGraphicsContent method add:

// load GUI Manager registered objects content
mGUIManager.LoadGraphicsContent(mGraphics.GraphicsDevice, mContent);

In our Update method add:

// update all the GUI Controls
mGUIManager.Update(aGameTime);

Finally, in our Draw method, and this is important, AT THE VERY END add:

mGUIManager.DrawGUI(ref aDevice);

This will draw the HUD on top of what is drawn by the post processor. This will keep the HUD unaffected by what the Post Processor does no matter what type of shader is used.

Now all we have to do is create an instance of our TestHud object and place this in our Main function of the game, we also need to register it with the GUIManager like so:

// create GUI
TestHud lTestHUD = new TestHud(@"Content/Textures/guiconcept", Vector2.Zero, "GT");
mGame.GUIManager.RegisterGUIComponent(lTestHUD);


*Note here, the "GT" parameter is the name of a basic pixel shader that I have mapped to this name via the ShaderManager described in the Hazy Mind Tutorials. It is recalled by the DrawComponent function of our GUIComponent class.

This should give us the results shown below.


As we can see, our Health bar shows up as we had hoped. Now if we want to make new GUI components, we can simply build a library of easily extended GUIComponents that only require us to implement 3 functions and add two lines of code to our main class to see immediate results. I hope that this will save you time in not only implementing sweet GUI Components for your Indy games, but also speeds up rapid prototyping for you. Please feel free to either e-mail me or post comments with any questions or... well... comments that you may have.

Monday, September 24, 2007

GUI Manager Tutorial Pt. 1

Well... the time has come finally for me to start my tutorial series for the GUI Manager built off of the Hazy Mind engine. It is really important that you understand the structure of this architecture prior to reading this tutorial. What I will discuss should be helpful for other architectures and probably could easily be adapted to work with them. The concept is very OOP, yet knowing how the Hazy Mind post processor is constructed will save us a lot of time. Go Here to review these tutorials if you haven't yet.

To start lets take a quick review of the overall layout of the game loop:



This is a highly simplified version of the actual game loop but we can see from this that the Post Processor redirects the engine's rendering to an off screen image. This image is then post processed by re-drawing the image using a sprite batch and a pixel shader. This allows us to use shaders that can blur/invert colors/tint/shift or do whatever other operation on each pixel of the screen. We can also use this technique to draw 2D images on the screen over the regular scene (GUI Controls also known as a HUD - Heads Up Display)

So here is our modified Game Loop:



Notice here how we simply add an additional layer to our render stack. This one however will draw images that lay over the top of the post rendered scene. placing this here allows us to avoid modifying the GUI components when we want to post process the scene. Instead we will design our GUI Manager to register controls that can hold their own shader information so each component can have different effects.

Our GUIManager class:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using System.Collections.ObjectModel;

namespace HMEngine3D.HMPostProcessors
{
#region Utility Classes
public class GUIComponentCollection : Collection { };
#endregion

/**
* GUIManager
* Handles all of the 2D GUI Components that are
* drawn to the screen during game play.
*/
public class GUIManager
{

// Collection of GUI Components
private GUIComponentCollection mGUIComponents;

// Spritebatch to render with
private SpriteBatch mSpriteBatch;

/**
* Constructor
*/
public GUIManager()
{
mGUIComponents = new GUIComponentCollection();
}

/**
* RegisterGUIComponent
* Registers the given component with this
* manager so it knows to draw it.
*
* Parameters:
* aGUIComponent - The GUI Component to register.
*/
public void RegisterGUIComponent(GUIComponent aGUIComponent)
{
mGUIComponents.Add(aGUIComponent);
}

/**
* LoadGraphicsContent
* Loads the Graphics content of each of the
* gui components registered with the manager.
*
* Parameters:
* aDevice - The device to load to.
* aLoader - The content manager to use for loading.
*/
public void LoadGraphicsContent(GraphicsDevice aDevice,
ContentManager aLoader)
{
mSpriteBatch = new SpriteBatch(aDevice);
foreach (GUIComponent lComponent in mGUIComponents)
{
lComponent.LoadGraphicsContent(aDevice, aLoader,
ref mSpriteBatch);
}
}

/**
* Update
* Updates all of the registered GUI Components
*
* Parameters:
* aGameTime - The elapsed time since the last update.
*/
public void Update(GameTime aGameTime)
{
foreach (GUIComponent lComponent in mGUIComponents)
{
lComponent.UpdateComponent(aGameTime);
}
}

/**
* DrawGUI
* Calls the draw function of each registered GUIComponent.
*
* Parameters:
* aDevice - The GraphicsDevice object to draw with.
*/
public void DrawGUI(ref GraphicsDevice aDevice)
{
foreach (GUIComponent lComponent in mGUIComponents)
{
lComponent.DrawComponent(ref aDevice);
}
}
}
}

The construction of this manager is fairly straight forward. It simply generates a container utility class that holds a group of GUIComponents which I will explain in a moment. Each registered component is added to the list using the RegisterGUIComponent function. During the loading/updating/drawing functions, we loop through this group and perform the appropriate function. Notice how we use the ref qualifier on the mSpriteBatch variable when loading. This keeps the amount of memory used to draw the objects to a minimum since each component will contain its own drawing loop, this object can be shared.

So how do we construct a GUIComponent? We need to make it abstract! This is important so that way we can extend its functionality by enforcing a set of functions that need to be implemented by extending children classes. This way the GUI Manager knows how to render all of the children no matter what their functionality.

Here is the GUIComponent abstract class:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using HMEngine3D.HMShaders;

namespace HMEngine3D.HMPostProcessors
{
/**
* GUIComponent
* Abstract definition of the GUI components that
* can be managed by the GUIManager
*/
public abstract class GUIComponent
{

protected Vector2 mPosition = new Vector2(0, 0);
protected string mEffect = "";
protected SpriteBatch mSpriteBatch;
protected RenderTarget2D mRenderTarget;


/**
* Constructor
*
* Parameters:
* aPosition - The position of the GUI Component on the screen in pixels.
* aEffect - The Shader Effect to use to draw the GUI Component.
*/
public GUIComponent(Vector2 aPosition, string aEffect)
(
mPosition = aPosition;
mEffect = aEffect;
}


/**
* LoadGraphicsContent
* Loads the Graphics content, and the sprite batch used in this
* component.
*
* Parameters:
* aDevice - The device to load to.
* aLoader - The content manager to use for loading.
* aSpriteBatch - The Sprite Batch object to draw with.
*/
public void LoadGraphicsContent(GraphicsDevice aDevice,
ContentManager aLoader,
ref SpriteBatch aSpriteBatch)
{
LoadGUIComponents(aDevice, aLoader);
mSpriteBatch = aSpriteBatch;
mRenderTarget = new RenderTarget2D(aDevice,
aDevice.Viewport.Width,
aDevice.Viewport.Height,
1,
aDevice.DisplayMode.Format);

ShaderManager.GetShader(ref mEffect, out mShader);
}

/**
* LoadGUIComponents
* Must be implemented by the extending class.
* Loads all the graphics content for this component.
*
* Parameters:
* aDevice - The device to load to.
* aLoader - The content manager to use for loading.
*/
protected abstract void LoadGUIComponents(GraphicsDevice aDevice,
ContentManager aLoader);

/**
* UpdateComponent
* Updates the component for the current state.
*
* Parameters:
* aElapsedTime - The time since the last update.
*/
public abstract void UpdateComponent(GameTime aElapsedTime);

/**
* SetPosition
* Sets the position of the GUI Component.
*
* Parameters:
* aPosition - The new position of the upper left corner of the
* GUI Component.
*/
public void SetPosition(Vector2 aPosition)
{
mPosition = aPosition;
}

/**
* getPosition
* Returns the current position of the GUI Component
*
* Return Values:
* Vector2 - The current position of the
* upper left corner of the component.
*/
public Vector2 GetPosition()
{
return mPosition;
}

/**
* DrawComponent
* Draws the component by setting up render loop and calling
* implemented Draw function
*
* Parameters:
* aDevice - The GraphicsDevice object to draw with
*/
public void DrawComponent(ref GraphicsDevice aDevice)
{

// hijack the render target to draw the component off screen
aDevice.SetRenderTarget(0, mRenderTarget);

// draw the component shader free
mSpriteBatch.Begin(SpriteBlendMode.AlphaBlend,
SpriteSortMode.Immediate,
SaveStateMode.SaveState);

Draw();
mSpriteBatch.End();

// resolve render target (normally done by framework but
// since we took over the draw we have to do it ourselves)
aDevice.ResolveRenderTarget(0);
aDevice.SetRenderTarget(0, null);

if (mEffect != "")
{
// set up the effect and sprites
HMShader lShader = HMShaderManager.GetShader(mEffect);
if (lShader != null)
{
Effect lEffect = lShader.Effect;
lEffect.Begin();
mSpriteBatch.Begin(SpriteBlendMode.AlphaBlend,
SpriteSortMode.Immediate,
SaveStateMode.SaveState);

// draw the sprites
foreach (EffectPass lPass in lEffect.CurrentTechnique.Passes)
{
lPass.Begin();
mSpriteBatch.Draw(mRenderTarget.GetTexture(),
new Vector2(0, 0), Color.White);
lPass.End();
}

mSpriteBatch.End();
lEffect.End();
}
}
}

/**
* Draw
* Must be implemented by extending classes.
* Defines how to draw this Component.
*
* Parameters:
* aSpriteBatch - The SpriteBatch object to draw with.
*/
protected abstract void Draw();
}
}


*Note: If we look in the DrawComponents function, we see a reference to the HMShaderManager class, this is part of the Hazy Mind engine and simply maps Shaders that we pre-load to given names. This saves on memory and loading time.

So as we can see here, there are 3 abstract functions:
  • LoadGUIComponents - used to load the specific assets for the component, is called by the loading function of the GUIManager.
  • UpdateComponent - used to update the component based on the elapsed time, called by the Update function of the GUIManager.
  • Draw - used to draw the component using the passed in Sprite Batch reference, called by the DrawComponents function of the GUIComponent class, this sets up the render loop for
this component and allows each component to contain its own shader effect.

* note I have recently updated this section to fix a problem with sprite tinting, I have instead used the method of rendering off screen the contents of the components prior to rendering the component using the defined shader associated with the component. This allows for the basic sprite batch shader to handle tinting of sprites.

In the next tutorial, I will show you how to extend this abstract class to make a GUI component object. I will also show you how to interlace this GUIManager with the Hazy Mind engine, or any other engine that uses a standard game loop.

Friday, September 21, 2007

OMG! Rant Warning... Visual Studios stupid bugs

HAHAHA! Sorry had to get that out of the way... So today at work, one of the guys I work with showed me the dumbest error I have ever seen. It has been an issue that we have been dealing with for a few weeks ever since we all go dual monitors. It turns out that if you want to change the build configuration in Visual Studios .net 2003 and your second monitor is to the left or above the primary monitor, the drop down windows in the configuration build manager do not work. Hmmm... very strange right? It seems it has a problem drawing the drop down boxes in negative virtual space, and of course, instead of windows adjusting your origin based on your most left monitor, they instead use a negative coordinate system for monitors in this configuration. If you move the dialog back on the primary monitor or to a monitor in the right/bottom relative position of the primary monitor, no issues.

Another issue I noticed today, in wincore.cpp, Microsoft has so lovingly used the Goto statement in about 7 places. This is so strange as the goto has really become a thing of the past - especially in C++... Someone should do a count to see how many goto statements are in MFC alone, that would be an interesting figure to ponder. Gives me more reason to love the fact that I do not work at Microsoft.

Leave it to Microsoft to lead the way in reverse innovations. Keep up the good work Mr. Gates!

Wednesday, September 19, 2007

Busy Busy Busy

AH...... It's been a few weeks since I posted the last post about BioShock. I beat it finally, when I got free time between work and life. Now that thats over (Note: I loved BioShock, but the ending was very disappointing) I actually might have some free time to work on the game again. I went to a lecture last night from Tom Wujec, he was discussing imagination and innovation. Very interesting stuff and very inspiring.

This leaves me with the promise I gave the last time about the GUI Manager tutorial. I will get it up here, I have just been un-motivated lately after getting off of work. I plan on having it up here sometime either this week or next so check back soon.

I have been drawing a lot more though. I recently bought a book by DragonArt on drawing fantasy creatures. This has been very helpful in teaching me some of the basic flaws in my drawing style. I have noticed an immediate improvement in my drawing ability, which in turn has caused me to be more inspired to draw more. When it comes out looking good you tend to get less frustrated. Another thing Tom Wujec mentioned was a quote about how Steve Jobs hires based not on programming/technical skills but based on the ability to communicate and the ability to draw/be creative. I would have to agree with this view, anyone can learn to program, it takes much more skill and motivation to learn how to create.

As far as my drawing... when I get my scanner that I just bought from New Egg, I will scan some of the decent stuff I have drawn lately and post it here. Feel free to tell me that it sucks, I don't mind. I am learning and criticism and feed back are all part of the flow of creativity and growth.

Wednesday, September 5, 2007

BioShock Review and how it has consumed my life

BioShock BioShock... oh how I love thee... Unfortunately you steal my soul and all of my free time that I could be spending coding. Oh well, I just graduated a few months ago, I don't think that I need to conquer the world in one year. I do on the other hand need to conquer BioShock which I have no doubt in my mind that it will be the game of the year. Hands down this game rocks where other FPS games have been left wanting. The storyline is amazing and the whole feel is creepy and dark. The architectural design of the levels are breathtaking, especially if you have a direct x 10 compatible card. FPS fans this one is a 10 star title all the way.

As far as my programming is concerned, I did take a break from the game today and did some more work on my GUI Component Manager for the Hazy Mind engine. I got it working and will be posting tutorials here soon (maybe even tonight if I get ambitious - although BioShock is sooooo tempting ;)