Menu

Need general design assistance

2003-03-18
2003-03-27
  • William Seppeler

    I'm looking for assistance with some general design decisions.  I don't necessarily need any code examples.  I just need some conceputal understanding.  My questions are in regards to general game design and how I use Kyra to implement some of these basic concepts.

    Let me first give a little background on what I'd like to create:  I want to make a simple real time strategy game.  Unit actions (move/attack) are independant of each other, and each unit must have some basic AI.  So you could say: move from point A to point B, if there's a collision with an obsticle, try to move around it, if there's an enemy in range, attack it, and so on.  There's nothing revolutionary on what I'm trying to do.  It's a basic concept part of every RTS type of game.

    The first thing I'm stuck on is picking a good data structure for  creating my units.  I figure a unit structure would contain a sprite (of course), it'll also have values for armor class and health, but what else?  Would I need to store vector information like direction and speed or is that what the sprite data structure is for?  Sorry for the dumb question, but I'm just trying to conceptually understand the relation of a unit actions in the game world to a sprite and it's associated actions.  Are sprite actions and unit actions one in the same?  I need a good general overview on how sprites and unit objects relate to each other.

    Along the same lines as sprite and unit relations, I need a good general understanding of how units would be managed in the game.  Do I just create a large circular list like what's done in the tutorial for spellEffects?  Then for each game cycle/tick I traverse that circular list to update units?  At any one time, the game could have as many as 200+ units in the game.  Are there performance issues I need to make in my data structure design?  Would I just have one huge circular list for all units, structures, and weapon fire (ie: missles in flight) in the game or would I have a separate list for each type of data object?  Does any of this relate to the ImageTree used for Sprites?

    My next topic is about the use of Events.  I figure I need a Timer event to control the game speed.  I'll also make heavy use of Keyboard and Mouse events.  Do I also make use of Events when it comes to unit collisions?  Or do I handle unit collisions and movement by traversing that circular list forementioned?

    When providing feedback, understand that I'm not looking to do anything too complicated, but I'd also like to implement these basic concepts in a well structured way such that I can easily add features as my game developes.  So initially, I'd just like to be able to give a unit a command such as "move here" or "attack that".  And if an enemy come into range "defend".  But as the game developement progresses, I'd like to be able to queue commands for a single unit.  Like setting way points to "move here" then "move there" or "attack that structure" then "move over there".  Maybe create the concept of partrol by performing a circular command list.  So I'd like to start with a basic design structure that I could easily extend without breaking anything.

    Lastly, what are my trade offs?  I'm not looking to make anything over complicated and my hopes are the game will play fast even on low end machines.  How would I choose a data structure or implementation design that would be "CPU friendly"

    I know my questions seem pretty dumb, but I'm missing some of the most elemental concepts when it comes to game design.  Any and all help would be most appreciated.

     
    • William Seppeler

      Never mind...do ignor my previous post.  Alot of these concepts are beyond me at first, but I've been making good progress on my own.

       
    • Lee Thomason

      Lee Thomason - 2003-03-22

      I'm going to answer anyway because it's a good question and will come up again. :)

      My thoughts:

      - Data structure for units.
      Definately create your own, that probably has a pointer to the KrSprite. Using the KrSprite (either through "userdata" or subclassing) is possible, but not the easiest approach in my experience. The demos do that a lot because they are quick and fast, but it would be better to separate the pieces.

      - Circular list
      Probably makes sense. Using the "unit structure" you create, put it all in a list and walk it. I don't think you can do much better on something that needs to be updated every frame. None of it should really relate to the ImageTree().

      - Events & Unit collisions
      Events won't help, but Kyra does provide a collision detection API that probably will. That's useful if you need pixel-perfect collision detection. If bounding box detection is good enough, it's overkill. You could implement bounding box detection on your and it would be faster.

      - Movement and animation
      Movement and animation are tricky. If your animation *implies a certain movement rate*, for example, the way a person walks. A walking animation has to be played back with very specific distances between frames. If you use that, then using the sprite alignment function is the Sprite Editor makes sense, and using DoStep to move the sprite makes sense.

      On the other hand, for movement not linked to animation (space ships, for instance), then you don't really need DoStep or alignment. You can move the object at any speed appropriate for the game.

      lee

       
    • William Seppeler

      Lee, first let me say I really appreciate all your corespondence.  Through this web board and the emails.  But I still need assistance on data structure design.  I've taken several approaches, but I'm still having problems making it all work, and now I may need to start over (mentally that is).

      I've been working using the method of thought that everything in my game is an Object.  An Object is really just an extention of a Sprite.  The Object holds data such as health, armor, a current command list, and so on.  Based on the type of Object it is determins what data it contains.  Therefore, my design was to have a top level class called Object and from there, I created sibling classes for Structure, Unit, Particle and Barrier.

      This plan worked out well as I could create one big circular at the top level of my game.  The plan was once per game tick, I would traverse that Object list and update each Objects internal data structure.  For example, if it was a Unit Object, the update() routine would look at it's command stack.  If there was a command, such as move(x,y), it'd have to determine if the Sprite needed to be turned or not.  In a sense, the Unit update() routine would be responsible for updating the Sprite's action based on the current command and if the command was completed (ie: the Sprite's position was equal to the (x,y) position, then update the Sprite's action to stop.  Another example would be a Building structure whos only command would be either constructing a unit or doing research.  The internal data structure for Building would be tracking different variables and hence would have a completely separate update() routine.

      My problem is, now at the top level of my game, I can have that generic Object list, but I don't have a "clean" approach to calling each update() routine based on what it's Type was.  I could put in a case statement, but I can imagine that going through that extra logic for each Object in the list for each game tick (the big O) could get pretty costly.

      If I want to continue using my current Object structure, the only way I could see keeping my "top level" fast and also being able to call an Object specific update() routing would be to keep a separate circular list for each Object Type.  It's not a big inconvience, but I did like the generic approach of one list.  Creating separate lists also wouldn't change my design significantly.

      The other approach I'm concidering now is by using that SetUserData and GetUserData routines of the Sprite class to store all my Object data (per your last message).  Where as this may squash all my Object types down to one top level class (KrSprite), I'm still faced with the issue of trying to write an Object Type specific update() routine.  As I said, I know I could write a generic update() routine that determines the type of Object I'm working with first, then perform the appropriate action, but since that's part of an "inner loop" of the whole game, I'm afraid of the performance cost.

      The last issue that I'm having may be the breaking decision.  I don't know how I'm going to do collision detection with my current model.  I guess I could do a collision check before I do any Object update() routines.  If I wait until after an Object update(), I'll have to do a tree walk then traverse my top level Object list(s) a second time per game tick.  Ouch!  I'm beginning to think my simple approach to the main game loop is getting too painful in regards to speed.  Am I doing something wrong or am I missing some basic understanding on how to handle updates to my game Objects?

      Oh, in regards to movement and Sprite control, I did finally grasp the concept on what I need to do.  I plan to use Sprite allignment for almost everything.  Basically, every Object in my game will probably get a DoStep() call for each game tick.  Even if the Object is not moving, I'll probably have some kind of animation going on.  Maybe a blinking light or turning sattlelight dish.  Probably something.  The only exception will be particles that I'll use for weapon fire and explosions.  I'll probably handle particles just like the tutorial does with spellEffects.  Even then, a weapon fire with animation would pretty pretty neat.  So, assume I have around 200 Units in the ImageTree and about just as many Barrier sprites, then through in about 40 buildings and say on average there's about 200 particles.  In all, say about 1000 Sprites all getting a DoStep call once per game tick.  Is this even feasible or would my game crawl?  Should I start working on performance issues or have I not even reached the threshold yet?

       
    • Lee Thomason

      Lee Thomason - 2003-03-26

      My pleasure! I enjoy talking games.

      The list of Objects, each having an update() method, sounds like a very good approach. Calling 200 functions for a modern processor is surprisingly fast. Kyra itself begins to feel the load at about 2000 objects (on a P3 700) but that has as much to do with problems doing 64bit math than it does with the function overhead...for 200 objects, I doubt you'll even be able to measure the function call overhead.

      And you are certainly correct to avoid the "case" statement.

      I'd guess you would want something like:

      class Object
      {
        public:
          virtual void update() = 0;
      }

      class Unit {
        public:
          virtual void update();
      }

      where there is no implementation for Object::update (an abstract function) but there is an implementation for each other update method.

      I would avoid the userData in your system. Having the Object have a pointer to the KrSprite is probably a better way to go. And it will make your compiler happier.

      On collision detection:

      - Doing the collision detection before the update is a good idea, if that sequence of events is going to work for you

      - You may want to measure the cost of the walk. (The GlPerformance class can help measure time spent in functions.) An extra walk may or may not be a problem...it's hard to know. Above about 500 objects definately a probrem, below 100 objects no big deal. In between, hard to know.

      - Collision detection for a large set of objects is expensive. Kyra doesn't know enough about how your program works to do a great job. It does a good job. But for really fast collision checking, you'll need to implement high level culling in your code. (Culling, in this case, meaning cheaply throwing away things you know can't collide, and leaving the trickier cases up to the engine to figure out.)

      In general, 1000 Sprites isn't too bad. (The text example works with up to about 4000-5000). Check out the Performance notes on the "engine" page. At that number, I'd implement it and see how it worked out. The performance may be no issue for you.

      Also, for large sprite sets like that one, it can be a big savings to remove sprites that are off screen, if you can do so quickly. (Again, the culling thing. Do fast easy checks in your code, leave the rest up to Kyra.) That way Kyra isn't crunching out computations for objects that won't be drawn.

      Hope that helps!
      lee

       
    • William Seppeler

      <I>class Object
      {
      public:
      virtual void update() = 0;
      }

      class Unit {
      public:
      virtual void update();
      }

      where there is no implementation for Object::update (an abstract function) but there is an implementation for each other update method.
      <\I>

      I really have to ask on this approach as this was my exact approach, but I got errors when compiling.  From memory, the error was something like "can't redifine update() function" or "two implementations of the same function" or something like that.  See, Unit is a subclass of Object, so I really have something like:

      class Object {
        public:
        void update();
      }

      class Object::Unit {
        public:
        void update();
      }

      My syntax could be off, but it when something like that.  Were you implying that I should have Unit be a completely separate class from Object?  I went for the subclass approach because I figured I might have some common variables like "color" to indicate team color or something like that.

      However, I did use the approach on using a completely separate class to accomplish what you described.  I created a completely independant class call Node.  I was able to use this generic Node class as an abstraction of my Objects to add them to a single list while still being able to call an Object Type specific update() routine.  The Node class went something like:

      <b>
      #include <unit.h>
      #include <building.h>

      class Node {
      public:
        Node(Unit&);
        Node(Building&);
        void callupdate(Unit&);
        void callupdate(Building&);
      private:
        Unit unit;
        Building building;
      }
      </b>

      The callupdate() routine was something like:

      <b>
      void callupdate(Unit& unit) {
        unit.update();
      }
      </b>

      Again, my syntax may be off, but I essentially had to create a separate class.  Whereas this approach sorta did what I needed, I couldn't help but feel it was a little hooky.  Plus, it does have a serious flaw!  Because I'm traversing my Object list, when I select a node, I don't know what it's Type is, so when I call the Node callupdate() routine and I pass it the address of Object&, I get an error because I don't have a declared method for Object& types.  I only have Unit& and Building&.

      I'm trying my best for a really generic clean approach to this game design as I'm really using it as a prototype for many game concepts.  So please bear with me as I state my design goals again.  I'm sure you have a pretty good idea already, but I'll try to make it short.

      I want to treat everything as an Object.  Such that I can have one list of all Objects in the game.  The main loop will just traverse that Object list each game tick and update each Object.  Now, because there will be a variety of different Object Types, the update() routine will vary depending on what type of Object it is.  How can I traverse that top level list of Objects and call a single update() routine that will be Object specific?

      This design seems so simple, but the answer is eluding me, AND ITS DRIVING ME MAD!!!!!

      First and foremost, is what I want to do even possible?  And if so, is there an easy approach, or do I need to resort to some convoluted solution.  I do know two solutions that will work.  I can either have a separate Object list for each Type of Object in the game.  I don't like this approach because I then have to change other modules in my code when creating a new Object Type, plus I think it's slopy.  The other approach is to have a case statement to figure out the Object Type prior to making an update() call.  This approach would probably need a variable belonging to the Object call that indicates what type of Object it is (ie: Object Type being it's subclass).  So I'd probably have to do something like:

      if ( object.type == NODE ) {
        Unit& unit=object;  <--- can I do this?
        unit.update();
      }
      else if ( object.type == BUILDING) {
      ...

      Come to think of it, I don't even know if that solution is possible.  DAMN IT!  My design seems so simple, why can't I see how to implement it  :(

       
    • Lee Thomason

      Lee Thomason - 2003-03-26

      Well, I left out some all important syntax. :)

      in the h:
      class Object
      {
      protected:
      KrImage* image; // maybe

      public:
      virtual void update() = 0;
      }

      /* note the public Object */

      class Unit : public Object {
      public:
      virtual void update();
      }

      ...

      in the cpp:

      void Unit::update()
      {
        // do something here
      }

      Also, you'll have to have a linked list of pointers to objects; because a Unit is a different size than a Building, you need to 'new' it rather than create an array of them.

      struct ListNode
      {
        Object* object;   // the Unit, Building, etc.
        /* Object object */  // would be wrong

        ListNode* next;   // if this is a sigly linked list
      };

      That is also how the update() calls the right sub-type (Building, Unit) when the parernt Object::update() is called. When you say:

      Object* object = new Unit( blah );

      The language hooks up the virtual functions of the Object you just created to the methods of Unit. So object* really points to a Unit...but you can treat it just like an Object. And it is that as well. (For those of you thinking I'm skipping some detail here, I am.)

      Very important. Don't do this:
      Object obj = *object;

      That's called slicing. Nothing good will happen from that and it leads to a world of frustration and bugs. If you are using polymorphism (==virtual functions), you need to use a pointer to the object.

      Unit, Building, etc. as a subclass of Object is the way to go. Remember too that all your update methods, for polymorphism to work, must have the same parameters and return types.

      The Node approach...is frought with trouble. As you point out. :)

      In the end, I think the basic Object parent of [Unit, Building, etc] is a good and simple design. Sounds like you just need to clean up the syntax and implementation a bit.

      lee

       
    • William Seppeler

      I will definitely revist my initial approach.  I was using GlCircleList as my list container.  I felt I needed that circular linked list so I could add and remove Objects with ease (ie: create units, destroyed units,...)

      Using an Object* was my initial thought, but for some reason, I couldn't get that:

        virtual void update()=0;

      part of the Object class to work.  The compiler would complain about my definition of update() in my Unit subclass.  And something about function already exists....can't create another or something like that.  I'll have to revist that approach again.....I knew....I just knew I was on the right track the first time around.

      Thanks for the help.  If it works, I'll post my results.

      PS:  I noticed that GlCircleList is not documented on the web site's API page.  I had to dig through the header files to find it.  As you pointed out in your C++ assesment, a C++ header file, pretty much documents itself :)

       
    • William Seppeler

      IT WORKS....IT WORKS!!!

      Thanks Lee, it finally works exactly how I want it too.  My syntax was appearantly off the first time I tried to set this up.  When declaring my update() function in the Object class, I forgot to put the "=0" at the end.  So you were CORRECT.  The syntax I needed in Object was:

        virtual bool update()=0;

      I made it a boolean return so I have a signal to easily remove items from my Object list when they are no longer needed.  It works fantastic and my program reads very clean now.

      The only major part I have to write now (for the main game loop) is the command interpreter.  I will add a command_stack to my Object class.  Each command will be of the Command class.  The command interpreter is simply the process of reading user input, creating a Command, and pushing it on the respective Object's stack.  The respective Object update() routine is what's responsible for servicing each command on the stack.  That's why I was so hung on getting that update() procedure ironed out.

      When it's all done, I will have a nice solid framework for developing any simple game.  All my game concepts will just be additions to this basic framework.

      Thanks a million for the assistance.

       

Log in to post a comment.