Elegant Solution for Unit Actions (RTS)

Started by
10 comments, last by Zaei 22 years, 4 months ago
I am at a point where I need to make a descision. I am creating an RTS (ah, another =), and I need to decide on how to implement action control. A simple solution would be to have a great big switch(), and use states. Unfortunatly, due to the overall design, this wont work, because there are no set units (everything is loaded from data, which allows for strange combinations). So, I am thinking about having a set of Function pointers that will make up a units action set. Then, when the user tells a unit to do something, that action pointer is set as the current action, and that function is called on each tick. I just wanted to get some input, to see what other people think of my solution, before I implement. Thanks much. Z.
______________"Evil is Loud"
Advertisement
quote:Original post by Zaei
I am at a point where I need to make a descision. I am creating an RTS (ah, another =), and I need to decide on how to implement action control. A simple solution would be to have a great big switch(), and use states. Unfortunatly, due to the overall design, this wont work, because there are no set units (everything is loaded from data, which allows for strange combinations). So, I am thinking about having a set of Function pointers that will make up a units action set. Then, when the user tells a unit to do something, that action pointer is set as the current action, and that function is called on each tick.

I just wanted to get some input, to see what other people think of my solution, before I implement. Thanks much.


I'm a fan of class factories and they might just work for you here. If you don't know what they are your in luck there's an article here on GameDev and all over the net you can get info on this pattern.

Essentially, I see something like this:
Every action that your units can perform would be assigned a number and that number is associated with a class. Then when the unit is directed to perform that action the class associated with the action is instantiated from the class factory and 'attached' to the object performing the action.

This is just off the top of my head --- you may find another design pattern that fits the bill much better than the class factory... but anything beats a big switch statement... in terms of maintainability and reuse switch statements suck

HTH,

Dak Lozar
Elysian Productions, Inc.

ADDENDUM:
I should clarify my statement concerning switch statements sucking In the class factory you may have a switch statement - that is O.K. because the switch is in a centralized location and is the only place you need to update with the addition of new classes that the factory understands or handles. There are ways around the use of switch statements in factories one was is to use a STL map to hold the id:class pair. Another way is to utilize a Windows dll and to pull the class from the dll using the Win32 API GetProcAddress().



Edited by - Dak Lozar on December 14, 2001 2:08:06 PM
Dave Dak Lozar Loeser
"Software Engineering is a race between the programmers, trying to make bigger and better fool-proof software, and the universe trying to make bigger fools. So far the Universe in winning."--anonymous
This harkens to a quote of mine in the past that I unfortunately can''t find. I asked this question and it was never answered:
How would you create a pattern for a "default action" in an RTS?

Example: Starcraft (I hope we''re all familiar with it, I still play that game at lunch at work). The right mouse button is the default action key. If you have a military unit selected and right-click on another of your own military units, it follows the clicked unit. If you have a repair unit selected and click on another military unit, it repairs it. If you right-click on an enemy unit, it attacks it.

Now, the different behaviors for the military and repair units are easy to model--just have a virtual Unit::RightClickAction (Object &target). However, how do you figure out what to do for different targets?

In other words, there are two parties in the action: the source and the target. It''s easy to take different actions if your source is of different types, but what to do with the target?
Design Patterns are a wonderful thing. I had forgotten I had that book ::smacks self in the face:: I think that the State Pattern fits the bill even better.

Stoffel: To simplify the example, we can assume that there are 3 types of units on the field, Friendly, Neutral, and Enemy (we discard units that arent affiliated with a certain side, but may attack you, or the enemy at will). So, you have to build a context action for the default, depending on the type. If the target is an enemy, attack, else move. Since there is descision logic here, this would probably go into game code, instead of a state, for reuse. I would build all of this into a simple function CreateContextAction(Object* source, Object* target), which would determine what actions the source should take, and set the states up, and put them into the correct places.

Now that I think about it, an even more effective solution would be to create a queue of State objects, so that you can order a unit to "Go Here X, Y", then "Build This Building Here B, X, Y", etc.

Z.
______________"Evil is Loud"
quote:Original post by Stoffel
<SNIP>
In other words, there are two parties in the action: the source and the target. It''s easy to take different actions if your source is of different types, but what to do with the target?


Stoffel - can you elaborate a bit more?

I think I understand what you are asking so... I''ll step out on a limb and try to help - or at least extend the conversation a bit more

From what I gather your concerned with selecting a source unit and then selecting multiple targets of different types... is this correct?


SELECTED : SOURCE1, TARGET1:TYPEA, TARGET2:TYPEA, TARGET3:TYPEB


So we have a source selected (SOURCE 1) and we also have 3 targets - Two of type A and one of type B.

Take a look at
this site for a description of some of the more widely used patterns. Specifically the Behavioural Patterns section as I think what you are asking pertains to behavior as opposed to creation.

And if your really into design patterns I suggest you take a look at the following... as I found it interesting and amusing at the same time:
Big Ball of mud


Dak Lozar
Elysian Productions, Inc.
Dave Dak Lozar Loeser
"Software Engineering is a race between the programmers, trying to make bigger and better fool-proof software, and the universe trying to make bigger fools. So far the Universe in winning."--anonymous
yeah Stoffel, I was actually thinking about this yesterday. I think the problem is kind of like overloading functions. Say you have A,B,C all derived from Z, or if not derived they act close enough (there could be strategies or whatever).

So Z has a method: virtual void act(Z*)=0; A,B,and C all have to implement it. Each one of them wants to actually implement three methods: void act(A*), void act(B*), and void(C*) so in all you''d have nine results. However you can''t do that, because you don''t have virtual parameters, just virtual method invokers.

Just thinking as a I go but how about something like this?
  void act(Z* z){    if(dynamic_cast<A>(z))        sub_act(static_cast<A>(z);    else if .... //for all subclasses}  


then you have sub_act pure virtual and overloaded for every class (maybe use templates) and every subclass can implement all methods. You do end up writing N^2 methods, so if you have a lot of sub classes you''ll want to do it some other way.
I have the say problem with an rts I''m working on. I''m thinking about adding a little scripting to my unit data to control what they do.
Scripting logic is probably a bad idea, unless you REALLY know what you are doing, and are willing to commit to the time to create a really good script debugger, and other tools.

Z.
______________"Evil is Loud"
the function pointer syntax is pretty ugly, you could use an object instead, with a virtual function execute() (or overload operator() ) and use those instead.

The way I''m doing mine (it isn''t RTS, more of a gauntlet/zelda/diablo kind of thing) is that I have a stack of transformations called to_do. Then whenever the stack is empty I ask the unit''s controller (either AI, or the player) for an action (using an enum for now). Then I use that to index into a map of TransformationGroups. TG''s store pointers to various transforms, maybe I''ll chain them all together. Then each tick you pop off the top TG and for each transformation you call Apply(thisUnit) I think the system is going to work pretty well. Some nice things: the unit class doesn''t have many variables, just pointers to a controller and puppet which it owns, and a pointer to a map of actions to transforms which it shares. The controller tells it what to do (like step forward, attack, etc..) map takes those actions and gives you a set of transforms to apply (such as change image, change x,y coords, etc..) so it tells you how.
I am using State Objects, with OnEnter(), OnExit(), and Update() functions. In each actor, there is a queue of these objects (stacks can give you some problems, because they are FI,LO, something that gets pushed on, may never get popped off). So, I simply create a new object, and add it to the stack. I can then subclass the state object, for a moving state, attacking state, etc.

Z.
______________"Evil is Loud"

This topic is closed to new replies.

Advertisement