Advertisement

Ordering system question

Started by September 27, 2002 11:09 AM
26 comments, last by Dauntless 22 years, 3 months ago
quote: Original post by Flarelocke
I''m a bit confused by the code you posted. By using a priority queue of orders, you seem to indicate that the order is a class, but by the example order(move), you seem to indicate that the order is simply a function call.

The priority queue of orders is data private to the orderable object. When an order is issued (and this is why I extended the move method to take an order_issuer object), it is inserted into this queue along with other data. The orderable object thus uses some set of discriminants to determine which order is the most "important" and executes that first. The definition of important has to be able to change, thus wonderfully complicating the commander-commanded interaction without affecting the code.

A company of infantrymen may trust their own luitenant or captain more than an external corporal or other newly-inserted commander - particularly one who has proved his ineptitude (see Band of Brothers for one example of this) - and as such may take the lower-ranked officer''s commands as being more important. Alternatively, events around them may cause certain orders - or even fundamental instincts - to cause them to disregard protocol. That way, your soldiers wont stand in the path of heavy fire and be mowed down like sheep when they know they don''t have a chance. They''ll break formation and seek cover instead (given the appropriate instincts and AI routines).

In effect, there is an order class (which stores details about a particular order) and a set of order-generating algorithms (which operate on all objects that support orderable interfaces - which may or may not be an inheritance scheme).

<aside>
One of the interesting advantages of templates is that it makes it possible to "share" interfaces between completely unrelated classes. If you wished to write an algorithm that operated on arbitrary objects that provided a method doSomething without using templates, you''d need to derive them all from a single base class that provides the virtual doSomething signature and then operate on a pointer to said base class which will actually reference one of the derived classes, thus creating an artificial relationship between your types. None of the derived classes ISA base.

Using templates, however, the method signature only needs to be present in the class for which the template is being instantiated. This is what generic programming enthusiasts and apologists call conforming to a concept. Incidentally, this is what the STL is based on, as std::vector, std::string and so forth obviously don''t inherit from the same classes in all cases. Their iterators do, however, which reinforces the fact/argument that you can use inheritance with templates rather than instead of templates.
</aside>

quote:
I don''t know anything about the runtime costs of polymorphism, so I can''t downplay their significance

They''re not that significant. An array access (dereference), a pointer dereference and you''re pretty much done in most compiler implementations.
Oluseyi (& Dauntless) -
You seem to be taking a rather complex approach to this problem. Would the use of inheritance or templating even been required?? I would have thought each different type of order would be quite unrelated from the others.

The only thing I can see they have in common is the data they are working on (that of the unit), but is this enough to bring them together for inheritance/templating?

My thoughts would be to create a few different order classes, perhaps 6 for the differnet areas you put at the start Dauntless, and in your main game class create one object for each of these classes. Then when you want to move you call the units move function, which in turn calls the move order object (passing a reference to itself in). This order object then changes the internal of the data unit and does whatever it needs to do.

The linking between these order classes would be for them to call each other rather than be inherited. For example the attack order object will probably call the move order object if you are out of range. These two order objects would be completely different in the way they work, but could still work together even if they don''t inherit from the same base order class.

Doolwind
Advertisement
quote: Original post by Doolwind
You seem to be taking a rather complex approach to this problem. Would the use of inheritance or templating even been required?? I would have thought each different type of order would be quite unrelated from the others.

At heart, no. Each order should be atomic (affecting only one state/having only one effect) and external logic should generate additional orders as necessary. But then we''re no longer talking about orders, we''re talking about actions (move, attack, etc). Orders are potentially complex aggregates of actions and timing data/other constraints.

The rationale to my method is that it transparently adds functionality for mutiny, order prioritization and so forth (something I remember being inspired to desire by a thread in this forum quite a while ago).

quote:
My thoughts would be to create a few different order classes, perhaps 6 for the differnet areas you put at the start Dauntless, and in your main game class create one object for each of these classes. Then when you want to move you call the units move function, which in turn calls the move order object (passing a reference to itself in). This order object then changes the internal of the data unit and does whatever it needs to do.

You just violated the data hiding principle - external parties should know nothing of internal data. Why should an (amorphous, I might add) "order" object modify a unit''s position? A unit should receive an order object and modify its own properties according to data in the order. That more closely parallels real life, making the conceptual gap shorter for the developer.

You also talk of the game class creating one object for each order type - essentially globals from the perspective of the units. Ownership is abstractly defined, which is bad (conceptual gap again) and simultaneous execution of identical orders is difficult - if at all possible - since their is only one object for each type of object. Furthermore, as you add new orders you have to create new order classes, add new order methods to the unit class and then add new order-implementing methods. Whew! Maintenance nightmare!

While the problem may appear simple right now, it may no longer be by the time the game is close to shipping. Designing a sufficiently robust method can mitigate this, or even make it a non-issue. Finding the balance between robustness and simplicity is where software engineering magic comes in: very often you can''t get it right until you''ve done it at least once.
quote: Original post by Oluseyi
The rationale to my method is that it transparently adds functionality for mutiny, order prioritization and so forth (something I remember being inspired to desire by a thread in this forum quite a while ago).


You just violated the data hiding principle - external parties should know nothing of internal data.



Sorry, I worded that incorrectly. When you call move on a unit it would call the order object passing a reference to itself. The order object wouldn''t directly change the structure of the unit but instead call another function of the unit (eg the MoveTo function). This seems a bit complex I know, but as it is only happening once when the unit is first given the order the overhead won''t effect performance.
This also allows for mutiny etc as well as when the order object tells the unit to open fire on that unit or move there it can choose not to.
It may seem as though the unit is getting an order twice but in fact it is taking the overall order (which the user gave, such as attack these units) and breaking it into seperate smaller orders which it then gives to the unit (such as move over there, keep cover here, then fire, any of which the unit can reject as each is called upon it). These are the simplest of things, such as move to x,y, of which the implementation must be kept internally to the unit as it knows the only way it can move from where it is to x, y (an aeroplanes implmentation of moveTo will be completely differnet from a tanks, but the order class just tells it to move there).

quote:
You also talk of the game class creating one object for each order type - essentially globals from the perspective of the units. Ownership is abstractly defined, which is bad (conceptual gap again) and simultaneous execution of identical orders is difficult - if at all possible - since their is only one object for each type of object.


Are you implying that a unit would therefore hold the order object until it has completed it?? This seems like a bit of a waste, unless you are going to accept multiple orders, which I don''t know Dauntless is concerned with. Of course I now realise that if you wanted multiple orders you couldn''t just create one of each each order type, but if you were to only have one order being given to a unit at a time then there should be no problem with my approach. Simultaneous execution of identical orders would be quite easy as only one order would be given at a time. If needed you would iterate through a list of selected units and give the order, but once the order is given to one unit it is no longer held on to by that unit and the next unit in the list can use the same object. But yes, you do have a very good point here I overlooked .

Doolwind
quote: Original post by Doolwind
This seems a bit complex I know, but as it is only happening once when the unit is first given the order the overhead won''t effect performance.

Do you cache the order breakdown? If not then the process is repeated each time the unit receives the order (let''s use "order" for the high level instruction and "action" for the individual components the order breaks into).

quote:
It may seem as though the unit is getting an order twice but in fact it(1) is taking the overall order (which the user gave, such as attack these units) and breaking it(2) into seperate smaller orders which it(3) then gives to the unit (such as move over there, keep cover here, then fire, any of which the unit can reject as each is called upon it(4)).

[Labels added for clarity]

Correct me if I''m wrong:
(1) is the unit receiving the order;
(2) is the order;
(3) is some object that breaks the order down; and
(4) is the unit receiving the order

(4) is immaterial since it isn''t part of the process description. Context would suggest that (3) is the same as (1), though your earlier descriptions would instead suggest that (3) is actually the same as (2). Why does the order data need to pass from (1) (the receiver) to (3) and then implicitly back to (1)? What does (3) know about orders or about (1) that (1) itself doesn''t have access to?

Viewed in steps like this, it becomes clear that this method is in fact more complex and less efficient.

quote:
Are you implying that a unit would therefore hold the order object until it has completed it?? This seems like a bit of a waste, unless you are going to accept multiple orders, which I don''t know Dauntless is concerned with.

No, I''m asking what happens when you try to profit from parallelism in order processing? What happens if you do some round-robin processing so that perceived rate is faster? (Though there''s virtually no reason to do round-robin processing at the application level - particularly for games - any more). You can''t share the data as that leaves the order in an inconsistent state. Frankly, unless there''s a good reason for not doing so, energy should not be invested in controlling the lifespans of objects. And if there is a good reason for doing so, preallocating a pool of order objects and then handing out handles to functions that need orders is probably more efficient and flexible.
Oluseyi-

Sorry for being unclear in my description, I''ll give it another go

1. User sends an Order to a unit
2. Unit then calls the specific Order Object
3. This Order object then knows which Action functions of the unit to call to get them to complete the overall Order


So the unit has higher level functions which correlate the orders. They also have lower level Actions which the order object calls.

The calling of the Order object by the unit (2) would be put in the base class of all units (probably abstract), this would make it easy to add new Orders which you said could be hard to do with my approach.

I do see what you mean were you would pass the actual order object to the unit and miss out one of my steps but I feel breaking this up would give a little more flexibility for the future, without a great performance hit.

Doolwind
Advertisement
What about sequential orders? "Matthews. Take Guerny and Cole up to the embankment by the abandoned outpost and stand ready. At my signal, launch two rockets at elevation 23 degrees, 42 degrees North. Be careful."

While that is a dramatization, the scenario is very real - and very necessary to eliminate micromanagement of units (particularly if you only interact with units via the commanding officer). You''re not able to store sequences of orders with your scheme.
Oluseyi-

You are completly correct . And if Dauntless is putting this towards the game I have heard so much about lately (which I am sure he is) then my style will be quite useless won''t it . I am currently putting a more simplified C&C clone together and I was just thinking of how I would best apply it to that, but if you were to queue orders then your way would be required. Thanks for your patience with my dodgy posts.

Doolwind
Thank you for making me explain my rationale to the general public - myself included.
quote: Original post by Oluseyi
At heart, no. Each order should be atomic (affecting only one state/having only one effect) and external logic should generate additional orders as necessary. But then we''re no longer talking about orders, we''re talking about actions (move, attack, etc). Orders are potentially complex aggregates of actions and timing data/other constraints.


Ahhh, this issue was confusing me too. I was getting confused between the order object itself, and the action(s) it represented. Now I see what you mean by the order being atomic....a singular representation that should have only one effect in and of itself, yet may be chained together with other order objects to produce the desired action(s)

quote: Original post by Oluseyi
The rationale to my method is that it transparently adds functionality for mutiny, order prioritization and so forth (something I remember being inspired to desire by a thread in this forum quite a while ago).


Yes, this is very important as orders can be overriden by several factors. The one tha comes off the top of my head is psychology...a unit under duress may not follow orders. The second is that order objects will be embedded in communication token objects. I like to thikn of these tokens like IP packets....they can be dropped, intercepted, or blocked.

so, once I figure out how to implement the order object...and I''m still not sure if I should take Doolwind''s multiple order approach, or your templated approach (since templates are still real fuzzy to me), I''ll have to figure out how to carry the orders to the officer and unit objects.



The world has achieved brilliance without wisdom, power without conscience. Ours is a world of nuclear giants and ethical infants. We know more about war than we know about peace, more about killing than we know about living. We have grasped the mystery of the atom and rejected the Sermon on the Mount." - General Omar Bradley

This topic is closed to new replies.

Advertisement