Bye-bye Baby! Hello 70 points!

Today I got a little sidetracked by cars. I was working on road and pavement surfaces and thought about adding cars to the game. Or car wrecks, to be more precise.

Initially I just wanted to add cars that were already wrecked, since that’s pretty easy. But then I thought it might be feasible to actually have pristine cars that would explode as the game progresses and make them part of the gameplay. So I did an experiment with a single car to see how it would go.

I took a picture of a car, cut out the outline, added a shadow and dropped it on one of the new roads I had made.

Then I messed up the bodywork a little bit and removed the wheels. After that I added several layers of textures that made it look broken and burnt.

I think it turned out pretty well for a 30 minute experiment so I might be adding destructible cars to the game after all, although I’m not going to use this particular one because of the angle of the photo.

Simple undo/redo system for C#

So you want to add an undo/redo system to your program? OK.

First, we need to separate the GUI from the code that actually does stuff. This is sometimes called “a command design pattern”. Each command should know how to “do” stuff and how to “undo” it. It may be as simple as this:

public interface Command
{
    void Do();
    void Undo();
}

Second, we need a class that will keep track of what commands have been applied and which ones haven’t. Let’s call this class History. It knows where we are and what to do (or undo) next. Again, pretty simple. So let’s add some more functionality while we’re at it. Our History class will notify us when a command is executed and will know if there are any unsaved changes. Here goes:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


public class History {

    private List commands = new List();
    private int lastExecuted = -1;
    private int lastSaved = -1;

    public delegate void Changed(bool haveUnsavedChanges);
    public event Changed OnChanged = (h) => { };

    public void Clear() {
        commands.Clear();
        lastExecuted = -1;
        lastSaved = -1;

        OnChanged(false);
    }


    public void Save() {
        lastSaved = lastExecuted;

        OnChanged(false);
    }


    public bool Modified {
        get { return lastSaved != lastExecuted; }
    }


    public int Size
    {
        get { return commands.Count; }
    }


    public int LastExecuted
    {
        get { return lastExecuted; }
    }


    public void Limit(int numCommands) {
        while (commands.Count > numCommands) {
            commands.RemoveAt(0);
            if (lastExecuted >= 0) {
                lastExecuted--;
            }
            if (lastSaved >= 0) {
                lastSaved--;
            }
        }
    }


    public void Add(Command command, bool execute) {
        if (lastExecuted + 1 < commands.Count) {
            int numCommandsToRemove = commands.Count
            - (lastExecuted + 1);
            for (int i = 0; i < numCommandsToRemove; i++) {
                commands.RemoveAt(lastExecuted + 1);
            }
            lastSaved = -1;
        }
        if (execute) {
            command.Do();
        }
        commands.Add(command);
        lastExecuted = commands.Count - 1;

        OnChanged(true);
    }


    public void Undo() {
        if (lastExecuted >= 0) {
            if (commands.Count > 0) {
                commands[lastExecuted].Undo();
                lastExecuted--;
                OnChanged(lastExecuted != lastSaved);
            }
        }
    }


    public void Redo() {
        if (lastExecuted + 1 < commands.Count) {
            commands[lastExecuted + 1].Do();
            lastExecuted++;
            OnChanged(lastExecuted != lastSaved);
        }
    }
}

You can drop these two classes into your project and start using them right away.

NOTES

You may run into a situation where you think you need to undo/redo multiple commands at once. For example, you have an editor that allows the user to select and change multiple objects simultaneously. There is a simple solution for this – implement a single additional Command – one that keeps a nested list of other commands and calls their Do() and Undo() methods all at once. Reverse the call order for the Undo().

Also, if you’re wondering what that “execute” flag is good for in the History.Add() method, imagine an editor that allows moving objects by dragging them. You probably don’t want to have a separate Move command for every tiny mouse move, do you? What you really want is a single Command that will put the object back into the place where it was when the user pressed the mouse button and started dragging it, right? Well, just remember the original coordinates and keep updating the object yourself with each mouse move. When the mouse button is released, create a Move command, give it the original and the new coordinates and add it to the history without executing it.

Český jazyk

This post is in Czech. But only just…

Co se stane, když lidi zleniví a začnou komolit a kumulovat anglické odborné výrazy v běžné řeči? Toto:

ajtema = položka
alfový imidže = obrázky s údajem o průhlednosti
autlajna = obrys
bagy = chyby v programu
begraundovej = na pozadí
čůznout = vybrat
debagátor = nástroj pro ladění programů
defaultní = výchozí
deskripšna = popis
devajs = přístroj
dizejblovaná = vypnutá
eksepšna = výjimka
ekspa = úroveň zkušeností herní postavy
ekstenžna = přípona názvu souboru
esajnout = přiřadit
esefikska = zvuky
fajlnejmy = názvy souborů
fajlujou = nejdou
fajtit = bojovat
fíčura = charakteristická vlastnost nebo rys
fiksnout = opravit
gárdovat = hlídat
getnout = stáhnout
invalidnout = zneplatnit
karentní = stávající
kjůnutej = zařazený ve frontě
klása = třída
kolamny – sloupce
kolory = barvy
kondišna = podmínka
kveráče – dotazy
kvesčna = otázka
lajna = řádek
lejer = vrstva
limba = končetina
loknout = zamknout
louduje = nahrává
lýd = vedoucí
merdžátor = nástroj pro sloučení dvou variant textu
nafejkovat = napodobit, vytvořit maketu
nakauntit = spočítat
očekovat = zkontrolovat
odzůmovat = oddálit
opšny = nastavení
pejntění = zobrazování
prioritnější = důležitější
proprta = vlastnost
randomní = náhodný
recenzátor = recenzent
rektangly = obdélníky
rekvestnout = vyžádat
restórnout = obnovit
risórsy = data
rispónovat = znovu se objevit
roudmapa = plán práce
sabmišna = podání
šejpy = tvary
selektnout = vybrat
serčovat – hledat
skanclovat = zrušit
skily = schopnosti
skrýna = obrazovka
sortnout = seřadit
stórovat – ukládat
tajly = dlaždice tvořící herní plochu
templejta = šablona
tred = vlákno programu
voeditovat = upravit
vohekovanej bak = nedbale opravená chyba
vybildovat = sestavit
vydebagovat = vyladit
vyimportovat = exportovat
vykilovat = vypnout
vyklínovat = promazat
vylevlování = zvýšení zkušeností herní postavy
vyreportovat – nahlásit
zafiksovat = opravit
zasetovat = připravit si pracovní místo
zkonvertovat = převést
zmerdžovat = sloučit

A nyní si vyzkoušíme celé věty:

  • Jste si jistý, že to de vybildovat z toho escéemka? Já jsem na slabejch třech errorech.
  • A getla sis to z escemka?
  • Nojo, getla.
  • A co tou komandlajnovou konvertační sračkou to vobesrat?
  • Je to sejvnutý v globálních devajsech?
  • Jakej je tam zrovna karentní jazyk?
  • No a ta zbraň enejbluje kritikly.
  • Musíš tam akorát naparsovat ten textovej fajl s těma idéčkama a nebo tam dát defaultní.
  • Ty kdybys posílal eksepšnu karent tredu, tak jí můžeš poslat úplně komukoli, žejo.
  • Máme to vyrendrovaný jako frejmy v tyfu.
  • To forsneš když sejvuješ prefsy a vono se to ouvrajtne.
  • Lebo on jak louduje tu klasu, tak se to vyjebe a je to v piče.
  • Mě už vizuálně viděl.

Promiňte, odcházím zvracet. Nashledanou příště.

From Russia with love

Devastro is being released in Russia! A localized Russian version of Devastro is about to go into retail stores. Under a different name, as you can see from this revisited CD cover:

The cover was almost completely re-created by Noviy Disk, the company that stands behind the release. For comparison, here is the original cover design that I did:

The new one is more simple and “in your face”, whereas I think my design was more subtle and had more detail.

Config survey summary

Indie game developers might find this useful. What sort of computers will their games run on? Impossible to tell, sure, but easier to predict if you look at statistics for other games. Valve has been publishing their HW Survey Summary for quite a while but people playing their games could be considered hard-core audience. Let’s see what we can dig up for Devastro.

The following data comes from Devastro 1.5 or later so it’s fairly recent. The number of machines this info comes from? 5-digit number, OK? The game has to start successfuly in order to send the stats. This means that machines without OpenGL support are not included in the charts at all. But hey, it’s all damn lies anyway, right?

I had never actually checked this before and there were two big surprises for me here – most people [playing the game] already have dual-core CPUs and most Macs already have Java 1.5. Time to ditch Retroweaver and learn to use multithreading?

Operating system

Mac OS X version

Windows version

Mac CPU architecture

Number of CPUs

Java version on Mac

(The Windows version uses a JRE that is bundled with the game.)

OpenGL implementation

OpenGL vendor

OpenGL renderer

Packing textures

When preparing the graphics for an OpenGL or Direct3D game it’s generally a good idea to pack all images into larger textures instead of creating hundreds or even thousands of small ones.

Packing textures has two advantages. First, it saves video memory because there’s less padding to power-of-two dimensions that’s required. Second, it makes rendering significantly faster because there are less texture state changes (ie. glBindTexture). That’s an expensive operation and doing it 20 times instead of 600 per frame really makes a big difference.

Sometimes it’s called a “texture atlas”.

For Devastro, I did all the packing manually right in Photoshop. Whenever I finished a sprite, I opened all my existing textures, searched for a free spot and pasted the sprite there. Then I had to start a custom sprite editor and select the new sprite within the texture so the game would know it’s coordinates. That was… quite time consuming… to say the least, hehe.

So for my future games I wanted to upgrade my workflow in this area.

The basic idea for allocating the images effectively is creating a quad-tree that keeps track of what parts of the large texture are used and which are free. Then you add your images into the tree, largest first, dividing the quads according to their size and adding more textures as necessary.

There are tools that create a texture atlas for you automatically, but it’s usually done “offline”. I wanted to do it on the fly when the game starts. That way there’s no extra action to be taken between exporting the images from Photoshop and starting the game to see them.

It took me a while to get right but now it’s working perfectly and it’s surprisingly fast, too.

Devastro updated!

Devastro has just gone through a major overhaul. What’s new? Plenty! Now there’s water in the game! And tanks, new enemies, twice as many levels, more intuitive controls, a truly grandiose finale and countless other improvements.




If you’ve been reading this blog you already know about some of the changes but now you finally get the chance to try it out yourself.

What’s new:

  • Tanks
  • Over 50 levels
  • Water
  • Giant flying acid-spitting brains
  • Aliens with laser guns and with animation
  • New and improved sound effects
  • Improved visual effects (thanks to smooth alpha blending)
  • Muzzle flashes for the machine guns
  • Full version now separate from demo (has higher audio quality)
  • Support for localization
  • Improved controls (ie. you can hold the “walk” button)
  • Status bar and better team management
  • Clouds that cast shadows on the ground (looks great)
  • More cutscenes (and new graphics for the General)
  • Reworked finale (hilarious!!)

Devastro updates, part II

Just finished the AI for the new aliens! Now the only remaining “big” item on my TODO list is to finish the rest of the new levels and go through the original ones and add some of the new stuff to them as well.

Otherwise it’s just small stuff, fit and finish as they say, some code cleanup, testing, writing announcements, updating website etc.

And now – screenshots!

You can swim. If you are in water, the aliens won’t see you.
You can swim. If you are in water, the aliens won’t see you.

Can you guess how it’s possible to drive the raft?
Can you guess how it’s possible to drive the raft?

New graphics for the General and a new font.
New graphics for the General and a new font.

First encounter!
First encounter!

Getting overwhelmed, run!!
Getting overwhelmed, run!!

Giant flying acid-spitting brains from Outer Space! Ironically, they are very very dumb.
Giant flying acid-spitting brains from Outer Space! Ironically, they are very very dumb.

This is art

People have had countless arguments whether games are art or not. Now it’s clear – yes, games are art. At least when something goes wrong. Observe:

A new Vertex array rendering technique doing its magic.
A new Vertex array rendering technique doing its magic.

I sense a strong “goddamn you OpenGL” accent here.
I sense a strong “goddamn you OpenGL” accent here.

One of the author’s recent works called “WTF is going on!?”
One of the author’s recent works called “WTF is going on!?”