Advertisement

Unit design question

Started by September 23, 2002 10:53 AM
44 comments, last by Dauntless 22 years, 3 months ago
quote: Original post by Dauntless
I can see what you mean by splitting up data between constants and variables. I was originally thinking of having a two dimensional array with the first "row" being the initial values of the attributes, and the second "row" being the current values. I guess that would be a more unwieldy not to mention not as well documented way of doing it. And it helps having constants to help out any debugging I guess, since those values couldn''t change. I was thinking a constant type could be MobilityType = "tracked"; (or "wheeled" for example....although I don''t know if it would be good to use them as literal constants or not).


I''d avoid string literals where possible - they take up lots of space, plus you have to parse them. It is much easier and quicker to use enums.

quote:
Oluseyi-
I think I see what you are saying about loading values from external files, but what about object data persistence? Suppose for example that a Platoon of infantry has an attached anti-tank weapon team (my unit design will be modular), and that the Platoon takes damage and the AT weapon team is killed. Along comes Platoon B who still has its AT team, but they have no ammo. How can you make it so that the data intrinsic to the unit is not lost?


Simple - you just don''t destroy the object. Instead you just flag it as dead, so that it no longer moves or gets AI updates, and leave it on the map until someone salvages it. If population is a problem, stick a timer on them so they disappear after a certain period of time.


quote: Is Ruby on the order of complexity of Python? Pickles in Python seem cool, but I know nothing of Ruby. It also doesn''t help that I''m totally self-taught so I don''t have anyone (other than the forum here) to get any guidance with
Nothing is as simple as Python. The only thing that Ruby offers that Python does not is the ability to subclass builtin types, which is of dubious usefulness. Besides, throw in Pygame(SDL wrapper for Python) and Python bindings for everything and its mother(Python bindings are actually easier to make than Perl bindings, starting cold, because of boost:ython), and Ruby seems positively superfluous. Of course, some people say that about Python, with respect to Perl.

As for your virtual dilema(pun most definately intended), declaring a function virtual means that a pointer to a base class that holds a reference to an instance of a derived class(that a pointer to a base class can hold a reference to an instance of a derived class is called polymorphism) will call the derived class''s overrided function instead of the base class''s function. Otherwise, a pointer to a base class will always call the base class''s function, regardless of whether or not that function has been overloaded in the actual instance the pointer holds. Perhaps easier to understand with an example:


  #include <iostream>using namespace std;#include <cstdlib>class base{public: void myfunc(); };class derived : public base{    public: void myfunc(); };class vbase{public: virtual void myfunc(); };class vderived : public vbase{    public: void myfunc(); };int main(){    base* tobase;    vbase* tovbase;        derived d;    vderived v;        tobase = &d    tovbase = &v    //  base::myfunc();    tobase->myfunc();//  vderived::myfunc();    tovbase->myfunc();        system("pause");}void base::myfunc(){cout << "base::myfunc()" << endl;}void derived::myfunc(){cout << "derived::myfunc()" << endl;}void vbase::myfunc(){cout << "vbase::myfunc()" << endl;}void vderived::myfunc(){cout << "vderived::myfunc()" << endl;}  


See? The output of this program is in those two comments:
base::myfunc()
vderived::myfunc()

You might expect that the output would be:
base::myfunc()
vbase::myfunc()

The reason it is not is the reason for the existence of the virtual keyword.
---New infokeeps brain running;must gas up!
Advertisement
I do it like Sandman, there''s attributes that don''t change and there''s attributes that do.

Okay, now a question from me, what''s with all the pointers?
Why would you need a pointer to the constant data?
The only thing I can think of is if somehow the number of unit types change(like if you design your own). Otherwise, we have things called enum''s and arrays.
flarelocke-
arrgghh, my head's sorta spinning, but I think I get it. tobase* is a pointer of a 'base' class, but you have created an object of type derived, which is a subclass of base. So when you called tobase->myfunc, even though it holds the address of a derived object, it's really calling the base::myfunc (instead of derived::myfunc.....oww, my head really hurts now...). And since tovbase is a pointer to a vbase type but holds the address of a vderived type, it is doing the same thing. But since vbase has it's method declared as virtual, when tovbase->myfunc is called, it's overridden(?) and you get the print out of vderived::myfunc, instead of vbase::myfunc as before (since the pointer is actually holding the address of a vbase type).

Wow, that took me like 30 minutes to get through my head...and I'm still not sure if I really understand it.

Oluseyi-
I'm still trying to get a handle on your code for the UnitAggregation. Does the Unit::SignalAddedtoAggregation method mark that the Unit class has been...hmm, searching for words here...."joined" to the UnitAggregation? Part of my trouble is I forgot what the this-> pointer is (is it a pointer to the default constructor?), and I'm not familiar with the std::list standard C library function. Can you explain what's going on for a neopphyte programmer?

P.S. how do you guys add the code snippets into the post? What tags do you use?

[edited by - Dauntless on September 23, 2002 9:24:02 PM]
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
Roland-
I think it would make sense in my design since units would be modular and customizable. Actually, I''m wondering how I''m going to be able to do it. If you''ve seen the game Kohan, it''s sort of how I''d like to do things...basically you have a unit template into which you add your modularized components. For example, lets say you define a platoon as 36 men. You can break up the "modules" into 4-8man groups and 2-2man teams. For an armored platoon, you can define it as 4 modules, each module representing one vehicle.

Actually, if I''m reading Oluseyi''s code snippet at all correctly, I''d have to implement something like the UnitAggregation class, since there should be a way to link all the modules into one cohesive unit.

I really want my game to have a feel like you are creating an organized army....complete with a table of organization and equipment. The advantage is that you can design your own armed forces template. The caveat to this is that while you can go hog-wild with organized unit varieties, the price will be reflected in logistics and maintenance. This is why armies typically only have a handful of armored vehicles, and a score of different infantry types, and that unit organization is relatively standard across the board (with some exceptions).
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
quote: Original post by Dauntless
flarelocke-
arrgghh, my head''s sorta spinning, but I think I get it. tobase* is a pointer of a ''base'' class, but you have created an object of type derived, which is a subclass of base. So when you called tobase->myfunc, even though it holds the address of a derived object, it''s really calling the base::myfunc (instead of derived::myfunc.....oww, my head really hurts now...). And since tovbase is a pointer to a vbase type but holds the address of a vderived type, it is doing the same thing. But since vbase has it''s method declared as virtual, when tovbase->myfunc is called, it''s overridden(?) and you get the print out of vderived::myfunc, instead of vbase::myfunc as before (since the pointer is actually holding the address of a vbase type).

Wow, that took me like 30 minutes to get through my head...and I''m still not sure if I really understand it.
If what you said is any indication, then yes, you do. It''s not usually something you''ll "try" to understand. You''ll use it one day and expect it to work that way, it won''t, and then you''ll add the virtual keyword, and then it will seem extremely natural to you to do it this way. Or you could write an abstract base class, and realize that doing so is essentially useless or worse, nonsensical, if this was not the behavior.

quote: Oluseyi-
I''m still trying to get a handle on your code for the UnitAggregation. Does the Unit::SignalAddedtoAggregation method mark that the Unit class has been...hmm, searching for words here...."joined" to the UnitAggregation? Part of my trouble is I forgot what the this-> pointer is (is it a pointer to the default constructor?), and I''m not familiar with the std::list standard C library function. Can you explain what''s going on for a neopphyte programmer?

The this pointer points to the object calling it. It''s pretty useless except when you''ve got an argument to a member function that has the same name as a member variable of the class to which the function is a member as well. Then, the compiler doesn''t know whether you''re talking about the argument or member variable, so it just assumes you''re talking about the argument. So you have to use this->a = a to show that you want to assign the member variable to the argument of the member function. Also sometimes used as a more pleasant alternative to prefixing all member variables with m_, so you get the same benefit, but only have to write it once per function. Also useful when you need to pass a pointer to the calling instance to an outside function inside one of the instance''s member functions.

The std::list function is just a container(you can think of it as a sort of glorified array the size of which can vary as you push_back() more variables onto the array). Not to be nitpicky, but it''s part of the standard template library, which is, IIRC, part of the standard C++ library(not the standard C library).

Basically, a unit hands off its stuff to the unit aggregation when it dies. The unit aggregation may or may not have a visible analog; there isn''t enough code to say for sure. It could be a purely abstract concept used to store ammo for the dead, or it could be a platoon, and the platoon gets use(in the programming sense; not necessarily given to an arbitrary unit within the platoon for their use, but the platoon code could decide to do so, or to just leave it there until the platoon is completely destroyed(forgotten equipment disappears?).) of the dead unit''s ammo.

quote: P.S. how do you guys add the code snippets into the post? What tags do you use?

We use the source tag, square bracketed in a manner similar to the code and/or quote tags. Please don''t use it for short and/or few lines of code. Having a 400 pixel tall ugly iframe where I could see a 10 pixel tall line of code is annoying.

It''s technically in the faq, but the faq isn''t very well written, so it''s somewhat difficult to find.
---New infokeeps brain running;must gas up!
Advertisement
I''m not too sure, but I remember someone saying in some article here that polymorphism and "advanced" class abilities are something to avoid in games. Is this true? Also, I think that in the past, most games were less "data" driven, and more variables were hard coded. Nowadays, you see many games driven off databases, making it much easier for map editors to edit values and expansion packs to be made. Patches are also easier to create. I remember that creating mods for games used to be really hard, cause you had to either patch the exe, or patch what ever external files you could find. Now modding is much easier.
quote: Original post by Dauntless
Oluseyi-
I''m still trying to get a handle on your code for the UnitAggregation. Does the Unit::SignalAddedtoAggregation method mark that the Unit class has been...hmm, searching for words here...."joined" to the UnitAggregation? Part of my trouble is I forgot what the this-> pointer is (is it a pointer to the default constructor?), and I''m not familiar with the std::list standard C library function. Can you explain what''s going on for a neopphyte programmer?

Flarelocke''s explanation is perfect. The only thing I have to add to that is that the UnitAggregation class allows one to issue commands to the entire aggregation (eg, a platoon, which is really an abstract concept), which then iterates over the individual units attached to it:
void UnitAggregation::IssueCommand( const Command & c ){  for_each( units.begin(), units.end(), bind1st(IssueCommand(c)) );} 


quote: Original post by fuzztrek
I''m not too sure, but I remember someone saying in some article here that polymorphism and "advanced" class abilities are something to avoid in games. Is this true?

I wouldn''t agree. Modern/"advanced" programming techniques have been developed to help us deal with large applications and their designs. In the past, a major roadblock to using them effectively was that they made your application noticeably slower, but modern compilers do a much better job (plus modern processors are faster).

quote:
Also, I think that in the past, most games were less "data" driven, and more variables were hard coded. Nowadays, you see many games driven off databases, making it much easier for map editors to edit values and expansion packs to be made.

True as this is, database is an often overused term. It sounds impressive and professional and enticing to customers, and is a reasonably familiar concept, so developers may use it instead of "repository."
i think the article said not to go more than 2 levels deep with inheritence. or osmething like that. Perhaps instead of database i should have used "list of variables with values"? Too me a database is just an easier larger way to manage a list of variables with values. I know some companies actually creat their own database, with relationships and everything.

¬_¬
quote: Original post by Fuzztrek
i think the article said not to go more than 2 levels deep with inheritence.

My current project goes, at most, 5 levels deep. I''d actually like to know what everyone thinks of my method for managing units. It may strike some ideas here or, perhaps, I''m heading hard and fast in the wrong direction.

I''ve put a lot of effort towards developing this game "engine" which I want to use for several games in a series. The problem is that I have to move a LOT of units. I''ve got a solution that I''ve been happy with so far, but I''ve been wondering if there may be a better solution.

I''ve had to use a lot of similar techniques discussed here. I have a CUnitDescriptor class that holds the static data for a unit. I also have a CEntity class that holds the dynamic data of the seperate entity. This class points to it''s descriptor.

Normally, I''d have a class that manages all the units. This would hold an array of units, update the units, create and destroy units, etc. I decided to take a different approach with this project. What if the units are in a private static array in the manager class and the manager class contained only methods and no non-static properties? The actual units could be derived from the manager class. The unit would only need a pointer to its own data in the static array which would be assigned in the constructor. The unit cannot see the data of other units (because it''s private) but the manager class can see that data so that it can set up interactions, such as collision.

Am I making sense? And if so, does this sound like a solid plan?

- Jay

There''s an old saying in Tennessee... well, it''s in Texas but probably in Tennessee too. It say''s, "Fool me once, shame on... shame on you... uh... ya fooled meh uh can''t get fooled again."
- George W. Bush


Get Tranced!
Quit screwin' around! - Brock Samson

This topic is closed to new replies.

Advertisement