• entries
    707
  • comments
    1173
  • views
    434110

Untitled

Sign in to follow this  

83 views

It's been a couple days, so I thought I'd pop in and post a little update about what I'm up to.



Project
Haven't technically started a project yet, but I've got one I'm working on writing a design document for. Once I'm finished with the design document I'll move on to the technical document and go from there.



Programming
I've been working on writing a basic framework over the past few days. I wanted to write something that was rather "wrapper-less" for the simple fact that the one thing I'm always writing is wrappers. The only time I ever get anywhere is when I don't use them (most because I never know really what to put in a wrapper.)

WinUI
Anyway, I did end up writing a Win32 Window wrapper and I actually like what I came up with.

I completely ignored the message loop; I'm always fidgeting with it, trying to find the best place to fit in into the class, but I realized one important thing: it doesn't belong in the class. On a black-white scale, the message loop doesn't technically have anything at all to do with the message loop.

I opted to write some enums to replace all of the possible combinations of WS_ and WS_EX_ flags. I ended up just going with pretty much the same list WinForms has: NoFrame, ThinFrame, NormalFrame, LockedFrame, ToolFrame, LockedToolFrame.

Then I added some flags for specifying how the window should be created: HasIcon, HasMinimizeBox, HasMaximizeBox, HasSystemMenu, CenterInScreen, CenterInParent, CreateAtDefaultPosition, CreateWithDefaultBounds, CreateMinimized, CreateMaximized, and SizeIsClientSize.

Inside the window class I handle pretty much the main messages that a game has to deal with: Close, Destroy, Moving, Move, Sizing, Size, KeyDown, KeyUp, Character, MouseMove, MouseButtonPressed/Released/DoubleClicked, MouseWheel, FocusGained, and FocusLost. (If you can think of any other important messages I should add, let me know!)

Once I've spruced it up and added the controls that I want, I'll more than likely toss it up here some where. It's main uses are going to be for writing quick dialogs for debug/dev purposes, making a dialog for my assert system, and for creating a window for games.

Stats
I'm sure I've made my opinion clear at some point on stats in games. Pretty much anything that the game stores, should be viewable by the player (in one form or another; even if it's just from the debug console or something.) Some games do this, but most of the time they seem to leave out the interesting ones.

Anyway, I just got done with the second pass of my stat system. Though it may seem like a lie, when I sat down about an hour ago to write the system, I wasn't thinking about game-specific stats; I was thinking about engine stats (average frame delta, average frame rate, up-time, down-time, memory available, etc.)

I wanted some way to track some set of arbitrary stats and have some way to easily see them (and possibly modify them). So, I created 4 classes: StatVariableBase, StatVariable, StatVariableEx, and StatManager.

Before I even start, I'm going to go over the very first issue that I had: generic value types. Obviously I was going to be storing stats in a list, but after coding the initial pass, because I wasn't thinking ahead, I ran into a problem: I can't store a list of arbitrary types without using something like boost::any. Boost's any class was the first thing I looked into, but I turned away because of the fact that I'd have to keep track of whether or not it was a regular stat or an extended stat and cast accordingly.

The solution I ended up with was StatVariableBase; a non-template base class for the other two classes to derive from. In it I store the pointer as a ulong and then StatVariable and StatVariableEx cast accordingly. I was very hesitant to do this obviously (as, IMO, it's very bad programming practice), but I decided to go with it anyway (sometime down the road I may regret it, but I'll cross that bridge when I come to it.)

With that out of the way, I'll explain the system. To put it simply: StatVariable is used to store simple data types or types that have a global/static ToString function (int, bool, enum, etc) whereas StatVariableEx is used to store class that have a ToString method.

StatManager simply stores stats, allowing you to add them and render them (I haven't added much functionality yet; obviously removing them and some more advanced rendering options are on the to-do list.)

Some stuff requires a little "fenagling" if you will as the system has a pretty strict format. You can store data types and supply a "string (*)(const DataType &)" function pointer or you can store data types and supply a "boost::function" functor. With the former, you can leave out the function pointer and the system will default to boost::lexical_cast, but that will only work if lexical_cast can cast the value.

For the example I've created a TimeSinceStartStat class that contains a format string and a ToString() method. Then I store an instance of that class and use it as a stat. The reason being that my TimeDifference class has a Format() method that takes a format control string instead of a ToString() method.

Anyway, the example code:

struct TimeSinceStartStat
{
string Format;

TimeSinceStartStat() { this->Format = ""; }

string ToString() const
{
return Time::GetTimeSinceStart().Format(this->Format.empty() ? "%Hours%h %Minutes%m %Seconds%s" : this->Format);
}
};

string BoolToString(const bool &Bool)
{
return Bool == false ? "false" : "true";
}

struct DevApp : public BaseApplication
{
dbGameFontLib::GameFont Font;

StatManager Stats;
real32 TimeDelta;
TimeSinceStartStat Stat_TimeSinceStart;

virtual bool OnKeyUp(Input::Key Key)
{
base::OnKeyUp(Key);

if(Key == Input::Keys::Escape)
this->Window.Close();
return false;
}

virtual bool LoadContent(Graphics::Direct3DDevice9 &GraphicsDevice)
{
this->Window.SetText("Framework Test Bed");
if(!Font.CreateFromFile("Consolas.pfnt", GraphicsDevice))
return false;

if(!this->Stats.Initialize(GraphicsDevice, "Consolas.pfnt"))
return false;

this->TimeDelta = 0;
this->Stats.AddStat("Engine is running", new BoolStat(&this->Running, BoolToString));
this->Stats.AddStat("Mouse Cursor Position", new PointStat(&this->Mouse.CursorPosition));
this->Stats.AddStat("Frame Time Delta", new Real32Stat(&this->TimeDelta));
this->Stats.AddStat("Program Time Elapsed", new StatVariableEx(&this->Stat_TimeSinceStart));
this->Stats.AddStat("G-Button State", new StatVariable(&this->Keyboard.KeyStates[Input::Keys::G], Input::KeyStates::ToString));
return true;
}

virtual void Update(real32 Seconds)
{
this->TimeDelta = Seconds;
}

virtual void Frame()
{
this->Stats.Render();

this->Font.BeginBatch();
this->Font.DrawString("Framework Test Bed", -1, 0xffffffff, 0, 0, dbGameFontLib::FormatFlags::AlignBottomRight);
this->Font.EndBatch();
}
};

int main()
{
DevApp App;
App.Run();
return 0;
}


Note that BoolStat, PointStat, etc are typedefs using StatVariable and StatVariableEx.

And here is the resulting screenshot:


As with WinUI, once I get it fleshed out more I'll pop it up here somewhere so that you can see how it actually works and use it if you want.

Back to work...
Sign in to follow this  


0 Comments


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now