Intel sponsors gamedev.net search:   
Beals Studio's Development JournalBy Programmer16      

Saturday, November 7, 2009
Didn't really get any work done tonight, spent all night trying to find a couple sprites that I need. I really need to just sit down and catch up on my spriting, but I'm just not patient enough for it XD.

I changed the animation system again. I re-added the Name attribute because I need it for animation sets. Added the AnimationSet class, which is just a collection of animations. The AnimationSet file has a bunch of the Animation tags wrapped in a Animations tag. The Name value is required for animations in a set (and it has to be unique.)

I've also decided on the two movement schemes I'm going to use: the current key-based one (up moves forward, down moves backward, left rotates counter-clockwise, and right rotates clockwise) and the absolute movement scheme (the character's aim follows the mouse while the movement keys move up, down, left, and right.)

I'm currently working on the sprite system so that I can start implementing the object system. Hopefully I'll have something to show on that here in the next couple days.

In the meantime, it's time for bed.

Comments: 0 - Leave a Comment

Link



Thursday, November 5, 2009
I've been working on the animation system for my code base and working it into Invasion. The renderer hasn't been optimized yet (simple primitive/texture batching; uses DrawUserPrimitives()), but it does well enough:

10k explosion animations running at a steady (average) 231 FPS (about 44 FPS with vertical-sync enabled.)

Here's the animation file for it:
<?xml version="1.0"?>
<Animation Name="Explosion" FrameDelay="0.025">

	<Frame Index="0">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="0" Y="0" Width="64" Height="64" />
	</Frame>

	<Frame Index="1">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="64" Y="0" Width="64" Height="64" />
	</Frame>

	<Frame Index="2">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="128" Y="0" Width="64" Height="64" />
	</Frame>

	<Frame Index="3">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="192" Y="0" Width="64" Height="64" />
	</Frame>

	<Frame Index="4">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="256" Y="0" Width="64" Height="64" />
	</Frame>
	<Frame Index="5">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="0" Y="64" Width="64" Height="64" />
	</Frame>

	<Frame Index="6">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="64" Y="64" Width="64" Height="64" />
	</Frame>

	<Frame Index="7">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="128" Y="64" Width="64" Height="64" />
	</Frame>

	<Frame Index="8">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="192" Y="64" Width="64" Height="64" />
	</Frame>

	<Frame Index="9">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="256" Y="64" Width="64" Height="64" />
	</Frame>
	<Frame Index="10">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="0" Y="128" Width="64" Height="64" />
	</Frame>

	<Frame Index="11">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="64" Y="128" Width="64" Height="64" />
	</Frame>

	<Frame Index="12">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="128" Y="128" Width="64" Height="64" />
	</Frame>

	<Frame Index="13">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="192" Y="128" Width="64" Height="64" />
	</Frame>

	<Frame Index="14">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="256" Y="128" Width="64" Height="64" />
	</Frame>
	<Frame Index="15">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="0" Y="192" Width="64" Height="64" />
	</Frame>

	<Frame Index="16">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="64" Y="192" Width="64" Height="64" />
	</Frame>

	<Frame Index="17">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="128" Y="192" Width="64" Height="64" />
	</Frame>

	<Frame Index="18">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="192" Y="192" Width="64" Height="64" />
	</Frame>

	<Frame Index="19">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="256" Y="192" Width="64" Height="64" />
	</Frame>

	<Frame Index="20">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="0" Y="256" Width="64" Height="64" />
	</Frame>

	<Frame Index="21">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="64" Y="256" Width="64" Height="64" />
	</Frame>

	<Frame Index="22">
		<Anchor X="32" Y="32" />
		<Texture Path="explosion.png" />
		<SourceRectangle X="128" Y="256" Width="64" Height="64" />
	</Frame>
	
</Animation>




And the associated image (I used the generator from this page to generate it):


As with my previous animation system it's separated into two classes: Animation and AnimationController. You can either load an animation from a file or use it's AddFrame() method to programmatically create one.

The controller has 3 important methods: Play(), Update(), and Stop(). Play() takes an Animation instance and a Loopstyle (None, Normal, or Reverting), Update() takes the frame-delta, and Stop() takes no arguments.

I thought about implementing a pause system, but I don't see any reason to pause single elements (you would pause the entire game, which can easily be done through "GameTime.SetScale(float)". Passing 0.0f to this effectively pauses all of the logic.)

Anyway, back to work.

[edit]
I was hoping to get a lot more done before heading to bed, but I didn't lol. I spent most of the night playing with the system, testing out the loop styles and whatnot. Did find a large issue; after adding a certain amount of vertices (turned out to be every 1334 vertices) it would render one quad incorrectly (no texture.) Took a little while to track it down; I couldn't isolate the quad (as that fixed it), no invalid textures were detected at any point, and I couldn't pin-point a position. After about an hour or so, I figured out that it was because I had forgotten to check if an incoming value was null before assigning it.

Actually, I found two large issues. I have a class named PerSecondTracker that tracks a value and averages it (use it for my frame-rate counter, batches/second, stuff like that.) Well, I use a list so that I can dynamically change the size of the average list, which was supposed to have a default length of 50. I started to realize that, if I let my program sit there, the frame rate just kept degrading. I step through the code just to realize I had forgotten to control the length of the list, so it just kept growing and growing (though, I have to say, I don't think 24FPS is bad for averaging a list of over 100k values and rendering 10k sprites with barely optimizations lol.)

Beyond that, about the only thing I got accomplished is migrating Animation and AnimationController from the engine and into my code base. I revised them both a little (Animation had stuff it didn't need, like a Name variable and a GraphicsDevice. AnimationController didn't change any, but I had a bug in it.)

I also added a static method to Animation to simplify generating one from an image containing a sprite sheet.

Meh, time for bed. I have to work tomorrow.

Comments: 0 - Leave a Comment

Link



Saturday, October 31, 2009
I got the actor controller system started; two separate hierarchies: UserActorController and AIActorController.

The UserActorController uses a combination of the mouse and keyboard for control and is setup for the two different movement schemes. I'm not sure if I actually went over those, so here's a 'definition':
Absolute: The actor aims at the mouse, but WASD move you Up, Down, Left, and Right.

Relative: Again, the actor aims at the mouse, but W and S move you toward and away from the mouse.

I was going to stick with absolute however, I believe they each have their purpose. Absolute movement is good for running and gunning (say you want to run Up, but aim to the left and mow down a line of monsters.) It's also good for clearing out small areas or something like a boss fight.

Relative movement is a lot easier for actually traveling though. Switching between the two will be key-bound, so it should be pretty easy if needed.

At the moment the AI is just a simple function that checks to see if the target less than 150 pixels way. If so, they start attacking. Once they've started attacking, you have to get 300 pixels away before they'll stop.



The little dude being mobbed is actually I sprite I made for the original Invasion, but stuffed away because I decided not to do top-down back then. He was green and had a little detail, but I ruined that scaling him up to get rid of issues when rotating.

The gun and 'x75' at the bottom are just graphics test; nothing implemented yet.

Alrighty; bed time.

Comments: 0 - Leave a Comment

Link



Thursday, October 29, 2009
I got both absolute and relative character movement working; now I just need to abstract it into a IActorController hierarchy so that, once I get that far, I can implement AI.

I didn't get anywhere with the state system last night and I've decided to pass over it for now. I usually implement menus early on and then have to deal with them for the rest of the project's life (though, usually it was short-lived lol.) So, I'm going to move onto the map system now and come back to the state system later.

Back to work...

Comments: 0 - Leave a Comment

Link


Decided that, while adding multiplayer wouldn't be too incredibly hard, it's beyond the scope of the project as it is and I'd rather not worry about it right now. So, I've taken that off the list.

I also forgot that I had taken out playing as the infected as I couldn't think of a suitable weapon to give them (not planning on implementing melee weapons of any sort.) So, they're also out. This brings me down to 4 match types and limits Genocide to strictly human.

I really need to stop posting things at like 5 in the morning because I obviously don't pay enough attention lol.



Today saw some improvements on the core system as well as starting the state manager. I was busy most of the rest of the day, so that's all I've done so far.

I want to get the state system fleshed out before I head to bed and hopefully I'll be able to start implementing the core systems come tomorrow.

At the moment, it's looking like my biggest roadblock is going to be AI, but I can pretty much just set that nicely to:
10 FIND HUMAN
20 KILL HUMAN
30 GOTO 10


Back to work...

Comments: 0 - Leave a Comment

Link



Wednesday, October 28, 2009
An unknown infection hits several large cities and in a matter of minutes transforms most of the cities' inhabitants into monsters. Those that were "lucky" enough not to get infected are being massacred. Will you be a victim as well?



I've changed Invasion into a skirmish-style match-based game rather than a linear-storyline style game. At the moment I've got 5 different match types:

Genocide:
Your cliche "survive as long as you can, killing anything that moves" mode.
You can play as either a human or infected. As either side, you wander the city killing as many of the other side as you can before the time runs out.

Evac:
There's a horde of monsters between you and your evac point. Rescue what survivors you can and get there as soon as possible.

Infected:
Turns out, you weren't one of the "lucky" ones. Kill as many of those dirty bastards that weren't infected as you can before they all escape.

Reclamation:
Take back key points in the city so that it can be reclaimed from the monsters.

Fortress:
Help is on it's way; defend yourself until they get here.

While these are built in match types, they are completely script-controlled; so custom maps can have other systems. The above are pretty much just templates.

I'm planning on including 15 maps with the initial release and possibly releasing some more afterward. However, the tools will be included so that people can make their own maps.



So far the arsenal consists of a pistol, sub-machine gun, a pump shotgun, an automatic shotgun, and an automatic assault rifle.
First-aid kits are the only support items available.
I've decided to leave explosives of any kind out.



The following paragraph is a HUGE maybe; I'm going to look into what it would take to get a system like this up and running; if I feel it'll take too much, the game will be strictly single-player.

I want the game to be played either solo or Peer-To-Peer multiplayer (2 players only.) In multiplayer games, when someone dies they take over a different character. So, when you run out of controllable characters, it's game over (in modes like genocide and fortress, there are no extra characters to take over when you die.)

With the exception of the above, I've written and planned almost everything out and should have plenty of time to finish it by Halloween of 2010 (the deadline I've set.) However, this deadline doesn't include getting graphics and audio; once I've finished everything, I'll look into coming up with better resources.



Any questions/comments/suggestions are welcome as always.

On a side note, can anyone recommend a nice audio library for C#? The only requirement that I have is that it supports OGG files.

Back to work now.

Comments: 0 - Leave a Comment

Link



Saturday, October 24, 2009
I've been working strictly with C# for over a month now and...I've loved every moment of it. I tried to go back to C++ the other day, and I just couldn't be arsed. Sadly, my only real issue with C++ is Win32; I absolutely hate it.

Anyway, I've been working on a little graphics system to get my project up and running on top of SlimDX. Here's some screenies:


Graphics Test



Points



Lines



Grid (without back-color)



Grid (with back-color)


I basically have texture management, a primitive renderer (that handles points, lines, grids, planes, and textured-quads at the moment; might add circles, not sure), and the renderer batches by primitive type and whether or not a texture is involved (no sorting is involved at this stage. I simply store vertices up to a certain amount or until the primitive type/texture change.)



On the 'game' side of development, I implemented a fairly decent console using LuaInterface. Reflection has made scripting and implementing a console hundreds of times easier.

The console/engine system runs in a similar fashion to Oblivion's; you click on an object to get it's script name and then you can change any script-accessible parameter through the console.

For scripting, I'm using a "dual class" approach. For instance, I have Game and LuaGame; Game is what the engine interacts with and it holds an instance of LuaGame. LuaGame pretty much just forwards to Game. This way I don't have to worry about accidentally giving scripts access to something I don't want to.

I really need to head to bed; I have to work tonight. More tomorrow night or the following morning.

Comments: 1 - Leave a Comment

Link



Friday, October 16, 2009
I've been having a lot of PC issues lately. Things started loading very slowly, my RAM was being eaten, taking forever to shutdown/boot, etc. Well, I spent most of last week fixing some computers for people, just to end up having mine crap out on me.

I spent all night, finally got it to load Windows, just to find out that the System process was hogging whatever core it was attuned to. I was going to go buy a copy of Vista (because I haven't been able to find my copy since the day I installed it, like a year ago), but Cierra informed me that my copy of Win7 would be showing up soon, so I waited.

The night before it showed up, I figured out that putting the computer in sleep mode and then waking it up fixed the issue temporarily (until I restarted it.) So, I said screw it, backed everything up, and waited for Win7 to show up.

Got it last night, reformatted, and then installed the new OS...problem was still there. I trying to systematically break it down to what component was causing it, but the problem was the three I had left were essential: processor, motherboard, and hard-drive.

Anyway, my dad loaned me his extra hard-drive (which is total crap, because his "little" hard-drive is only about 20GB smaller than my main hard-drive lol.) Reformatted and installed again...viola! Problem solved.

So, tomorrow I'm ordering a TB hard-drive and waiting for that to come. Until them I'm just going to play around with the design for my game.

Comments: 3 - Leave a Comment

Link



Wednesday, September 16, 2009
Because I constantly seem to be going in a different direction.

At the end of my last post, I stated that a code-base isn't really what I'm after. This is where I always get stuck, what I always end up writing (and rewriting and rewriting and ...), mainly for the fact that I always pretty much end up trying to add a layer over top of C++; a VERY thin layer at that (which makes it even worse.)

In the end, what I'm after is a collection of utilities that improve productivity (funny, because what I end up doing kills my productivity.) So, I'm in the works of defining a list of utilities, rather than a list of modules to cover huge specific areas.

So far, I've got:
Debug
Debug will be the only actual "module." It contains 4 unique components, all which are inherent to debugging (assert, exception, callstack, and runtime log.)
Reason: Debugging is inherent to programming; it wouldn't matter if I was writing Win32 applications, server applications, or games, a debugging system will come in handy.

Algorithms
Contains some of the general algorithms I use in a lot of my code. This is where my string hashing functions are being put.
Reason: I have a few algorithms that I use constantly (mainly my hashing algorithms) that I'll need access to most of the time. This will probably be my smallest utility to start with, but I believe I'll be able to grow it a bit.

Math
Contains my symbols for basic math, 2D math, and 3D math.
Reason: Every library I run into either is insufficient for fluid programming (like Win32 RECT and POINT), is specific to one area (like D3D[X] symbols and CML), or a combination of both. On top of that, math is my favorite subject and I'm rather lacking in the 3D math area; writing my own matrix functions and such has really helped me learn a lot.

Patterns
Contains a couple of patterns that I use.
Reason: Patterns are pretty inherent when it comes to game development. Whether it be object/listener, policies, composition, factories, managers, singletons, etc; they're used all over the place. I have a small set that I want to start with and will add more as they come into play (and are used in more than one spot.)

Serialization
Contains an interface, Serializable, a derived class, NonSerializable, and an array of templated functions named Serialize.
Reason: For any state-based game (with saving/loading), Serialization is a must. The system I've implemented has worked for me very well in the past. It's implemented in a way so that I can use Serialize(Stream, SomeObject), and it gets correctly serialized (as long as a proper overload is available. If not, it gets serialized directly to a file and will be invalid if it containers pointers, streams, containers, etc, etc.)

I'll expand more on this specific utility tomorrow, because I'd really like some input on it (I'm contemplating making a post in the forums about it.)

Collections
Contains a set of collection type containers and my property-bag system.
Reason: First off, this is not a set of simple wrappers around STL containers to make them look better. I define STL containers and collections separately:
STL containers are used to store data that the handler of the container has to manipulate.
Collections handle containers in a program defined way.
Simply put, Collections are implemented in a way that I will derive from them to implement specific interaction methods (i.e. sorting, look-up by stored-type information, caching, zombies, etc.)
In any case that I do not need extra functionality, I'll be using plain STL containers.

Now, you may have noticed a small pattern: most everything covered above (with the exception of Debug) has a specific purpose, but has either/both generic input and/or output. Meaning, every utility listed above is a building block (i.e., they can't really be used on their own. Some of the stuff already implemented is actually declared this way; both Serialize and NonSerialize have a protected constructor; constructing one on it's own simply has no meaning.)
Note: After typing that, I'm realizing that I just stated I made an interface in a bloated, round-about way lol.

The best part is, this is exactly the stuff I've been screwing around with for the past few weeks in my lab-solution.

Anyway, it's time for bed. I'll be back earlier tomorrow night (around 9), as I'll be leaving to stay with Cierra for about 10 days (while her parents are on vacation.) Don't know whether or not I'll have any updates before I get back (might get more done actually; less access to the internet means more productivity (I won't be scrounging the forums lol.))

Comments: 0 - Leave a Comment

Link



Tuesday, September 15, 2009
Feeling rather well today; actually feel like I accomplished something lol.

The day before yesterday, or maybe the day before that, I decided to start fresh. My code-base (amply named dbeals) is...well, plain horrible. As I've said before, most of the code is copy-pasted from older version and then updated. However, the main issue is that a lot of it is branch code and most of it hasn't been tested (for example, I've been using the same split method for probably a year or so and just about a week ago I found an issue that resulted in an infinite loop.)

So, I archived all of my versions of dbeals (quite a lot sadly) into a zip file and onto a thumb-drive. Then, I recreated my basic directory structure for any library and made a new project.

However, before I started coding, I made a list of everything I wanted in the module. I went at it from my usual perspective and came up with pretty much everything the old versions had, so I scrapped it and tried to come up with a new perspective.



The very first thing I do for any project is setup a cross-module Common.h file. This defines my basic types (int8, uint32, etc) as well as the macro db_DebugBuild. Next to each basic type's definition, I have a list of it's size and range (ripped from MSDN to make it easier on me.)



After that, the first thing I did was scrapping my String module. It contained a bunch of utility functions that turned out to be exactly what boost::algorithm::string has (only, missing some things and less useful.) The only thing it had that boost::algorithm::string doesn't is a string hashing functions. As hashing is important for a few things (passwords and table lookups), I might implement just a hashing library at some point using a layout like tr1's random number generator (engines and distributions.) For now, I have a hash function that uses the DJB2 algorithm (I've also saved the SDBM algorithm in the source file; in-case I want to switch to it at some point, I won't have to look it up.)

So, I have dbeals::String::Hash


Debugging was the next beast I wanted to conquer. Obviously, a fully-featured debugging module would make life more fantastical, but I usually end up with a decent assert macro, a very, very poor call-stack walker, and an over-bloated runtime log system.

[CallStack]
I'm not sure if it's possible (or even ideal), but what I want from my call-stack is something that I've seen in some other languages. As an example:
void PrintDebugMessage(const string &Text)
{
	dbeals::Debug::CallStack CallStack = dbeals::Debug::CallStack::Capture();
	
	// We pass in 1 because 0 would be PrintDebugMessage();
	// CallStack is inverse-index based (0 is the last element, n - 1 is the first.)
	std::stringstream Stream;
	Stream<<CallStack[1].FileName;
	Stream<<"("<<CallStack[1].LineNumber<<") ";
	Stream<<"from "<<CallStack[1].SymbolName;
	Stream<<" : "<<Text<<"\n";
	OutputDebugStringA(Stream.str().c_str());
}



The obvious issue is that this will be quite slow if a lot of calls to PrintDebugMessage() are made (which is quite usual) if I use StackWalk64(), or it addes extra code to remember (if I do a scoped-variable Push/Pop system.) This comes down to the trade-off really: the former captures the whole stack, but is slow; the latter captures only functions that use the scoped-variable (implemented via a macro), but is faster. I'm going to try for the former; if it's too slow I'll try to optimize it (maybe capture can take a parameter that allows the user to specify a specific number of steps to go back. So, instead of Capture() for this I would use Capture(1).)

At the moment, CallStack looks like so:
dbeals::Debug::CallStack
	Entry
		FilePath
		LineNumber
		FunctionName
		UndecoratedFunctionName
		FunctionSignature
		
	EntryCollection typedef
	EntryCollection Capture(/*possibly*/Level)


[RuntimeLog]
For runtime logs I'm going for a different approach than normal as well. My normal approach is using a vector to "bottle up" messages and then when a log-listener is registered, all of the saved messages are passed to it and any future messages are passed to it. While this approach worked well, it had some major issues with it:

1) Because I stored listeners as functors (boost::function<>), once you registered a listener, you couldn't unregister it; unless I implemented named listeners (which I eventually did), but this was pretty much a hack just to allow unregistering.

2) Listeners, being functions, were pretty much just formatters combined with streams. This is why I used boost::function; I could create a class and bind a method from that class and pass it to RegisterListener(). I pretty much did this just for generalisation sake[1] ("what if someone just wants to be able to pass a function?")

3) When it came down to it, it wasn't all that useful or helpful.

So, to decide, I went with the "Problem/Solution" approach:

Problem:
I need to be able to report debug information from any point during execution and be assured that it is safely saved in some way, even in the event of a crash.

So, what I want is, no matter what happens, the debug information that I need to output WILL be output; THIS OPERATION CANNOT FAIL. Well, obviously that's pretty much impossible; there are too many things working against us. If we pass it to a formatter, the formatter could be invalid or throw an exception and presto, our data is lost. If we store it in memory, it's lost on crash. If we try to store it in a file, we might not have access to that file or any one of the operations from posting the information to streaming it to the file could fail. Passing it over a network to a remote debug system or whatnot has just as many issues. Immediate display won't work either (if we use messagebox, our app might be fullscreen, etc.)

Anyway, my point is, we can't assure that it won't fail; so we'll choose a lesser of the evils.

Solution (Part A):
We'll write the data directly to a file in plain binary.

This is a decent solution IMO; it brings down the number of operations required, which improves performance and it decreases the chance that the operation will fail due to code. However, this brings about it's own problem: our runtime log is useless to anyone that can't read binary.

Solution (Part B):
We'll implement a listener system and a class to handle the binary file.

The listener system will be an interface that we can derive from that will be used to format and/or route the data AFTER it has been written to the binary file. This way, our binary data should be safe and our application can still output the data in a human readable form (via OutputDebugString, cout, fstream, what have you.)

The binary file class will be used for loading the binary file (the runtime log class itself will serialize the data), this will make it easier for writing a tool to load and display the binary file in-case our human-readable form failed. This class will simply attempt to load the binary file and give access to the list of entries in it.

So, the runtime log system looks like so:
dbeals::Debug::RuntimeLog
    static OutputMessage(Level, Text)
	static OutputComment(Text)
	static OutputWarning(Text)
	static OutputError(Text)
	
	EntryListener
		pvirtual HandleEntry(Entry)
	
	DumpFile
		Entries
		Load(FilePath)


[Assert]
Honestly, I'm not really sure how to handle this. This is one of the few things in my old library that didn't really have anything wrong with it. It had built in support for a dialog that displayed the information supplied (as well as the callstack), the ability for the user to abort, break, or ignore, and the ability to change the handler function. The only issue I can think of with it is that, for the sake of generalisation again, it used boost::function.

Actually, it wasn't all just for generalisation (nor was the runtime log's use of boost::function.) I actually did this for one specific reason: I could bind a method in my engine to either and then my engine could handle it using the following determination:
if the engine is in a usable state (all of the subsystems are created and in usable states themselves), we can display the information using an internal GUI
if the engine is not in a usable state, but is fullscreen, we can abort.
if the engine is not in a usable state, but we're windowed, we can display the information using Win32.

However, switching to the listener system allows this type of interaction while providing a more elegant solution to the entire design.

Either way, because of the inherent use of the system, no matter what route I choose to go, the design will pretty much remain the same:
dbeals::Debug::Assert
	AssertHandler
		pvirtual OnFailure(Condition, Message)
		
	OnFailure(Condition, Message)
	SetAssertHandler(Handler)
		
db_Assert(Condition) macro
db_AssertEx(Condition, Message) macro


The only major changes to any of the debug components is based on whether or not I can get CallStack implemented the way I want to or not. If I can't, all of the Output*() functions in RuntimeLog will need to receive the filepath, line number, and function name as well as the information shown. Same goes for AssertHandler::OnFailure() and Assert::OnFailure(). Also, I'll add macros to RuntimeLog to simplify logging (so that you don't have to have __FUNCTION__, __FILE__, __LINE__, all over the place.)

[Notes]
[1] Generalisation :
Generalisation is one of my major fail-points. I'm constantly trying to design things based on what-if's and, as I'm sure all of you are aware, this leads to failed projects. This is the reason for this post-writeup of the dbeals library. I'm going to try to include a reason that I'm writing each thing, even if it's just "it makes doing X 100 times easier." If I can't come up with a reason, it goes on a list and any information for it goes into my Code Snippets - Labs folder. This way, if at some point I do come up with a reason to use it (or I run into a situation where, without it, increases development time or causes a lot of copy code, I'll have any information saved.

[2] Examples and Structures :
All of the examples I've got here are not tested; they're just there to give an idea.

Any of the structures I show (like the layout of RuntimeLog) only list what the user of the class will interact with. For example, RuntimeLog::DumpFile will have a public list of the entries in the file (if one has been loaded) and a Load() method that loads the file; this is all the user needs to use to load a dump file for use in a viewer tool.

Thats it for tonight; I have stuff to do tomorrow and I should have been in bed an hour ago lol. I'll try to go over some more of the design tomorrow night.

I'm going to leave this on a thought though, as I'd like some input. More often than not lately, I've been thinking that I'm just simply approaching this from the wrong direction. While the things listed above and stuff I've been working over the past few days/weeks are nice UTILITIES, they, nor a code-base, and not even an engine seem to be what I'm after. I guess the term 'framework' suits what I'm looking for (maybe 'environment' would be better.)

Pretty much, a game engine development kit (I don't really know how to explain it lol.) To those that are thinking "That's what an engine is, genius...", not exactly. IMO, an engine would be defined as:
A static piece of code that works in a defined way on either a defined set of data or a data-driven collection of defined data. (By static I mean that the engine isn't an SDK; an engine it's started and stopped, not used to write something else.)

I guess the best analogy I can come up with is, basing off of the car-engine to game-engine analogy, I want the factory that makes the engine. While the resulting engines can be very different, the factory uses standard equipment and materials to make the engines. Using A, B, and C pieces of machinery combined with D and E materials, I can make the same engine.

And it's at this point that I realize, pretty much what I want is XNA, but for C++ (maybe that's why I'm always trying to emulate XNA components in my library lol.)

Anyway, any comments, input, or suggestions (on anything in this entry or the last)? (Beyond "STFU AND MAKE A GAME ALREADY!" of course.)

Comments: 0 - Leave a Comment

Link



Monday, September 14, 2009
This entry expands on my last one (and now the reply I made to Slather) about my OpaqueData situation (this is the name that XNA uses to store data associated with content that doesn't fit into their standard object model, but needs to be stored for use by processors.)

When I started that idea, there were two intended uses for it: something to compliment WinForm's Tag property in controls and the above stated OpaqueData. I'm going to use the Tag property in this entry because it covers both situations that the system is intended for whereas OpaqueData only covers one.

Now, before I start, I want to point out that I still consider myself a newb when it comes to C#. I've made one assumption in the following that I haven't looked into (simply because, for my solution, it doesn't matter.) The assumption being that C# seems to store object members as references. So, passing MyObject instantiates a clone of the object, but the clone's properties are references to the original object. For example:
using System;

namespace Lab
{
    class Program
    {
        class MyObject
        {
            public string Name;

            public MyObject()
            {
            }

            public override string ToString()
            {
                return String.Format("Name: {0}", this.Name);
            }
        };

        static void Foo(MyObject Obj)
        {
            Obj.Name = "Programmer16";
        }

        static void Foo(string String)
        {
            String = "Programmer16";
        }

        static void Foo(ref string String)
        {
            String = "Programmer16";
        }

        static void Main(string[] args)
        {
            MyObject Obj = new MyObject();
            Foo(Obj); // Name = "Programmer16"
            Console.WriteLine(Obj);

            Obj.Name = ""; // Name = ""
            Foo(Obj.Name); // Name = ""
            Console.WriteLine(Obj);

            Foo(ref Obj.Name); // Name = "Programmer16"    
            Console.WriteLine(Obj);

            Console.In.Read();
        }
    }
}




So, the type passed in is not a reference itself, but it's members are. Anyway, this is kind of the idea behind the system.

So, with that out of the way, a full definition of what I wanted:
An object that could store specific information, no matter the type, and allow access to said information.

However, the information stored needs to be able to be of two different types: copy of data or reference to data (via pointers.)

An example: Lets take a TreeView and a TreeViewNode control from a game editor made with my imaginary GUI. The TreeView is an object browser in the editor, and thus lists different types of objects, separated by root nodes. Each rood node contains a specific type of item. Each of the TreeViewNode would have a Tag member that the editor's code sets to the item associated with it.

Two possible choices:
1) Store the tag as a copy of the data and store a pointer to the TreeViewNode in our item classes, so that when the item is changed, it can update the node (for example, if the item's name is changed, we'd want to update the node's text).
2) Store the tag as a reference to data. For the name changing example, if the TreeView had LabelEdit enabled, we would override the node's OnLabelChange and it would change's it's own text as well as the item's name. Viola, no need for an edit item dialog to change it's name; we just edit the label and move on.

However, there are situations where we would want to store an object as a copy of an object, yet still be able to have the information persist between retrievals. For example, say you have a TreeViewNode that represents a weapon. The editor requires that every weapon node keep a hierarchy list of type WeaponHierarchy (which, in this example, contains a reference to the weapon it's associated with and, if available, the item(s) it was derived from.) Seeing as how only the editor cares about this information (our Weapon class is part of the game engine itself and we don't want to have to create EditorWeapon), we can create a temporary object and store that in the node's Tag member. Our tag member now contains arbitrary data of a specific type and will persist until the node is destroyed or the Tag member is changed.

I hate to be repetitive, but the idea here is that we're using a non-template object to store data that would normally be either static or templated. The type of data that a specific object contains should never change (unless it changes EVERYWHERE; i.e. between the different states of a game or editor or something.) Normally we would use a specific class (i.e. derive WeaponTreeNode from TreeViewNode and add the WeaponHierarchy member) or use a templated class (TreeViewNode is no longer ArbitraryData, but a type defined by T.) However, in the situations this system is intended for, neither of these would suffice (a static type means that TreeViewNode has a specific purpose, which it doesn't and a templated class would mean that every TreeViewNode in the tree would have to contain the exact same type of data, which is usually never the situation.)

So, setting the data to an int in one function, then to a vector in another, is not the intended use. We're storing state-specific, type-specific data, in an arbitrary data container. I say state-specific because, as long as states are well-defined and everything adheres to each state, changing the type of data is perfectly acceptable. State-specific information is very useful in several situations; for example (as I love to do), making your engine double as a built in, run-time editor. Every tile in the "CurrentMap" class could store engine-specific data when in play mode, but store editor-specific data when in edit mode.

Anyway, I could go on and on, giving different examples, but I'm not going to. Here's the code for the class (named TagItem at the moment.) It's a pretty simple wrapper that stores the values passed in. The only thing really needed is the CastTo<>() method. It attempts to cast the value to a pointer-to-pointer of ValueType, and, barring failure, attempts to cast it to a regular pointer of ValueType. Successful casting (for either attempt) returns a pointer. This way, it doesn't matter to me whether I'm manipulating a reference or a copy.[1]

namespace dbeals
{
    /*
    TagItem is an arbitrary container for storing state-specific, type-specific data as either copies of objects or references to objects.
    To store a copy use either:
        TagItemInstance = ObjectInstance;
    or
        TagItemInstance = *ValidObjectPointer;

    To store a reference use either:
        TagItemInstance = &ObjectInstance;
    or
        TagItemInstance = ValidObjectPointer;

    Do NOT store references to temporary. I'm not sure how defined this is (if at all.) I only tested it with a vector, which resulted in an empty vector after the object went out of scope,
    but I don't think that's reliable behavior.
    */
    struct TagItem
    {
        boost::any Data;

        // Default Constructor
        TagItem()
        {
        }

        // Copy Constructor
        TagItem(const TagItem &Tag) : Data(Tag.Data)
        {
        }

        // Value Constructors
        template <typename ValueType>
        TagItem(const ValueType &Value) : Data(Value)
        {
        }

        template <typename ValueType>
        TagItem(ValueType *Value) : Data(Value)
        {
        }

        // Modifiers
        void Clear()
        {
            this->Data = boost::any();
        }

        void Swap(TagItem &Tag)
        {
            this->Data.swap(Tag.Data);
        }

        // Queries
        bool IsEmpty() const
        {
            return this->Data.empty() == true;
        }

        bool IsNotEmpty() const
        {
            return this->Data.empty() == false;
        }

        // Removed : CanCastTo<>()
        /* I removed this because it just bulks things up. The whole design is to be simple using the form:
        ValueType *Result = 0;
        if((Result = Tag.CastTo<ValueType>()) != 0)
            // Do something here

        This requires only one cast, whereas using CanCastTo<>() would require two casts.
        To use this class in that manner (not for which it was intended, but inherently can be use in such a manner), just use multiple casts:
        ValueType0 *Result0 = 0, ValueType1 *Result1 = 0;
        if((Result0 == Tag.CastTo<ValueType0>()) != 0)
            // It's ValueType0, do something with it.
        else if((Result1 == Tag.CastTo<ValueType1>()) != 0)
            // It's ValueType1, do something with it.

        or
        if(Tag.CastTo<ValueType0>())
        {
            ValueType0 *Result = Tag.CastTo<ValueType0>();
            // do something with it.
        }
        else if(Tag.CastTo<ValueType1>())
        {
            ValueType1 *Result = Tag.CastTo<ValueType1>();
            // do something with it.
        }
        */

        // Accessors
        template <typename ValueType>
        ValueType *CastTo()
        {
            ValueType **Result = boost::any_cast<ValueType *>(&this->Data);
            if(Result)
                return *Result;

            ValueType *Result2 = boost::any_cast<ValueType>(&this->Data);
            if(Result2)
                return Result2;
            return 0;
        }

        template <typename ValueType>
        const ValueType *CastTo() const
        {
            return const_cast<TagItem *>(this)->CastTo<ValueType>();
        }

        // Assignment Operators
        TagItem &operator =(const TagItem &Pointer)
        {
            this->Data = Pointer.Data;
            return *this;
        }

        template <typename ValueType>
        TagItem &operator =(const ValueType &Value)
        {
            this->Data = Value;
            return *this;
        }

        template <typename ValueType>
        TagItem &operator =(ValueType *Value)
        {
            this->Data = Value;
            return *this;
        }
    };
}





Where I'm looking for input:
Everywhere, of course. However, more specifically:
1) My const version of CastTo<>(). I hate doing it the way I have at the moment, because if, for some retarded reason or because of a programmer-error, the non-const version changes the value, it could causes some major issues and hard to track down bugs (i.e. the thinking "I'm calling a const version; so it must be memory corruption or something causing my data to change".)
I took out the code I was attempting to use because I evidently used undo and it's not what I was using (I copy-pasted the non-const and modified it to be const.) The first line attempted to cast to const ValueType ** and used the template argument ValueType *, but constantly reported the error that it could not cast from 'dbeals::string **' (a typedef for std::string) to 'basic_string<blah blah blah> *'. Pretty much no matter what I did, I got that exact same error.

2) Most importantly, is this really a viable solution? It works exactly how I want it to in all of my tests, but I'm not positive it's even a good idea (especially the CastTo<>() method.)

[edit]
3) How to get around the issue of string literals. Passing in string literals is (well, seems to be) completely safe. You can only retrieve it using CastTo<const char>(), so the returned pointed-to-object cannot be modified, but is this defined behavior? I guess there's not really a problem with it, as long as it is defined behavior.

[1] - This kind of breaks the design, because, by my own definition, I should know whether or not it's reference or copy data. However, I left it as this for the simple fact that it's less complex. Yes, I should know what I'm dealing with, but my interaction with the data shouldn't change depending on whether or not it's a reference or a copy. Splitting it into two different functions (as my original pass last night did), causes me to have to change a lot of code if I decided to pass something by reference rather than a copy.

Anyway, any comments, suggestions, etc, etc are welcome. Also, looking for a good name for it. I was thinking ArbitraryDataContainer or some-such, but I'm not sure. I'm leaving it as TagItem for now.

Comments: 0 - Leave a Comment

Link



Sunday, September 13, 2009
Gah, it's been almost a month again.

I've been working a lot over the past 3 weeks though, so I kind of have an excuse (not really, seeing as how no matter how long I work at my job, I still come home and put in between 4-12 hours programming; depending on the day (I average about 6 hours programming time every night.)

Anyway, the only thing I've accomplished code-wise is...well, really nothing. I stripped my string utility functions out of my code base (because I discovered boost::algorithm::string, which replaced pretty much every one of them.)



While screwing off with little ideas that pop in my head (like I usually do), I can up with an enum-value class and a flag-value class (trying to 'fix' the enum situation in C++ is something I keep coming back to; for some reason I find it fun. Plus, I learn lots of little quirks of the language while doing so.)

Here it is in all of its (horrid, horrid) glory:
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
namespace dbeals
{
    //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    template <typename EnumerationType>
    struct EnumValue
    {
        EnumerationType Value;

        EnumValue()
        {
        }

        EnumValue(EnumerationType Value)
        {
            this->Value = Value;
        }

        EnumValue(const string &Name, EnumerationType Value)
        {
            this->Value = Value;

            std::pair<std::map<EnumerationType, string>::iterator, bool> Result = EnumValue<EnumerationType>::_container().insert(std::make_pair(Value, Name));
            if(!Result.second)
                throw std::exception((format("The enum value '%1%' has already been registered with the name '%2%'.") % Value % Result.first->second).str().c_str());
        }

        const string &ToString() const
        {
            std::map<EnumerationType, string>::iterator Itor = _container().find(Value);
            if(Itor == _container().end())
            {
                try
                {
                    static string Dummy = "";
                    Dummy = boost::lexical_cast<string>(this->Value);
                    return Dummy;
                }
                catch(boost::bad_lexical_cast)
                {
                    throw std::exception((format("Failed to cast the value '%1%' to a string.") % this->Value).str().c_str());
                }
            }
            return Itor->second;
        }

        static EnumerationType Parse(const string &Text)
        {
            string Copy = String::trim_copy(Text);
            for(std::map<EnumerationType, string>::iterator Itor = _container().begin(); Itor != _container().end(); ++Itor)
            {
                if(String::equals(Copy, Itor->second))
                    return Itor->first;
            }

            throw std::exception((format("The enum-value '%1%' does not exist in this enumeration.") % Text).str().c_str());
        }

        operator EnumerationType() const
        {
            return this->Value;
        }

        const EnumValue &operator =(const EnumValue &Value)
        {
            this->Value = Value.Value;
            return *this;
        }

        const EnumValue &operator =(EnumerationType Value)
        {
            this->Value = Value;
            return *this;
        }

    private:
        static std::map<EnumerationType, string> &_container()
        {
            static std::map<EnumerationType, string> Enum;
            return Enum;
        }
    };

    //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    template <typename EnumerationType>
    struct FlagValue
    {
        EnumerationType Value;

        FlagValue()
        {
        }

        FlagValue(EnumerationType Value)
        {
            this->Value = Value;
        }

        FlagValue(const string &Name, EnumerationType Value)
        {
            this->Value = Value;

            std::pair<std::map<EnumerationType, string>::iterator, bool> Result = FlagValue<EnumerationType>::_container().insert(std::make_pair(Value, Name));
            if(!Result.second)
                throw std::exception((format("The enum value '%1%' has already been registered with the name '%2%'.") % Value % Result.first->second).str().c_str());
        }

        string ToString() const
        {
            string Output = "";
            for(std::map<EnumerationType, string>::iterator Itor = _container().begin(); Itor != _container().end(); ++Itor)
            {
                if(this->Value & Itor->first)
                {
                    if(!Output.empty())
                        Output += "|";
                    Output += Itor->second;
                }
            }
            return Output;
        }

        static EnumerationType Parse(const string &Text)
        {
            std::vector<string> Tokens;
            String::split(Tokens, Text, compare_with('|'));
            EnumerationType Output = (EnumerationType)0;
            string Copy;
            for(std::vector<string>::iterator StrItor = Tokens.begin(); StrItor != Tokens.end(); ++StrItor)
            {
                for(std::map<EnumerationType, string>::iterator Itor = _container().begin(); Itor != _container().end(); ++Itor)
                {
                    if(String::equals(String::trim(*StrItor), Itor->second))
                        Output |= Itor->first;
                }
            }
            return Output;
        }

        operator EnumerationType() const
        {
            return this->Value;
        }

        FlagValue operator |(const FlagValue &Value) const
        {
            return FlagValue(EnumerationType(this->Value | Value.Value));
        }

        const FlagValue &operator |=(const FlagValue &Value)
        {
            this->Value |= Value.Value;
            return *this;
        }

        FlagValue operator |(EnumerationType Value) const
        {
            return FlagValue(EnumerationType(this->Value | Value));
        }

        const FlagValue &operator |=(EnumerationType Value)
        {
            this->Value |= Value;
            return *this;
        }

        FlagValue operator &(const FlagValue &Value) const
        {
            return FlagValue(EnumerationType(this->Value & Value.Value));
        }

        const FlagValue &operator &=(const FlagValue &Value)
        {
            this->Value &= Value.Value;
            return *this;
        }

        FlagValue operator &(EnumerationType Value) const
        {
            return FlagValue(EnumerationType(this->Value & Value));
        }

        const FlagValue &operator &=(EnumerationType Value)
        {
            this->Value &= Value;
            return *this;
        }

        const FlagValue &operator =(const FlagValue &Value)
        {
            this->Value = Value.Value;
            return *this;
        }

        const FlagValue &operator =(EnumerationType Value)
        {
            this->Value = Value;
            return *this;
        }

    private:
        static std::map<EnumerationType, string> &_container()
        {
            static std::map<EnumerationType, string> Enum;
            return Enum;
        }
    };
}



I made a couple examples using some macros to make it easier to read:
namespace BuildTypes
{
    enum enum_type { };
    db_EnumValue(enum_type, DebugBuild);
    db_EnumValue(enum_type, ReleaseBuild);
} db_DeclEnumType(BuildTypes::enum_type, BuildType);

int main()
{
    BuildType MyBuildType;
#if defined(_DEBUG) || defined(DEBUG)
    MyBuildType = BuildTypes::DebugBuild;
#else
    MyBuildType = BuildTypes::ReleaseBuild;
#endif
    std::cout<<MyBuildType.ToString()<<std::endl;
    return 0;
}



And some flags:
namespace BuildOptions
{
    enum enum_type { };
    db_FlagValue(enum_type, IncludeComments);
    db_FlagValue(enum_type, IncludeWarnings);
    db_FlagValue(enum_type, IncludeErrors);
} db_DeclFlagType(BuildOptions::enum_type, BuildOption);

int main()
{
    BuildOption Options;
    Options = BuildOptions::IncludeComments | BuildOptions::IncludeErrors;
    
    BuildOptions ParseTest1 = BuildOption::Parse("IncludeComments");
    BuildOptions ParseTest2 = BuildOption::Parse("includeComments"); // Fails; case-sensitive
    BuildOptions ParseTest3 = BuildOption::Parse("IncludeComments | IncludeWarnings|IncludeErrors"); // Works fine; spaces are trimmed
    
    std::cout<<Options.ToString()<<"\n";
    std::cout<<(BuildOptions::IncludeWarnings | BuildOptions::IncludeErrors).ToString()<<std::endl;
    return 0;
}



This has pretty much everything wrong with it that wrapping intrinsic types does, plus probably a bunch of other stuff. One of the other things I note is I'm pretty sure the parse method has a large chance of failing (seeing as how I register the values in their constructor and I'm pretty positive I'm not garaunteed that all of these are constructed when I'm wanting them to be.)
The ToString() and Parse() methods actually aren't too slow though; at each interval of iterations it ran only a fraction of the time longer (less than a quarter of a second.)

I honestly wish it didn't have the problems I'm pretty sure it has, because I'd stick with it then. Either way, I had fun doing it and learned a couple things, so the time wasn't COMPLETELY wasted (ok, it was; shut up .)



I spent the other night trying to come up with a nice content system; I pretty much failed. I had something similar to XNA's content system, but shittier. Plus, it's kind of hard to emulate the OpaqueData parts (I can, and it's pretty type-safe as long as I use RTTI and typeid(), but that slows it down.) I'm thinking about having another go at it, but using boost::any instead of a byte-storage class.



Another lab-project I've been working on is a robust buffer system. The idea behind it was coming up with a buffer class that I could easily use to create/manage memory pools (error buffers, serialization, etc), read the contents of a file (deserialization), stream linear chunks from a file (serializing/deserializing lists), and stream chunks from a file in a grid-based manner (streaming maps.) Pretty much just trying to come up with some generalized solutions for some of the things I've thought about implementing into a game (or know I'll need in a game; like serialization.)



Figured out some of the things I've been curious about (getting the name of the operating system, enumerating disk-drives, calculating available disk-space, calculating available memory, and calculating processor speed.)



Lol, I always look back at what I've done over a period of time and think to myself "If I spent half of the energy on making a game that I put into screwing around ('learning stuff'), I'd have finished SOMETHING by now." Most of my energy seems to go into writing frameworks and wrapping shit.

On the up-side, the design document for The Alchemist is almost finished and after tonight (Sunday night), I have 4 days off. I'm going to try really hard to get the GDD finished and even harder to get the TDD done before I head back to work.

Too much blathering and straying and not enough sleeping; I've been up for about 20 hours and it's time for bed. Gnight!

Comments: 2 - Leave a Comment

Link


All times are ET (US)

 
S
M
T
W
T
F
S
1
2
3
4
6
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

OPTIONS
Track this Journal

 RSS 

ARCHIVES
November, 2009
October, 2009
September, 2009
August, 2009
July, 2009
June, 2009
May, 2009
February, 2009
January, 2009
December, 2008
November, 2008
October, 2008
September, 2008
August, 2008
July, 2008
May, 2008
April, 2008
March, 2008
February, 2008
January, 2008
December, 2007
November, 2007
October, 2007
September, 2007
August, 2007
July, 2007
June, 2007
May, 2007
April, 2007
March, 2007
February, 2007
January, 2007
December, 2006
November, 2006
October, 2006
September, 2006
August, 2006
July, 2006
June, 2006
May, 2006
April, 2006
March, 2006
February, 2006
December, 2005
November, 2005
October, 2005
September, 2005
August, 2005
July, 2005