Jump to content

  • Log In with Google      Sign In   
  • Create Account

The Game Logic - how do you handle that?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
8 replies to this topic

#1 xynapse   Members   -  Reputation: 151

Like
0Likes
Like

Posted 31 July 2012 - 09:42 AM

Guys,
what are the approaches used to handle the game/level logic, which prevent you from falling into a spaghetti code?

I have a player in 3d isometric adventure game where you escape from a set of rooms.
Each room is different and sometimes you will need to go back, to pickup an item which is needed in your current ara ( you pickup a wrench in room 3, which you will use in room 6 to open up the safe ).




Let's imagine a simple level :

Posted Image

Player
Doors
Key



Ok, now that is easy aye?You can make this easily with a single BOOLean like:

// PSEUDOCODE //


bool bHasKey = false;


// Event handler
case ITEM_PICKEDUP:
{
   if(item == key)
   {
	bHasKey = true;
   }
}
break;


case PLAYER_COLLIDES_DOORS:
if(bHasKey == true)
{
  pDoors->Open();
  pKey->Destroy();
}
break;


It's easy but i would like to avoid this kind of coding game logic - as it turns very quickly, that if you handle 10 rooms per level in one game state, and you have 5 levels, your game state class for level handling will grow into a piece of if/else/switch/case stuff that you'll debug continously scratching your head and asking - why is this here, and that there..?

Let's go to another theoretical room.
So, this time player has tu pull 3 switches in specific order - which in return opens the door for 5 seconds.

Posted Image


Player
Switch
Doors

Now this level plus the previous one are slowly moving towards a mess - but as long as there are three to four rooms per level - you can handle it somehow.

What if my Level (map) has 10 rooms, each one more and more complicated, and i have 5 levels ready - making 50 rooms in total.
This kind of coding would be a disaster sooner or later...


How do you approach that ? single state machine per level ? ( how would you apply it to this model ? ) anything else?

I just can't figure out how to do it - my brain is empty after several days of decoding ima4.



Thanks !
perfection.is.the.key

Sponsor:

#2 Servant of the Lord   Crossbones+   -  Reputation: 19547

Like
0Likes
Like

Posted 31 July 2012 - 12:23 PM

Give doors a counter that gets incremented or decremented when certain things happen, and when the counter reaches a certain point, the door is open (and if the counter drops back below the certain point, the door shuts again).

// PSEUDOCODE //
typedef unsigned int DoorID;
struct Door
{
DoorID doorID;
unsigned int counter;
unsigned int neededCounter;

bool IsDoorOpen() { return counter >= neededCounter; }
}
struct Key
{
DoorID doorID; //The door this key goes to.
bool playerHas; //True if the player has this key.
}
struct Switch
{
DoorID doorID; //The door this switch goes to.
bool activated;
bool switchDeactivates; //True if the switch automaticly de-activates itself after a certain amount of time.
uint32 timeLeft; //The amount of millaseconds before the switch de-activates itself.
uint32 durationBeforeReset; //The amount of millaseconds to set 'timeLeft' to, when the switch is activated.
}
struct Enemy
{
DoorID doorID; //The door that gets incremented when this enemy is killed.
int health;
bool isDead;
}
struct Level
{
std::vector<Door> doors;
std::vector<Key> keys;
std::vector<Switch> switches;
std::vector<Enemy> enemies;
}

// Event handler
case KEY_COLLIDED:
{
   if(currentLevel.keys[key].playerHas == false)
   {
currentLevel.keys[key].playerHas == true;
DoorID doorID = currentLevel.keys[key].doorID;

currentLevel.doors[doorID].counter++;
   }
}
break;
case SWITCH_ACTIVATED:
{
   if(currentLevel.switches[switch].activated == false)
   {
	    currentLevel.switches[switch].activated == true;
if(currentLevel.switches[switch].switchDeactivates == true)
{
  currentLevel.switches[switch].timeLeft = currentLevel.switches[switch].durationBeforeReset;
}

   }
}
break;

case PLAYER_COLLIDES_DOORS:
if(pDoor.IsDoorOpen() == false)
{
//Don't let the player past if the door isn't open.
}
break;

If you're using C++, you should actually make the Door and Key and Enemy and Switch be classes, and handle their logic within the class itself, but I didn't want to deviate too far from what you already had.
Every door should be of the same struct or class, and the level should just contain a vector of all the doors, and a vector of all the keys, and a vector of all the enemies, and a vector of all the switches, etc...
If you understand polymorphism, and if it makes sense in your code arctitecture, you might even just have one vector with derived entity types.

That's at a basic level, but depending on your programming skill you might want to do something more advanced.
You could create programmable logic gates between your entities. See here and here for one posssible design.
You could use a scripting engine to describe logic between entities.
It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


#3 kuramayoko10   Members   -  Reputation: 386

Like
1Likes
Like

Posted 31 July 2012 - 12:43 PM

Complementing what Servant said and answering to this issue:

It's easy but i would like to avoid this kind of coding game logic - as it turns very quickly, that if you handle 10 rooms per level in one game state, and you have 5 levels, your game state class for level handling will grow into a piece of if/else/switch/case stuff that you'll debug continously scratching your head and asking - why is this here, and that there..?


If you are working with OO you could use Polymorphism.
You could create a generic class named Item. Then you would have Key, Switch, etc. as children of Item.
That way you could create a method in Item called Grab() and Act() that is overwritten by the children.
Then in your class you would only need a vector of Items that call Act() or Grab() given a collision.
At runtime the computer will know if this is just an Item or a child of Item and will call the method Act() accordingly.

A quick example:
[source lang="cpp"]class Item{public: Item() {} ~Item() {} virtual void act() {} };class Key : public Item{private: int type; bool equipped;public: Key(); ~Key(); void act() { //open door of same type }};//In your game somewherevector<Item*> items;//Key *goldenKey = new Key();//items.push_back(goldenKey);//Event Scope{ vector<Item*>::iterator iter; iter = items.begin(); while(iter != items.end()) { //if players collides with something and attack button is pressed iter->act(); }}[/source]

Edited by kuramayoko10, 31 July 2012 - 02:26 PM.

Programming is an art. Game programming is a masterpiece!

#4 xynapse   Members   -  Reputation: 151

Like
0Likes
Like

Posted 31 July 2012 - 02:23 PM

@kuramayoko10, Servant of the Lord - yes my engine is OO, so polymorphism is a known practice.

Thanks to both of you for a bit of light on this.

I just need more sleep to finish up this title ;)


Edited by xynapse, 31 July 2012 - 02:24 PM.

perfection.is.the.key

#5 JWalsh   Moderators   -  Reputation: 463

Like
0Likes
Like

Posted 01 August 2012 - 10:55 AM

Hey Xynapse,

This ties in very closely to a tutorial (possibly series) I'm writing. Do you mind if I use your problem and possibly diagrams as an example? The tutorial is going to focus on the structure of a game, both the game engine components, as well as the game-play components. It'll address the design goals of each set and how you can engineer the game and engine for best performance & flexibility trade-offs.

Cheers!
Jeromy Walsh
Sr. Tools & Engine Programmer | Software Engineer
Microsoft Windows Phone Team
Chronicles of Elyria (An In-development MMORPG)
GameDevelopedia.com - Blog & Tutorials
GDNet Mentoring: XNA Workshop | C# Workshop | C++ Workshop
"The question is not how far, the question is do you possess the constitution, the depth of faith, to go as far as is needed?" - Il Duche, Boondock Saints

#6 Cornstalks   Crossbones+   -  Reputation: 6989

Like
0Likes
Like

Posted 01 August 2012 - 11:45 AM

One possibility is to use an event system. There are different design patterns to implement event systems, two popular ones seem to be the observer pattern and the publish-subscribe pattern. Using this, you can create messages/events that get fired off when something happens, and have listeners/subscribers that consume these events. Here's a crude example (I'm sure someone could make many improvements on it):

struct Event
{
    enum Type
    {
	    DOOR_OPENED,
	    DOOR_CLOSED,
	    SWITCH_ACTIVATED,
	    SWITCH_DEACTIVATED
	    // ...whatever other types of messages there are...
    } type;
   
    std::string senderId;
   
    struct DoorEvent
    {
	    bool isOpen;
	    // ...whatever other data you need...
    };
   
    struct SwitchEvent
    {
	    bool isActivated;
	    // ...whatever other data you need...
    };
   
    union
    {
	    DoorEvent doorEvent;
	    SwitchEvent switchEvent;
    };
};
/*
Then you have some options on how to set this up:
    1.    Let each object subscribe to events from another object
		    Example 1:  Door A subscribes to events from switch X.
					    Door A also subscribes to events from the room (to
					    know if the player enters/leaves the room).
		    Example 2:  Door A subscribes to *ONLY* SWITCH_ACTIVATED events
					    from switch X.
					    Door A also subscribes to *ONLY* DOOR_CLOSED events
					    from the room.
    3.    Let some system class manage events and objects can subscribe to
	    listen to events.
		    Example 1:  Door A subscribes to the main event system class.
					    Switch X has a SWITCH_ACTIVATED event, which gets
					    posted to the main event system class, and then
					    distributed to all other subscribers
		    Example 2:  Door A subscribes to the main event system class
					    and requests SWITCH_ACTIVED events from *ONLY*
					    objects with the ID of 'X' and additionally it
					    subscribes to *ALL* DOOR_CLOSED events.
With such a system, objects only have to care about themselves. You can
easily "chain" conditions together by how you craft your event handling.
Door A opening requires door B to be closed and switch X to be activated,
for example.
*/

Another option is to use a finite state machine like you said, but you could break it down even further and do a finite state machine for each object in the room (where one object's state depends on the other objects' states, rather than thinking of the entire room's state as a whole).

This is where scripting languages become useful though. You can put this all in C++ code, but it requires you to recompile (and go through the level again) if you make changes to things.
[ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

#7 Postie   Members   -  Reputation: 964

Like
0Likes
Like

Posted 01 August 2012 - 06:19 PM

The "easy solution" you gave for the key and door example is not particularly good. It'll serve you well if your entire game consists of a single room with a single key, but won't work beyond that without getting very messy.

A better way of keeping track of something like that is to give the key a unique entity ID, and have the door need that specific entity ID to unlock. Then you can scatter multiple keys in a room with different ID's, but only one of them will open the door.

To support more advanced behaviour you can add an event driven system as suggested by cornstalks. Something like a pressure plate for example. If the pressure plate changes state it could send an event to the door to tell it to open or close. Levers, buttons or whatever else you can think of are just variations on this theme.

I'm not sure what sort of game you're trying to create, but it might be worth looking at the developer blogs for Legend Of Grimrock. They discuss how they handle scripting of events like this and might provide some insight.
Currently working on an open world survival RPG - For info check out my Development blog: ByteWrangler

#8 xynapse   Members   -  Reputation: 151

Like
0Likes
Like

Posted 02 August 2012 - 03:22 AM

Hey guys, sorry for a delay - but i was off for some time.

@JWalsh - sure thing, go on and let me know where can i read about this - i am very interested in this.

@Cornstalks && @Postie - i already have events driven system implemented, and yes this is a great solution - everything within a level can dispatch 'messages' to it's receivers and thanks to that i can react on any kind of events happening in the game. Just need to 'pack' it somehow into a generic solution, that i could use within all levels in the game...

Well i think that i could delegate that 'logic handling' to the script files instead of going in C++ - i think it would be better to separate that from the main game code, and just let scripting handle the logic 'somehow' - but from the other side, it will take time to prepare the scripting implementation - and that time could be used to code the logic handling within the game. It's my first public title, and i would like to avoid using 3rd party libs with licenses.

I am still interested how people handle logic in their games, not everyone has scripting implemented - so how do they handle their game logic clean and clear ?

I'll prepare a sketch for the first level and drop it in here so we can all see what kind of game we're talking about. Maybe that would be more useful than my YT post.


Thanks for any input guys, it is great we can share ideas.

Edited by xynapse, 02 August 2012 - 05:22 AM.

perfection.is.the.key

#9 Kyall   Members   -  Reputation: 287

Like
0Likes
Like

Posted 04 August 2012 - 11:59 AM

I would recommend scripting aswell. Here's an example of the logic for a level of the game I'm working on right now:

# A clock and an enemy counter 
Clock clock
clock.start()
enemy_count = 0


# When the clock hits 10 seconds, spawn an enemy and increment the enemy count 
on clock.istime(10s):
   spawn_enemy 
   enemy_count ++

# When an enemy is killed, decrement the enemy count and if the count is now zero finish the mission  

on enemy.health == 0:
    enemy_count --
    if enemy_count == 0:
	  global::pass_mission()

# Namespace 
global:
   def fail_mission():
	 # Update some stats here maybe 
	 game::show_mission_retry()
   def pass_mission():
	 # Update some stats here maybe
	 game::show_mission_success()

# If the satellite is killed before all the enemies then fail the mission 
satellite:
  health = 40
  on Damage( x )
	 health = health - x
	 if health <= 0:
	    global.fail_mission()

I say Code! You say Build! Code! Build! Code! Build! Can I get a woop-woop? Woop! Woop!




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS