SDL2 MenuManager

Hello!
I’m doing something like menu and I have problem with it. If it’s not about sdl please report this topic and send to the other category.

I got some classes like MenuManager, FileManager, inputManager and MenuScreen:

FileManager.cpp

[details=Summary]

#include "FileManager.h"

FileManager::FileManager()
{
	idFound = false;
}


FileManager::~FileManager()
{
}

void FileManager::LoadContent(const char*fileName, std::vector<std::vector<std::string>> &attributes, std::vector<std::vector<std::string>> &content)
{
	attributes.clear();
	content.clear();

	std::ifstream openFile(fileName);
	if (openFile.is_open())
	{
		while (!openFile.eof())
		{
			std::string line;
			std::getline(openFile, line);

			line.erase(std::remove(line.begin(), line.end(), ' '), line.end());

			if (line.find("Load=") != std::string::npos)
			{
				state = ATTRIBUTES;
				line.erase(0, line.find('=') + 1);
				tempAttributes.clear();
			}
			else
			{
				state = CONTENTS;
				tempContents.clear();
			}
			line.erase(std::remove(line.begin(), line.end(), '['), line.end());
			std::stringstream str(line);
			while (str)
			{
				std::getline(str, line, ']');
				if (line != "")
				{
					if (state == ATTRIBUTES)
					{
						tempAttributes.push_back(line);
					}
					else
					{
						tempContents.push_back(line);
					}
					std::cout << line << std::endl;
				}
			}
			if (state == CONTENTS && tempContents.size() > 0)
			{
				attributes.push_back(tempAttributes);
				content.push_back(tempContents);
			}
		}
	}
}

void FileManager::LoadContent(const char*fileName, std::vector<std::vector<std::string>> &attributes, std::vector<std::vector<std::string>> &content, std::string id)
{
	attributes.clear();
	content.clear();

	std::ifstream openFile(fileName);

	idFound = false;
	if (openFile.is_open())
	{
		while (!openFile.eof())
		{
			std::string line;
			std::getline(openFile, line);

			line.erase(std::remove(line.begin(), line.end(), ' '), line.end());

			if(line.find("Start=[" + id + "]") != std::string::npos) // npos zwraca true jeśli nie znajdzie żadnego tekstu w pliku
			{
				idFound = true;
				continue;
			}
			else if (line.find("End=") != std::string::npos && line.find(id) != std::string::npos)
			{
				idFound = false;
				break;
			}

			if (idFound)
			{
				if (line.find("Load=") != std::string::npos)
				{
					state = ATTRIBUTES;
					line.erase(0, line.find('=') + 1);
					tempAttributes.clear();
				}
				else
				{
					state = CONTENTS;
					tempContents.clear();
				}
				line.erase(std::remove(line.begin(), line.end(), '['), line.end());
				std::stringstream str(line);
				while (str)
				{
					std::getline(str, line, ']');
					if (line != "")
					{
						if (state == ATTRIBUTES)
						{
							tempAttributes.push_back(line);
						}
						else
						{
							tempContents.push_back(line);
						}
						std::cout << line << std::endl;
					}
				}
				if (state == CONTENTS && tempContents.size()>0)
				{
					attributes.push_back(tempAttributes);
					content.push_back(tempContents);
				}
			}
		}
	}
}

void FileManager::UnloadContent()
{
	tempAttributes.clear();
	tempContents.clear();
}

[/details]

MenuManager:
[ details=Summary]

#include "MenuManager.h"

MenuManager::MenuManager()
{
}


MenuManager::~MenuManager()
{

}

void MenuManager::SetMenuItems()
{
	for (int i = 0; i < menuItems.size(); i++)
	{
		if (menuImages.size() == i)
		{
			menuImages.push_back(null);
		}
	}

	for (int i = 0; i < menuImages.size(); i++)
	{
		if (menuItems.size() == i)
		{
			menuItems.push_back("");
		}
	}
}

void MenuManager::SetPosition()
{
	int posx = posX;
	int posy = posY;

	for (int i = 0; i < animation.size(); i++)
	{
		for (int j = 0; i < animation[i].size(); j++)
		{
			animation[i][j]->setValue(animation[i][j]->getPositionX(), posx);
			animation[i][j]->setValue(animation[i][j]->getPositionY(), posy);
		}

		float width = menuImages[i].getWidth() + animation[i][0]->getText().getWidth();
		float height = menuImages[i].getHeight() + animation[i][0]->getText().getHeight();

		if (axis == 1)
		{
			posx += 10 + width;
		}
		else if (axis == 2)
		{
			posy += 10 + height;
		}
	}
}

void MenuManager::SetAnimation(SDL_Renderer *renderer)
{
	for (int i = 0; i< menuItems.size(); i++)
	{
		for (int j = 0; j <animationTypes.size(); j++)
		{
			if (animationTypes[j] == "Fade")
			{
				tempAnimation.push_back(new FadeAnimation);
				tempAnimation[tempAnimation.size() - 1]->LoadContent(menuItems[i], con, posX, posY, renderer, Image);
			}
		}
		animation.push_back(tempAnimation);
		tempAnimation.clear();
	}
}


void MenuManager::LoadContent(std::string menuID, SDL_Renderer *renderer, int fontSize)
{
	null.loadFromFile("Images/no.png",renderer);
	itemNumber = 0;

	file.LoadContent("Images/menus.cme", attributes, contents, menuID);

	for (int i = 0; i < attributes.size(); i++)
	{
		for (int j = 0; j < attributes[i].size(); j++)
		{
			att = attributes[i][j];
			con = contents[i][j];

			if (att == "Item")
			{
				this->menuItems.push_back(con);
			}

			else if (att == "Image")
			{
				Image.loadFromFile(con, renderer);
				menuImages.push_back(Image);
			}

			else if (att == "Position")
			{
				std::string pos[2];
				pos[0] = con.substr(0, con.find(','));
				pos[1] = con.substr(con.find(',') + 1);

				this->posX = atof(pos[0].c_str());
				this->posY = atof(pos[1].c_str());
				std::cout << this->posY << " " << this->posY << std::endl;
			}

			else if (att == "Axis")
			{
				axis = atoi(con.c_str());
			}

			else if (att == "Animation")
			{
				animationTypes.push_back(con);
			}

			else if (att == "Align")
			{
				align = con;
			}
			else if (att == "Font")
			{
				Text.setFont(contents[i][j], fontSize);
			}
		}
	}

	SetMenuItems();
	SetPosition();
	SetAnimation(renderer);
}

void MenuManager::UnloadContent()
{
	Text.free();
	Image.free();

	attributes.clear();
	contents.clear();
	animation.clear();
	menuImages.clear();
	menuItems.clear();

	for (int i = 0; i < tempAnimation.size(); i++)
	{
		delete tempAnimation[i];
	}
	tempAnimation.clear();

	for (int i = 0; i < animation.size(); i++)
	{
		for (int j = 0; j < animation[i].size(); j++)
		{
			delete animation[i][j];
		}
	}
	animation.clear();
	animationTypes.clear();
}

void MenuManager::Update(SDL_Renderer *renderer, InputManager input)
{
	if (axis == 1)
	{
		if (input.KeyPressed(SDLK_RIGHT) || input.KeyPressed(SDLK_d))
		{
			itemNumber++;
		}
		else if (input.KeyPressed(SDLK_LEFT) || input.KeyPressed(SDLK_a))
		{
			itemNumber--;
		}
	}
	else
	{
		if (input.KeyPressed(SDLK_DOWN) || input.KeyPressed(SDLK_s))
		{
			itemNumber++;
		}
		else if (input.KeyPressed(SDLK_UP) || input.KeyPressed(SDLK_w))
		{
			itemNumber--;
		}
	}

	if (itemNumber < 0)
	{
		itemNumber = 0;
	}
	else if (itemNumber > menuItems.size()-1)
	{
		itemNumber = menuItems.size() - 1;
	}

	for (int i = 0; i < animation.size(); i++)
	{
		for (int j = 0; i < animation[i].size(); j++)
		{
			if (itemNumber == i)
			{
				animation[i][j]->setValue(animation[i][j]->getActive(), true);
			}
			else
			{
				animation[i][j]->setValue(animation[i][j]->getActive(), false);
			}
			animation[i][j]->Update(renderer);
		}
	}
}

void MenuManager::Draw(SDL_Renderer *renderer)
{
	for (int i = 0; i < animation.size(); i++)
	{
		for (int j = 0; i < animation[i].size(); j++)
		{
			animation[i][j]->Draw(renderer);
		}
	}
}

[/details]

inputManager:
[details=Summary]

#include "InputManager.h"

InputManager::InputManager()
{
}


InputManager::~InputManager()
{
}


void InputManager::Update(SDL_Event event)
{
	this->event = event;
}

bool InputManager::KeyPressed(int key)
{
	if (event.key.keysym.sym == key && event.key.state == SDL_PRESSED && event.key.repeat == 0)
	{
		return true;
	}
	return false;
}
bool InputManager::KeyPressed(std::vector <int> keys)
{
	for (int i = 0; i < keys.size(); i++)
	{
		if (KeyPressed(keys[i]))
		{
			return true;
		}
	}
	return false;
}

bool InputManager::KeyReleased(int key)
{
	if (event.key.keysym.sym = key && event.key.state == SDL_RELEASED && event.key.repeat == 0) // event.type == SDL_KEYUP
	{
		return true;
	}
	return false;
}
bool InputManager::KeyReleased(std::vector <int> keys)
{
	for (int i = 0; i < keys.size(); i++)
	{
		if (KeyReleased(keys[i]))
		{
			return true;
		}
	}
	return false;
}

bool InputManager::KeyDown(int key)
{
	if (event.type == SDL_KEYDOWN && event.key.keysym.sym == key && event.key.repeat == 0)
	{
		return true;
	}
	return false;
}
bool InputManager::KeyDown(std::vector <int> keys)
{
	for (int i = 0; i < keys.size(); i++)
	{
		if (KeyDown(keys[i]))
		{
			return true;
		}
	}
	return false;
}

bool InputManager::KeyUp(int key)
{
	if (event.type == SDL_KEYUP && event.key.keysym.sym == key && event.key.repeat == 0)
	{
		return true;
	}
	return false;
}
bool InputManager::KeyUp(std::vector<int> keys)
{
	for (int i = 0; i < keys.size(); i++)
	{
		if (KeyUp(keys[i]))
		{
			return true;
		}
	}
	return false;
}

[/details]

MenuScreen:

[details=Summary]
#include "TitleScreen.h"

TitleScreen::TitleScreen()
{

}

MenuScreen::~MenuScreen()
{

}

void MenuScreen::LoadContent(SDL_Renderer *renderer)
{
	TitleText.setFont("Fonts/arial.ttf", 50);
	//TitleText.LoadText("TitleScreen", { 0xff,0xff,0xff }, renderer);
	keys.push_back(SDLK_ESCAPE);

	menu.LoadContent("Title", renderer, 30);
}

void MenuScreen::UnloadContent()
{
	menu.UnloadContent();
	GameScreen::UnloadContent();
	TitleImage.free();
	TitleText.free();
}

void MenuScreen::Draw(SDL_Renderer *renderer)
{
	menu.Draw(renderer);
}

void MenuScreen::Update(SDL_Renderer *renderer, SDL_Event &event)
{
	input.Update(event);
	if (input.KeyPressed(keys))
	{
		ScreenManager::GetInstance().AddScreen(new SplashScreen, renderer);
		//std::cout << "KEY1" << std::endl;
	}
	menu.Update(renderer, input);;
}
[/details]

menu.cme :

[details=Summary]

Start=[Title]

Load=[Position][Axis][Font][Animation][Align]
[100,100][2][Fonts/arial.ttf][Fade][None]

Load=[Item]
[New Game]
[Load Game]
[Options]
[Credits]
[Exit]

End=[Title
][/details]

and when I run this, there should be menu on the screen like it’s written in menu.cme. Debugger says :

and I don’t know how I should fix this problem.

You have a few for() loops that go from 0 to thing.size(), but they should be 0 to thing.size()-1. void MenuManager::SetAnimation(SDL_Renderer *renderer) has two of these loops, and I see a bunch more scattered through the code.

When you run into an error like this, make sure you look at what the debugger is telling you. It found a single instance of array out of bounds, so you should know if has something to do with accessing one of your vector variables. With that information, see where you’re accessing one with the myVector[n] operator/method.

A lot of modern compilers and debuggers are getting better at giving humanized versions of issues to make it more obvious what a developer needs to do to fix the bug.

1 Like

I think setAnimation is good. I mean, I’m learning from coding made easy sfml platformer tutorial and he got it like this. I watched coding made easy allego 5 platrofmer tut and it’s the same as well. https://www.youtube.com/watch?v=akAzMDmYh3I&index=20&list=PLCB3138ADCE90F2EC 9:39. I’ll try to change it though

Do you know any good tuts how to use debugger? Because I got problems with using it.

Ah, correct, I was wrong. For some reason I thought you were using <= instead of <. But my point still stands, somewhere in your code, you are accessing animation[] out of bounds. See the |> this in your debugger output? If you can expand it, and see the contents of your vector, which should help you figure out which variable you’re accessing wrong. I haven’t used any IDE in a long time, and I’m not sure which one you’re running specifically (MSVC?), but you should be able to go up in the stack trace and find the spot in your actual code that runs out of bounds.

In void MenuManager::Draw, you have for (int j = 0; i < animation[i].size(); j++) - in the middle, I think it should probably be j < animation[i].size(). That might be your issue, and that’s the only one I spotted, and it is an animation type, which your debugger caught.

For things like that, I’d really suggest avoiding loops that use variables like i and j. Using a descriptive name, even though it’s more typing, is going to make it easier to avoid bugs like this. I’m guilty of doing quick single-letter variable names on occasion myself, but be careful nesting things like i, j, l, t - at a cursory glance, it’s very hard to spot a mis-named variable. There may be some sort of semi-intelligent plugin for your IDE to help you catch misnamed variables like that, but again, I’m not familiar enough with them to suggest anything.

Also, you may want to, in the future, upload your code using tools like gist.github.com - that way, your code displays with line numbers, which make it a little easier to point out issues, or let’s people link to pieces of code, especially when you paste in more than one or two files. It wasn’t a huge deal this time, but I literally had to read all your code line by line to spot that issue (and even I misread it the first time around, oops).

As far as a debugger tutorial, I’m not sure. If you’re using MSVC, there might be some good articles or tutorials on how to use it more effectively - just do some googling around for things like getting the most out of MSVC debugger, I’m sure you’ll pick up a few tricks.

I hope that helps :smiley:

1 Like

Oh, my god. Yes, you’re right! There was much more loops like this. Now it works. I got a problem with the position, but I’ll fix it by myself. I didn’t even realized that I made that mistake like in these loops. Thanks so much. I really appreciate it. One more, what about these line numbers? What did you mean?