|
In order of no progress to done:
Red,
Orange,
Yellow,
Blue,
Light blue,
Green,
Light green.
Last updated 2/12/09 at 12:01 am PST
Cripes! 2.0 -- See the Class Layout
Arenamatic Code w/ cConsole
| Friday, February 27, 2009 |
 On the Subject of Musicshake |
Posted - 2/27/2009 5:02:42 PM | I popped onto GameDev.net today and spotted John Hattan's review of Musicshake, a free-to-use music composition tool. It's rather reminiscent of FL Studio, but I never could figure that one out... mostly because none of the instruments seemed to fit. Musicshake is a lot simpler though, and John compared it to a Strumstick in easiness. I really have to agree. I downloaded Musicshake and it got me started with a randomly created tune from a genre of my choice; I picked Techno/Electronic. I played around with the interface a bit to get used to it, and it's really simple.
To test the waters as I went, I picked instruments that sounded good and messed with the pattern grid and chord selection. It's a bit of trial and error when selecting instruments, but the selection box helpfully adds tags to the left of the instrument names showing what type they are, i.e. [Melody], [Chord Harmony], or any of the others out there. I didn't have to worry about writing a melody, because Musicshake's chord selector for each bar generates one for you. The chords look like wavy lines above the columns, and one would assume that depending on how high or low the line is at a given point in the bar, you get a certain note. But I don't know terribly much about how this thing was made...
The end result was two [Melody]s, two [Chord Harmony]s, and two [Rhythm]s (one with a reverb/normalize tone) combining into a really rather nice and atmospheric tune... so I dubbed it "Atmosphere". Click Here to take a listen.

Also, how do you get the blasted image to center itself? align="center" and style="float:center" don't work. 
EDIT: Thanks, Gaiiden. 
| |
| Thursday, February 26, 2009 |
 Cripes 2.0: Basic Strategy support |
Posted - 2/26/2009 4:00:39 AM | I have a very rough and basic Strategy setup going now. I have an IStrategy template interface which defines a pure virtual Execute(T&). The templating means I'm able, if I so desire (and I rather don't), to create strategies for other objects besides an Entity. The T& is a reference to the object whose strategy it is.
I then created an IUpdateStrategy interface, same template as before, but with a non-virtual overloaded Execute(T&, DWORD frame_delta). It also has a protected DWORD field to store the passed-in frame delta in, allowing the Execute(T&), which all IStrategies require, access it.
Finally, I made a simple Collide strategy, which doesn't actually test for collision just yet. It requires a World& and a Keyboard& to use for strategic context, the latter mainly because I'm testing it with the player sprite and I need to be able to move it myself. Those two are passed in via the constructor.
I created a Collide strategy and passed it the player entity, and moved its update code from my Game::Update() function to the Collide::Execute() function. It works! But I feel bad design on the horizon, and I really need to just sit down and thing about how I can best use the Strategies.
~Jonathan
P.S. johnhattan's ConFusebox 2 is insanely addicting. 
| |
| Wednesday, February 25, 2009 |
 Twitter, and Cripes 2.0: World change |
Posted - 2/25/2009 2:08:56 AM | I caved in and signed up on Twitter. I don't know how much I'll use it, but I like how EasilyConfused uses his, so... I also downloaded Spaz to use to update myself. Maybe it'll be easier to keep this going that way. My twitter link is http://www.twitter.com/twisol, if anyone wants to follow it.
I also mucked with World like I said I might last time, cutting out the Map class and merging EntityMap into World. It definitely feels a bit cleaner. It makes it easier to provide my Strategies with a context, too, so the Entity can test for collision against another entity. I'm still working on how that'll happen, though. Still, things feel like they're shaping up.
Ideally, I don't want to have anything worry about what kind of Entity an Entity is (i.e. Bullet) except the Entity itself and its Strategy. Entity already inherits from IDrawable, so World knows it can draw it (and doesn't need to know anything else). Like I said, I'm working out how exactly the whole Strategy thing will work, but in the end, Entities will really just be treated as Entities or IDrawables, and nothing else.
~Jonathan
| |
| Monday, February 23, 2009 |
 Cripes 2.0: 2am memo |
Posted - 2/23/2009 5:48:55 AM | This post is more a collection of my thoughts than anything, because I'm too tired to write properly. Comment as you like, if there's anything worth commenting on.
1. Noticed while browsing Wikipedia's Design Patterns area that the Entity resource management design I was calling a pimpl might actually be a flyweight. Or they're close enough to not matter.
2. Read up on the Strategy design pattern, ironically the same thing I was suggesting previously for handing Entity behavior (though I mentioned functors, I was thinking of the same thing). I'm going to take that as a good omen.
3. Considering mucking with my drawing setup (World, Map, EntityMap), because I'm not sure it's the best way to go about it. I could toss the entire Map class at the moment, because I'm not really using it at all. I figure any console application that uses a World would be able to use differently-sized-and-sprite'd Entities to represent the static game world, just like I'm doing with my maze walls. Entities don't -have- to do anything, which is one of my reasons why Entity isn't going to be fully abstract. You can always use a generic Entity.
4. There was something I realized during #3, but I forgot it. I'm sure it'll come back to me. 
5. I remembered what #4 was supposed to be about. I'd like to implement some kind of Z-order for drawing my entities, because at the moment it's just based on what order you add them the EntityMap. Luckily, no sprite should ever overlap with another sprite in Cripes 2.0 - they're alll solid and collidable... well, they will be when I implement collision - so that's way, way at the bottom of the list. (It IS on the list, though; otherwise it wouldn't be in this post)
6. This is the largest codebase I've ever worked on. I feel a little cluttered with all of my files, but I know this is a small project in the grand scheme of things. How does everyone else organize the clutter?
I think that's good for tonight... Good night, all.
~Jonathan
| |
| Sunday, February 22, 2009 |
 Cripes 2.0: Some all-around refactoring. |
Posted - 2/22/2009 3:50:36 AM | I decided to create an IDrawable abstract class and have Entity inherit from it, and did some fixing up so that the EntityMap uses IDrawable instead. I had to use pointers instead of letting the EntityMap own the actual Entities, so now I think I'll be making some kind of manager class that will actually own the entities. I also gave the Entity class a virtual Update() method that takes a DWORD frame_delta that it can use to decide if it's time to change its appearance or move or whatnot. The basic Entity doesn't do anything, while the Missile - which, yes, I've successfully made without much effort - will move itself based on what its aim COORD is.
I'm actually kind of torn between using virtual methods or passed-in functors here. One problem is that the entity Update() method doesn't have any context in the world, so it can't check itself for collision as it moves, or wrap itself around the map (because it doesn't know the width and height, so it doesn't know when or where to set its value to the other side). That's one of the reasons I'm starting to lean heavily towards a passed-in functor... I might actually do that, because it's the only solution that sounds sensible.
I'm also considering doing something with my MazeGen along the lines of splitting it into an object that actually comes up with the maze, and one that takes its output and converts it into entities. The latter might be incorporated into the World as another method... perhaps it could take a MapLoader* - MazeGen's parent abstract class - as a parameter. MapLoader's been pretty much unused, actually - just forward-thinking design - but this might be the time to use it. Hmm.
As a short footnote, my Keyboard class design - or more correctly, my keypress management algorithm - is growing on me, and I'm not even using it! I think, with tweaking, it could be used in a normal application too, though at high update-rates you kind of lose the benefits of the three-state: you're checking fast enough that with human reflexes the chances of a tap going unnoticed are slim. In Snipes, though, I've locked it at one frame every 150ms, which is ample time for a tap to be missed.
That's three topics there (plus a footnote): IDrawable, Entity update functors, and MazeGen abstraction/decoupling. Thoughts?
~Jonathan
| |
| Friday, February 20, 2009 |
 Cripes 2.0: More on sprite management |
Posted - 2/20/2009 9:50:26 PM | Okay, I thought I'd go into more depth on how I started managing my sprite resouurces. I started by picking out the bits of the Entity that correspond to the sprites, and encapsulating them in a new class, SpriteSet. These "bits" are the CHAR_INFO* vector and width/height fields. I then gave my Entity a SpriteSet pointer. (Along this entire process I'm leaving both versions in, so I can compile and test the code as I go. It also makes for easier refactoring.)
Next, I went to my EntityFactory and refactored it to work with SpriteSets instead of Entities. As I did this I realized I was working with the C++ "pimpl" idiom, and I thought it fit quite well. I think this is one of the times when pimpl is a "logical part of a good design", as EasilyConfused pointed out here (but I honestly don't know much about good design at this point!). I also renamed the EntityFactory to a SpriteManager, because it's more fitting the way I have it now. It's more or less unchanged, except that its Construct() member is now Get(), and returns a const SpriteSet pointer instead of an Entity. The Entity now takes a SpriteSet pointer in its constructor, which could be NULL if you don't have a sprite for it to draw. This setup means you can swap sets of sprites out for an entity, to change its appearence dramatically. For example, my Sniper guy might suddenly turn into a Ghost Snipe if I wanted him to!
During this process I cleaned my Entity class up quite nicely, and with a few tweaks I should be able to inherit subclasses like Missile. I think the best part of this pimpl design is that the sprite management, which should stay constant through all of my Entities, doesn't need to be rewritten every time I subclass a new Entity. I also moved my Corner# entities back into a single Corner with each corner sprite in the sprites file. After all this resource-mongering was done, I checked my MazeGen speed and it was down to fifteen ticks on average. That's shocking.
| |
 Cripes 2.0: A short note |
Posted - 2/20/2009 3:52:04 AM | Holy smokes!! I put in the resource managing stuff, and my maze generation time dropped to under fifty ticks. No needless copying of sprite buffers apparently! Each Entity now holds a const SpriteSet* to its associated set of sprites. The SpriteSets are created only when loading them in from a file; when EntityLoader Construct()s an Entity, it constructs it with a pointer to its SpriteSet. I had no idea it would make that much of a difference! Better, I think I can turn my Corner# entities back into one Corner entity, but I'll save that for tomorrow... er I mean later today.
For your regularly scheduled journal entry, please scroll down one post. Nothing else to see here!
| |
| Thursday, February 19, 2009 |
 A short interlude |
Posted - 2/19/2009 9:30:55 PM | My brain's starting to hurt from working on Cripes, heheh. Don't get me wrong, it's fun when I get things done (see the maze generator: that was pure win), but when there's some downtime I get in a slump. Luckily, I do have other things to distract me. They're not terribly gamedev-related, but I hope nobody minds if I devote a post to them! I promise I'll write some more about my Entity refactoring at the end.
Before we get into it, I is a leet hackerz. Okay, not really, but I was adding some of the GameDev forums to the My Favorites list, and I noticed that the dev journal forum didn't have a link. I grabbed the bookmark-URL-link for another forum and replaced that forum's ID with the devjournal forum (53), and voila. (Staffers: Don't kill me! *flee*)
Website Programming
Okay, so.. My father runs Daycare.com, among a bunch of other websites, and I've been doing some (paid!) work for him on it. One of the things he's having me do is take Excel and .csv databases of daycare listings and fix them up to be imported into his daycare listings database. phpMyAdmin has a handy import page which can accept CSV files, handily.
So I've done about seven or eight of these so far, and generally I have to parse the fields out into the proper format for his database. I immediately learned how to use Excel's Proper(), Mid(), Left(), and Right() functions, of course. Down the road, I learned to use If(), if there was a certain value in an original column so I could parse/merge it into another column that was going to be imported.
This last one, though, I hit on a revelation, and I thought it was pretty nifty. I realized that every cell is basically just a variable. Each cell can have a formula that stores a new value in the variable. In that sense, it's just like the kind of programming that I'm used to. So lets take an example.. There's a column, "AgeStartEnd", which holds the youngest and oldest ages a daycare would take. Each cell has values like "04W10Y", and you can probably tell what that means if you stare at it. This is where my epiphany hit me. Speaking in terms of this realization, I created a row-wise program (a series of formulas lined up in a row) which takes an input (in this case, "04W10Y" as an example) and results in an output (the last cell/variable in the program). In effect, the program split the value down the center ("04W" and "10Y"), parsed the ages into readable text ("04 weeks - " and "10 years"), removed the trailing zero if there was any ("4 weeks - " and "10 years"), and concatenated the two back together ("4 weeks - 10 years").
Quite neat, really. Oh, and never make me build IF-ELSEIF-ELSE ladders in Excel. Ever. I had to do that above, one rung each for D, W, M, and Y. Really, try doing that when the IF()'s form is IF(condition, if-true, if-false). The next rung in the IF would go in if-false, instead of being something relatively readable. I had to build the weekday converter (i.e. "MON" to "Monday") in a notepad file (with the godsend of indentation) to do it properly.
Hmm, I spent too long on Excel - it's growing on me, especially with my epiphany. I also did some nifty stuff with Apache's mod_rewrite. Given that it can't really do complex replacements like with if-else ladders to change stuff, I created one rewrite rule which captured state codes ("daycare.com/CA") and translated it to a daycare_listings.php?state_$1 page. The PHP page took that value, expanded the state code to the full state name, and redirected to full_state_name_daycare_listings.html.
Writing production code is definitely interesting.
Cripes 2.0
Alright, cripes! I'll talk about Cripes now (please, don't laugh at that). I'm going to figure out a resource manager somehow, because of this post by pulpfist. I'm surprised it didn't occur to me earlier, considering how I already knew that having a Corner with every state it could be, and having probably fifty of those corners, was slow. So that's going in as part of my Entity refactoring, probably near the beginning.
I also want to abstractify and extend my Entity class out somehow, into Mobs and Missiles. Obviously the EntityMap will have to handle both, and so it'll work with Entity pointers instead of owning the actual objects. My EntityFactory will also need to be fixed up to handle the construction of both. I'm thinking of working with some kind of pluggable factory design, here.
After all that's done, I'm going to see what I can do about collision detection. That'll be fun!... well, I had problems with it in Cripes 1.0, but hopefully I've done well enough this time around.
Comments please? I mean, I just wrote all this out for you! *beg*
| |
| Wednesday, February 18, 2009 |
 Cripes 2.0: Entity refactoring continues |
Posted - 2/18/2009 4:53:04 AM | Not much to say. Entity refactoring is breaking stuff that I have to fix as I go, and I'm about ready to just drop it and take a break and/or keep tweaking the rest of it.
In other news, something I did along the way sped my maze generator by about 20 ticks. I'm getting consistent readings of 180 to 205 ticks, rather than the 200+ I was getting before. Interesting.
| |
| Tuesday, February 17, 2009 |
 Some small fixes/changes |
Posted - 2/17/2009 5:33:51 AM | I'm giving myself some time to think about the next big task to tackle (collision detection... no pun intended!), so today I went through the code and made some minor changes, mostly cleanup. I'm also thinking about how I want to handle missile Entities, like the bullets fired by the player and the snipes. I'm thinking I'll have to refactor the Entity code a bit, but it shouldn't be too bad.
Done-Today List- "Pause" on lost focus
To do this, I compare GetTopWindow(NULL) with GetConsoleWindow() in the main loop. If they're not the same (ie. I don't have focus), I call keyboard.Clear() to clear the Keyboard state. I also flush the console input buffer. This is OK because the only messages that come in are keyboard, mouse, and buffer resize events. It also lets me WaitForSingleObject() on the input buffer, so I know exactly when I regain focus.
- Renamed EntityLoader to EntityFactory
Well, it is!
- Changed my movement code very slightly
Changed from if (keypress[VK_*]) {++y;} y = ModFloor(y, min, max); to y = ModFloor(y+keypress[VK_*], min, max);. Since keypress[VK_*] returns a bool, and a bool converted to an int is 0 or 1, it works nicely and is more compact.
I'm such a slow worker :(
EDIT: Also:
- Replaced my CHAR_INFO buffer copy loops with a simple memcpy() call.
Because I wanted to. It seems like it'd be faster to just copy a block of memory at once rather than copy each individual element and its members over the course of a loop, anyways.
| |
| Sunday, February 15, 2009 |
 Snipes 2.0: Maze generation design and code |
Posted - 2/15/2009 1:34:45 AM | First: Happy Valentines Day!
Second: I spent my free time over the past couple days trying to speed up my maze generator. I failed, and I sort of understand why, but I'm still a little confused. When I started, I noticed that when I was randomly selecting a wall to break down, if the wall I was breaking down had a visited room on the other side, I grabbed another number ad nauseum until the next wall led to a new room. I figured that the wasteful looping could be optimized out. Well, my "solutions" inevitably involved generating a list of "good" walls each iteration, or removing the entries for the "bad" walls from the surrounding rooms. Not only did it actually slow down the generation (by about 100+ ticks, according to a GetTickCount() delta), most of the time it didn't even work! So, I'm just going to leave it, no matter how much I want to speed it up. At least it works.
Well, that said, I do want to explain how it works. Maybe one of you will see something that can be sped up that I missed. Here's the source code for MazeGen::Generate(), with comments explaining what I'm doing.
struct cell_info
{
cell_info* next[4];
cell_info* last_visited;
bool visited : 1;
bool top_wall : 1;
bool left_wall : 1;
};
std::vector<Entity> MazeGen::Generate(unsigned width, unsigned height) const
{
cell_info* cells = new cell_info[width*height];
for (unsigned y = 0; y < height; ++y)
{
for (unsigned x = 0; x < width; ++x)
{
unsigned i = x+y*width;
cells[i].last_visited = NULL;
cells[i].visited = false;
cells[i].top_wall = true;
cells[i].left_wall = true;
cells[i].next[0] = &cells[x+ModFloor(y-1, 0, height)*width];
cells[i].next[1] = &cells[ModFloor(x-1, 0, width)+y*width];
cells[i].next[2] = &cells[ModFloor(x+1, 0, width)+y*width];
cells[i].next[3] = &cells[x+ModFloor(y+1, 0, height)*width];
}
}
cell_info* current = &cells[0];
do
{
current->visited = true;
if (current->next[0]->visited && current->next[1]->visited &&
current->next[2]->visited && current->next[3]->visited)
{
current = current->last_visited;
continue;
}
unsigned which_wall;
do
which_wall = rand()%4;
while (current->next[which_wall]->visited);
switch (which_wall)
{
case 0:
current->top_wall = false;
break;
case 1:
current->left_wall = false;
break;
case 2:
current->next[which_wall]->left_wall = false;
break;
case 3:
current->next[which_wall]->top_wall = false;
break;
}
current->next[which_wall]->last_visited = current;
current = current->next[which_wall];
} while (current->last_visited != NULL);
for (int i = 0; i < rand()%5+25; ++i)
{
unsigned which_wall = rand()%2;
unsigned index = rand()%(width*height);
if (which_wall == 0)
{
if (cells[index].top_wall)
cells[index].top_wall = false;
else
--i;
}
else
{
if (cells[index].left_wall)
cells[index].left_wall = false;
else
--i;
}
}
std::vector<Entity> walls;
for (unsigned y = 0; y < height; ++y)
{
for (unsigned x = 0; x < width; ++x)
{
unsigned cornerflag = 0;
unsigned i = x+y*width;
if (cells[i].left_wall)
{
Entity wall = loader->Construct("VertWall");
wall.X = x*8;
wall.Y = 1+y*6;
walls.push_back(wall);
cornerflag += 8;
}
if (cells[i].top_wall)
{
Entity wall = loader->Construct("HorizWall");
wall.X = 1+x*8;
wall.Y = y*6;
walls.push_back(wall);
cornerflag += 2;
}
if (cells[i].next[0]->left_wall)
cornerflag += 4;
if (cells[i].next[1]->top_wall)
cornerflag += 1;
if (cornerflag != 0)
{
std::string cornerID = "Corner";
cornerID += boost::lexical_cast<std::string>(cornerflag);
Entity corner = loader->Construct(cornerID);
corner.X = x*8;
corner.Y = y*6;
walls.push_back(corner);
}
}
}
delete[] cells;
return walls;
}
| |
| Friday, February 13, 2009 |
 Cripes 2.0: MazeGen fixes/changes |
Posted - 2/13/2009 2:39:35 PM | So far today I've tweaked the MazeGen algorithm a bit, making it remove walls randomly and speeding it up. I still have some optimizations I'd like to try, but it's loading almost immediately now, instead of taking upwards of two seconds.
First, I added a for loop that randomly removes a random amount of walls (between 25 and 30 seems to be perfect). This didn't help much with speed, but it definitely made the maze more fun to run around. Before this change, it was really just one long corridor with the occasional small branch. Now, it's a whole sprawling complex of hallways, junctions, and dead-ends. Awesome.
I also changed how I was storing and handling CornerWalls. On a hunch, I guessed that what was really slowing the generation down was how every CornerWall stored a sprite for every kind of corner wall that could be made. I fixed this by splitting the CornerWall's fifteen sprites into fifteen Corner# entities, each with just one sprite. It sped up immediately! Good thing the other walls (HorizWall and VertWall) only need one frame.
Should I upload a demo so everyone can run around the mazes themselves? It's pretty nifty. ;)
| |
 Cripes 2.0: Maze Generation DONE! |
Posted - 2/13/2009 5:26:13 AM | I'm practically dancing in my seat right now! :D I just finished the maze generation. I basically have a MapLoader class with a pure virtual function called Generate(), which takes a width and a height, and returns a vector of Entities (presumably the walls). I derived a MazeGen class from it, which implements a maze-building algorithm and then creates and positions wall and corner entities based on the algorithm results. I haven't added in any code to randomly remove walls, so it's a basic maze right now. Still, I just had to take a screenshot!

Wooooooooooooooooooooo. :D
It takes over a full second to load each maze, unfortunately, because I have so many corner entities. I'm hoping that my random-wall-removal will help mitigate that, but we'll see.
EDIT: Matched the cell sizes to the original Snipes. Each is 8 chars wide, 6 chars high. I wasn't expecting the discrepancy... Also, made my walls blue. So so so much better. It looks almost real! Now for collision detection... well, tomorrow. *yawn*
| |
| Thursday, February 12, 2009 |
 Cripes 2.0: Maze generation (to-do) |
Posted - 2/12/2009 9:54:49 PM | Alright, I'm getting tired of running around on a field of color that makes my eyes bleed. I think Cripes 2.0 is far enough along that I can implement a map generator relatively easily, so I'm going to do that before I go on. It'll make testing collision detection (which I'll implement after this) a bit easier too. Well, to lengthen this post out a bit I'll post my ideas on how I'm going to do this.
The map is going to be generated like a typical bounded 2D maze, except I'm going to open the outer walls up for passage-carving as well, so it'll be able to loop around like it's supposed to. Once every cell has been "touched" I'll run through and remove a certain amount of walls randomly. After that, I'll pass over the result and create wall Entities for each cell, and place them accordingly.
The internal format I'll use to generate the map will be a 2D array of ints, each starting with a value of 3. I'm making each cell responsible for its left and top wall, which lets the cells tile without any extra information or overlap. 0 means no walls, 1 means left wall, and 2 means top wall. Obviously, 3 means both walls. As the generation algorithm progresses, it'll subtract a number from each cell to represent the carving out of the corridors. And after it's done, it shouldn't be very hard to ManufactureEntity("HorizWall"), ManufactureEntity("VertWall"), and ManufactureEntity("CornerWall") (for where walls meet).
Unlike most of my other plans, I put a fair amount of thought into this, and I really like how it sounds. The idea I had before about making the walls Entities, I really like that one. :) We'll see how it goes!
~Jonathan
| |
 Cripes 2.0: EntityLoader |
Posted - 2/12/2009 6:26:49 PM | Progress! I've created an EntityLoader class which can load entity definitions from a file with LoadFile(), and return a ready-made Entity to calling code with ConstructEntity(). To try it out, I hand-wrote a Player entity definition into a file and had EntityLoader load it, then I passed the result of loader.ConstructEntity("Player") to the entity map's AddEntity method. Works great! ^_^
EDIT: Class layout updated. Also, I added a link to it at the top of the journal, by the color-coded project name.
| |
Page: 1 2 »»
All times are ET (US) |
|
OPTIONS
Track this Journal
ARCHIVES
August, 2009
July, 2009
March, 2009
February, 2009
December, 2008
November, 2008
October, 2008
August, 2008
July, 2008
June, 2008
May, 2008
April, 2008
November, 2007
July, 2007
June, 2007
May, 2007
April, 2007
March, 2007
February, 2007
December, 2006
October, 2006
September, 2006
July, 2006
May, 2006
April, 2006
|