Laserbrain Studios

Main Games Blog Contact

Categories

Smooth lighting

2 CommentsMarch 10th, 2012 by Christian Knudsen

(Categories: Art, Hostile Takeover, Programming)

As mentioned briefly in the last blog post, I’ve been working on smooth lighting. That’s now complete and working as well as I think I’ll get it to. Check out these comparison screenshots:

Click to enlarge Click to enlarge

Click to enlarge Click to enlarge

As I also mentioned in the last blog post, this was somewhat inspired by the Project Zomboid programmer adding smooth lighting to that game (and now I notice that they also posted some comparison screenshots just yesterday!). I believe he accomplished this by overlaying a multiplied and untextured polygon that completely matches the shape of each ground tile, with the vertices of that polygon set to color values that create a smooth transition. I tried this as well, but wasn’t quite able to get the same results, and switching the OpenGL blending between normal mode and the multiply mode each frame also added a bit of overhead, so I went for a simpler method where I just supply the smoothing color values to the vertices of the sprite itself, skipping the overlay entirely.

The Project Zomboid method results in much smoother lighting on the ground (there really aren’t any hard edges at all), while my method still retains some of the per tile lighting, which I kinda like. It’s sort of halfway between the old X-COM hard lighting and the completely smooth lighting of Project Zomboid. I’m adding a toggle to the options menu to turn smooth lighting off if you prefer the old school hard edges.


Shadows or no shadows?

3 CommentsSeptember 21st, 2011 by Christian Knudsen

(Categories: Art, Hostile Takeover, Programming)

I’m currently optimizing the map drawing code for Hostile Takeover‘s isometric engine. It’s basically all about minimizing the amount of tiles and layers that the code has to loop through each frame to check for stuff to draw – and also trying to minimize the amount of texture bindings for OpenGL. Anyway, as part of this, I tried disabling the wall shadows (which I originally added to create an ambient occlusion effect) to see if they had any substantial impact on the time it takes to draw a frame. They don’t really, which didn’t surprise me much, but the test has left me with doubt as to whether or not wall shadows should even be in the game.

The image on the left is with wall shadows, the image on the right is without:

Click to enlarge   Click to enlarge

On one hand, I think the wall shadows make the structures pop a bit more and make everything feel less flat. On the other, I can’t shake the feeling that the wall shadows make it seem as if the walls are floating ever so slightly above the ground. So I can’t decide if I should keep them in or discard them. What do you think?

By the way, the shadow for the “Hensley International” sign is also missing in the right-hand image. That’s just because I completely disabled environment shadows for this test. Stuff like that sign will have shadows – that much I’ve decided on. Of course, it might just be weird if walls don’t have shadows when everything else does…


Z-layering

7 CommentsSeptember 10th, 2011 by Christian Knudsen

(Categories: Art, Hostile Takeover, Programming)

The world of Hostile Takeover has been pretty flat for a while. There’s been no z-layer, meaning that everything has been on the same level. That’s changed now. In the below images, you can see a building that’s currently 2 stories high (it’ll probably be 4 stories when done, but there’s really no limit to how many z-layers I can add). When you enter such a building, none of the layers above will be drawn, giving you an unhindered view of the level you’re on.

Click to enlarge   Click to enlarge

I’ve also added doors that automatically open and close when you need to pass through them, as well as windows. I’m currently working on adding elevators to transport you between the levels of a building (I really don’t want to make an animation for walking up/down stairs — at least not yet). You can already see the elevator doors on the first level of the building.

One final thing I’ve been working on is the map editor. It runs completely in the game engine, so I just press ‘D’ (for ‘developer mode’) when playing to move walls and stuff around. I’ll probably include the map editor with the game to allow players to make their own maps. Along with the scripting language, this will let players create missions for the game, so there will probably be two modes: the campaign with the full story and individual player made missions.

The map editor is still a bit buggy. It works fine for my own purposes as I’m aware of its limitations (such as placement of walls and stuff only working as intended when the map isn’t rotated), but I need to clean it up a bit before it can be used by other people. Look for it in the next development video, which I’ll probably make when I’ve got elevators working.


Image optimizations

No CommentsFebruary 22nd, 2011 by Christian Knudsen

(Categories: Art, Hostile Takeover, Programming)

It’s usually a bad idea to start optimizing your program too early, but I’d been running into some performance issues with my Hostile Takeover engine. The game would slow down considerably when there were many sprites to draw to the screen. The issue was most noticeable when there were many characters on the screen at once, since each character consists of about 14-16 subsprites that are all individually drawn to the screen:

The 14 subsprites of a character

Until now, the subsprites for a single character frame were mostly gathered on a single texture atlas, but there were still some instances where the subsprites were spread out over several atlases – the weapon subsprites, for instance. When a character carrying a weapon was drawn to the screen, the code would first draw all the subsprites “behind” the weapon subsprite, then switch over to the texture altas with the weapon and draw that, and then switching back to the original texture atlas to draw the remaining character subsprites “in front of” the weapon subsprite. Switching texture altases like that is expensive, as the GPU has to upload the new texture altas data.

I had originally just planned on gathering all the subsprites that make up a character frame on one texture atlas, so that the code wouldn’t need to switch back and forth between different atlases when drawing a single character. I did that, and I did get a slight performance increase, but it wasn’t enough. So, what I’ve done now is decrease the image quality of the sprites themselves.

Until now, the sprites were all RGBA8888. This means that for each color (RGB) and the alpha value (A), 8 bits of memory would be used, making the images 32-bit (8×4). This allows for 16,777,216 different colors. This also takes up a lot of memory, though, which again means that the GPU has to push a lot of data for each sprite that needs drawing.

The sprites are now instead 16-bit or RGBA4444. This immediately halves the amount of data and memory used, and provides a pretty significant performance boost. 16-bit “only” allows for 4,096 different colors, though. Compared to the 16,777,216 in 32-bit, you’d think that the difference in quality would be pretty severe. I don’t think it is, though. See for yourself:

RGBA8888 screenshot
RGBA8888 (click to enlarge)

RGBA4444 screenshot
RGBA4444 (click to enlarge)

At a quick glance, I almost can tell the difference. But if you look closely, you’ll notice that there’s a slight grain to the RGBA4444 image. This is because dithering is used to simulate a higher number of colors. I actually kinda like this grain effect. It adds a texture and feel to the images that I can’t quite put my finger on. So I think I’ll stick with this. Increased performance, lower memory use and a grain effect that I kinda like. It’s a win-win!

What do you think?


Creating semi-random character appearances – Part 2

No CommentsOctober 22nd, 2010 by Christian Knudsen

(Categories: Hostile Takeover, Programming)

The continuation of last month’s Part 1 is long overdue, but here it finally is. In Part 1, I described the process for creating the many subsprites that go into creating the full character sprites. In this blog post, I will focus on how the multiple subsprites are actually drawn to the screen to correctly form the complete character.

From:

The 14 subsprites

To:

Single frame of walking animation

Two things need to happen for these subsprites to be drawn correctly. 1) The program needs to know where to bind the subsprite from, and 2) it needs to know where to blit it on the screen.

All these subsprites are pretty small in size and storing them all as individual images would not only result in a mess of thousands of image files, it would also present a problem for the game engine. The engine uses OpenGL for drawing stuff to the screen and OpenGL prefers its textures (the images to draw) to be power of 2 in size, meaning that the height and width of the image must be 16, 32, 64, 128, 256, 512 or 1024 pixels. Extensions exist for OpenGL that allow the usage of any size, but not all video cards support this extension, and there’s actually really no reason for using non-power of 2 textures. Why? Well, because even though the subsprites are all very small, they can be packed together in a so-called texture atlas, and this texture atlas can just be power of 2 sized:

Texture atlas

This mean that the extension for non-power of 2 textures isn’t needed, but there’s also another benefit of packing subsprites together in one large image file. Whenever OpenGL has to bind (prepare for drawing to the screen) a texture, it takes time as the texture data must be moved to the GPU. If all the subsprites were in separate image files, I’d have to constantly bind new files, but when related subsprites are packed together on a texture atlas, I only have to bind this texture atlas once and can then just tell OpenGL which part of the texture atlas to blit to the screen.

Telling the program where to draw from
And that’s where we get to the first requirement: Telling the program on which texture atlas each subsprite can be found and what the individual subsprite’s coordinates are on this texture atlas. Thankfully, I don’t have to manually create a texture atlas and paste the subsprites onto it. A lot of free programs exist for doing this automatically. You just set the size of the texture atlas and select which files should be packed in it, and the program does it all for you – packing the subsprites together in the most efficient manner. Furthermore, these programs can then output the coordinates of each subsprite on the texture atlas in various formats. It’s then just a matter of getting this coordinate information into the program and creating a procedure for grabbing the correct information for each subsprite.

In code terms, I’ve created a procedure that gets called with the subsprite’s name and then returns the number of the texture atlas and the coordinates where the subsprite can be found. Here’s a snippet of the Pascal code that returns the subsprite positions of all the sprites that go into making the ten frames of a male character walking south:

   PROCEDURE GetWalk1SpritePositionValues(SpriteName : String);
   BEGIN
      IF SpriteName = 'walk1-1malehair-1' THEN SetSpritePositionValues(walkmale1, 334, 257, 12, 11)
      ELSE IF SpriteName = 'walk1-1malehair-2' THEN SetSpritePositionValues(walkmale1, 442, 257, 12, 11)
      ELSE IF SpriteName = 'walk1-1malehat-1' THEN SetSpritePositionValues(walkmale1, 394, 257, 12, 11)
      ELSE IF SpriteName = 'walk1-1malehat-2' THEN SetSpritePositionValues(walkmale1, 409, 209, 12, 16)
      ELSE IF SpriteName = 'walk1-1malehat-3' THEN SetSpritePositionValues(walkmale1, 406, 232, 12, 14)
      ELSE IF SpriteName = 'walk1-1malehat-4' THEN SetSpritePositionValues(walkmale1, 337, 209, 12, 16)
      ELSE IF SpriteName = 'walk1-1malehat-5' THEN SetSpritePositionValues(walkmale1, 167, 392, 22, 17)
      ELSE IF SpriteName = 'walk1-1malehat-6' THEN SetSpritePositionValues(walkmale1, 345, 162, 18, 15)
      ELSE IF SpriteName = 'walk1-1malehead-1' THEN SetSpritePositionValues(walkmale1, 497, 162, 12, 17)
      ELSE IF SpriteName = 'walk1-1maleleftarm-1' THEN SetSpritePositionValues(walkmale1, 327, 270, 8, 10)
      ELSE IF SpriteName = 'walk1-1maleleftarm-2' THEN SetSpritePositionValues(walkmale1, 232, 488, 9, 24)
      ELSE IF SpriteName = 'walk1-1maleleftarm-3' THEN SetSpritePositionValues(walkmale1, 286, 232, 9, 24)
      *snip*
      ELSE IF SpriteName = 'walk1-10malerightfoot' THEN SetSpritePositionValues(walkmale1, 351, 270, 7, 10)
      ELSE IF SpriteName = 'walk1-10malerighthand' THEN SetSpritePositionValues(walkmale1, 202, 245, 9, 36)
      ELSE IF SpriteName = 'walk1-10malerightshoe-1' THEN SetSpritePositionValues(walkmale1, 202, 499, 9, 13)
      ELSE IF SpriteName = 'walk1-10malerightshoe-2' THEN SetSpritePositionValues(walkmale1, 494, 179, 11, 27)
      ELSE IF SpriteName = 'walk1-10malerightshoe-3' THEN SetSpritePositionValues(walkmale1, 259, 484, 9, 13)
      ELSE IF SpriteName = 'walk1-10maletorso-1' THEN SetSpritePositionValues(walkmale1, 410, 0, 35, 37)
      ELSE IF SpriteName = 'walk1-10maletorso-2' THEN SetSpritePositionValues(walkmale1, 48, 409, 35, 36)
      ELSE IF SpriteName = 'walk1-10maletorso-3' THEN SetSpritePositionValues(walkmale1, 460, 59, 35, 33)
      ELSE IF SpriteName = 'walk1-10maletorso-4' THEN SetSpritePositionValues(walkmale1, 425, 59, 35, 33)
      ELSE IF SpriteName = 'walk1-10maletorso-5' THEN SetSpritePositionValues(walkmale1, 390, 59, 35, 33);
   END;

The SetSpritePositionValues procedure that gets called is just a simple procedure for passing on the values to a set of global variables that I then use when calling the OpenGL blitting procedure:

   PROCEDURE SetSpritePositionValues(TextureMap, X, Y, Width, Height : Word);
   BEGIN
      GetSpriteMap := TextureReference[TextureMap];
      GetSpriteXPosition := X;
      GetSpriteYPosition := Y;
      GetSpriteWidth := Width;
      GetSpriteHeight := Height;
   END;

So the first value that gets passed is the number of the texture atlas (in this case, it is a constant value called ‘walkmale1′), then the X and Y coordinates, and then the width and height.

All these procedures fill up a lot of lines of code, but they are quickly made since it’s just a matter of formatting the coordinate information given from the image packing program. This is the quick and easy part. The program now knows where to draw from. It’s the second requirement that takes a lot of time.

Telling the program where to draw to
For everything related to drawing and placing of characters, it all starts with the character template sprite. This is just the basic character frame with no clothes on:

Character template sprite

This sprite is actually never used in the game, since the character sprites are all drawn by using the subsprites to form a complete character sprite. But the height and width of this template sprite is what’s used for drawing the subsprites to the screen. The upper left corner of this template sprite has the coordinates (0,0). When I for example need to figure out what the coordinates of the character’s pants are in relation to this, I just go into GIMP, paste the pants onto this template sprite, move them to the upper left corner to ‘reset’ the coordinates, and then drag the pants to the correct position on the sprite and read how far the pants have been dragged along the X and Y axis:

Getting the pants' relative coordinates

This gives me the pants’ relative coordinates (relative to the template sprite’s (0,0) coordinates): (6,43). When the character is drawn to the screen, I already know what the character’s overall coordinates are. Let’s say the character’s coordinates are (201,750). To figure out where the pants subsprite should be drawn, I just add the pants’ relative coordinates to the character’s overall coordinates:

(201,750) + (6,43) = (207,793)

So I just tell OpenGL to blit from the coordinates of the texture atlas previously found and to the screen coordinates (207,793). This will draw the pants in the correct spot. It’s quick and easy when everything’s programmed correctly. What takes time is manually determining what each subsprites’ coordinates are relative to the template sprite. That takes a lot of patience and use of GIMP. So, again, I get to listen to a lot of podcasts while doing this stuff!

There’s still one thing left to do before the character sprite is ready to be drawn. As it consists of multiple subsprites, these all have to be drawn in the correct order or you risk having a character with his legs outside his pants, or his head over his hat. How this is managed will be the subject of Part 3.


The basics of isometric programming

16 CommentsAugust 6th, 2010 by Christian Knudsen

(Categories: Programming)

When I started work on Hostile Takeover, I had only admired existing isometric games and never thought about programming one myself. So when I decided that isometric would be the graphical style I’d go for, I had to do a lot of reading up on the subject and wrap my head around this way of “faking” a 3D presentation with 2D graphics. I thought I might as well share some of the basics I’ve come to understand – maybe you’re gearing up for your own isometric game, but if not, I hope you still find this somewhat interesting.

There were two main issues I had to figure out to get the most basic stuff up and running: drawing the tile sprites in an isometric grid and calculating which tile the mouse cursor is over. As with pretty much everything in programming, the answers to both issues were some surprisingly simple mathematical formulas.

Drawing an isometric grid
An isometric tile is basically a square tile rotated 45 degress around the z-axis and tilted around the x-axis so it’s twice as wide as it is high:

Rectangular tile to isometric tile

When drawing a grid made up of non-isometric tiles, it’s pretty easy as all the tiles on the same row have the same y-value, but with an isometric grid all tiles are offset half a tile both vertically and horizontally:

Non-isometric and isometric grid
Image borrowed from this article

This is where basic math came to the rescue. The algorithm I use for drawing tiles is:

   FOR Y := 1 TO SizeY DO BEGIN
      FOR X := 1 TO SizeX DO BEGIN
         DrawX := ((X - Y) * 38) + 400;
         DrawY := (X + Y) * 19;
      END;
   END;

My tiles are 76 pixels wide and 38 pixels high, which is why I multiple with 38 (half of 76) and 19 (half of 38). I add 400 to the x-value to keep the left half of the grid from being drawn at negative x-coordinates (i.e. beyond the left edge of my computer screen). This gives me the x and y coordinates for blitting the tile sprite to the screen. Simple!

Calculating tile mouse over
When it comes to figuring out which tile the mouse is over (so that the game will know which tile the player character should move to when the player clicks the mouse button!), it was slightly harder to figure out, but, again, some simple math came to the rescue.

I already knew what the screen coordinates of the mouse cursor is, since I used that for actually drawing the mouse cursor. The screen coordinate is just which pixel on the screen the mouse cursor is currently at. My screen resolution is 1280×800, so the mouse cursor’s x-coordinate will be 1 – 1280 and the y-coordinate 1 – 800. I also already know where my tiles are on the screen, since I just previously figured out the algorithm for drawing them to the screen. Combining this, I can make an algorithm for translating the mouse cursor’s screen coordinates to an isometric tile coordinate:

   ScreenX := ScreenX - 438;
   TileX := Trunc((ScreenY / 38) + (ScreenX / 76));
   TileY := Trunc((ScreenY / 38) - (ScreenX / 76));

So if I know that my mouse is at, say, [410, 235] on the screen, calculating which tile the mouse is over is as easy as:

TileX = (235 / 38) + (-28 / 76) = 5 (rounded down)
TileY = (235 / 38) – (-28 / 76) = 6 (rounded down)

You’ll notice that I start with subtracting 438 from the mouse cursor’s x-coordinate. This is because I added 400 to the x-coordinate when drawing the tiles, so I have to subtract this again (as well as half a tile width) before calculating which tile the mouse is over.

Scrolling map
Now, this is all well and good for a map that isn’t larger than the computer monitor’s resolution. What do you do if you want a map that’s larger than that and want to allow the player to scroll the map? Easy, you just add an offset x and y value to the algorithms:

   FOR Y := 1 TO SizeY DO BEGIN
      FOR X := 1 TO SizeX DO BEGIN
         DrawX := (((X - Y) * 38) + 400) - OffsetX;
         DrawY := ((X + Y) * 19) - OffsetY;
      END;
   END;

and:

   ScreenX := (ScreenX - 438) + OffsetY;
   ScreenY := ScreenY + OffsetY;
   TileX := Trunc((ScreenY / 38) + (ScreenX / 76));
   TileY := Trunc((ScreenY / 38) - (ScreenX / 76));

When the player scrolls the map horizontally, just increase or decrease the OffsetX value accordingly, and do the same with the OffsetY value when the player scrolls vertically.

And there you have it. The basics of drawing and interacting with an isometric map. I had to play around with the values a bit, though, so when I for example add 400 to the DrawX value, that number is just something I arrived at with trial and error. It would differ for different monitor resolutions and depending on where you want the default map position to be at (i.e. before the player does any scrolling).

If you want to read more about isometric programming theory, there’s a bunch of enlightening articles over at GameDev.Net.

Oh, and in case you’re wondering, the code snippets are Pascal. I program in FreePascal with SDL and OpenGL for cross-platform development.