• Create Account

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.

13 replies to this topic

#1GrimV5  Members

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.

#2Tebriel  Members

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.

#3GrimV5  Members

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.

#4Ludus  Members

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.

#5phil_t  Members

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.

#6frob  Moderators

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 occasionally write about assorted stuff.

#7Servant of the Lord  Members

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' or 'SotL' 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 -

#8L. Spiro  Members

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.

#9GrimV5  Members

Posted 04 February 2014 - 11:29 AM

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

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

*/
backGround = sf::RectangleShape(sf::Vector2f(640.0f,480.0f));
button1 = sf::RectangleShape(sf::Vector2f(100.0f,50.0f));
button2 = sf::RectangleShape(sf::Vector2f(100.0f,50.0f));
backGround.setFillColor(sf::Color::Blue);
button1.setFillColor(sf::Color::Red);
button2.setFillColor(sf::Color::Red);

/* 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.*/

};//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.*/
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.*/

};//Update()

};//TidyUp()

};//Halt()

};//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.

#10GrimV5  Members

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.

#11fastcall22  Moderators

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



zlib: eJzVVLsSAiEQ6/1qCwoK i7PxA/2S2zMOZljYB1TO ZG7OhUtiduH9egZQCJH9 KcJyo4Wq9t0/RXkKmjx+ cgU4FIMWHhKCU+o/Nx2R LEPgQWLtnfcErbiEl0u4 0UrMghhZewgYcptoEF42 YMj+Z1kg+bVvqxhyo17h nUf+h4b2W4bR4XO01TJ7 qFNzA7jjbxyL71Avh6Tv odnFk4hnxxAf4w6496Kd OgH7/RxC

#12Servant of the Lord  Members

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).

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).

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.
{

}

//This is a destructor. It's a function called when a class gets destroyed.
{

}
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' or 'SotL' 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 -

#13GrimV5  Members

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).

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.

#14GrimV5  Members

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).

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).

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.
{

}

//This is a destructor. It's a function called when a class gets destroyed.
{

}
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 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;
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.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);
};

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);
};

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.