GPWiki.org
GPWiki.org
It is currently Wed Jun 19, 2013 1:06 pm

All times are UTC




Post new topic Reply to topic  [ 27 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Wed Jul 14, 2010 10:06 pm 
Dexterous Droid
User avatar

Joined: Wed Aug 18, 2004 7:40 pm
Posts: 3746
Location: South Africa
As mentioned in another topic, I started programming a game about a dragon who has to defend coffee trees from imps. This is what it looks like: (my artist hasn't made any art so sprites are placeholder)
Image Image
Ignore the robot thing in the top left. The player (dragon) is the sheep without a path illustration. The imp is the sheep with the path visualisation. Since my first thread, the "sentinel" became a dragon again. The "7" markings are the A* costs of walls. COL marks the coffee collection points and EXIT marks the exits. The dragon adds cost to tiles around him, which makes the imps avoid him.

I was making good progress on the project until I started running into problems with the way I had organised my classes. Basically they needed to be able to talk to each other but weren't linked up properly. So I ended up starting to hack things to get functionality and as soon as I had to scale up the number of imp characters in the game (from 1 to 8 ) and made them spawn out of the 4 corners of the map; things became very pear-shaped. The problems I ran into were due to the way the pathfinding was run, and the frequency of the path finding routine. The map was finding the path for the imp and directly manipulating the imp's path property. This worked, but when I had to add in logic to find the coffee and the exit, I put this in the pathfinding as well. So the map would check to see if the imp wanted coffee or an exit and then find the appropriate path. Then I needed the imp to change state when it collected coffee, so I made the Map alter the imps state if it was on a collection tile. You can probably see where this is going. Downhill. Another problem was that the Drawer expected Entity's to follow a certain format so that it could draw them, instead of having a Drawable interface and letting everything figure out its own way to get drawn.

I know what the solution is now: be more strict about encapsulation. A class must only perform functions related to itself. All functionality that it provides to other classes should be to do with its own data and it shouldn't go modifying other classes in order to accomplish its ends.

I was also stupid and didn't seperate my game logic from the rendering logic. So FPS became the game logic frequency. So I also need to fix my timestep and seperate the game logic and graphic drawing. This basically caused problems with the pathfinding when I wanted to run it every X frames... it would run differently on different people's computers.

Also, my state change logic sucked, and I ended up with a (now) unfixable bug where my dragon keeps changing state to idle in the middle of his walk cycle, making the animation chop and change. I plan to correct this by using this article.

What am I driving at? I want to know how to design properly. Is UML the answer? More documentation? I made a pretty brief technical design document (posted in last thread), which mapped out a progression and some classes. I remember hating UML in my comp sci course, but our lecturer sucked - if its useful I'll relearn it.

Here's my attempt at a class diagram. I improved it quite a bit but then pressed Delete accidently in ArgoUML and the bloody thing doesn't have an undo function >:( Which would be another question: what's a good, free UML drawing program? (If UML is even worth it :rolleyes )
Image

_________________
Whatever the mind can conceive and believe, it can achieve


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 14, 2010 11:45 pm 
Corpse Bride
User avatar

Joined: Tue Jul 01, 2008 11:44 pm
Posts: 2217
Location: England
This sounds similar to Magecraft's AI: CPU controlled entities which have states that cause them to follow paths to gather things.

1. Each mage had an action state.
Code:
Mage[id]->Actionstate


When a condition was satisfied, a routine was called to prepare the next action, and the mage's action state would be changed. The conditions depend on the state, so we use something like:

Code:
Switch(Mage[id]->Actionstate) {
case 0:
//test if we have arrived at our destination? If so, do <something> and Mage[id]->Actionstate = 1
case 1:
//is our nemesis in acquisition range? If so, do <something> and Mage[id]->Actionstate = 2
case 2:
//etc
}


The <somethings> may involve finding a new path.

2. I had a path property for each mage, which described the path it was to follow. This included a list of coordinates, and path length, and a current position marker..

3. When I wanted to find a path, I cleared the map of pathing data, run A*, extracted the path and stored that in the appropriate mage's path property.

In pure OO, a path find function would be a within the map class. I think that's the crux of the problems you're having. You need to move that pathing data out of the map, and into a mage/sheep/sentinel/dragon.

_________________
I ain't pushing no moon buttons.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jul 15, 2010 6:27 am 
P2k
User avatar

Joined: Tue Aug 23, 2005 5:11 am
Posts: 2145
I prefer to separate my Entity from rendering completely. I have the entity class only deal with game logic, nothing visual at all. Then I have a renderer class specifically for each entity class and then my renderer goes through all the entities and picks the right renderer for each and feeds that renderer the corresponding entities to render. Not only does it make the code much cleaner, it lets you approach the rendering more flexibly so you can optimize it much better.

So in your example, you make a EntityRenderer base class, with a Render(Entity) function then inherit it with ImpRenderer and override the Render function with however you want to render the Imp. Then in your Render loop, you pass each entity to its corresponding renderer (each renderer will have only one instance)

It is a little bit more work up front, but it pays dividends later on.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jul 15, 2010 10:08 am 
Dexterous Droid
User avatar

Joined: Wed Aug 18, 2004 7:40 pm
Posts: 3746
Location: South Africa
Thanks for the helpful replies.

Jasmine wrote:
This sounds similar to Magecraft's AI: CPU controlled entities which have states that cause them to follow paths to gather things.

1. Each mage had an action state.
Code:
Mage[id]->Actionstate


When a condition was satisfied, a routine was called to prepare the next action, and the mage's action state would be changed. The conditions depend on the state, so we use something like:

Code:
Switch(Mage[id]->Actionstate) {
case 0:
//test if we have arrived at our destination? If so, do <something> and Mage[id]->Actionstate = 1
case 1:
//is our nemesis in acquisition range? If so, do <something> and Mage[id]->Actionstate = 2
case 2:
//etc
}


The <somethings> may involve finding a new path.

2. I had a path property for each mage, which described the path it was to follow. This included a list of coordinates, and path length, and a current position marker..

3. When I wanted to find a path, I cleared the map of pathing data, run A*, extracted the path and stored that in the appropriate mage's path property.

In pure OO, a path find function would be a within the map class. I think that's the crux of the problems you're having. You need to move that pathing data out of the map, and into a mage/sheep/sentinel/dragon.


I had a similar thing where each entity has a state.
Code:
struct State{
    float minLife, maxLife;
    int nextState;
    State(float minl, float maxl, int next):minLife(minl),maxLife(maxl), nextState(next){}
    State(){}
};

The Entity had a requestState() function, that checked for the legality of the state before changing. I shouldn't have had that default nextState thing, but instead let each state handle its own intricacies. I'll have a thorough look through your MageCraft code :)

Struan wrote:
I prefer to separate my Entity from rendering completely. I have the entity class only deal with game logic, nothing visual at all. Then I have a renderer class specifically for each entity class and then my renderer goes through all the entities and picks the right renderer for each and feeds that renderer the corresponding entities to render. Not only does it make the code much cleaner, it lets you approach the rendering more flexibly so you can optimize it much better.

So in your example, you make a EntityRenderer base class, with a Render(Entity) function then inherit it with ImpRenderer and override the Render function with however you want to render the Imp. Then in your Render loop, you pass each entity to its corresponding renderer (each renderer will have only one instance)

It is a little bit more work up front, but it pays dividends later on.


That sounds like a great idea. So (in my case) the Drawer would maintain a list of Entity objects to draw, and then call the related Renderer for each item in that list. To get the right renderer, do I need to add a enum EntityType {DRAGON, IMP, etc}and then check the type of each entity before sending it to a renderer? Or could Singleton be applied to make it more automated.

Code:
Renderer DragonEntity::getRenderer(){
  return Singleton<DragonEntityRenderer_singl>::getInstance();
}


Then the Drawer queries the entity for its renderer, and then passes the entity to the renderer? Seems kind of convoluted.

_________________
Whatever the mind can conceive and believe, it can achieve


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jul 15, 2010 8:55 pm 
Game Programming Guru

Joined: Sun Aug 15, 2004 6:20 pm
Posts: 1090
IGTHORN wrote:
(If UML is even worth it)

This is debatable. If it helps you, use it. It's particularly helpful when you have to describe your design to others (e.g. us :)). Personally I don't strictly follow it, I just make up my own notation when I need to lay a design out on a white board because I can't wrap my mind around the whole thing at once.

Jasmine wrote:
In pure OO, a path find function would be a within the map class. I think that's the crux of the problems you're having. You need to move that pathing data out of the map, and into a mage/sheep/sentinel/dragon.

Not a bad idea. But I think one of the advantages of storing data in the map object (assuming your map object is a kind of parent to these other moving entities) is that redundant path data can more easily be shared instead of always instantiating paths for each entity.

Struan wrote:
Then I have a renderer class specifically for each entity class and then my renderer goes through all the entities and picks the right renderer for each and feeds that renderer the corresponding entities to render.

Visitor pattern http://en.wikipedia.org/wiki/Visitor_pattern. I use this too, despite its own problems.

_________________
Heard in #gpwiki: <wzl> is there some serious grammar fail in your line or am i just too intelligent for your logic


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 16, 2010 6:35 pm 
P2k
User avatar

Joined: Tue Aug 23, 2005 5:11 am
Posts: 2145
sdw wrote:
Struan wrote:
Then I have a renderer class specifically for each entity class and then my renderer goes through all the entities and picks the right renderer for each and feeds that renderer the corresponding entities to render.

Visitor pattern http://en.wikipedia.org/wiki/Visitor_pattern. I use this too, despite its own problems.


Actually, I don't use the visitor pattern. I just use dictionary lookup for the type to figure out which renderer to use. A visitor pattern would work fine on the renderer but it would be more code to write for each new type and I am lazy.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 16, 2010 6:57 pm 
Dexterous Droid
User avatar

Joined: Wed Aug 18, 2004 7:40 pm
Posts: 3746
Location: South Africa
sdw wrote:
IGTHORN wrote:
(If UML is even worth it)

This is debatable. If it helps you, use it. It's particularly helpful when you have to describe your design to others (e.g. us :)). Personally I don't strictly follow it, I just make up my own notation when I need to lay a design out on a white board because I can't wrap my mind around the whole thing at once.


I also like the whiteboard! It really helps me map out my thoughts. I don't know if others could understand though, without explanation :)

I'll look into the visitor pattern. I also recently got the GoF book and am learning design patterns. I learnt the Singleton pattern a year ago and really like it for my ImageManager class (on workmad3's suggestion). I really like the sound of the State pattern as well for managing the Entity's states after my problems with my own poor state implementation.

I've heard a few people criticise the design patterns though, saying that each problem should have its own unique solution. To some extent I agree that the patterns should not be overzealously applied, and maybe for inexperienced programmers learning patterns causes more harm than good. What is your take on this?

Also, you all have had good suggestions. How do you get to have that knowledge? My current path is planning to the best of my ability, programming, hitting walls, backing up, re-evaluating and trying again. I do learn from my mistakes, but each mistake is very costly in terms of time. Is this the only way? If you have any suggestions of what I should study or whatever, my ears are open.

_________________
Whatever the mind can conceive and believe, it can achieve


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 16, 2010 10:52 pm 
Harmlessness does no harm
User avatar

Joined: Tue Sep 14, 2004 8:37 pm
Posts: 3854
Location: Ferriday, LA, US
IGTHORN wrote:
How do you get to have that knowledge? My current path is planning to the best of my ability, programming, hitting walls, backing up, re-evaluating and trying again. I do learn from my mistakes, but each mistake is very costly in terms of time. Is this the only way? If you have any suggestions of what I should study or whatever, my ears are open.


Learn from other people's mistakes. ;)

A portion of it definitely is trial-and-error... but to make things a bit more comfortable when you do fall on your face, it helps to read as much as possible about design. This includes references, but also postmortems and the like. You can learn a lot of useful nuggets of knowledge by listening to people's accounts of their own excursions.

Code Complete (McConnel), Design Patterns (Gamma, Helm, Johnson, Vlissides) are ones I hear come up a lot (I have both, and Code Complete has helped a lot, though Design Patterns is a considerably heavier read).

Also, it helps to be well-grounded in the standard language conventions. One book to check out is "The C++ Programming Language" (Stroustrup). Also, if you plan to make heavy use of the STL, then there is "The C++ Standard Library" (Josuttis).

You might also want to look into some of the recent books specifically geared to game engine design. They're considerably more expensive than "regular" programming books, but read the reviews (don't just go by rating) to learn the ins and outs, and see if you can check on out at the library if it's too expensive.

_________________
What most people don't understand about "enlightenment" is that it is not an end-goal; but where you find yourself just before taking a new "first step."


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jul 18, 2010 10:50 am 
Dexterous Droid
User avatar

Joined: Wed Aug 18, 2004 7:40 pm
Posts: 3746
Location: South Africa
theraje wrote:
Learn from other people's mistakes. ;)

Good suggestion :)

I'm going through the Gamma, etc book. I'll try get hold of Code Complete as well. Our libraries are a bit of a joke. Looks like Amazon will make a buck or two more off of me.

_________________
Whatever the mind can conceive and believe, it can achieve


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 19, 2010 12:45 am 
Elder Statesperson

Joined: Thu Mar 09, 2006 5:15 pm
Posts: 217
Location: Wellington, New Zealand
I have Code Complete and recommend it as well. One of the few books in my 'library' I keep coming back to.

(Amazon has made a buck or two of me too, not to mention shipping costs to NZ!)

_________________
www.littlemonkey.co.nz


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 19, 2010 8:03 pm 
Dexterous Droid
User avatar

Joined: Wed Aug 18, 2004 7:40 pm
Posts: 3746
Location: South Africa
I've sourced a copy of Code Complete, looking forward to some good literature.

Here's r2 of the class diagram.

Image
{edit: made thumbnail}

Just looking at it now, I'll remove the 1..* from Game to Level. There'll just be 1 level. I'm tossing up what to do when the level changes. Whether to kill the level when it ends and load the new level from file. Or to have some kind of changeLevel() routine, that just reuses the allocated items, reloading new stuff in.

The Drawer is a singleton, and when a new entity gets created, it must add itself to the draw list.

_________________
Whatever the mind can conceive and believe, it can achieve


Last edited by IGTHORN on Wed Jul 21, 2010 7:32 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 20, 2010 10:31 pm 
Game Programming Guru

Joined: Sun Aug 15, 2004 6:20 pm
Posts: 1090
This is a pretty busy diagram, considering it's not modelling something very complex. The first thing I noticed is the organization of the boxes. You should put Game towards the top of the page and layer all over the other boxes underneath it to make the tree easier to read. I recommend getting rid of the inheritance arrows and just place the interfaces directly after the class names, like "Entity : Drawable" and "Dragon : Entity" (change the shape/color of the boxes if this makes it difficult to read).

What's the difference between a Map and a Level? Can you merge Tile, Map, Level, and ImpManager all into one box and call it Map or Level?

What's the use in the whole drawList thing? Why can't you just walk the tree (rooted at Game), drawing things that need to be drawn? Why does Drawer need to be a singleton?

Why is ConcreteState a singleton? Singletons are often misused/not needed. I'm curious how this state machine framework is supposed to work, including the Message thing that's not defined. Do they need to be polymorphic (i.e. you can't get away with using an enum and switch)?

I see load(file : String) on a few components. Would it be beneficial to create some sort of Serializable interface?

Why is Animation so lonely? :(

_________________
Heard in #gpwiki: <wzl> is there some serious grammar fail in your line or am i just too intelligent for your logic


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 21, 2010 9:33 am 
Dexterous Droid
User avatar

Joined: Wed Aug 18, 2004 7:40 pm
Posts: 3746
Location: South Africa
Thanks for the critique!

sdw wrote:
Why is Animation so lonely? :(

Animation is an anti-social problem-child :lol

Animation should have been inside the Entity object. The entity has a vector of possible animations, and a currentAnimation that points to which one to play.

Quote:
What's the difference between a Map and a Level?

Map was going to be just the tile grid, with functions for path-finding, etc. Level was going to be the container for everything in the level, the map (tile grid) and all entities.

Quote:
Can you merge Tile, Map, Level, and ImpManager all into one box and call it Map or Level?

I wasn't too sure about the ImpManager, but I needed something to handle the imps spawning and pathfinding. Functionally, I could indeed merge Tile, Map, Level, and ImpManager. I would end up with a pretty bulky class though, with a large majority of the games logic happening there. Not sure if that would be a Bad Thing? It would simplify the communication between those components, though.

In its previous incarnation, Tile was just a struct to store map tile information. I was going to do the same thing again.

Code:
-::Tile.h::-
struct Tile{
    int tileX; //x co-ord on the tileset for this tile
    int tileY; //y co-ord
    //int mapX; //self-aware tiles, as it were...
    //int mapY;
    bool blocking; //tile is impossible to pass
    bool exitTile; //is this an exit tile?
    bool collectTile; //is this a coffee collection tile? i.e. the imp can start gathering and stop path-
    int cost; //this amount should not be changed, it is set at start up
    int costNow, costHeuristic; //the cost to get here so far and the cost heuristic from this tile
    int costDragon; //the cost due to dragon proximity
    inline int costTotal() const{return costNow+costHeuristic+costDragon;}

    sf::Vector2i prev; //what tile brought us here?
    bool visited; //if this has already been popped, don't add it again
    bool operator>(const Tile& rhs){
        return (costTotal() > rhs.costTotal());
    }
    Tile():tileX(-1),tileY(-1),blocking(false),exitTile(false),collectTile(false),costNow(INT_MAX),costHeuristic(0),costDragon(0),visited(false){}
};

-::Map.h::-
class Map{
      //.........
   public:
      std::vector< std::vector<Tile> > map; //to be allocated on map.load(file)
      //.........
};


Quote:
Why can't you just walk the tree (rooted at Game), drawing things that need to be drawn?

I wanted the draw list so that things would be drawn in increasing y-order. This way, sprites that were lower on the screen would be drawn after ones above it. Is there a better way to accomplish this without maintaining a list of all things that want to be drawn, and then sorting it before painting the frame?

Quote:
Why does Drawer need to be a singleton?

I wanted to make it a singleton because there would only ever be one of it, and I wanted a global point of access to it. The idea was to have all Drawable items make a call to the Drawer on construction, and add themselves to the drawList. They could make another call on destruction to remove themselves.

Quote:
Why is ConcreteState a singleton?

I'm following the tutorial here: http://gpwiki.org/index.php/State_pattern . It said the concrete states should be singletons. I'm not sure of the correct reason for this, but I imagine its for reasons of saving memory. The states don't contain any state attributes, only actions to perform when in that state.

I could probably get away with using enum and switch but I thought this way would make the code more managable. I want to be able to force some states to change directly after their animation ends. (The dragon is able to jump over walls. To do this, I teleport him over the wall and then play an animation of him jumping over. When the animation ends, the state should change and allow the player to move again.)

The Message is basically a game event. In the state pattern tutorial above, there is a different Message for each kind of event. Like a Jump event or a CastSpell event.

Quote:
Would it be beneficial to create some sort of Serializable interface?

I'm not sure. At the moment I only need to be able to load the data. And it simple enough that I can handmake the files. When I make the level editor it would be a useful addition.

_________________
Whatever the mind can conceive and believe, it can achieve


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 21, 2010 7:30 pm 
Dexterous Droid
User avatar

Joined: Wed Aug 18, 2004 7:40 pm
Posts: 3746
Location: South Africa
Revised per your suggestions.

Image

Game would handle input and create the drawing context. It would also Level.update(), say 100 times per second. When the level updates then it'll update all of the entities it contains. It will do path-finding for the imps as well spawning and removing imps. It will check for imp-dragon collisions.

When drawing is possible, the game will call Level.draw(sf::RenderWindow*). The level will check its list of current active entities and sort them by ascending y-coordinate. It will then call draw(sf::RenderWindow*) on each entity and the entity will draw itself in its current animation.

Inside each Entity there is a update() function that will handle state actions via a big switch statement. For every possible action that can change the state, there will be a actionX() function. For example, when an animation ends, then actionAnimationEnd() will be called. In this function, there will be a switch statement that decides what to do based on the current state. If the state is DRAGON_JUMP then the state will change to DRAGON_IDLE. If the player wants to move the dragon, the state needs to change to DRAGON_WALK... how would this work? The control happens in game and the state transitions are happening in Dragon :?

Entity.update() will also update the current frame number of the animation (but not draw anything). When an animation ends, then it will call actionAnimationEnd().

_________________
Whatever the mind can conceive and believe, it can achieve


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 21, 2010 10:15 pm 
Game Programming Guru

Joined: Sun Aug 15, 2004 6:20 pm
Posts: 1090
IGTHORN wrote:
I'm following the tutorial here: http://gpwiki.org/index.php/State_pattern . It said the concrete states should be singletons. ...


I took a look at that page and didn't really like what I saw. I've reworked it to use an enum and switch for comparison:
Code:

class Character
{
   enum State
   {
      Standing,
      Jumping,
      Falling
   }

   public State _State = State.Standing;
   public int Life = 10;

   public Character() {}

   void ChangeState(State state)
   {
      // onExit - seems kinda pointless :)
      switch ( _State ) {
         case Standing:
            break;
         case Jumping:
            break;
         case Falling:
            break;
      }

      _State = state;

      // onEnter -- You need a lot more states or transitions to do things
      // like going from jumping to standing for instance
      switch ( _State ) {
         case Standing:
            setAnimation("standing");
            break;

         case Jumping:
            setAnimation("jump");
            break;

         case Falling:
            setAnimation("fall");
            break;
      }
   }

   public void OnGameTick()
   {
      switch(_State) {
         case Standing:
            break;

         case Falling:
            MoveCharacterOnFallPath(); // do fall path
            break;

         case Jumping:
            MoveCharacterOnJumpArc(); // do jumping arc
            break;
      }
   }

   // set character animation
   public void SetAnimation(string animname)
   {
      // ...
   }

   public void OnMessage(Message msg)
   {
      if (msg is DamageMessage)
         OnMessage(msg as DamageMessage);
      else if (msg is JumpMessage)
         OnMessage(msg as JumpMessage);
      else
         assert(false, "I can't handle this message! " + msg.name);
   }

   public void OnMessage(DamageMessage msg)
   {
      if ( _State == State.Standing ) {
         Life -= msg.damage;
         if ( Life <= 0 )
            ChangeState( State.Falling );
      }
   }

   public void OnMessage(JumpMessage msg)
   {
      if ( _State == State.Standing ) {
         ChangeState( State.Jumping );
      }
   }

   public void OnMessage(CollideGround msg)
   {
      if ( _State == State.Falling )
         takeDamageOrSomething();

      ChangeState( State.Standing );
   }
}

To me, the switch + enum is easier to read and wrap my mind around. If you prefer the polymorphic classes, go for it. But don't implement it that way just because some overrated design patterns book says so :)

IGTHORN wrote:
I'm not sure. At the moment I only need to be able to load the data. And it simple enough that I can handmake the files. When I make the level editor it would be a useful addition.

I was thinking it could be useful to take a snapshot of the gamestate at any moment by calling Game.Save and it would magically stuff everything it contains that is serializable into a buffer. But it was just a thought.

IGTHORN wrote:
I would end up with a pretty bulky class though, with a large majority of the games logic happening there. Not sure if that would be a Bad Thing? It would simplify the communication between those components, though.

All of the game components need to come together somewhere. Unless you really think multiple ImpManagers would be beneficial then I would just inline it to the Level object. How complex could ImpManager be? Container for Imps and a loop that calls update/draw on all of them :)

IGTHORN wrote:
In its previous incarnation, Tile was just a struct to store map tile information. I was going to do the same thing again.

Is Dragon the only object that requires pathfinding? If not, you may want to segregate path searching data from the tiles. But if only one object does any pathfinding then I don't see the harm (it is likely more efficient than allocating per search query too).

IGTHORN wrote:
I wanted the draw list so that things would be drawn in increasing y-order. This way, sprites that were lower on the screen would be drawn after ones above it. Is there a better way to accomplish this without maintaining a list of all things that want to be drawn, and then sorting it before painting the frame?

If the order they're stored in the Level/ImpManager container doesn't matter, then the Level/ImpManager could just take care of this (again combining class duties). Otherwise I see what you mean that the drawList is in fact needed. If you're using DirectX or OpenGL you can use the depth buffer, but things could get hairy when you're faking the depth yourself. Maybe the drawList is the best solution :)

IGTHORN wrote:
I wanted to make it a singleton because there would only ever be one of it, and I wanted a global point of access to it. The idea was to have all Drawable items make a call to the Drawer on construction, and add themselves to the drawList. They could make another call on destruction to remove themselves.

Fair enough. Rather than use the cookie-cutter singleton template you could just create a simple namespace full of static functions and shared data, guaranteed to only have one instance plus it'll give you better syntax than the Singleton<Drawer>.getInstance().add(this); nonsense.
Code:
namespace Drawer
{
    private shared ArrayList<Drawable> _doodles;

    // I don't remember if C# supports static constructors
    // may need an anonymous struct instead
    void init() { _doodles = new ArrayList<Drawable>(); }

    public void add(Drawable d) { _doodles.add(d); }
    public void drawAll() { foreach ( e in _doodles ) e.draw(); }
}
...
Drawer.add(myDrawable); // Win!

_________________
Heard in #gpwiki: <wzl> is there some serious grammar fail in your line or am i just too intelligent for your logic


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 21, 2010 10:58 pm 
Game Programming Guru

Joined: Sun Aug 15, 2004 6:20 pm
Posts: 1090
IGTHORN wrote:
Inside each Entity there is a update() function that will handle state actions via a big switch statement. For every possible action that can change the state, there will be a actionX() function. For example, when an animation ends, then actionAnimationEnd() will be called. In this function, there will be a switch statement that decides what to do based on the current state. If the state is DRAGON_JUMP then the state will change to DRAGON_IDLE. If the player wants to move the dragon, the state needs to change to DRAGON_WALK... how would this work? The control happens in game and the state transitions are happening in Dragon :?


Code:
class Dragon : Entity, Drawable
{
   bool moveLeft, moveRight, moveJump;
...

void update()
{
   switch (state) {
      case DRAGON_IDLE:
         if ( moveLeft || moveRight )
            changeState( DRAGON_WALK );
         else if ( moveJump )
            changeState( DRAGON_JUMP );
         else
            // do something idly -- like blink the dragon's eyes
         break;

      case DRAGON_WALK:
         if ( moveLeft && moveRight )
            // do nothing?
         else if ( moveLeft )
            position.x -= speed;
         else if ( moveRight )
            position.x += speed;
         break;

      case DRAGON_JUMP:
         if ( moveLeft && moveRight )
             // do nothing?
         else  if ( moveLeft )
            position.x -= jumpMoveSpeed;
         else if ( moveRight )
            position.x += jumpMoveSpeed;
         
         // regardless, move the dragon vertically
         position.y += jumpSpeed;
         jumpSpeed -= gravity;
         // maybe you can come up with something more sophistocated :D
         break;
    }
}

void draw()
{
   // update animation
   if ( animation.currentFrame.timeElapsed ) {
      animation.nextFrame();
      if ( animation.ended )
         // trigger an event?
   }

   Draw.draw(animation.currentFrame);
}

void onMessage(KeyPressMessage m)
{
   // could also replace these with a dictionary
   switch ( m.key ) {
      case DRAGON_MOVELEFT: moveLeft = true; break;
      case DRAGON_MOVERIGHT: moveRight = true; break;
      case DRAGON_MOVEJUMP: moveJump = true; break;
   }
}
void onMessage(KeyReleaseMessage m)
{
   // could also replace with dictionary
   switch ( m.key ) {
      case DRAGON_MOVELEFT: moveLeft = false; break;
      case DRAGON_MOVERIGHT: moveRight = false; break;
      case DRAGON_MOVEJUMP: moveJump = false; break;
   }
}
void onMessage(CollideMap m)
{
   if ( state == DRAGON_JUMP )
      changeState( DRAGON_IDLE ); // or DRAGON_LANDING, or w/e
}

}


Maybe something like this?

_________________
Heard in #gpwiki: <wzl> is there some serious grammar fail in your line or am i just too intelligent for your logic


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jul 22, 2010 9:11 am 
Dexterous Droid
User avatar

Joined: Wed Aug 18, 2004 7:40 pm
Posts: 3746
Location: South Africa
Thanks, sdw! I'll now put it into practice.

_________________
Whatever the mind can conceive and believe, it can achieve


Top
 Profile  
 
 Post subject:
PostPosted: Mon Nov 15, 2010 3:18 pm 
Dexterous Droid
User avatar

Joined: Wed Aug 18, 2004 7:40 pm
Posts: 3746
Location: South Africa
After reading the first few chapters of Code Complete I've got a much better grasp on the design problem. I'd recommend it to anyone wanting to learn more about design.

_________________
Whatever the mind can conceive and believe, it can achieve


Top
 Profile  
 
 Post subject:
PostPosted: Mon Nov 15, 2010 5:44 pm 
Level 22 Norse Warrior-Librarian
User avatar

Joined: Mon Sep 04, 2006 5:25 pm
Posts: 517
Location: U.S.
Good deal :)

Does this mean we can look forward to another awesome game based around coffee beans and imps? :P

_________________
Worlds at War (Current Project) - http://www.awkward-games.com
Ganadu'r, The Eternal Sage (Other Current Project) - http://rpg.naget.com
Programming tutorials and web-design services: http://www.wyrmmage.com


Top
 Profile  
 
 Post subject:
PostPosted: Mon Nov 15, 2010 8:51 pm 
Dexterous Droid
User avatar

Joined: Wed Aug 18, 2004 7:40 pm
Posts: 3746
Location: South Africa
wyrmmage wrote:
Good deal :)

Does this mean we can look forward to another awesome game based around coffee beans and imps? :P


For real! Can't go wrong with coffee beans and imps. :D

I'm just finishing off this game at the moment, I'll probably be posting on here in the next little while to give it a round of play-testing.

It was seriously very enlightening to read Code Complete - I don't know if that was just because of the particular set of holes in my knowledge. Especially reading chapters 5 and 6, and 4 to a lesser extent. As I was reading, I eventually "got" the purpose of OO. After that I realised several things about the way I'd done my code and redesigned it. But at that point I also learnt the cost of implementing design changes late into development, so I'm continuing on with my old design, just to finish the project off. The project as a whole has definitely been a huge positive learning step, though.

_________________
Whatever the mind can conceive and believe, it can achieve


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 27 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group