Real Time Strategy handling sprite/character actions

Being that Real Time Strategy games are my favorite genre of game, I got back to working on my own. Of course I am at the very beginning still but one of the things that constantly comes to mind most of all things required in the game is how to handle the actions of the sprites or characters in the game.

If the player tells 20 workers to gather wood, 20 workers to gather gold and 20 workers to gather food, would the best approach be to iterate through a sprite instance with an some kind of action funtion that makes sure the character keeps doing what he is told, returns the goods and goes and gets more? With 100’s of peeps on the map that would be a lot of calculating.

How did Warcraft 2 do it? How do the newer games do it? This is one of the biggest things I cant really wrap my brain around.

Any ideas would be great, thanks for any pointers!

In an RTS, each unit has AI. It needs to do path finding, target choosing, weapons firing, etc. It is a lot of processing, but it is what must be done. You can make use of multithreading. It should degrade gracefully when there are too many units. Basically the units become dumber. Some units will get lucky and be given enough time to work out what they are doing, others won’t and they may not move for a short while after being given orders, etc.

Have you played SupCom? Best RTS ever! Mouse wheel zoom is revolutionary. It uses Lua scripts for the AI (and much of the rest of the game). You can even open the game’s data files and read the scripts. The unit cap is 1000 and it supports 8 players. You’d need a buff machine for 8000 units, but it is doable.

That does make sense. At first read I was saying in my mind there is no way I could program each little dude to have its own AI but then I started thinking, I know real shocker, if your dude is just standing there and an enemy unit passes by, would you want your guy to just let him walk by? What if your people get attacked while gathering resources? You want them to just keep chopping wood? Nice enlightening there. Thanks. One question remains though is how would you set it up. Would you create an array to hold each dude, then say every couple of game loops check the status of the guy and if hes doing A make sure he follows with b?

I dont really see any other way to do it, so I guess this would definitely give me enough to get working with.

I havent played Supreme Commander, even though RTS games are my favs, I got tired of paying $50+ for games that royally sucked. Now that its price is down, I may have to take another gander based on your opinion. Of course I’m still playing Stronghold Crusader, once I finish it, Ill move up to Stronghold 2, lol.

Thanks for the input!

Iv had a bit of experience with RTS AI.

This is basically the layout of my engine.

each unit has its ‘int’ state.
state can be anything like, attaching, walking, standing, building, upgrading, casting…
and while a unit is in a current state, each game cycle it continues through the action required of that state.

if you are worried about performance, when sending multiple orders to a unit, in regards to path finding as it will be the most cpu intesive process of an RTS. you can always set up a queue so that not all orders are sent in 1 game cycle but will get sent in the next tick. the loss in interaction time is unnoticable to the player.

but for starters i dont recommend this. path finding is honestly the only real challenge in an RTS. things to consider are how many levels of height your game will have (eg, water,low land, high land, sky: would be 4 levels). the only other thing that is a challenege with path finding is making the path seem diagnal when all calculations are done on a tilemap.

for this sort of a problem. In each Unit class, set another Unit class called target. and if the unit target is equal to null, then he can have a new enemy target. but if he currently has a target check range of enemy then he will run after or attack his current target.

I am finally putting the pieces together in my RTS game.

First I have a large scale. The goal is 5k units per player with up to 100k total. This is working fine so far. But perhaps people prefer micro games. Anyway I digress.

You can’t just run different units in different threads unless you are careful to synchronize. The standard way to do networking is to perform simultaneous simulation on each machine, so that you can’t have a unit running at a different speed on one machine vers another or you get desync.

I use an Agent system. Almost everything is an agent. Agents communicate via messages. If an agent sends a message, it can’t be received until the next game turn. This way any agents step method can be called at anytime and even different agents exe’d in different threads without loss of synchronizing as long as i sync on game turns.

Some messages are commands. Like gather resources. This command runs a routine, that is implemented as a coroutine. This makes state tracking much easier as i can write code something like this…


void gatherFood(){
      p=findClosestFood();
      walkTo(p);
      pickUpFood();
      walkTo(Base);
}

void walkTo(p){
    while(notAt(p)){
        stepTowards(p);
        Yield();//won't exe from here till next game turn. 
    }
}

void pickUpFood(){
    for(int i=0;i<turnsToPickUpFood;i++){
       gatherSomeFood();
       Yield();//again we "pause" until the next game turn. 
    }
}

Each game turn i just call Agent.step() and it runs from the last Yield() to the next Yield(), till done, and then grabs the next command.
This makes even complicated behaviors quite easy to do, since you just write the code as a plain procedure rather than as a state machine.

To deal with performance, path planing is only done on the client machine. If they want awesome kick arse stuff then they may need to sacrifice some response time if they have a slow machine. I also don’t buy that path planing is the biggest thing in RTSs. Its just one piece of the puzzle. For example using true los would kill performance to the point where its never used, so path planing stays at the top of the cpu hogs outside graphics.

Great info and very very much appreciated.

I’m not too worried about Path finding right now, I did come up with a pretty good routine in Blitz, spent alot of time working on it and reading up on pathfinding, when the time comes, Im confident I’ll be able to overcome that one.

Using a queue for orders sounds like a great plan, I’ve heard people talk about this before but I have yet to implement this kind of system. I may look into that further.

The ease of the agent messaging system is attractive also and we all love the ease of creating new commands and such.

I definitely have a lot to think about and once I start instituting a system, I’m sure it will get refined many many times. You all have given me something to refer to at those times. I really appreciate the help.

Thanks!

As you will be using alot of memory for holding the “units” you need to make sure that you only update WHEN required, especially if it is a server/client game so you send less data for quicker processing.

For example, the best way to do it for a server-client game would be like this, please note this is a quick example which could obviously be updated to make it more efficient.

public void update()
{
	for (Unit unit : unitList)
	{
		if(unit.hasUpdate())
			unit.writeUpdate(BUFFER_CLASS);
		unit.writeOther(BUFFER_CLASS);
	}
}

Now, basically unit.writeUpdate() would have more flags, for example;

Animation updates;
Movement updates;
Graphic updates;

Each of which would change a flag byte and then write the data, then the unit.writeOther writes the information which is required, such as “unit index”, for clientside entity finding, etc.

Example of very basic writeUpdate() method;

public void writeUpdate(ByteArrayOutputStream buffer)
{
	byte flag = 0;
	if(animationUpdateRequired)
		flag |= 0x1;
	if(graphicUpdateRequired)
		flag |= 0x2;
	if(movementUpdateRequired)
		flag |= 0x4;
	buffer.writeByte(flag);
	if(animationUpdateRequired)
	{
		buffer.writeByte(animationId);
		buffer.writeByte(animationSpeed);
	}
	if(graphicUpdateRequired)
	{
		buffer.writeByte(graphicId);
		buffer.writeShort(((graphicX << 8) + graphicY));
	}
	if(movementUpdateRequired)
	{
		buffer.writeByte(xPosition);
		buffer.writeByte(yPosition);
	}
}

This way you only send what the client needs, and you don’t update anything that hasn’t changed.

Obviously you have to reset the flags after each update and set them when the unit moves for example.

Hope it helps.

The KryoNet library has the ability to keep track of what was sent from/to each client, do a binary diff on what was last sent, and to transparently send only the minimum needed.