Advertisement

Auto-move feature in Warcraft II/Starcraft

Started by November 07, 2000 11:24 AM
9 comments, last by Yanroy 24 years, 2 months ago
You know that neat feature in Warcraft II and Starcraft where the units do things automatically when you click the right mouse button? How would you implement something like that? How does the game tell whether the right click means chop wood, board a transport or repair a building? I want to encapsulate this in the unit that is being used at the time of the mouse click, which complecates things. All the units in my game are derived from various subtypes of CUnit, so they have no working knowledge of the other unit types, and I handle all the units polymorphiclly (RunAI() overridden from CUnit, Draw() overridden from CUnit, Move() overridden from CMobileUnit...). I think I just went off on a tangent with my own question Please help, if you can figure out what I mean (the longer I type, the less I know what I mean!). --------------------

You are not a real programmer until you end all your sentences with semicolons; (c) 2000 ROAD Programming

You are unique. Just like everybody else.

Yanroy@usa.com

Visit the ROAD Programming Website for more programming help.

--------------------

You are not a real programmer until you end all your sentences with semicolons; (c) 2000 ROAD Programming
You are unique. Just like everybody else.
"Mechanical engineers design weapons; civil engineers design targets."
"Sensitivity is adjustable, so you can set it to detect elephants and other small creatures." -- Product Description for a vibration sensor

Yanroy@usa.com

Every unit would have its own logic interpreter. When you click on something, it sends what you clicked on into the unit''s logic interpreter.

different things to click on:
- enemy
- ally
- ground
- minerals
- etc.

then, each unit would look at what was clicked on, and decide on an appropriate action.

like, in starcraft, if you have an SCV selected, and click on minerals, it sends that info to the SCV logic interpreter, and it then knows that it is meant to mine the minerals.

===============================================
If there is a witness to my little life,
To my tiny throes and struggles,
He sees a fool;
And it is not fine for gods to menace fools.
This is my signature. There are many like it, but this one is mine. My signature is my best friend. It is my life. I must master it as I must master my life. My signature, without me, is useless. Without my signature, I am useless.
Advertisement
You know, that''s a really good question. I can''t think of a way to do it without using RTTI. Here was my first thought:

    // Base class:CActionMessage* CUnit::makeDefaultAction (const Target& tgt) = 0;//Derived unit classes:CActionMessage* CMarine::makeDefaultAction (const Target& tgt){  // marines always attack whenever right-clicked on enemy  if (tgt.isEnemy)    return new CAttackMessage (tgt);  type_id tgtType = typeid (tgt);  // gotta find out what type of target we have  type_id tgtId == typeid (tgt);   if (tgtId == CCarrier.getTypeId ())    return new CBoardMessage (tgt);  if (tgtId == CTerrain.getTypeId ())    return new CMoveMessage (tgt);  //...}    


Obviously yucky. I''m not sure what design pattern could get you polymorphism on botht the source & the target, but I''d really like to know. Any OOP wizards out there got a better way to do this? (Shouldn''t have returned my Design Patterns book).



Stoffel: Is typeid something built into the C++ language? I have never seen it before (but it turned blue ). Also, are the GetTypeID() functions and type_id built in?

--------------------


You are not a real programmer until you end all your sentences with semicolons; (c) 2000 ROAD Programming


You are unique. Just like everybody else.

Yanroy@usa.com

Visit the ROAD Programming Website for more programming help.

--------------------

You are not a real programmer until you end all your sentences with semicolons; (c) 2000 ROAD Programming
You are unique. Just like everybody else.
"Mechanical engineers design weapons; civil engineers design targets."
"Sensitivity is adjustable, so you can set it to detect elephants and other small creatures." -- Product Description for a vibration sensor

Yanroy@usa.com

The typeid operator and the type_id type are built in, but not the getTypeId method (classes get no methods automatically (except constructor and destructor, if they aren''t user-defined)).

getTypeId is however simple to implement:
    type_id CUnit::getTypeId(void){    return typeid *this;}    
Anonymous almost had what I was thinking down, except I was thinking that there would be a static function, i.e. some way to check if your type ID matches a target class without having to have an object of that class. MFC does this very well with RUNTIME_CLASS, but I don''t know if RTTI does it.

BTW, this all falls under that category of RTTI (Run-Time Type Information), and is part of the C++ standard. However, I feel (and many agree) that relying on RTTI is NOT good OOP, and if you have to use it, there''s something wrong with your code.

Again, anybody have an idea on how to do this without switch statements? That is, after all, the whole point of polymorphism.
Advertisement
Just to through another wrench into the works: I was hoping that I could make this completely encapsulated, so I could add new units more or less at random and still have the repair units repair them, resource gatherers gather new types of resources... Pluggibility (sp? ) is really nice. It would make an expansion pack a cinch. I would be willing to do a switch, if no one (including myself, of course ) can come up with a better solution.

--------------------


You are not a real programmer until you end all your sentences with semicolons; (c) 2000 ROAD Programming


You are unique. Just like everybody else.

Yanroy@usa.com

Visit the ROAD Programming Website for more programming help.

--------------------

You are not a real programmer until you end all your sentences with semicolons; (c) 2000 ROAD Programming
You are unique. Just like everybody else.
"Mechanical engineers design weapons; civil engineers design targets."
"Sensitivity is adjustable, so you can set it to detect elephants and other small creatures." -- Product Description for a vibration sensor

Yanroy@usa.com


I agree, this is a good question. This problem comes up in many other areas also, where you want to use polymorphism on both the actor and actee to determine an action.

I''ve always copped out and done a switch() on the actee inside of the derived actor. But I''m interested in hearing about a better way...
A switch wouldn''t really give you that much of a performance hit, from what I can see, if you do it a certain way. If you had some way of defining constants (not for unit type, that would give away all of your flexibility) for the background, such as RESOURCE, ENEMY, ALLY, NEUTRAL, NOTHING, NONACCESIBLETILE, etc, then, you consecutively switch on each one. The consecutive part is a must, because a good compiler will just set the switch up as a jump to some offset from the current instruction, with the case value being the offset. Get it? whatever, well, that probably didn''t help. You could also do something like this:
each unit has a team. 0x01, 0x02, 0x04, 0x08, and so on. now, when you retrieve the object being clicked on, you logically OR the ''team'' with that of the object you want to perform the operation with (attack, gather, heal, etc.) so, if 0x01 is the team of the clicked obj. and 0x01 is the team of the click*ing* object, then the ORed comb. will be 0x01 and you know they are friends. if 0x02 is a resource, and 0x01 is the team of the clicker, then the comb. will be 0x03, and you know the clicker should harvest. Get it?
    #define RESOURCE  0x01#define ENEMY     0x02#define ALLY      0x04#define BLANK     0x08#define HARVEST   RESOURCE | ALLY#define ATTACK    ENEMY | ALLY...void CUnit::ReactToRightClick (int x, int y){   dword dwCombo;   dwCombo = GetUnitType (x, y);   dwCombo |= m_dwType;   if (dwCombo == HARVEST)   {       HarvestResource (x, y);   }   else if (dwCombo == ATTACK)   {       AttackUnit (x, y);   }   else   {       MoveTo (x, y);   }}    


well, hope that helps, just stuff i came up with off the top of my head...
farmersckn
Yesterday is the past, tomorrow is the future. Today is a gift, that is why we call it the present.
Although not perhaps A C++ zealot''s most favorite way of doing things, I like that method actually, it''s pretty elegant. Nice post!

This topic is closed to new replies.

Advertisement