Intel sponsors gamedev.net search:   
Journal of dbaumgartBy dbaumgart      

I'm a freelance 2d artist; My portfolio can be seen here.

Feel free to contact me if you have any questions.

My external blog: artscum.wordpress.com

Wednesday, November 28, 2007
The GUI is interfacing with the rest of the game. I just gotta say, it is exciting when a lot of little things just come together and things start to work as you imagined. It's a moment where a lot of previous slogging pays off.
So here's the latest screenshot of Isostrat in edit mode:



Now I'll explain what's going on.

Program Icon


If you look there in the upper left corner, I made an icon! Neat! Yeah, this is quite trivial, but I finally stumbled upon the entry in the Pygame documentation about changing the icon so I figured it'd be a nice touch to have something other than the default.
It'll have to end up being something a lot more clever than a little tower on a gold square, for sure.

Game modes


The major states of the engine (Menu, Edit, Game) all more or less work and display what they're told to. Switching back and forth works (except from the "menu" because that's just a box right now and has no buttons, but uh, I could add some buttons without trouble)... ahem, and GUI panels and elements are successfully arranged and re-generated as asked. I'm actually only showing the edit mode because that's the only place where anything is happening so far.

Edit Mode: Info Window


The little window in the lower left displays the current tile's coordinates (if any) and the current mouse position relative to the window. This demonstrates 1: querying of information stored in the isomap and 2: alteration of text elements on-the-fly.

I admit that the hoops I jump through to do this aren't as elegant as they should be because I'm (almost) directly accessing the text elements when I ought be using the relay system. Added to TODO.txt.

Edit Mode: Terrain Painting


A terrain type can be selected from the generated list of buttons in the terrain painter panel (... note to self, rename it "terrain palette"), then that terrain can be 'painted' to the isomap using single clicks or mouse dragging. Neat!

One small issue that came up was the need for a tile to tell all of its neighbors to update their graphics when it is changed, otherwise the terrain transitions mess up. Obvious.

Also, I need to implement scrollbars for panels for when the number of terrain types becomes too numerous to fit into the current panel. I half-completed the slider then went on to do all the fun stuff because I was getting frustrated -- you can see the 'gutter' of the scrollbar on the right side of the terrain palette panel (it is very slightly different from the rest of the border trim), but it has no scrollbar 'thumb' yet.

This requires some addition to panel code to allow offsetting of elements in panels as the (non-existent so far) panel viewport is moved by the scrollbar. It's a good thing I figured out all the troubles that could arise with this kind of thing in making the map; should be a snap.

New Font: kompress6x16


Made a new compressed font because I was having trouble fitting text into all the new windows. I want to eventually have all original fonts or find a cheap replacement for fixedsys. And I'm finding (after my crappy smalltext5x8 font) that font-making is a real skill. You can't just slap a few pixels into what you think are letters, it's really quite subtle to weight and distort characters for maximum readability. And that zero looks like a mutated eight, I realize.

What next?


I need to make a bunch of placeholder graphics including a pile of terrain types for testing, and all the sorts of natural isofeatures/terraineffects (mountains, forests, rivers, shrubberies) so I can begin implementation of more game-map stuff. I pretty much know what I want for natural features, but I don't quite know how I want to handle 'unnatural' objects like units and cities. I'll tell you right now that it won't be as 'units' and 'cities'.

Also I've got to update panels like I said I would to make them scrollable and to make GUI elements easily accessible from elsewhere in the code.

I almost forgot: saving and loading maps is going to turn into a concern pretty soon now that I'm going to be dealing with maps. I do love how Paradox games has saves which are hundred megabyte text files that you can go in and edit, though this is (perhaps) not an optimal design for this sort of game.

Maybe I'll post a bit about design philosophy now that it's starting to look like I'll actually get toward working on the game itself. What fun!

Comments: 4 - Leave a Comment

Link



Monday, November 26, 2007
I did more digital painting than "honest work" over the weekend, which may show in the (lack of) content of this post. But then is not digital painting honest work? Maybe I'll post pixors when I feel like they're more complete... and relevant. Though they could be called concept art, they don't really feel like the game I'm making at the moment. Or maybe they do. Whatever. Moving on.

It looks like I messed something up


I started today looking to implement scrollbars in panels as well as panels inside panels which, in turn, maybe have scrollbars making scrolls in panels in panels. And so on.

I started by making a slider graphic, because to be successful you have to present an image of success (right?), then I went to modifying the panel-border-graphics-handling class to take care of slider graphics, in addition to the panel border trim it currently nutures. Here's the slider graphic I'm planning to test with:



Harmless looking, right? (Though a bit small, honestly.)

So in doing this I ran into my original code for creating and storing panel border style graphics and found that I could do a number of things there a lot more efficiently. Instead of hard-coding the rects for chopping up all the border style bits, I now store them in a text file. Instead of making a new surface for each little corner and sidebar, I create subsurfaces from the full border style "skin". Somewhere in doing this (and more!) I made a terrible mistake. See here:



Somehow, I think, the lightest color, or perhaps the one that most resembles colorkey green, in any image gets drawn as black, and the borderstyle which is supposed to have a colorkey-green fill, which should be drawn as transparent, is also drawn as black, which is why the isometric map grid cannot be seen.

What could it be?


It may have something to do with the 8 bit .pngs I'm using for graphics and the 8-bit Pygame surfaces which are made from said .pngs being used instead of 8-bit pngs being made to 8 bit pygame surfaces which were then drawn onto (what I assume) were 24-bit pygame surfaces ... with a colorkey set. Or something.

May it is something to do with palettes -- If I set a colorkey on an 8-bit surface which does not contain the colorkey in its palette, does it just choose some other color, making it appear as black? But the colorkey doesn't work even on the 8-bit graphic that has the colorkey in it, or even a 24-bit graphic maybe. Or something.

A quick change back to the old method of creating new surfaces for all the little bits of panel border graphics doesn't fix things. Maybe it's my 'new' image loading function which I switched everything to; But the new function is barely different from the old.

Ach, this sort of thing bugs me. I go try to make some cool feature then something else explodes on me that I don't really understand (which is the real problem). That which I don't understand is how the heck Pygame shuffles and converts (or doesn't) 8-bit vs. 24-bit surfaces / loads and handles files of said bit depths. It's times like this I wish that Pygame was better documented, so that I could just look things up instead of taking the empirical route to solving problems.

C'est la vie. More testing!

Edit: Ok, I re-read some Pygame documentation. The random insane black pixels don't happen now, but I'm having trouble getting the colorkey alpha working over the main map for some reason. Hrmm...

Edit2: Ha. Got it. So when I make a panel's graphic from the border-style graphic set, I draw to a new surface filled with black. So: If I draw an image of the colorkey color with the colorkey set onto an image filled black, what color will I get? Black, of course, duh. The problem I've found is that I am more consistently setting the colorkey on images, perhaps a bit before I ought to be.
It wasn't Pygame being stupid, just me. I love you Pygame. Let's never fight again.

Comments: 0 - Leave a Comment

Link



Friday, November 23, 2007
A certain logic has come to mind which suggests that for me to be able to test IsoFeatures (have they quads or not) and terrain extensively, I shall need to be able to manipulate them on the fly rather than have them hard-coded. This suggests, in turn, that I make my damnable GUI work, and by work I mean affect the map. So!

Sundry GUI Elements


With apologies to Karl Marx, here's a screenshot of me fooling around with making new border styles and testing image elements (of original art this time) and text GUI elements:



I finally figured out what was wrong with the text renderer:
Instead of building a text surface based on the width of the longest line of text, it built a text surface based on the longest word. Ok.

Then, at the stage when a list of rows containing words was regurgitated to make the previously mentioned text-surface-width for rendering, the space that the spaces between the words would take up was not being accounted for. So now I add one character-sized space between the words, starting from a value of -1 (to remove the space for the end of the last word). I had to go through a lot of nonsense to get this to make text surfaces that would align in my buttons properly.

Speaking of buttons, I recoded how they are made and rendered so they'd be more in line with the simpler style of the other GUI elements. It all works now, HA HA HA! (That's triumph you hear.)

Edit/Debug/Developer Mode


I need an editor/debugger/developer mode for viewing and manipulating the isometric maps of the game, so here I am making one:



As you can see, it's just barely started. The code is highly inelegant so far, but I mean to enable the ability to display all kinds of information in dynamic GUI elements. The experience of building the editor should make the in-Game GUI a snap. (I read on a post-mortem on Gamasutra that some development team would always give the new programmer the job of making the editor so that they'd learn the game engine through-and-through while building a system that didn't have to be all that stable. I begin to understand why many game editors are so clunky and unstable!)

The terrain-type buttons in box on the left are built on-start from the master dict of terrains. I hope to greatly automate the process of doing this with some kind of "build a batch of buttons using this given list or dict" function.

There's lots to do and lots of functionality to add. I'll probably end up making panels movable, stackable, and collapsible while I'm at it, and implement the ability to change the game resolution. But don't hold your breath yet!

Comments: 2 - Leave a Comment

Link



Wednesday, November 21, 2007
I'm making a foray back into GUI land because my ideas about implementing the contents of tiles and sub-quads within tiles (and so forth) needed more time to simmer/fester before I feel comfortable coding them. That, and I felt like doing some art, so I did digital painting rather than coding yesterday.

What I should do is write down what I think I need these things to do, then classify them into um, classes, then I'll be ready to build them. So I'll do that.
Tomorrow.

Today... Today I broke my code. As in, the program just sat there and didn't do anything. This is the first time that this engine has done that; Neat! I'm pretty sure it has to do with some convoluted set of loops in the text rendering code. I'm considering using the built-in pygame text rendering, but that'd be giving up, and I don't want to give up.

Overturning previous decisions


So I've decided to store panels in lists rather than dicts, with an easy function to find and return a panel by name, if required. (Does this mean that my love affair with dicts is over? Maybe, maybe not. They're still really cool.) My attitude is a bit more open now with regard to what I'll let my GUI do, so I might even make panels overlap, movable, and all sorts of other silly things -- but I'll probably worry about that when/if I make the game work in resolutions greater than 800 by 600, at which point the UI will be flying all over the place and shall need to be handled more... professionally.

Text and Image elements


I (mostly) implemented Text and Image elements for panels. They're basically blocks of text or an image that sits there and looks (or reads) pretty, and they can be changed on the fly. So the Text/Image elements can act as animations or information readouts. This may well come in handy when debugging all the map handling and the workings of tiles.

Problem is, as mentioned, the Text element broke itself on the text rendering code. The thing with the text rendering is that it may take in an optional rect in which to fit a block of text. Up to this point I have not been using this option, the text on the buttons has been rendered as a single and very short line, but now that I am using the optional rect, it has turned out that it's going to need a bit of effort to get working properly because it never worked in the first place. Woo.

A bunch of random GUI things


I now store all sub-elements of panels (Buttons, TextElements, ImageElements) in a single list, with appropriate function calls to return, say, only the buttons, and all the click-handlings and mouse movement effects work with this, though I think it could use some touch-ups in a spot or two.

I may want to be able to put panels-within-panels in the element list, but I've not made my mind up on whether to do this. Doing this might confuse mouse-click and mouse-movement code, but it should be simple enough to account for it: the panel will just call its sub-panels when it is itself called.

I also added the ability to load "Layouts", which are basically files that define a set of panels. I'll have a Layout for each gamestate: Menu, Game, and Edit. To switch the GUI to a different state should just be a matter of calling the correct Layout file.

To do:


- fix text rendering
- test image and text elements thoroughly, and make them work properly
- implement panels-within-panels, maybe
- do a once-over rewrite of all the messy bits in the GUI code

Edit: Here's a screenshot of the Text and Image elements in operation, with apologies to George Orwell:





Comments: 0 - Leave a Comment

Link



Monday, November 19, 2007
I'll start by defining my terms.

Tiles are the unit of the grid this game is built on both in terms of data and graphics rendering.
Quads (as I'm going to call them) are the four units in a quartered Tile. They certainly affect rendering, especially of terrain transition fringes, but may also contain data (I don't have the tile/quad thing fully built yet).

A word on dimensions


I originally used tiles which were 100 by 50 pixels for my own convenience but found, as soon as I went to implement Quads, that you can't place 50 by 25 pixel quads in a tile very cleanly: If 50 divided by 4 is 12.5, then where the hell do I draw the topleft of my West and East quads in a tile?

So I switched to using 128 by 64 pixel tiles. One elegant quality of using powers of two is that one can keep dividing and dividing them and there'll always be an int waiting for you at the end. That, and I should just suck it up and make my graphics based on powers of two because that's what anything run by a 3d engine uses, and for good reason. When I make GL-Isostrat, my graphics will be nearly ready to go.
Here's a colorful diagram to illustrate drawing and quartering tiles:



And here's half a sheet of full-sized dirt terrain tiles in 128 by 64:



So why quads, anyway?


To make transitions from one type of terrain to another, games are often content to have an image for every direction of "fringe" outward from a particular terrain type and all permutations thereof you could image. This works out to be about, um, about 32 unique images, though that could be cut down to 16 or so if the renderer combines transition images, and, granted even fewer if you did some silly tricks with flipping. And this was how I was going to do things. So I was whining about it to Marcel when he, in his optimizational leetness, told me that I ought to make the problem smaller by dividing it into pieces.

He did some of his crazy I-have-a-degree-in-math math and came up saying that I could get away with just doing 12 types of transition graphics. Here's a compass like thing to show how I'm going to talk about directions and quads:



See, I only need to consider each corner/quad separately, and they each have four possible terrain states. For example, to use "north" as a base (just rotate directions for the cases of other quads), the fringe necessary is: from NW, from N, from NE, and from NW,N,NE OR NW and NE (which amounts to just a check of NW and NE, the state of N is irrelevant). Or fringe from no direction, which is nothing of course. Again, the states:
NW, NE, N, NW and NE

But doesn't that add up to 16 transition graphics? Ah, it doesn't have to! I can re-use certain graphics for two quads. For example, the North quad's NW and the West quad's NW are basically the same, so all around it comes to 12 unique graphics. With flipping and rotating it could probably be 6, but that'd only work for flat terrain that wasn't drawn lit from any direction.
Here's a full sheet of 64 by 32 pixel transition quads for use over tiles:



By dividing tiles into quads I can also build regular tile graphics out of quads to make, for example, forests and towns or whatever. For example, here are some house tiles that would be semi-randomly assembled to make unique town tiles:



Other graphics for larger features would need to use the full 128 by 64 tile, of course.

So how does it work?


I'm not really up to posting a full explanation. If anyone really needs to know, they can ask. And I've still be tweaking the logic of it all to make it work.

But, in short, there are a bunch of definition files and lots of checking which is next to what to find out what of a random selection should go where. Terrain transitions also need to be layered in the correct order by a somewhat arbitrary defined terrain "height" value -- and, this in consideration, layered with multiple types on the same quad in instances of there being more than two terrain types next to one another as well as for shorelines (because the shore is an "innie" transition that is drawn into water tiles rather than an "outtie" transition which is drawn over adjacent tiles). Anyway, it was a lot of work.


I've pretty much covered my past work and caught up to my present state of progress so I will be updating less often I have been. I'll try for Monday/Wednesday/Friday updates rather than once every weekday.

Comments: 3 - Leave a Comment

Link



Saturday, November 17, 2007

The Video class


It may not look like much, but it is very important.
I was getting terrible slowdowns after very little graphical activity in all of my Pygame projects until I figured out that I was not capping my rate of updating video! As soon as I made the max refresh rate a quite decent 60 frames per second instead of as-quickly-as-possible, my project's performance picked right up. The response from just re-centering the map around picked up phenomenally.

The problem was that for every cycle of the game loop -- every data update cycle -- I was also re-rendering the entire screen. This made control sluggish, and would have made everything else painfully slow if I had been doing anything else. So I used a Timer class to only update the screen when a given number of millisecons have passed. At the moment, thats 25, which means I'm getting 40 FPS: ( 1000ms in a second / 40fps = 25 milliseconds). I'm also using a series of flags in various places that halt recalculation/re-rendering of certain objects if they have not been changed.

Here's the video class code:

import pygame
from util import config
from constants import ORIGIN
from util import timer

class Video:
	def __init__(self):
		self.screen = pygame.display.set_mode( config.get('windim'))
		pygame.display.set_caption(config.get('wincaption'))
		self.game = None
		self.msPerFrame = 25 # milliseconds per frame
		self.delta = 0

	def imbue(self,game):
		'' pass in the game object to be used ''
		self.game = game
		
	def update(self):
		# set fps; don't redraw more than necessary
		self.delta += timer.delta
		if self.delta >= self.msPerFrame:
			self.delta -= self.msPerFrame
			if self.game:
				# draw gui panels
				self.screen.blit( self.game.getRender() , ORIGIN )
			# then update the big display
			pygame.display.update()




The imbue is sort of a reference to Diablo 2. Heh heh. David Osborn and I first used it in Procyon so that we wouldn't have to create inter-dependant classes in only a particular order. This way we could do it any way we liked, then "imbue" them with each other afterward.

The Timer


Again, this is basically ripped off from Doc Osborn's Procyon timer function, though I used ints instead of floats because Taskmaster Marcel says that floats are Bad. I'm going to be using Timer to coordinate the framerate of animations and such. Doc O. (no, I can't type his name the same way 3 times in a row) had a cool idea to scale the delta by a float so that apparent game speed could be varied by a percentage, but it isn't really applicable in a strategy game.

Here's the code:

# timer
import pygame.time

class Timer:
	'' maintain timescale ''
	def __init__(self):
		self.delta   = 0 # amount of time passed since last frame
		self.elapsed = 0 # amount of time passed since beginning of game
		self.clock = pygame.time.Clock()
	def reset(self):
		self.update()
                self.delta = 0
		self.elapsed = 0
	def update(self):
		delta = self.clock.tick() # in milliseconds!
		self.elapsed += delta
		self.delta = delta
		
timer = Timer()




My advice is this: If you're making a Pygame game, don't render on every cycle ... unless you're way leeter than me and know something I don't. Your game will feel much, much faster.

Comments: 0 - Leave a Comment

Link



Friday, November 16, 2007
The problem of finding which tile I clicked on drove me nuts, and I've spent a lot of time working it through so you don't have to. So listen well. Or read, whatever.

Here's the picture to reference:



To start, I'm given the coordinates clicked-on in my game's window (and we assume that it is the game view which was clicked on, not any of the panels). So first I find what the coordinates are relative to the game viewport panel by subtracting the coords of the top-left of the gameview panel from the given click coordinates, so now I have coords relative to the game-view panel. (I am subtracting the x coord from the x coords and the y from the y to get the next set of coordinates, of course).

So I have the mouse coords relative to the game's viewport and I need to convert them to the pixel-position that it'd hit on the full size of the entire isometric mapgrid. Easy: I just add the current gameview's offset from the origin coordinates to the relative-to-the-gameview mouse click coordinates.

The map is a staggered grid, so I can't just find out what tile was clicked on at this point because if I imagined the tiles as squares encompassing their current dimensions (as the tile images actually do), they'd all overlap one another. So: I consider the even rows only as a grid of rectangles (while noting that the row number (or y coord) of any grid position must be doubled because theres an odd row of tiles between each of these even rows I'm working with). So now I want to find out which even-tile grid section the mouseclick falls on.

So I divide the absolute isomap click-coords by the dimensions of tile graphics (in my case, 128x64), multiply the y coord by two (because there are twice as many actual rows than what I'm working with), then I have the coordinates of the evens-only grid tile clicked on. I need to find out where in the even tile grid section I clicked, though, so I take those previous absolute isomap click-coords and modulus them by the dimensions of tile graphics: This gives me the remainder, which is the mouse-click coords relative to within this particular even tile.

Using these relative-to-inside-the-tile coords, I plunk them into a completely separate image that looks like this:



Then I find out what color pixel I clicked on in that image. With that, I know whether I clicked on the center, in which case I've clicked on the current evens-only grid tile, or if I clicked on one of the edges, in which case I select the appropriate neighbor in one of the odd-rows of tiles. If this sets the tile coords outside the tile-dimensions of the isomap, then I know that the click has fallen outside of the map entirely.
Done!



What's all that other crap with the grass and the beach and the terrain transition fringe, you ask? You'll just have to wait for another post to find out!

(I was trying to do some crazy stuff with 4 line equations instead of using a simple selection mask for a long time. It is thanks to this article on gamedev that I'm not driving myself insane anymore. On this topic.)

Comments: 0 - Leave a Comment

Link



Thursday, November 15, 2007

Text files to Dicts/Lists


Here's that shot again of my panel and buttons:



And this is the text file I read to make the above panel and buttons:

topminibar
 type:"panel"
 rect:(0,0,150,50)
 style:"greenTrim"
menubutton
 type:"button"
 rect:(3,3,48,22)
 icon:"Menu"fixedsys8x16"
 style:"greenButton"
 color:"lightcopper"
 target:"game"
 command:"switchstate"
 value:"menu"
foo1button
 type:"button"
 rect:(51,3,48,22)
 icon:"Load"fixedsys8x16"
 style:"greenButton"
 color:"lightcopper"
 target:"game"
 command:"exitgame"
 value:(0)
foo2button
 type:"button"
 rect:(99,3,48,22)
 icon:"Save"fixedsys8x16"
 style:"greenButton"
 color:"lightcopper"
 target:"game"
 command:"exitgame"
 value:(0)
editbutton
 type:"button"
 rect:(3,25,48,22)
 icon:"Edit"fixedsys8x16"
 style:"greenButton"
 color:"lightcopper"
 target:"game"
 command:"switchstate"
 value:"edit"
foo4button
 type:"button"
 rect:(51,25,48,22)
 icon:"????"fixedsys8x16"
 style:"greenButton"
 color:"lightcopper"
 target:"game"
 command:"exitgame"
 value:(0)
exitbutton
 type:"button"
 rect:(99,25,48,22)
 icon:"Exit"fixedsys8x16"
 style:"greenButton"
 color:"lightcopper"
 target:"game"
 command:"exitgame"
 value:(0)



Each button has its position and size within the panel specified as a Pygame rect object in pixels; Tedious, possibly, but I don't feel like messing around with automatic layout generators. Type tells what sort of element is being defined, the icon is either the name of an image or a text string and font name... and color refers to the fonts color, if not the default. The string/font are turned into an image elsewhere, if they're present. style refers to the the border and background graphic set to use, and the target/command/value set refer to what the button does upon being triggered.
Most of these buttons just exit the game, except for one which is supposed to switch the gamestate to edit mode. It, uh, doesn't work yet.

Now for the function which makes it all possible: This function should really be called "makeDictOfDictsFromFile" or something, it takes a text file like the above and makes a dict of dicts with various variables indexed by name, and those variables are converted to an int, float, or string depending on the symbols around them. Code:

def readFile(path):
	'' extract information from file and return in a dict ''
	info = {}
	lines = open(path).readlines()
	for l in lines:
		if l[0] == '#': continue # pound denotes a comment, ignore line
		elif l[0] != ' ': # new entry & new entry's type
			l = l.strip()
			tempdict = {}
			tempdict['name'] = l
		elif l[0] == ' ': # ' ' == dealing with variables in an entry
			value = None
			l = l.strip()
			l = l.split(':',1)
			name = l[0]
			l = l[1]
			#print l
			if l[0] == '(': # then it's a number or series of numbers
				l = l[1:len(l)-1] # remove start+end parentheses
				l = l.split(',') # split at commas
				if len(l) == 1: # then there's just one number
					if l[0].find('.') == -1: # no '.'; is int
						value = int( l[0] )
					else: # is a float
						value = float( l[0] )
				else: # there are a series of numbers; make a list!
					value = []
					for n in l:
						if n.find('.') == -1: # no '.'; is int
							n = int( n )
						elif n.find('.') > -1: # is a float
							n = float( n )
						value.append(n)
			elif l[0] == '"': # then it's a string or series of strings
				l = l[1:len(l)-1] # remove start & end quotes
				l = l.split('"') # split strings apart at quotes
				if len(l) == 1: # just one string
					value = l[0]
				else: # more than one
					value = l
			else: # something is wrong
				print "Error reading ", path
			#print value
			tempdict[name] = value
		else: # something is wrong
			print "Error reading ", path
		info[ tempdict['name'] ] = tempdict
	return info



I also made of these which makes a straightforward list out of the lines in a textfile with types determined by the same syntax. It's useful.

You can somewhat see in my code my style for making something I'm unfamiliar with: comment like crazy. I basically write out in comments what I want to happen, then I go through and make that happen. I was never trained to code by any particular process, but I think this is called writing "pseudocode".

Speaking of not trained properly, I avoided string manipulation for a long, long time and only really learned it right here to do this textfile reading so that I wouldn't have to hard-code everything. Hard-coding is bad! I know many tutorials and teaching methods spend a lot of time on string manipulation (and rightly so, it's important!), but it just looked so damn boring so I didn't do it. But now I am.

Text Rendering


Pygame has a text rendering utility built in, but I thought it might be a bit slow. Actually, it's probably pretty quick and it was just our rendering that was slow (until Doc Osborn figured that out) when we tried to use it in ATTAS. Whatever the case, I got my own text rendering code pretty much entirely from Laura, though I had to understand it to convert it back to using raster graphics from OpenGL.

You know what? I'm not going to write an in-depth explanation of how the text renderer works. It just does. Ok here's a quick version: Fonts are made by cutting up an image of all the characters in a font, then these character images are assigned, by character, spots in a Python dict. The text renderer takes a given string, font, and optional rect then (if no rect) renders the text on a straight line or (if rect) in a rectangle by cutting the string up into words separated by spaces then words into characters. Characters are inserted into a string for a new line of text until the length that'd hit the edge of the rect is reached -- in which case it takes the word-in-progress and sends it to the next line. This output is then taken and rendered to a Pygame surface of appropriate size, which is returned to whoever called it. Done.

The code is pretty messy and I'm not sure if it'd work if I tried to do some of the other things I was meaning to do, like rendering large blocks of text in a defined rectangle. It's untested, is what I'm saying. Theoretically, though ...

...lets look at screw-ups! This is my text rendering failing to work as ordered:



The rendering itself worked, as I discovered through much testing, but I had the x and y dimensions of character images within the font image switched, so when the font image was made into a bunch of little character images (which are later pasted together to make the rendered text), it was made all wrong! And that's terrible.
But it works now, as can be seen in my favorite screenshot of my six happily-named buttons in a panel which is at the top of this post (and in the post two before this one).

So here is an example of a very small test font I made:



It works if I need something written very small, but it isn't that pretty.

Misc.


In the name of efficiency I tried changing my coding font from Courier New to something else, but I couldn't stand it so I'm back ... and I'll never leave you again, Courier New!

I seem to be writing the word "actually" too much, so I will now not write it unless it is absolutely necessary.

My hard drive hasn't failed yet. I moved some fans around and cleaned my computer case out, so it isn't running as hot as it was. Still not holding my breath here, though.

Finally, here's a set of test tile quads for deciduous forest fringe:



I use full green (0,255,0) as the colorkey color because I'd rather stare at green than some ugly color like magenta. And no one ever uses (0,255,0) where (0,254,0) wouldn't work in its place, if things came to that.

Comments: 0 - Leave a Comment

Link



Wednesday, November 14, 2007
Yesterdays post was a bit boring, so I'm going to try to make this one fun with a bunch of code and a couple screenshots.

So here's how I make my isometric map:

def makeMatrix(self,dim,terrain=DEFAULTTERRAIN):
        '' make new isometric matrix in self ''
        self.isomatrix=[]
        for y in range(dim[1]):
            temprow=[]
            for x in range(dim[0]):
                temprow.append( IsoTile((x,y),self,terrain) )
            self.isomatrix.append(temprow)



New Tiles are set into rows and the rows are set into columns to form a big grid. The advantage in doing it the way I have (and how it works with my rendering) is that draw order is implicit. Tiles in the back will be drawn first, tiles in the front will be drawn last.

This next function finds and returns only the tiles that are visible in the viewport, so that I'm not attempting to render the entire map all the time.

def findVisibleTiles(self):
    '' return list of all visible tiles in viewrect (and update their positions, too) ''
    vislist = []
    for row in self.isomatrix:
        for tile in row:
            tile.updateDrawPos()
            tilepos = vector.add(tile.drawpos,self.offset)
            if tilepos[0] >= - TILEDIM[0]: # low x bound
                if tilepos[0] <= self.rect.width: # high x bound
                    if tilepos[1] >= - TILEDIM[1]: # low y bound
                        if tilepos[1] <= self.rect.height + TILEDIM[1]*4: # high y bound
                            vislist.append(tile)
    return vislist	



Yeah, that's an awkward stack of ifs. I was thinking of doing a simple point collide within the game-view rectangle, but tiles whose toplefts are outside the viewport need to be drawn too, so the rect for this purpose would have to be moved and made larger. I'll deal with it later.

As an aside, the vector.add is just a function written by the magnanimous Doc Osborn which adds the elements in tuples. So instead of writing blah=(foo[0]+bar[0],foo[1]+bar[1]), I can go blah=vector.add(foo,bar).

The offset variable used there is how far (in pixels) the viewport is scrolled from the origin (0,0), in pixels.

Here's a simplified version of my map drawing function that renders the output from findVisibleTiles:

def drawMap(self):
    self.surface.fill((0,0,0))
    # render vislist 
    vislist = self.findVisibleTiles()
    for tile in vislist:
        drawpos = vector.add(tile.drawpos,self.offset)
        self.surface.blit(terrain.getTerSurf(tile.terType,tile.terEntry),drawpos) # render terrain   



The self.surface is the final render of the game viewport and is taken and drawn to the main screen along with all the GUI panels elsewhere in the code.

The style of isometric map I chose to go has a sort of staggered grid that looks like a square ... from far away when you squint a bit, like Civilization, rather than the diamond grid of Transport Tycoon. Here's a helpful diagram I whipped up:



Note that the coordinates and the apparent dimensions of the staggered grid are somewhat counterintuitive, and whether a tile is on an odd or even row becomes important for determining where other tiles are relative to it.

Here's the function for finding the position at which a tile is to be drawn:
    def updateDrawPos(self):
        '' update the gamewindow NONrelative position where the tile is drawn''
        if self.posHelper == EVEN: #if even
            self.drawpos = (self.pos[0]*TILEDIM[0], self.pos[1]*TILEDIM[1]/2)
        elif self.posHelper == ODD: #if odd
            self.drawpos = (self.pos[0]*TILEDIM[0] + TILEDIM[0]/2,self.pos[1]*TILEDIM[1]/2)


(The html doesn't like one of the pluses in the code. It goes where that big gap is.)

Here're the p1x:



On the left is a shot of the first time I saw it work. T'was a beautiful sight, but my viewport rectangle was too big and I forgot to use colorkey alpha. On the right is the map rendering correctly after I fixed it up.

Comments: 3 - Leave a Comment

Link



Tuesday, November 13, 2007
(I noticed a lack of pictures, so here's one that shows what I'll be talking about, a couple panels and some buttons. The cursor is actually hovering over the lower-right button here, as you may see by the mouseover effect:)



GUI panels are stored as a list (or can be called up as a list on short notice), so to handle mouseclicks I run Pygame's collidepoint check for the rectangle of for panel in panels while giving the mouse click position taken from the input class. The input manager is, by the way, is the first line response to all mouseclicks and keyboard key events.

Right, so for panel in panels, if the mouseposition collides with the panel, that panel is passed the mouseclick (which contains position, which mouse button, and whether the click is down or up) and the loop is broken, because one click should never trigger multiple panels. If no panel is clicked, the mouseclick is passed to the game-map itself. Or I suppose if the game isn't started yet, it'll just be ignored.

So when a panel receives a mouseclick, it normalizes the click position to its own rectangle. A click in the gamewindow at position [450,450] on a panel which has dimensions of [300,300] at (by topleft corner) position [200,200] will make that click's position be [150,150], probably, because buttons in a panel are stored in positions relative to their parent panel. Basic stuff. Then the same process which happened for panels is repeated for buttons: for button in panel.buttons collidepoint with the button's rect. If there's a collision, click() the button and break the loop.

When a button is click()ed, it sends its triad of Target (who to), Command (what to do), Value (how much ... or a string) to the Relay object, which routes the Command/Value to the given Target. This should be a sub-section...

Relays and Triggers



I had a hell of a time dealing with buttons in my last few projects, so I came up with an idea inspired by mapping in various Quake engines. I really don't know if it's the right thing to do, but it seems like it'll work, so: A Relay object holds a name/reference of everything that adds itself to the Relay, and everything can access the relay object (because it gets imported to anything that uses it), and, in turn, Relay can access everything that added itself to Relay. When a button is triggered it sends Relay its Target/Command/Value, then relay sends the command/value to the target.

Classes that'll be triggered by Relay have a semi-generic trigger function that takes in Command/Value which are then sorted out internally.

I use this in only one place so far, to sent an exit command to the gamestate managing class. One might be encouraged to use the whole Pygame event queue system thing with custom events but it seemed like asking for trouble (and more work) to mix up game events with input events. Now that I think about it, I could very well reproduce the event queue system a little more simply for my own needs but I don't think that a player will be able to mash enough mouseclicks through that it'd ever be necessary.

Mouseover button effects


Getting the mouseover effect working on buttons was an unfortunate process. With a little more application I bet I could get something the mousedown effect working too, but I'm going to wait until I feel inspired.

To do mouseover effects, I run a process similar to the mouseclick collide checks whenever the mouse is moved around the game window. When the mousemove successfully collides with a panel, it is stored as currentHoverPanel (or something). If the previous currentHoverPanel is a different panel, all buttons' mouseover effects are turned off in that panel. Then the currentHoverPanel runs the mouse position collide check to find out if it collides with a button. If so, that button switches a flag on and displays its mouseover effect instead of its mouseup effect.

The real problem I had with this is that you only ever get the new mouse position. This makes it simple enough to activate effects on the new position. The trick is being able to de-activate the effect at previous position, which you do by storing the old position and reseting it when it changes. As I write this now it seems obvious, but it sure wasn't before!

I don't know what I'll do for mousedown. Maybe switch a timer on in the button that switches a mousedown flag on for something like 200ms then reset itself when its done.

In other news...


My hard drive is making imminent-explosion type sounds. Having backed it up, I wait. I wait.

I was also thinking that when I'm done with the strategy game, I can build a totally generated non-linear role-playing game with the same engine. Then you could export your save game to the RPG and oppose your own self-as-wizard-overlord from the bottom up.

Dwarf Fortress did something like that; Can I not dream?

Comments: 2 - Leave a Comment

Link



Monday, November 12, 2007
When working on ATTAS I watched Laura sweat over recreating the funcationality of Java's Swing in Python, first in Pygame, then she ported that effort all over to the Osborn Special 2d OpenGL rendering system which was meant to act a bit like Pygame. In short, it appears to be huge pain to make an extensive GUI library (but I suppose everyone knows that). So what I'm going to do is aim low. I don't need transparent movable, resizable windows that snap together into customizable configurations a la World of Warcraft. Cool as that'd be. (Though I can appreciate that expending such effort now might save me trouble in the future with GUIs, it might be better to actually complete a modest GUI than to plan and never finish an awesome GUI. I can always go back and make it better later.)

To start, I'm going to imagine that all I need is vaguely static panels which may contain interactive buttons and non-interactive elements like text and graphics. (There are some other things I may need like scrollable selection lists and text entry fields, but I'll fall off that bridge when I come to it.)

Panels


All a panel really needs to know is its dimensions, its position, and what's in it. My panel class basically a Pygame rect object, which contains dimensions and a position. Then all I need is to store buttons and elements in it. One would be responsible to make buttons a type of element, but I'm keeping them separate for now because I'm lazy I do mouseclick collision with buttons and not with elements, and stuff.

So buttons and elements I store in a dict rather than a list so I that can call them by 'name'. (And if I need a list in no particular order, I just call buttons.keys()!) In making the ATTAS GUI, Laura used the index of panels/elements in lists to imply draw/mouse-collision order, so when a window was selected and brought to the front of the viewport, it was set to the top of the list (and when drawing to the screen, you just iterate through the list in reverse order). The order was implicit; Neat! But I don't need any particular order because I don't plan there to be an overlapping.

Border styles


If I'm going to have panels, they're going to have to be pretty. I got the idea of cutting up a border-style "skin" image from Laura's implementing of [some java window dressing thing that I forget the name of].
Here's how it works:



Note that UI border skins all have the same ratios in sizes between corners, edges, and center tiles, but they can be any size.

Here's some border styles in action, plus all the styles I whipped up, including up/down/over buttons:



Here's a button showing off the mouseover effect (and my cursor wasn't picked up by the screenshot):



Oh yeah, it looks like I'm getting a bit ahead of myself because text rendering is working there. What a headache that was -- good thing Laura made the whole thing for ATTAS and all I had to do was adapt it back to Pygame. Thanks Laura!

The button collision and mouseover detection is another whole barrel of fun too.

A tangent, on the process of coding and writing


Actually there is quite a lot in my GUI classes that I ought to clean up. Instead of using the Panel's rectangle object for everything like I should, I derive dimensions and positions from the rect on-creation because I only thought of using it after building the GUI classes for the first time. I tend to focus on a particular area of the game, plow through a bunch of development, then find my interest drift somewhere else. It may be valuable to keep a list of issues organized by area so that I don't forget outstanding issues. I think I'll start doing that right now.

And further, I'd like to say that I find it very valuable to leave a problem for a few days and take care of other stuff, then come back to the original problem. I usually see it in an entirely new way and realize that I did a lot of ridiculously stupid things. Even better is coming back and writing about it because in the process of explaining myself I think of how much more elegant this could be -- both in code and in writing, heh heh.

Comments: 0 - Leave a Comment

Link



Saturday, November 10, 2007
Over the last few years I've tried a number of times to make an isometric strategy game. The effort would go along for a while then collapse for any number of reasons. Maybe I got distracted with school projects, maybe I didn't actually know how to program so I just made concept art, maybe my code became such a quagmire that I couldn't go on. Well, it's all of 'em. So I dug up a couple of the more presentable images from previous efforts to give an idea of what I've done before.

Codename: Foundation


This is one of the first semi-serious attempts and was started something like 3 years ago -- before I knew anything about coding. The plan was that I'd do a bunch of design and graphics, and my partner Laura would program it in Java.

I learned that one can't seriously be the primary designer for a game if one knows nothing about code. There are whole structures that need to be understood/cared-about for a game to even be successfully started. Plus Laura isn't interested in my idea of strategy games, and she was trained more for business application and database programming at the time, not graphics-heavy stuff. Let me tell you, our first combined effort was a goblin-themed arcade game, and it was a real mess.

Anyway, here are some isometric tiles I did for Foundation. Note a very limited palette and flexible interpretation of projection lines.



Codename: SoS 5



Now this is from something like 2 years ago, and I actually know how to code for myself a bit, though not terribly well. I learned programming in C and a bit of Java, then really took off through Python 6 months later. In the SoS (Sovereign of Sorcey; yes, vs. Master of Magic, get it? Ha ha. Ok.)... in the SoS series, I was coding in Python, but I didn't really understand a lot of things and had difficulty visualizing operations if I couldn't see the code for them right in front of me. Here's a sample of my class for the isometric map:

class IsoMatrix:
        '' this is the tile matrix itself ''
        def __init__(self,dim):
            self.matrix=[] #new matrix
            width=dim[0] #tiles wide
            height=d[1] #tiles high
            x=0 #x cycler
            y=0 #y cycler
            """ it shall belike so: matrix[column][row]/ should be matrix[row][col]:
            0 [ 0 , 1 , 2 ]
            1 [ 0 , 1 , 2 ]
            2 [ 0 , 1 , 2 ] """
            while y&lt;height: # num of rows
                temp_row=[]
                while x&lt;width: # num of cols
                    #pass isotile x and y for self.posx/y
                    temp_row.append( IsoTile(x,y) )
                    x=x+1 #for next pass
                x=0 # reset x after the row is done
                self.matrix.append(temp_row) #add the newly made row
                y=y+1 #for next pass






It's painfully literal, I did some kind of C-style loops, and I commented on everything so that I wouldn't forget what something did. Actually I still comment very generously, it's good fun.

So that's from SoS 5, which is the first iteration that really took off. You don't need to hear about Sovereign[s] of Sorcery 1 through 4. Really. Moving on. I got a very, very crude GUI working in which, seriously, almost everything was hard-coded. In my fourth year at ACAD I imposed a slightly more advanced but similarly painful GUI on the Procyon project with David Osborn. Sorry Dave. I understand how to make GUIs much better now (though the details of my implementation in my current iteration of the project are a post for another day).

Pictures:



(Click to enlarge that sucker.) Here's that awful hard-coded GUI layout that somehow manages to look pretty good in a screenshot. And this shot is actually from an error shot file I had, so you can see that there's a bit of the border on the far right didn't get drawn.



And here are some fun mountain tiles floating in the sky. I never did implement terrain beyond the very basics. The previous shot was of a map filled with "void" -- I figure it'd be appropriate for a fantasy world to have an edge you can look off of and see stars. Plus, it's sort of a postmodern self-reference to the medium of tile based games that acknowledges its own abstraction. Sorry, art school moment.

Codename: SoS #6



Here's SoS 6. I focused more on getting the terrain to do certain things over making a horrible but pretty GUI. I also had the thought that changing the angle of the isometric grid would make my life a lot easier (and, looking back, I think that this line of thought is what made me push for a similar look in Procyon). It actually was easier, but I don't think that this projection would ultimately give the same kind of visual depth of an angled isometric view. So: one can see here how terrain edge blending is sorta-working as is the blending going on between terrain types for tiles, all using colorkey masks.



No, it isn't always pretty, but a little work on making different colorkey masks might have made it work. I think I need to make a chart to explain how I was doing things.
There:



So I almost used this method of making tile-edges on my current project, but found a simpler version of the draw-every-transition method with help from my friend Marcel (here's the shout out), who is a smart dude, has a Teutonic/embedded hardware programmer mastery of Logic, Discipline, and Efficiency, and further has that naive love of strategy games only people born in Europe can possess.

SoS 7 was a wash. The current iteration starts with the "Strat" series, the full name of the engine (I recently decided) would be "Isostrat". I've done two radical forks so far and am now on Isostrat2.

... which is what this journal is now going to be all about.

Comments: 0 - Leave a Comment

Link



Friday, November 9, 2007

Why Python


Well, the real question is not "Why Python?" but more "Why not Cpp?".
Yes, Python is slower than Cpp, and yes, there are fewer l33t libraries and things, and yes, no one will take me seriously. But the game I want to make is small enough (in graphical demands especially) and computers fast enough that I think it's worthwhile to trade everyone elses' game-running efficiency for my game-writing efficiency. For me. And if people don't take me seriously, too bad for me too.
Also I know Python and don't know Cpp. The real reason is intertia.

Link: Python

Why Pygame


The real question is why I'm not using OpenGL.
- It doesn't need to be 3d. My planned game takes place on a geographic 2d plane without complex animation or physics.
- I'm not very good at OpenGL, though I have twisted it to my ends on a few occasions. It'd take a lot of work to learn to utilize it properly. OpenGL should figure into my work someday, but not for this.
- I don't really like the aesthetic of 3d graphics, usually. And while I don't mind 3d modeling, I loathe animating because it's just so tedious. This'll be an exercise in pixel art and digital painting.

Link: Pygame

Theory


Some of the abstractions necessary to make a game work on a computer may get in the way of enjoying the theme or content of the game. Take Civilization; If you remove all the content (or keep it, even) what you have is a game about shuffling counters over a grid of tiles. Instead of semi-arid broken grassland with scatterings of small trees, a square is given a generic 'plains' graphic and a movement cost, food value, and production value of '1'. This is a necessary abstraction of theme into game mechanics so that the game can deal only with the level of detail necessary. When you get down to it, the gameplay of Civilization is really working within a grid-and-counter framework little different than Chess or Checkers (with a number of additional mechanics added in, yes).

So: Is the game about shuffling counters over a grid in the most optimal manner or is it about "building a civilization to stand the test of time" (to paraphrase the blurb on the box of Civ 1). I suppose the answer should be that it is about both, but still ... What I'd like to is this: instead of building gameplay around the mechanics that arise due to abstraction of the theme, I want to try to build gameplay around the theme itself and keep the abstractions, well, abstracted from the player. So the player shouldn't really have to worry exactly about what counter is on which tile, they should be worrying about "building a civilization to stand the test of time", like how much bread and games to give to the peasants or whether to pass sumptuary laws rather than the choice of moving a sword-unit to [24,17] or shuffling shield and food icons over terrain.

I want to make a game about macro-management in the theme of the game rather than micro-management of the mechanics of the game. This is my design ideal, duly stated, and it is what I'm going to be aiming for. Oh, and the theme of the game I have in mind is that of pre-industrial warlordism ... with magic! in the vein of Master of Magic crossed with the sort of politicking of Europa Universalis on a Civilization kind of tile-based map -- but without too much focus on the tiles! Makes sense? Good.

And to spice up the post, here are some sketches of isometric junk I did over the last year or two:








Comments: 1 - Leave a Comment

Link



Thursday, November 8, 2007
And this shall be a journal documenting that process. Come, see! Vicariously share my struggles, hardships, success, and failure.

So what's this going to be like?


For the first bit, I'm going to review all the work that I've done up to this point on my ongoing struggle to make an isometric strategy game. Now that I've gotten this far and learned this much I recognize that my experience might be helpful to other people trying to do the same thing, and by documenting my work, they may be spared my pain.

Further, if this project explodes and I forget all about it, I'll at least have this documentation to prove to others (and remind myself) that I worked really hard on something.

I also must admit that it is sometimes difficult to continue a project when it gets to a slow part. By committing myself out in the world (ok, just teh interwebs), I force myself to be more committed to completing the project.

My goals


  1. Knowledge: I want to learn more about programming/game-making by programming and making a game. I'll also probably learn a hell of a lot about everything else the game touches, too.

  2. Attention: People will pay attention to and like me. No, really, it makes me feel good when people appreciate and enjoy the things I make. (At least I'm admitting it!)

  3. ... Profit!: Well, I'd like to be able to make money making games on my own terms. We can all dream.

A bit about me


I've played computer games for almost my entire conscious life. It should suffice to say that I enjoy them and would like to make them. Though my parents are both programmers of various sorts, I was always more into art (small 'a'!). After highschool/community college (during which I made piles of unfinished Quake1/2/3 and Half-life maps) I ran away to Canada and went to art school at the Alberta College of Art and Design and majored in Media Arts and Digital Technology. During my third year there (which would be, what, 2 years ago now?) I finally took a programming class and found that it was tons of fun, and that the logic of scripting and building 3d game maps were a heck of a lot like coding. I worked on projects there with David Osborn, who is a one righteous dude and a master of code (and got me into gamedev.net). During our 4th year we worked on Procyon, a tile-based adventure/rpg sort of thing, and for a while after graduation we worked with a larger team on a space-flyer/rpg called ATTAS.

And to give some idea of what I'm shooting for, a few of my favorite games are as follows: the Civilization series (most espectially Alpha Centauri, which was far more innovative than any other of the sequels), the old rpg Darklands, Half-life 1/2, the Baldur's Gate series and Planescape: Torment, and all of the games by Paradox, the Hearts of Irons, the Europa Universalises (Universali? Universallae?), Vicky/Ricky, etc.

I love games that are complex and thoughtful. Unfortunately most games made nowadays are going the same way the movie industry has gone and are becoming less of both. Sure they make lots of money, but they also make lots of dumb.

If you don't like 'em, why don't you try making your own?

Ok. Let's see how it works out.

Comments: 5 - Leave a Comment

Link



All times are ET (US)

 
S
M
T
W
T
F
S
1
2
3
4
5
6
7
11
18
20
22
24
25
27
29

OPTIONS
Track this Journal

 RSS 

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