Building a list of interfaces
Obviously I''m a beginner, so I have no idea how to do this
I want to create a Strategy Game in which the main playable element is a logical organization of Units called a Cluster. Instead of having one graphic icon representing one instance of something (say for example one tank), instead an icon represents a group of units. For example, I can create a Cluster that is made up of 4 tanks, and call it a Cluster TankPlatoon.
Cluster''s are essentially container classes (not sure whether I should use vectors or some other kind of template) that hold the Unit classes inside them. Here''s my problem. It is possible to create a Cluster with heterogenous Units inside them. For example, I could create a MechanizedInfantryPlatoon Cluster which is made up of 4 IFV (Infantry Fighting Vehicle) and 4 squads of infantry. Obviously the infantry have different member functions than the IFV''s do (for example, the infantry Move() will be less than the IFV Move(), and non-AT infantry will be unable to Attack() armored targets, but the IFV.Attack() will be able to .
So, how do I build a container class that can query the public interfaces of all the individual Units that make it up. That way, when the player issues an order to the Cluster container, the container will know whether that function is available, and also what function to call from which unit?
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
You could have all of the objects derive from a class that has all of the supported functions available, but if a particular function is impossible for one unit, have that unit's function throw an exception. Then catch that in your container class.
Or you could provide a bit-field for features, and request all of the contained units' bit-fields, then AND them together. The result bitfield would only have 1's if every unit supported an action.
The first method only really makes sense if you want any unit that can perform an action to do it, not considering whether anyone cannot. The units that cannot perform the action also tell the container class so you can pop up "these units will not participate in the action" to the player.
The second menu would allow you to pop up a menu with only commands that everyone could carry out.
[edited by - Nypyren on January 18, 2003 10:05:41 PM]
Or you could provide a bit-field for features, and request all of the contained units' bit-fields, then AND them together. The result bitfield would only have 1's if every unit supported an action.
The first method only really makes sense if you want any unit that can perform an action to do it, not considering whether anyone cannot. The units that cannot perform the action also tell the container class so you can pop up "these units will not participate in the action" to the player.
The second menu would allow you to pop up a menu with only commands that everyone could carry out.
[edited by - Nypyren on January 18, 2003 10:05:41 PM]
If I have a superclass with all possible functions, don''t they call that "percolating upwards" and avoid having designs like that?
While I see how it could be done that way, there''s another fly in the ointment.
Let me break down my terminology better:
Unit class- A Unit object is a singular instance and can be thought of as building blocks to create what the player REALLY interacts with...the Clusters. A Unit is in turn composed of several other clss objects, namely function modules. For example, a Unit which is a vehicle is made up of several other classes like EngineModule , MobilityModule (is it wheeled, tracked or hover), OffenseModule (what weapons does it have), IntelligenceModule (what is its sensor suite and does it have EW capabilities), etc etc.
Essentially, the Modules are the member functions of the Unit. Why did I do this? Because I want to have a design plug-in architecture so that players can design their own weapons, engines, mobility forms (maybe have mecha, cavalry or grav vehicles for example) etc. If I just hard-coded functions into the Unit class, this would have been very diffucult I would imagine.
Cluster class- This is a container object in which you put in Unit objects. Clusters are actually what the player interacts with in the game, and Order objects are issued to the Cluster. Order objects are essentially function calls to the Cluster as a whole. Cluster''s must therefore maintain some kind of interface list so that when it recieves an Order, it knows if it is capable of doing it, and possibly override functions.
Here''s the problem though, I could have something like Order->Attack(), and the container would see that there are Unit Infantry objects and Unit Tank objects, and could therefore call either Unit''s appropriate function call. However, let''s say that one of the Order''s parameters is Order->Attack(Cluster TankPlatoon), where the argument is the target that will be attacked. Well, the Infantry.Attack() method is useless against armored units (unless it has an attached AT team), but the IFV.Attack() method can be used.
Should I just use a bit field like you suggest and when the Infantry.Attack() gets called, throw an exception? The problem with that is would the Infantry Units therefore waste their attack? I suppose I could set up some conditional logic that said, "check the target parameter...IF AttackClass < ArmorRating...then do not attack target".
But would the building of an interface list even be necessary? I''d test these ideas out, but unfortunately, while theoretically I understand pointers, when I try to code my own ideas, I keep running into compile errors, so I know I''m getting my polymorphic code section wrong somehow.
While I see how it could be done that way, there''s another fly in the ointment.
Let me break down my terminology better:
Unit class- A Unit object is a singular instance and can be thought of as building blocks to create what the player REALLY interacts with...the Clusters. A Unit is in turn composed of several other clss objects, namely function modules. For example, a Unit which is a vehicle is made up of several other classes like EngineModule , MobilityModule (is it wheeled, tracked or hover), OffenseModule (what weapons does it have), IntelligenceModule (what is its sensor suite and does it have EW capabilities), etc etc.
Essentially, the Modules are the member functions of the Unit. Why did I do this? Because I want to have a design plug-in architecture so that players can design their own weapons, engines, mobility forms (maybe have mecha, cavalry or grav vehicles for example) etc. If I just hard-coded functions into the Unit class, this would have been very diffucult I would imagine.
Cluster class- This is a container object in which you put in Unit objects. Clusters are actually what the player interacts with in the game, and Order objects are issued to the Cluster. Order objects are essentially function calls to the Cluster as a whole. Cluster''s must therefore maintain some kind of interface list so that when it recieves an Order, it knows if it is capable of doing it, and possibly override functions.
Here''s the problem though, I could have something like Order->Attack(), and the container would see that there are Unit Infantry objects and Unit Tank objects, and could therefore call either Unit''s appropriate function call. However, let''s say that one of the Order''s parameters is Order->Attack(Cluster TankPlatoon), where the argument is the target that will be attacked. Well, the Infantry.Attack() method is useless against armored units (unless it has an attached AT team), but the IFV.Attack() method can be used.
Should I just use a bit field like you suggest and when the Infantry.Attack() gets called, throw an exception? The problem with that is would the Infantry Units therefore waste their attack? I suppose I could set up some conditional logic that said, "check the target parameter...IF AttackClass < ArmorRating...then do not attack target".
But would the building of an interface list even be necessary? I''d test these ideas out, but unfortunately, while theoretically I understand pointers, when I try to code my own ideas, I keep running into compile errors, so I know I''m getting my polymorphic code section wrong somehow.
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
Ok... maybe instead of having complicated inheritance all over the place, you have two main functions: bool ProvidesAbility(...) and void ExecuteAbility(...). Each of those functions would take an Order class...
This Order class would contain what type of order it is and all of the arguments (you could do an array or derive different Order classes -- AttackOrder, MoveOrder, DefendOrder, etc.)
When a unit runs it''s ExecuteAbility(...) with an AttackOrder, it will in turn call it''s own Attack(...) function (if it''s available).
When you want to see if a cluster can perform an order, call ProvidesAbility(order); on all of the units in the cluster, and see if none, some, or all of the units can perform it, and decide what to tell the player.
If you decide to tell the cluster to perform the order, call ExecuteAbility for each unit in the cluster. If a unit can''t, they can simply do nothing, or fall back to a similar order.
If you REALLY got tricky, you could provide a priority list of Orders, "if you can''t do the first one, do the second. if you can''t do the second, etc..."
In your IFV/Infantry cluster, say the IFV can move 4 units and the Infantry can move only 3. Since the entire cluster can''t move 4 units together, either you move at the slowest rate, or you split the cluster. You would have to call "ProvidesAbility(new MoveOrder(4 units));" to find out that "some" units can move 4 units. Your game logic can then decide that that move is impossible since you can''t split a cluster (or whatever), and the player''s cursor turns red.
If, however, you''re attacking a neighboring cluster, and they have 3 tanks and 5 powered-suit infantry, and you have a cluster of 2 tanks and 6 anti-infantry rocket batteries, you could give the cluter two orders: Attack(Tanks), Attack(Infantry) (in that order). Since the tanks can hit other tanks, they will perform that without first slaughtering the infantry. The AIRBs wouldn''t be able to dent the tanks with their puny Scatter rockets, so they''d check the next Order in the list, which is "fire at infantry"... which they can do.
This Order class would contain what type of order it is and all of the arguments (you could do an array or derive different Order classes -- AttackOrder, MoveOrder, DefendOrder, etc.)
When a unit runs it''s ExecuteAbility(...) with an AttackOrder, it will in turn call it''s own Attack(...) function (if it''s available).
When you want to see if a cluster can perform an order, call ProvidesAbility(order); on all of the units in the cluster, and see if none, some, or all of the units can perform it, and decide what to tell the player.
If you decide to tell the cluster to perform the order, call ExecuteAbility for each unit in the cluster. If a unit can''t, they can simply do nothing, or fall back to a similar order.
If you REALLY got tricky, you could provide a priority list of Orders, "if you can''t do the first one, do the second. if you can''t do the second, etc..."
In your IFV/Infantry cluster, say the IFV can move 4 units and the Infantry can move only 3. Since the entire cluster can''t move 4 units together, either you move at the slowest rate, or you split the cluster. You would have to call "ProvidesAbility(new MoveOrder(4 units));" to find out that "some" units can move 4 units. Your game logic can then decide that that move is impossible since you can''t split a cluster (or whatever), and the player''s cursor turns red.
If, however, you''re attacking a neighboring cluster, and they have 3 tanks and 5 powered-suit infantry, and you have a cluster of 2 tanks and 6 anti-infantry rocket batteries, you could give the cluter two orders: Attack(Tanks), Attack(Infantry) (in that order). Since the tanks can hit other tanks, they will perform that without first slaughtering the infantry. The AIRBs wouldn''t be able to dent the tanks with their puny Scatter rockets, so they''d check the next Order in the list, which is "fire at infantry"... which they can do.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement