Jump to content

  • Log In with Google      Sign In   
  • Create Account

FREE SOFTWARE GIVEAWAY

We have 4 x Pro Licences (valued at $59 each) for 2d modular animation software Spriter to give away in this Thursday's GDNet Direct email newsletter.


Read more in this forum topic or make sure you're signed up (from the right-hand sidebar on the homepage) and read Thursday's newsletter to get in the running!


cleaning up a state


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
13 replies to this topic

#1 GrimV5   Members   -  Reputation: 101

Like
0Likes
Like

Posted 30 January 2014 - 08:46 PM

that's pretty cool. I actually have a question about states.  I've read that states should have a clean up method of some sort that should be called when the game is closed or the currently running state is changed to a new state.  My question what exactly is supposed to happen in this clean up method? Its not like i can delete the variables or objects in a state , otherwise when i try to run the code I'll get a debug assertion error.



Sponsor:

#2 Tebriel   Members   -  Reputation: 544

Like
0Likes
Like

Posted 30 January 2014 - 10:32 PM

I'm a little rusty on the whole patterns thing, but I think it depends entirely on what you're using it for. The question is a little broad.  But, why can't you clean up resource demanding objects?  They can always be reloaded/recreated on initialization.  If you have a very elaborate menu/configuration state which uses lots of graphics, animations, music and so on, it'd be wasteful to leave all that taking up resources while your game is executing.



#3 GrimV5   Members   -  Reputation: 101

Like
0Likes
Like

Posted 30 January 2014 - 10:42 PM

It seems the best I would be able to do is set them all to null. Because when I try to use delete on anything it would give me a debug assertion error when I run the program. Plus I would assume that the garbage collector would handle it after the state is popped off the vector. 


Edited by GrimV5, 30 January 2014 - 10:43 PM.


#4 Ludus   Members   -  Reputation: 970

Like
0Likes
Like

Posted 30 January 2014 - 11:53 PM

What happens during the activation of a state? Is anything allocated into the memory? Are entities created with "new"? Are the textures of those entities' sprites loaded? Are sound effects or music loaded? As a general rule, whatever you allocate during the activation of a state should be deallocated during its deactivation.



#5 phil_t   Crossbones+   -  Reputation: 4109

Like
2Likes
Like

Posted 31 January 2014 - 12:00 AM

What's the context for this question? You're not really giving us much to go on.

 

 

 


Because when I try to use delete on anything it would give me a debug assertion error when I run the program.

 

Huh? Did you allocate something with new? If so, then you need to delete it when you're done using it. If you're getting a "debug assertion", you're probably deleting something you never allocated, or your heap is corrupted.

 

 

 


Plus I would assume that the garbage collector would handle it after the state is popped off the vector. 

 

C++ doesn't have a garbage collector, what are you talking about?


Edited by phil_t, 31 January 2014 - 12:01 AM.


#6 frob   Moderators   -  Reputation: 22787

Like
2Likes
Like

Posted 31 January 2014 - 12:12 AM


I've read that states should have a clean up method of some sort that should be called when the game is closed or the currently running state is changed to a new state.  My question what exactly is supposed to happen in this clean up method?

A state is a concept.  Exactly what it means is entirely up to you.

 

A state might be a single number or enumeration.  Your code may have something like "int state = 0; ... state = 1; ... state = 2..." or more likely it would be named: "int state = ThingState_PreInit; ... state = ThingState_Initialized; ... state = ThingState_Active".

 

A state might be an index to a data table. "StateEntry* currentState = AllPossibleStates[0]; ... currentState = currentState->Next.AfterSuccess;"

 

A state might be an object on a stack. A state might be an item in a queue. A state might be some other object or structure.

 


It seems the best I would be able to do is set them all to null. Because when I try to use delete on anything it would give me a debug assertion error when I run the program.

 

That has nothing to do with states and everything to do with memory management. In order to use the C++ language effectively you must understand memory management and object lifetimes.  If you create or allocate something it is your responsibility as the developer to ensure it is properly cleaned up. This is true on all languages; even those with automatic garbage collection require you to properly manage object lifetimes.

 

If you created objects and simply point them to null you are probably creating memory and resource leaks. Sometimes you can get away with those bugs for a short time, but they are bugs that can result in very nasty problems. 


Check out my book, Game Development with Unity, aimed at beginners who want to build fun games fast.

Also check out my personal website at bryanwagstaff.com, where I write about assorted stuff.


#7 Servant of the Lord   Crossbones+   -  Reputation: 21191

Like
0Likes
Like

Posted 31 January 2014 - 01:32 AM

that's pretty cool. I actually have a question about states.  I've read that states should have a clean up method of some sort that should be called when the game is closed or the currently running state is changed to a new state.  My question what exactly is supposed to happen in this clean up method? Its not like i can delete the variables or objects in a state , otherwise when i try to run the code I'll get a debug assertion error.

 

In my states, I have Activated() and Deactivated(), and the gamestates also have constructors and destructors (destructors are mostly unused, since most dynamic memory uses smart pointers).

Activation is when gamestates get switched. Since my gamestates are hierarchical, the entire parent-child hierarchy is activated. 

 

Not every state needs to do something when activated or deactivated. If you're managing some heavy resources, you might want to allocate and free the resources there, but in general, I wouldn't do that unless you find (through actual use, not premature optimization) that you actually need the memory.

 

One non-memory related use for activation is this:

Imagine you had one or more timers associated with a state. The states get switched, the pause menu gets displayed, and the old state with the timers is no longer active. Depending on what those timers are doing, some of them you'll want to pause() and resume() when the state gets deactivated() and activated(), and others you'll want to continue to let running even when the state isn't active.

 

Any directly time-based changes to a state you can manipulate through the Update(deltaTime) function calls (which in my code doesn't get called when the state is deactivated), but if you want to do something like "Do X every five minutes", or "Y was triggered, so in 30 seconds trigger function Z one time", I'd use a timer for that.


Edited by Servant of the Lord, 31 January 2014 - 02:09 AM.

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]


#8 L. Spiro   Crossbones+   -  Reputation: 14419

Like
2Likes
Like

Posted 31 January 2014 - 10:31 PM

This sounds so familiar I can’t help but wonder if this stems from something on my site or something I posted some time ago.

Breaking your game into states is a good idea.
If you have managed your memory properly, when a state starts and ends, the amount of memory allocated should be the same.
This is a good way to detect leaks and pinpoint where they are. If you detect a leak when you change from the main menu to the game-start screen, you know you have a leak in your main menu state class and it should be easy to track down.


But there is nothing really tricky about how to implement this.
All classes have a constructor and destructor, but I recommend requiring them to implement a Create() and Destroy() instead, because some resources require a pointer to a higher-level object, such as CGame, and you won’t necessarily have the option to release those resources inside a destructor (which can’t accept parameters). You could implement only Destroy() and use the constructor to replace Create(), but it’s just cleaner to let the constructor and destructor be its own pair and let Create() and Destroy() be their own pair.


Otherwise there is nothing about it.
If the state has released everything it has allocated, the state manager can check how much memory has been allocated before and after a state and if there are any discrepancies it should print a warning.


L. Spiro

Edited by L. Spiro, 31 January 2014 - 10:33 PM.

It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013
I went to my local Subway once to find some guy yelling at the staff. When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums

#9 GrimV5   Members   -  Reputation: 101

Like
0Likes
Like

Posted 04 February 2014 - 11:29 AM

sorry for taking so long to reply here is one of states I made:

 

#include "MainMenuState.h"
#include "State.h"
#include "SEngine.h"
#include <SFML/Graphics.hpp>
 
MainMenuState MainMenuState::mainMenuStart; // static MainMenuState variable
 
/* Load() method for MainMenuState, initializes all variables in MainMenuState.
 */
void MainMenuState::Load(){
backGround = sf::RectangleShape(sf::Vector2f(640.0f,480.0f));
menu = backGround.getLocalBounds();
backGround.setOrigin(menu.left,menu.top);
button1 = sf::RectangleShape(sf::Vector2f(100.0f,50.0f));
button2 = sf::RectangleShape(sf::Vector2f(100.0f,50.0f));
button1.setPosition(menu.left + 270.0f, menu.top + 300.0f);
button2.setPosition(menu.left + 270.0f, menu.top + 390.0f);
backGround.setFillColor(sf::Color::Blue);
button1.setFillColor(sf::Color::Red);
button2.setFillColor(sf::Color::Red);
};//Load()
 
/* Handling method for MainMenuState, handles events that may occur in the
   MainMenuState. The only parameter it takes is a reference to the state
   machine, SEngine.*/
void MainMenuState::Handling(SEngine* gameEng){
 
};//Handling()
 
/* Paint method for MainMenuState, draws the components of this state onto the screen.
   The only parameter it takes is a reference to the state machine, SEngine.*/
void MainMenuState::Paint(SEngine* gameEng){
gameEng->getWindow()->clear();
gameEng->getWindow()->draw(backGround);
gameEng->getWindow()->draw(button1);
gameEng->getWindow()->draw(button2);
gameEng->getWindow()->display();
};//Paint()
 
/* Update method for MainMenuState, updates certain values or components that may
   have been affected by events in Handling method. The only parameter it takes
   is a reference to the state machine, SEngine.*/
void MainMenuState::Update(SEngine* gameEng){
 
};//Update()
 
void MainMenuState::TidyUp(){
};//TidyUp()
 
void MainMenuState::Halt(){
};//Halt()
 
void MainMenuState::Continue(){
};//Continue()
 
button1 and button2 are both RectangleShape objects from the SFML library.  I tried putting the following lines in TidyUp():
delete button1;
delete button2;
 
The compiler doesn't have an issue with this but if I try to run the program then I get the debug assertion error.


#10 GrimV5   Members   -  Reputation: 101

Like
-2Likes
Like

Posted 04 February 2014 - 11:30 AM

 

 

C++ doesn't have a garbage collector, what are you talking about?

 

This only proves to me that my C++ teacher wasn't that great, he never clarified certain things so I was left to assume.



#11 fastcall22   Crossbones+   -  Reputation: 4476

Like
0Likes
Like

Posted 04 February 2014 - 12:44 PM

button1 and button2 are both RectangleShape objects from the SFML library. I tried putting the following lines in TidyUp():
delete button1;
delete button2;


button1 and button2 are RectangleShape objects from the SFML library, but they weren't allocated on the heap with new. Therefore, no delete is necessary:

int main() {
    sf::RectangleShape a = sf::RectangleShape(sf::Vector2f(640.0f,480.0f));
    sf::RectangleShape* b = new sf::RectangleShape(sf::Vector2f(640.0f,480.0f));

    // Do stuff with a, b

    delete b;
} // a is destroyed automatically


c3RhdGljIGNoYXIgeW91cl9tb21bMVVMTCA8PCA2NF07CnNwcmludGYoeW91cl9tb20sICJpcyBmYXQiKTs=

#12 Servant of the Lord   Crossbones+   -  Reputation: 21191

Like
3Likes
Like

Posted 04 February 2014 - 02:36 PM

As fastcall22 mentioned, only delete what you new - and if you're a beginner, don't new or delete at all. And if you're a professional, and using modern C++ techniques, you'd use smart pointers 95% of the time anyway so you'd still mostly ignore new and delete.
 

C++ doesn't have a garbage collector, what are you talking about?

This only proves to me that my C++ teacher wasn't that great, he never clarified certain things so I was left to assume.

It's the student's job to ask questions if they don't understand. 
It's the teacher's job to answer questions - that's what they are there for (otherwise, you might as well just read a book).
The teacher can't answer questions that the students don't ask. wink.png
 
Plus, C++ does automatically collect some memory. It doesn't have a "garbage collector", but it still frees memory automatically in certain circumstances. Maybe your teacher was trying to explain how C++ memory was collected, and having heard the phrase "garbage collector" sometime in the past, you might've automatically assumed that the teacher was talking about garbage collection?
 
I'm guessing this is the case, because this is exactly the problem you are having with your code: You think certain memory isn't getting freed that C++ is already freeing for you, and you're trying the manually free memory that has already been freed (or that will automatically be freed later). smile.png
 
As popular as it is to do so, don't assume your teacher is ignorant. Just ask questions if you don't understand. If, after asking questions, something still doesn't make sense, then ask on these forums and we can clarify.
 
Here's how basic C++ variables work: (Don't roll your eyes, this is important to understand!)
int x = 0;
That line of code creates an integer named 'x', that is initialized to '0'. It will automatically be freed when the execution of the code reaches the end of its scope.
 
Example:
void func()
{
     int a = 20;
 
     if(true)
     {
          int b = 70;
          
     } //The variable 'b' gets freed after this point.
    
     int c = 0;
     
} //The variables 'a' and 'c' get freed after this point.
 
If you do this:
void func()
{
     int a = 20;
 
     delete &a; //Programmer  manually frees 'a' (not good).
     
} //Compiler automatically frees 'a'. But since was *already* freed, the program will either
  //crash (if you're lucky) or do unexpected crazy stuff that's really hard to track down and debug.
For every variable in your code, either you're managing its memory, or the compiler is. But not both. Every variable should have an owner: Who owns it? The function? The class? Or are you manually managing it?
 
Now let's look at the lifetime of variables in structs: (classes behave the same way)
struct MyStruct
{
     int x;
};
'x' is owned by 'MyStruct'. When 'MyStruct' has its memory freed, 'x' will also be freed.
 
Observe:
void func()
{
     MyStruct myStruct;
     myStruct.x = 20;
     
} //'myStruct' is freed here. Because 'myStruct' is freed, 'myStruct.x' is also freed at the same time.
 
Now classes also have constructors and destructors, which is important to use. All your 'Load()' code should go in the class's constructor. That's what it's for.
All your 'TidyUp()' code should go in the class's destructor. That's why destructors exist.
//This is a constructor. It's a function called when a class gets created.
MainMenuState::MainMenuState()
{
    
}
 
//This is a destructor. It's a function called when a class gets destroyed.
MainMenuState::~MainMenuState()
{
    
}
Unless you're manually doing something that needs to be manually undone, you don't really need to use a destructor in this MainMenuState class.

Edited by Servant of the Lord, 04 February 2014 - 02:38 PM.

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]


#13 GrimV5   Members   -  Reputation: 101

Like
-1Likes
Like

Posted 04 February 2014 - 04:36 PM

 

 

It's the student's job to ask questions if they don't understand. 
It's the teacher's job to answer questions - that's what they are there for (otherwise, you might as well just read a book).
The teacher can't answer questions that the students don't ask. wink.png

 

This is not the issue and I don't assume he's ignorant. It's just that english is not his first language and while he can speak it pretty well, he still has an issue understanding what other people ask or say to him.  I've physically walked up to the board to write (even draw) out my problem and barely half the time does he understand. And while his TA CAN speak english, he is near impossible to get a hold of. So I resort to an C Reference handbook and C++ forums to get my answers.



#14 GrimV5   Members   -  Reputation: 101

Like
0Likes
Like

Posted 04 February 2014 - 04:54 PM

As fastcall22 mentioned, only delete what you new - and if you're a beginner, don't new or delete at all. And if you're a professional, and using modern C++ techniques, you'd use smart pointers 95% of the time anyway so you'd still mostly ignore new and delete.
 

 

C++ doesn't have a garbage collector, what are you talking about?

This only proves to me that my C++ teacher wasn't that great, he never clarified certain things so I was left to assume.

 

It's the student's job to ask questions if they don't understand. 
It's the teacher's job to answer questions - that's what they are there for (otherwise, you might as well just read a book).
The teacher can't answer questions that the students don't ask. wink.png
 
Plus, C++ does automatically collect some memory. It doesn't have a "garbage collector", but it still frees memory automatically in certain circumstances. Maybe your teacher was trying to explain how C++ memory was collected, and having heard the phrase "garbage collector" sometime in the past, you might've automatically assumed that the teacher was talking about garbage collection?
 
I'm guessing this is the case, because this is exactly the problem you are having with your code: You think certain memory isn't getting freed that C++ is already freeing for you, and you're trying the manually free memory that has already been freed (or that will automatically be freed later). smile.png
 
As popular as it is to do so, don't assume your teacher is ignorant. Just ask questions if you don't understand. If, after asking questions, something still doesn't make sense, then ask on these forums and we can clarify.
 
Here's how basic C++ variables work: (Don't roll your eyes, this is important to understand!)
int x = 0;
That line of code creates an integer named 'x', that is initialized to '0'. It will automatically be freed when the execution of the code reaches the end of its scope.
 
Example:
void func()
{
     int a = 20;
 
     if(true)
     {
          int b = 70;
          
     } //The variable 'b' gets freed after this point.
    
     int c = 0;
     
} //The variables 'a' and 'c' get freed after this point.
 
If you do this:
void func()
{
     int a = 20;
 
     delete &a; //Programmer  manually frees 'a' (not good).
     
} //Compiler automatically frees 'a'. But since was *already* freed, the program will either
  //crash (if you're lucky) or do unexpected crazy stuff that's really hard to track down and debug.
For every variable in your code, either you're managing its memory, or the compiler is. But not both. Every variable should have an owner: Who owns it? The function? The class? Or are you manually managing it?
 
Now let's look at the lifetime of variables in structs: (classes behave the same way)
struct MyStruct
{
     int x;
};
'x' is owned by 'MyStruct'. When 'MyStruct' has its memory freed, 'x' will also be freed.
 
Observe:
void func()
{
     MyStruct myStruct;
     myStruct.x = 20;
     
} //'myStruct' is freed here. Because 'myStruct' is freed, 'myStruct.x' is also freed at the same time.
 
Now classes also have constructors and destructors, which is important to use. All your 'Load()' code should go in the class's constructor. That's what it's for.
All your 'TidyUp()' code should go in the class's destructor. That's why destructors exist.
//This is a constructor. It's a function called when a class gets created.
MainMenuState::MainMenuState()
{
    
}
 
//This is a destructor. It's a function called when a class gets destroyed.
MainMenuState::~MainMenuState()
{
    
}
Unless you're manually doing something that needs to be manually undone, you don't really need to use a destructor in this MainMenuState class.

 

Well, I don't actually use a constructor for my states anyway, here is my initialstate header:

 

#ifndef InitializeState_h
#define InitializeState_h
#include "State.h"
#include "SFML/Graphics.hpp"


class InitializeState : public State{
public:

void Load();
void Handling(SEngine* gameEng);
void Paint(SEngine* gameEng);
void Update(SEngine* gameEng);
void TidyUp();
void Halt();
void Continue();
static InitializeState* Initialize(){
return &gameStart;
}


protected:
InitializeState() {}

private:
static InitializeState gameStart;
int ready;
sf::RectangleShape shape;
};


#endif
 
Here is my main that calls the Initialize function:
 
#include "SEngine.h"
#include "InitializeState.h"


using namespace std;

int main(){
  char word[] = {'T','e','s','t','i','n','g'}; //ignore, just for testing purposes
  SEngine engine;
  engine.Load(word);
  engine.changeState(InitializeState::Initialize());

  while(engine.isRunning()){
   while(engine.getWindow()->isOpen()){
    engine.Handling();
    engine.Update();
    engine.Paint();
   }
  }

  return 0;
};

And here is my state engine class:

#include "SEngine.h"
#include <SFML/Graphics.hpp>
#include "State.h"


void SEngine::Load(const char* title, int xsize, int ysize){
  running = true;
  width = xsize;
  height = ysize;
  window.create(sf::VideoMode(width,height), "Game Engine");
};

void SEngine::changeState(State* state){
  if(!states.empty()){
   states.back()->TidyUp();
   states.pop_back();
  }

  states.push_back(state);
  states.back()->Load();
};

void SEngine::Handling(){
  states.back()->Handling(this);
};

void SEngine::Update(){
states.back()->Update(this);
};

void SEngine::Paint(){
  states.back()->Paint(this);
};

bool SEngine::isRunning(){
  return running;
};

void SEngine::endGame(){
  running = false;
};

void SEngine::PushIt(State* gstate){
  if(!states.empty()){
   states.back()->Halt();
  }

  states.push_back(gstate);
  states.back()->Load();
};

void SEngine::PopIt(){
  if(!states.empty()){
   states.back()->TidyUp();
   states.pop_back();
  }

  if(!states.empty()){
   states.back()->Continue();
  }
};

void SEngine::TidyUp(){

  while(!states.empty()){
   states.back()->TidyUp();
   states.pop_back();
}


};





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