Sign in to follow this  

Class instance problem

This topic is 4202 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm working on a top-down 2d tile-based rpg using SDL and C++. I have a class called Battle that takes care of all the combat-related stuff. But I only want to create an instance of this class when the battle actually starts, and I don't want to create a global instance. Is there a way to declare a class instance without instanciating(sp?) it? I know this is possible in C#. Or maybe someone can tell me where I should be creating the class instance? Here is my main loop. The Battle gamestate is set when the battle begins. Right now I get a compiler error saying that Melee does not exist.

// Entry point
int main(int argc, char *argv[])
{
	init();

	currentstate = GAME;

	//Main loop: loop forever.
	while (Done != 1)
	{
		switch(currentstate)
		{
		case START:
			//Start();
			break;
		case GAME:
			game();
			break;
		case MENU:
			//MainMenu();
			break;
		case INSTRUCTIONS:
			//Instructions();
			break;
		case BATTLE:
			//create new battle if battle hasn't started
			if(battle_begun == false)
			{
				Battle Melee;
				battle_begun == true;
			}
			showBattle(Melee);
			break;
		}
	}
	//Shutdown();
	return 0;
}



Here is my showBattle function. This displays the battle screen, text, player options and handles the player input for the battle.

//fight battle with monster
void showBattle(Battle& Melee)
{
	// Make sure nothing from the last frame is still drawn. //
	clearScreen();

	

	if ( (SDL_GetTicks() - g_Timer) >= FRAME_RATE )
	{
		//draw the battle screen
		drawIMG(Melee.surBattleImage,BATTLE_SCREEN_X,BATTLE_SCREEN_Y);

		//display battle main text
		string battleText = "You have encountered a " + Mob.name + "!";
		displayText(battleText,200,155,15,255,255,255,16,17,48);

		//display battle instructions
		string battleInstructions = "Click a number below to perform an action";
		displayText(battleInstructions,163,220,15,255,255,255,16,17,48);

		//display battle action text
		displayText(Melee.strBattleActionText,200,175,15,255,255,255,16,17,48);
		
		//only allow player to use keyboard on their turn
		if(Melee.player_turn == true)
		{
			handleBattleInput(Melee);

			//display battle player choice - Attack
			string battlePlayerAttack = "1 - Attack";
			displayText(battlePlayerAttack,163,250,15,255,255,255,16,17,48);

			//display battle player choice - Run
			string battlePlayerRun = "2 - Run";
			displayText(battlePlayerRun,163,270,15,255,255,255,16,17,48);

			//display battle player choice - Drink Potion
			if(Hero.potions > 0)
			{
				string battlePlayerPotion = "3 - Drink Potion";
				displayText(battlePlayerPotion,163,290,15,255,255,255,16,17,48);
			}
		}
		else
		{
			cout << "test" <<endl;
		}

		// Tell SDL to display our backbuffer. The four 0's will make //
		// SDL display the whole screen. //
		SDL_UpdateRect(g_Window, 0, 0, 0, 0);

		// We've processed a frame so we now need to record the time at which we did it. //
		// This way we can compare this time the next time our function gets called and  //
		// see if enough time has passed between iterations. //
		g_Timer = SDL_GetTicks();
	}
}


Thanks, Han

Share this post


Link to post
Share on other sites
Quote:
Original post by Hannibal111111
if(battle_begun == false)
{
Battle Melee;
battle_begun == true;
}
showBattle(Melee);
break;

The reason the compiler complains is because the variable Melee only exists within the if()-statement. To make Melee available for showBattle, and let it remains it's data, you should move that line 'Battle Melee;' up.
Another thing to watch out for is the line
battle_begun == true;
which should probably be
battle_begun = true;


As to when you should create the class instance, whole books are written about design issues like that. With the code snippets you've shown now it it hard to advise you on that. I guess reading some forum threads about 'game design' might help you along.

Share this post


Link to post
Share on other sites
Quote:
Original post by Hannibal111111
Right now I get a compiler error saying that Melee does not exist.

Melee goes out of scope at the end of the if block, hence the error. Yes, making it global would work, but I'd suggest you rethink your architecture instead. Sorry for the brief response, I must eat now. :)
Daniel

Share this post


Link to post
Share on other sites
Quote:
Original post by DaBono
The reason the compiler complains is because the variable Melee only exists within the if()-statement. To make Melee available for showBattle, and let it remains it's data, you should move that line 'Battle Melee;' up.


As a clarification, I don't think DaBono literally meant 'up', he just meant one level of scope higher, e.g.

case BATTLE:
{
//create new battle if battle hasn't started
if(battle_begun == false)
{
battle_begun = true;
}
Battle Melee;
showBattle(Melee);
break;
}




However, why even pass an uninitialized variable in the first place? If you're not doing anything to the variable Melee before passing it to showBattle, why doesn't showBattle just instantiate the Battle instance?

Share this post


Link to post
Share on other sites
The reason that I can't instantiate the Battle class in showBattle() is because that part of code is called every time the main loop is run and Battle is the current gamestate. Thus, a new instance of the class would be instantiated every time the loop is run instead of just one instance for the entire battle. I only want one instance of Battle but I can't seem to figure out where to instantiate it. Any other suggestions?

Thanks,

Han

Share this post


Link to post
Share on other sites
In which case what you probably want is :


Battle* Melee = 0;
while (Done != 1)
{
...
if(!Melee)
{
Melee = new Battle;
}
showBattle(*Melee);


This will create an instance of Battle is created the first time this code is called. The Melee variable has to be created at a level were it doesn't go out of scope for the duration of the battle. Putting in the switch statement means the Melee variable of one loop of the while will not be the same Melee as the next loop.

You have to destroy the object at some point or you get a memory leak. When the battle is finished call

delete Melee;
Melee = 0;



This needs to be called before the next battle begins.

It might help you to look for a tutorial on moving from C# to C++. A quick Google search showed a bunch about moving from C++ to C# but not the other way round so you may have to search a little harder.

Share this post


Link to post
Share on other sites
You definitely don't want to use pass-by-value "showBattle(Battle Melee)". Pointers or references are fine. I like references because they are safer and they look elegant, but pointers might give you more control.

Share this post


Link to post
Share on other sites
If showBattle is just to display the battle on screen then "const Battle& Melee" or "const Battle* Melee" would be best to prevent showBattle from altering the object state. This won't work if your Battle class contains members which change as it is shown, such as current animation frames for characters.

I'd use a const reference. I generally use pointer args where the argument may be null, but this is just my preference.

Share this post


Link to post
Share on other sites
Actually it is a badly named function, I have renamed it doBattle as it takes care of showing the battle as well as handling the battle logic. So if I want to use a reference, is this how my main function should now look?

//create battle but don't instantiate it
Battle& Melee = 0;

//Main loop: loop forever.
while (Done != 1)
{
case BATTLE:
//create new battle if battle hasn't started
if(!Melee)
{
Melee = new Battle;
}
doBattle(&Melee);
break;
}

And would this be how my doBattle function prototype should then look?

void doBattle(Battle& Melee);

Btw, what does Battle& Melee = 0 exactly do?

Thanks,

Han

Share this post


Link to post
Share on other sites
Quote:
Original post by Hannibal111111
Btw, what does Battle& Melee = 0 exactly do?


Nothing legal (unless perhaps - not sure this would help actually - you have a constructor Battle::Battle(int), and in that case it probably doesn't do what you want). You *usually* should not declare reference *variables*, except to "reference" (alias) existing variables.

Suggestions:

1) Think really, really hard about why you *want* to delay the construction of the Battle object. It probably doesn't matter. The usual valid reasons to delay construction are:

a) you need more information before you can do it properly. That doesn't appear to be the case here.
b) it's a huge object, and you don't want to use the memory in the other game states. In that case, you're probably better off redesigning game loop.

2) Don't stumble around in the dark trying to understand references; learn them.

Share this post


Link to post
Share on other sites
Ok, I have taken your suggestion and created the Battle object at a high level. Right now the code initiates some class variables in the constructor, but if I want to begin a new battle these would need to be reinitiated. So would you suggest I created another method, maybe call it init() to accomplish this or would there be a better way?

Thanks for all your help,

Han

Share this post


Link to post
Share on other sites

This topic is 4202 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this