Day 6 - Game Menuing Woes and Future Screen Sneak Peeks
- by dapostolov
So, after my last post on Day 5 I dabbled with my game class design. I took the approach where each game objects is tightly coupled with a graphic.
The good news is I got the menu working but not without some hard knocks and game growing pains. I'll explain later, but for now...here is a class diagram of my first stab at my class structure and some code...
Ok, there are few mistakes, however, I'm going to leave it as is for now...
As you can see I created an inital abstract base class called GameSprite. This class when inherited will provide a simple virtual default draw method:
public virtual void DrawSprite(SpriteBatch spriteBatch)
{
spriteBatch.Draw(Sprite, Position, Color.White);
}
The benefits of coding it this way allows me to inherit the class and utilise the method in the screen draw method...So regardless of what the graphic object type is it will now have the ability to render a static image on the screen. Example:
public class MyStaticTreasureChest : GameSprite {}
If you remember the window draw method from Day 3's post, we could use the above code as follows...
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
foreach(var gameSprite in ListOfGameObjects)
{
gameSprite.DrawSprite(spriteBatch);
}
spriteBatch.End();
base.Draw(gameTime);
}
I have to admit the GameSprite object is pretty plain as with its DrawSprite method...
But ... we now have the ability to render 3 static menu items on the screen ... BORING! I want those menu items to do something exciting, which of course involves animation...
So, let's have a peek at AnimatedGameSprite in the above game diagram. The idea with the AnimatedGameSprite is that it has an image to animate...such as ... characters, fireballs, and... menus!
So after inheriting from GameSprite class, I added a few more options such as UpdateSprite...
public virtual void UpdateSprite(float elapsed)
{
_totalElapsed += elapsed;
if (_totalElapsed > _timePerFrame)
{
_frame++;
_frame = _frame % _framecount;
_totalElapsed -= _timePerFrame;
}
}
And an overidden DrawSprite...
public override void DrawSprite(SpriteBatch spriteBatch)
{
int FrameWidth = Sprite.Width / _framecount;
Rectangle sourcerect = new Rectangle(FrameWidth * _frame, 0, FrameWidth, Sprite.Height);
spriteBatch.Draw(Sprite, Position, sourcerect, Color.White, _rotation, _origin, _scale, SpriteEffects.None, _depth);
}
With these two methods...I can animate and image, all I had to do was add a few more lines to the screens Update Method (From Day 3), like such:
float elapsed = (float) gameTime.ElapsedGameTime.TotalSeconds;
foreach (var item in ListOfAnimatedGameObjects)
{
item.UpdateSprite(elapsed);
}
And voila! My images begin to animate in one spot, on the screen...
Hmm, but how do I interact with the menu items using a mouse...well the mouse cursor was easy enough...
this.IsMouseVisible = true;
But, to have it "interact" with an image was a bit more tricky...I had to perform collision detection!
mouseStateCurrent = Mouse.GetState();
var uiEnabledSprites = (from s in menuItems
where s.IsEnabled
select s).ToList();
foreach (var item in uiEnabledSprites)
{
var r = new Rectangle((int)item.Position.X, (int)item.Position.Y, item.Sprite.Width, item.Sprite.Height);
item.MenuState = MenuState.Normal;
if (r.Intersects(new Rectangle(mouseStateCurrent.X, mouseStateCurrent.Y, 0, 0)))
{
item.MenuState = MenuState.Hover;
if (mouseStatePrevious.LeftButton == ButtonState.Pressed
&& mouseStateCurrent.LeftButton == ButtonState.Released)
{
item.MenuState = MenuState.Pressed;
}
}
}
mouseStatePrevious = mouseStateCurrent;
So, basically, what it is doing above is iterating through all my interactive objects and detecting a rectangle collision and the object , plays the state animation (or static image).
Lessons Learned, Time Burned...
So, I think I did well to start, but after I hammered out my prototype...well...things got sloppy and I began to realise some design flaws...
At the time:
I couldn't seem to figure out how to open another window, such as the character creation screen
Input was not event based and it was bugging me
My menu design relied heavily on mouse input and I couldn't use keyboard.
Mouse input, is tightly bound with graphic rendering / positioning, so its logic will have to be in each scene.
Menu animations would stop mid frame, then continue when the action occured again. This is bad, because...what if I had a sword sliding onthe screen? Then it would slide a quarter of the way, then stop due to another action, then render again mid-slide... it just looked sloppy.
Menu, Solved!?
To solve the above problems I did a little research and I found some great code in the XNA forums. The one worth mentioning was the GameStateManagementSample.
With this sample, you can create a basic "text based" menu system which allows you to swap screens, popup screens, play the game, and quit....basic game state management...
In my next post I'm going to dwelve a bit more into this code and adapt it with my code from this prototype. Text based menus just won't cut it for me, for now...however, I'm still going to stick with my animated menu item idea.
A sneak peek using the Game State Management Sample...with no changes made...
Cool Things to Mention:
At work ... I tend to break out in random conversations every-so-often and I get talking about some of my challenges with this game (or some stupid observation about something... stupid)
During one conversation I was discussing how I should animate my images; I explained that I knew I had to use the Update method provided, but I didn't know how (at the time) to render an image at an appropriate "pace" and how many frames to use, etc.. I also got thinking that if a machine rendered my images faster / slower, that was surely going to f-up my animations.
To which a friend, Sheldon, answered, surely the Draw method is like a camera taking a snapshot of a scene in time. Then it clicked...I understood the big picture of the game engine...
After some research I discovered that the Draw method attempts to keep a framerate of 60 fps. From what I understand, the game engine will even leave out a few calls to the draw method if it begins to slow down. This is why we want to put our sprite updates in the update method. Then using a game timer (provided by the engine), we want to render the scene based on real time passed, not framerate.
So even the engine renders at 20 fps, the animations will still animate at the same real time speed!
Which brings up another point. Why 60 fps? I'm speculating that Microsoft capped it because LCD's dont' refresh faster than 60 fps?
On another note, If the game engine knows its falling behind in rendering...then surely we can harness this to speed up our games. Maybe I can find some flag which tell me if the game is lagging, and what the current framerate is, etc...(instead of coding it like I did last time)
Sheldon, suggested maybe I can render like WoW does, in prioritised layers...I think he's onto something, however I don't think I'll have that many graphics to worry about such a problem of graphic latency. We'll see.
People to Mention:
Well,as you are aware I hadn't posted in a couple days and I was surprised to see a few emails and messenger queries about my game progress (and some concern as to why I stopped).
I want to thank everyone for their kind words of support and put everyone at ease by stating that I do intend on completing this project. Granted I only have a few hours each night, but, I'll do it.
Thank you to Garth for mailing in my next screen! That was a nice surprise!
The Sneek Peek you've been waiting for...
Garth has also volunteered to render me some wizard images. He was a bit shocked when I asked for them in 2D animated strips. He said I was going backward (and that I have really bad Game Development Lingo). But, I advised Garth that I will use 3D images later...for now...2D images.
Garth also had some great game design ideas to add on. I advised him that I will save his ideas and include them in the future design document (for the 3d version?).
Lastly, my best friend Alek, is going to join me in developing this game. This was a project we started eons ago but never completed because of our careers. Now, priorities change and we have some spare time on our hands. Let's see what trouble Alek and I can get into! Tonight I'll be uploading my prototypes and base game to a source control for both of us to work off of.
D.