User:Sdw/Journal
From GDWiki
Contents |
[edit] --sdw 14:16, 24 Sep 2005 (EDT)
I've recently done some work with polymorphism. Now, I had always known generally how inheritance worked but never found a practical way to use it. I could not be more wrong, as I've found how great polymorphism is for structuring games. A single container can be used to store all the various entities of the world.
What I've done is created a class to represent the basis of all entities in my world, called BaseEntity. Inside this class I store all the various properties any regular object in the game would have, such as position, dimension, texture, yadda yadda yadda. The magic of polymorphism starts when I create virtual functions like Think, Draw, and Physics which will control every possible entity. In each derived class from BaseEntity will be the same Think, Draw, and Physics function to be called to make each entity a bit more distinct. While the player entity would contain input information in its Think function, a monster entity would contain AI in its Think function. And to control all of it I iterate the entity container and call the three simple functions.
For all entities in container entity.Physics() entity.Think() entity.Draw() loop
And that's all that needs to be done. Simplified.
[edit] --sdw 19:35, 30 Dec 2004 (EST)
I came across an article on gamedev about bitwise operations and what you can do with them. I never really knew for sure what they did exactly, or whether they were very useful at all. But when the articles began explaining how you can extract RGB values by using these operations, it spark an interest with me. After reading it I immediately thought of a great way to implement it. Now instead of putting a big UDT for each tile into my map file, I use a single byte. By using the bitwise junk I can pack everything I need into a byte. Byte has 8 bits, so I use the first 2 for the tile x surface coordinate, 3 more bits for the y, and then 1 bit for the walkable value (I'm thinking of removing). So I'm really only using 6 bits of this byte. So 2 bits give me a maximum of 4 different values (2^2=4), 3 bits give me 8 different values (2^3=8), and 1 bit acts as boolean. The reason I can do this is because of how my tiles are set up in each image. Each tileset is 256x256, each tile is 64x32. This means 4 tiles fit across the tileset horizontally (the 2 bits multiplied by 64), and 8 down the tileset vertically (3 bits multiplied by 32). It works great so far, and really but down the file size.
So the bits are used like this:
00110000 <- X coordinate 00001110 <- Y coordinate 00000001 <- Walkable
So I just bitshift each byte accordingly and then combine them all using OR. Then when I'm ready to extract the values (when I read from the map) I use the appropiate mask to AND the values out and then bitshift them back to normal. Since C wants hex I had to find a binary->hex converter. Now thinking about all the repetition that's going to be in the map files there's room for compression.
[edit] --sdw 16:58, 22 Dec 2004 (EST)
Modified/Improved my game loop structure tremendously. Was wandering the web looking for good isometric collision tips when I came upon this whole loop structure thing. At first glance reading through it I thought it was crap and most of it was pretty much not needed. But I tried it out and I likes it. Nice and smooth movements makes sdw a happy person, even when using SDL_GetTicks() which I've been meaning to change. Aside from that I've been building an all-purpose map editor to end all map editors, forever. The only problem with that is that I made it in VB :\ So I may have to find another cross platform RAD for building all my game utilities. Python looks like a suitable candidate, I could pick up on it quickly and begin porting. Would suck if the speed differences between the languages were noticeable.
Next, but really first, on my mind is isometric collision detection. I've been thinking about putting circles around sprites for all the collision checks to make a very clean, good looking collision detection system. But that looks like too much work so I'm just going to use something like line intersection. But that sounds like too much work too :( I'd better do some more research on the topic before getting in deep and later on deciding I should have taken a different route.
[edit] --sdw 22:26, 9 Dec 2004 (EST)
Ok, so I haven't gotten a lot done in the way of my font class I had planned. Well, I finally got to work today and did some coding. Technically I started yesterday, but as soon as I sat down my little nephew came over to pick a fight with me. So I had to go teach him a lesson. After getting my ass whooped I decided to retreat to my computer. But like usual I started up a game of CS:S instead. So today I had no distractions and actually felt like coding and writing into my wiki log. Here's what I came up with:
class CFont {
public:
CFont(const char *, int, int, int, int, int, int);
~CFont();
void SetPosition(int, int);
void SetColor(int, int, int);
void SetText(const char *, ...);
void Draw(void);
protected:
bool isSet;
char text[256];
TTF_Font *font;
SDL_Color color;
SDL_Rect position;
int w;
int h;
GLuint texture;
};
That defines it, here's the real mechanics:
int nextpoweroftwo(int x) {
double logbase2 = log(x) / log(2);
return (int)round(pow(2,ceil(logbase2)));
}
CFont::CFont(const char *txt, int size, int r, int g, int b, int x, int y) {
if(!(font = TTF_OpenFont(txt, size))) {
Log("Error loading font: %s", TTF_GetError());
isSet = false;
return;
}
color.r = r;
color.g = g;
color.b = b;
position.x = x;
position.y = y;
isSet = true;
return;
}
CFont::~CFont() {
glDeleteTextures(1, &texture);
TTF_CloseFont(font);
}
void CFont::SetPosition(int x, int y) {
position.x = x;
position.y = y;
return;
}
void CFont::SetColor(int r, int g, int b) {
color.r = r;
color.g = g;
color.b = b;
SetText(text);
return;
}
void CFont::SetText(const char *fmt, ...) {
SDL_Surface *initial;
SDL_Surface *intermediary;
SDL_Rect rect;
va_list ap;
//Get out arguements and slap them onto text[256]
va_start(ap, fmt);
vsprintf(text, fmt, ap);
va_end(ap);
initial = TTF_RenderText_Blended(font, text, color);
/* Convert the rendered text to a known format */
w = nextpoweroftwo(initial->w);
h = nextpoweroftwo(initial->h);
intermediary = SDL_CreateRGBSurface(0, w, h, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
SDL_BlitSurface(initial, 0, intermediary, 0);
glDeleteTextures(1, &texture);
/* Tell GL about our new texture */
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, 4, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, intermediary->pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
SDL_FreeSurface(initial);
SDL_FreeSurface(intermediary);
position.w = initial->w;
position.h = initial->h;
if (!isSet) { isSet = true; }
return;
}
void CFont::Draw(void) {
if (!isSet) { return; }
glLoadIdentity();
glTranslatef((GLfloat)position.x, (GLfloat)position.y, 0.0f);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_ONE);
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 1.0f); glVertex2f(position.x, position.y + h);
glTexCoord2f(1.0f, 1.0f); glVertex2f(position.x + w, position.y + h);
glTexCoord2f(1.0f, 0.0f); glVertex2f(position.x + w, position.y);
glTexCoord2f(0.0f, 0.0f); glVertex2f(position.x, position.y);
glEnd();
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glDisable(GL_TEXTURE_2D);
glFinish();
return;
}
So there wasn't a lot that could be done in the way of optimization as long as the text I was drawing changed every frame. But for static text like the letters "FPS" themselves I used a separate instance of the CFont class. Thats as optimized as I could get it, except that I could probably use a smaller font size and such, but I'm stubborn. This font class is going to be wrapped up inside a nice GUI package I'll be making in the not-so-soon future. So everything still seems to be pretty modular and such, as opposed to KoFo which I have hardcoded down to every pixel that's displayed.
So what's next? I think I'll just manually draw each piece of text without using some sort of array or linked list. I think I can keep it simple that way, to just bundle it in with the GUI drawing loop and stuff like that. Yeah...
Haha, reading over my code I had a hard time figuring out whether bool isSet is even doing anything productive. Well, it's suppose to make the font not draw itself if it wasn't able to initialize itself properly. In that case isSet should prolly be public. Oh well, I wait till shit happens and I'm forced to change it.
[edit] --Sdw 22:15, 29 Nov 2004 (EST)
So I'm browsing GameDev and I find someone's posted an easy way to get OpenGL to work with SDL_ttf. Well, I tried it out since I need this type of feature and it didn't work so well. I had to fix up the code a bit, but it works now:
int nextpoweroftwo(int x) {
double logbase2 = log(x) / log(2);
return (int)round(pow(2,ceil(logbase2)));
}
void SDL_GL_RenderText(const char *fmt, TTF_Font *font, SDL_Color color, SDL_Rect *location, ...) {
SDL_Surface *initial;
SDL_Surface *intermediary;
SDL_Rect rect;
int w,h;
GLuint texture;
char text[256];
va_list ap;
va_start(ap, fmt);
vsprintf(text, fmt, ap);
va_end(ap);
/* Use SDL_TTF to render our text */
initial = TTF_RenderText_Blended(font, text, color);
/* Convert the rendered text to a known format */
w = nextpoweroftwo(initial->w);
h = nextpoweroftwo(initial->h);
intermediary = SDL_CreateRGBSurface(0, w, h, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
SDL_BlitSurface(initial, 0, intermediary, 0);
/* Tell GL about our new texture */
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, 4, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, intermediary->pixels);
/* GL_NEAREST looks horrible, if scaled... */
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
/* prepare to render our texture */
glLoadIdentity();
glTranslatef((GLfloat)location->x, (GLfloat)location->y, 0.0f);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_ONE);
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 1.0f); glVertex2f(location->x, location->y + h);
glTexCoord2f(1.0f, 1.0f); glVertex2f(location->x + w, location->y + h);
glTexCoord2f(1.0f, 0.0f); glVertex2f(location->x + w, location->y);
glTexCoord2f(0.0f, 0.0f); glVertex2f(location->x, location->y);
glEnd();
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glDisable(GL_TEXTURE_2D);
/* Bad things happen if we delete the texture before it finishes */
glFinish();
/* return the deltas in the unused w,h part of the rect */
location->w = initial->w;
location->h = initial->h;
/* Clean up */
SDL_FreeSurface(initial);
SDL_FreeSurface(intermediary);
glDeleteTextures(1, &texture);
}
I had to make OpenGL draw it with black colorkeyed out and also gave it one of those va_list arg list thing-a-ma-jigs. It seems to work, but it's fairly slow. FPS rates dropped from 97-99 to varried 79-95, mostly upper 80's low 90's. This is not tested on Linux as of yet. I suspect that creating and destroying surfaces like this every frame is not too efficient, therefore I'm prompted to implement this in a more modular way to fit in nicely with my current work. Here in lies the problem, I don't know where the hell I'm going to stick these textures and fonts. Furthermore, I need to chop up the above function into smaller ones. Right now I'm thinking I'll have 1 function load the surface, another does the drawing, and the final destroys the surfaces and textures and such. As I type this out it seems like this could all be wrapped up with a fairly simple fonts class. The constructor takes the arguement of the TTF path, the destructor then destroys the texture/surface/fonts/everything. A function inside the class can draw whatever text is fed to it, and the class can also store height, width, last x/y coordinates and such. Seems like it'd work, but I don't feel like messing with it now. The more crap like this I find while on my "coding breaks" just distracts me from my game thinking. So now instead of thinking about sprite class structuring, image orders, map properties, which collision detetction to use, whether to make skins, what default skin should the game use, and where the hell am I going to come up with sounds and graphics, I'm now looking at how I'm going to draw simple text. I realize the situation, take a step back and examine it. I then conclude that there's simply too much to do and so I close down shop for the day and go pop in an episode of Family Guy to help clear my mind of C++ and games in exchange for refilling it with humor.
...then I remember that I was suppose to do my research project today instead of messing with fonts. This sucks. At least I got to write in a journal at my own will for the first time :D

