gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

DS development > Game state stuff

#168062 - ritz - Wed Apr 08, 2009 8:36 pm

Hi all,

I hoping to find some help in regards to game state management and related topics (such as AI timing execution, running scripting byte code, realtime battle modes, etc). I have something already in place and it works not too bad, but at a high level it just feels inadequate, too limiting. I've decided to toss it out and start over. I really wanna do this right but can't seem to find resources to help me (I've scraped the web fairly well already). Does anyone have links to articles, books I could buy, viewable source code, or anything like that? I'm not interested in the very basics, but more of the "professional-grade" type stuff. Maybe even someone in the actual industry lurking here have hints/tips or places I should look to learn?

#168064 - Miked0801 - Wed Apr 08, 2009 9:07 pm

Finite state machine stuff right? And related topics. If so, this may belong in another, non-DS specific group.

And yes, there are plenty of professional developers who lurk and post here.

#168068 - silent_code - Wed Apr 08, 2009 11:27 pm

Think your design through and use that. :^)

As with most, if not all programming tasks, there are a lot of solutions to these problems. Take two equally skilles and experienced programmers and they will both produce solutions, that the other one might find "odd" in some way.
You have to use your code, so:
Think your design though und use that.

Speaking of "professionals:" you wouldn't want to know what "professionals" sometimes have to work with... ;^p
_________________
July 5th 08: "Volumetric Shadow Demo" 1.6.0 (final) source released
June 5th 08: "Zombie NDS" WIP released!
It's all on my page, just click WWW below.

#168076 - sgeos - Thu Apr 09, 2009 1:14 am

Limitations are part of the game. You really ought to know what both your project requirements and technical requirements are before you start so that you know which limitations will not affect you.

#168079 - sajiimori - Thu Apr 09, 2009 4:27 am

I ran into the same problem you're encountering: There's very little good writing out there about How To Design Game Code. The traditional snippets of advice are not nearly enough, and books seem to completely dodge the question of overall architecture, instead focusing on scene graph data structures and other technical details.

All I can say is: Rewrite, rewrite, and rewrite again. Keep anything that you're 100% satisfied with, and throw out everything else, as many times as it takes (and it took me a lot). Don't worry about getting it perfect next time; just make it better.

Even if somebody gives you an amazingly awesome solution, you won't understand what's so great about it until you struggle with other solutions.

That said, I'll talk about any specific topics you're interested in.

#168082 - TwentySeven - Thu Apr 09, 2009 10:59 am

Object Orientated coding methodologies seem like they were designed for game logic, even if you have to somewhat emulate it through flat C and structures, instead of proper classes (I'm looking at you, quake engines).

So thats where I'd start. Start out with a list of objects represented by a structure or class type, with a 1:1 correlation between that list and stuff in your world.

Give each object an update method, and a draw method, and process both every frame.

#168086 - ritz - Thu Apr 09, 2009 3:27 pm

Miked0801 wrote:
Finite state machine stuff right? And related topics. If so, this may belong in another, non-DS specific group.

Yes, FSM with AI, etc. But what about code flow, let's say, for a scenario during real-time battle amongst multiple objects and there's user input such as picking a spell from a menu that's been triggered while handling on-going effects such as text feedback on the still-running main game screen?
Yea you're right, this might be the wrong forum... but I like you guys! You're all my mentors in this! Plus, I don't really read any other forums or websites these days.

sgeos wrote:
Limitations are part of the game. You really ought to know what both your project requirements and technical requirements are before you start so that you know which limitations will not affect you.

I understand this to be very true but I haven't prepared (or wanted) a detailed or even high-level design document. This is just a hobby for me - I've never made a game before, never made 3D stuff before (well, except some stupid class in university a billion years ago), and never did DS hardware programming before. I just decided to fart around with the DS for fun when I heard you could code on it. Whenever I do something new, I've always done things bottom-up the first time on purpose... lots of insight for me. So, I've already put something together like I mentioned earlier but now I want to learn it more formally. I'd like to understand how others have done it (specific to their requirements or not)... be it in a book, article, or source code. Then I'll have more ideas when I go ahead and rewrite some of my stuff.

sajiimori wrote:
Even if somebody gives you an amazingly awesome solution, you won't understand what's so great about it until you struggle with other solutions.

I agree with you fully on this. I'm only gathering insight to help for my next time 'round :)

TwentySeven wrote:
Give each object an update method, and a draw method, and process both every frame.

I'm essetially doing this already, however I'm not using an OO language... I do enough "proper" OO coding at work to make me *not* want to use it for my own hobby code :)

#168090 - sgeos - Thu Apr 09, 2009 6:35 pm

ritz wrote:
I understand this to be very true but I haven't prepared (or wanted) a detailed or even high-level design document.

This is fine if you are making a throwaway prototype or a short lived demo. You can use a code and fix life cycle model for these types of projects without worrying about painting yourself into a corner because the project will not continue long enough for that to happen. Hence knowing your project requirements are important.

#168091 - sajiimori - Thu Apr 09, 2009 9:31 pm

ritz, the complex battle situation you described has lots of interesting architectural things to say about it. I'll write a few paragraphs about various aspects of it, but I'm working blind: I don't know what would be most helpful for you.

First off, I don't do "proper" OOP, but I do use C++ language features to their fullest, to make my code as good as possible, using my own definition of "good", not a dogmatic one. I don't care where the idea of virtual methods came from; I use them where I want to.

Battle among multiple objects

Each object is derived from a base Actor class, which are automatically added to a global gActorList, which is iterated over each frame to update every actor. The actors focus on their own business, and primarily talk to each other using virtuals in the base class, rather than knowing each other's details.

The non-const Actor virtuals are geared toward notification, like "somebody is attacking you", not "omg recoil and lose hit points!!", and the const virtuals are geared toward queries as usual. Minimize the set of base Actor virtuals.

Actors don't have to be physical entities in the world; they can be anything that needs to be updated every frame. Avoid putting data in the base class; I never have any.

User input

Characters (a kind of Actor) own a CharacterControl, which sends commands to the character. The controller could send commands based on AI or an input device, and the commands should be at the same granularity as physical input buttons, e.g. 'attack', 'walk', etc, not "play the sword swing animation".

If some user input directly controls a player character, it should be handled (one way or another) inside a derived CharacterControl (e.g. PlayerCharacterControl).

If user input needs to be directed toward different things at different times (e.g. sometimes it controls the player character and other times it controls a menu), then higher level code needs to determine which control module needs to be updated. For instance, if a menu exists, update the menu, else update the player character controller.

Auxiliary effects

Effects can be actors, too. Which screen they're on doesn't matter; the actor list is screen-agnostic. If an actor controls a sprite on the top screen, then it has a Sprite object that's configured to appear on the top screen.

#168092 - ritz - Thu Apr 09, 2009 10:45 pm

sgeos wrote:
This is fine if you are making a throwaway prototype or a short lived demo.

It pretty much is. The furthest I plan on going without any real requirements or goal is a mini-mini-game using what I'm learning on-the-fly. Just getting my feet wet. I appreciate your advice and will definately use it if/when I toss my demo/mini-game and attempt a more fuller game using things I've figured out so far.

sajiimori wrote:
<insert sajiimori's post here>

Thanks for all that info... definately informative and useful for me.

#168093 - TwentySeven - Fri Apr 10, 2009 12:34 am

While I 100% agree with sajiimori when working with real OO (C++,C#), you can slum it with abusive pure C for similar effects and quick prototyping.

First thing I'd define is a fat base "class" like this, with some common base variables and method calls, and a chunk of padding on the end.

Code:


#define MAX_ENT_PAD 256

typedef struct FatEnt_s
{
   int16   type;
   int inuse;
                void (*Think)(FatEnt_s* this);
                void (*Draw)(FatEnt_s* this); 
                void (*OnDeath)(FatEnt_s* this); 
                //whatever other "base" methods
   byte padding[MAX_ENT_PAD];
} FatEntity_t;



Then you can have an array of these:
Code:

#define MAX_ENTS 2048
FatEntity_t  Entitys[MAX_ENTS];



Then, to actually build something useful out of this you define "inherited" *cough* versions of the fat ent.

Code:

typedef struct PlayerEnt_s
{
   int16   type;
   int inuse;
                void (*Think)(PlayerEnt_s* this);
                void (*Draw)(PlayerEnt_s* this); 
                void (*OnDeath)(PlayerEnt_s* this); 
                /// This must stay identical to the fatent ^^

                int x;
                int y;
                int hitpoints;
} PlayerEntity_t;


SpawnPlayer(int EntNum,int SX,int SY)  //ent num to be spawned at
{
      PlayerEntity_t *Player;
      Player=(PlayerEntity_t*)&Entitys[EntNum];

      memset(Player,0,sizeof(FatEntity_t); //0 it

      Player->type = PLAYER_ENTITY; //Some enum for each ent type, poor mans RTTI
      Player->Think=&PlayerThink;
      Player->x= SX;
      Player->y= SY;
      Player->inuse =1;
}

//and somewher else you can have loops like
for (int j=0;j<MAX_ENTITYS;j++)
{
    if (Entitys[j]->inuse)
    {
          if (Entitys[j]->Think)  Entitys[j]->Think(&Entitys[j]);   
          if (Entitys[j]->Draw)  Entitys[j]->Draw(&Entitys[j]);   
    }
}




Of course, you'll want to make sure sizeof(PlayerEntity_t) < sizeof(FatEntity_t). Just check that in an assert on startup.

#168094 - the-anonymous-coward - Fri Apr 10, 2009 1:03 am

Haven't read this yet, but I am looking forward to it.
http://lazyfoo.net/articles/article06/index.php

#168096 - gauauu - Fri Apr 10, 2009 4:03 am

TwentySeven wrote:

First thing I'd define is a fat base "class" like this, with some common base variables and method calls, and a chunk of padding on the end.


I've done similar things as well. Although often, instead of padding, I define a number of u32 variables, and use those as whatever state for each "subclass" (not sure that it's better or worse, just slightly different).

#168097 - Miked0801 - Fri Apr 10, 2009 4:55 am

Why fake it when the real thing is readily available?

#168098 - TwentySeven - Fri Apr 10, 2009 5:39 am

Quote:
Why fake it when the real thing is readily available?


Quote:
however I'm not using an OO language... I do enough "proper" OO coding at work to make me *not* want to use it for my own hobby code :)


He asked.

#168110 - sajiimori - Fri Apr 10, 2009 9:05 am

I just skimmed the Lazy Foo' article. Here's a few tips on how to further iterate on it. Some of these are easy to implement, and others are a bit challenging, but in my opinion, all are worthwhile.

- Try eliminating the enum of game states.

- Try having just one update() virtual on the GameState class, rather than multiple virtuals. (Keep the virtual destructor, of course.)

- If a new game state needs extra information to decide how to behave, try passing it that information directly, rather than leaving it "clues" sitting around in global variables.

- Try wrapping SDL_Surface in a class that automatically does cleanup, rather than trying to remember to free them at the right times. (Even if you hate OOP, at least use destructors; they're the best feature of C++.)

- On exit, just call exit(0) and skip the fancy footwork.

- If there's anything that needs to be done regardless of which state you're in (like checking if the window was closed), do it in the main loop, rather than repeating it in every state class. Hint: If that conflicts with the way SDL_PollEvent is used, perhaps the results of the event loop should be stored for later use, rather than acted upon immdiately... gInputState anyone?

- Find a not-ugly way to eliminate the myDot global. Make yourself a brand spankin' new Dot object every time, rather than reusing a potentially dirty one. Hint: Even if you don't have a global Dot value, you could still have a global Dot pointer...

- Make sure game objects mind their own business. The camera is beyond the scope of a lowly Dot's responsibilities, and far be it from the benevolent Overworld to monitor the poor Dot and force it into all Houses that it touches... and since when do rooms cause their occupants to move around?

- Something is fishy about every game mode independently drawing the dot. Try making a Scene object that has a list of things to render, and have the Dot add itself to that list automatically. After all, the fact that it has a visual representation is its own business.

- Try desigining classes that don't have invalid states. If it's wrong to not call init(), then why make it possible to not call it?

A final paraphrase from I forgot who: "Good software obviously has no mistakes. Bad software has no obvious mistakes."

#168173 - ritz - Mon Apr 13, 2009 2:57 am

Perfect, lots of great stuff to get started. Thanks everyone!

P.S. This post made with my newly opened DSi... pretty neat so far :)