Передвижение персонажа по карте с помощью библиотеки SFML или проходим в разные места

в 9:20, , рубрики: c++, SFML, visual c++, Программирование

Даже не знаю, поможет ли кому данная статья или нет, но так как в момент написания своей мини-игры я не нашёл ответа на данный вопрос, решил написать решений.

Месяцев 2 назад я начал писать мини-игру, просто для оттачивания имеющихся умений и приобретения новых. Игра была задумана 2D. Полистав немножко интернет, нашёл вполне нормальную библиотеку для начинающего (ну и не только) — SFML. Идея была придумана и библиотека выбрана.

В скором времени возникла проблема с столкновениями со стенами и блоками. Ведь я не опытный, и проблема для меня была глобальной я опять начал искать в интернете готовые решения или подсказки. Нашёл библиотеку (движок) для работы с физикой — box2D. Но юзать настолько сложную библиотеку для простенькой игры — это глупо, и я решил как-то выйти из ситуации.

Первый мой код имел не очень презентабельный вид, но всё же приведу его пример:

for (int i = (rect.top) / 32; i < ((rect.top + rect.height) / 32); i++)
			for (int j = rect.left / 32; j < ((rect.left + rect.width) / 32); j++)
			{
				if (Map[i][j] == '*') //Если это блок
				{
					if (dir == 1) rect.left = j * 32 + rect.width;
					if (dir == 2) rect.left = j * 32 - rect.width;
					if (dir == 3) rect.top = i * 32 + rect.height;
					if (dir == 4) rect.top = i * 32 - rect.height;
				}
			}

Как вы увидели, карта состояла из символов, и юзать TileMap я не то, чтобы не собирался, а скорее сказать не умел. В общем-то код работал, и всё было супер, если бы не одно «но»… И это «но» слишком большое: если персонаж стоял так, как показано на рисунке, то он не может пройти вперед.

image

Я не знал как с этим бороться и в итоге немножко забил на игру. Но интерес брал своё, и в один прекрасный день мне в голову пришла идея. Я решил, что юзать положение персонажа по центру спрайта будет намного легче. То есть по типу:

rect = IntRect(48, 48, 32, 32); 

Но как вы могли догадаться, это ничего нам не дало, и опять я начал думать. Ничего лучше, чем уменьшить квадрат персонажа, я не придумал. То есть если раньше мы брали размеры квадрата 32х32, то теперь я брал размер 16х16. Но этого мне показалось мало, и я решил брать 24х24. Про этот способ я и напишу.

Я избавился от цикла и решил просто брать направление нашего движения (dir) и смотреть, соприкасается ли эта точка с каким-либо блоком, и если она соприкасается, то просто давать персонажу ответный толчок от стенки. Этот способ показал результат.

void Collision(int dir) //Функция обработки столкновения
	{
		if (dir == 1)
		{
			int x = int((rect.left - rect.width)/32);
			int wy = int((rect.top + rect.height)/32);
			int ny = int((rect.top - rect.height)/32);
			if(Map[wy][x] == '+' || Map[ny][x] == '+' || Map[wy][x] == '*' || Map[ny][x] == '*')
				rect.left = x * 32 + rect.width;
		}
		else if (dir == 2)
		{
			int x = int((rect.left + rect.width) / 32);
			int wy = int((rect.top - rect.height) / 32);
			int ny = int((rect.top + rect.height) / 32);
			if (Map[wy][x] == '+' || Map[ny][x] == '+' || Map[wy][x] == '*' || Map[ny][x] == '*')
				rect.left = x  * 32 - rect.width;
		}
		else if (dir == 3)
		{
			int y = int((rect.top - rect.height) / 32);
			int lx = int((rect.left - rect.width) / 32);
			int rx = int((rect.left + rect.width) / 32);
			if (Map[y][lx] == '+' || Map[y][rx] == '+' || Map[y][lx] == '*' || Map[y][rx] == '*')
				rect.top = y * 32 + rect.height;
		}
		else if (dir == 4)
		{
			int y = int((rect.top + rect.height) / 32);
			int lx = int((rect.left - rect.width) / 32);
			int rx = int((rect.left + rect.width) / 32);
			if (Map[y][lx] == '+' || Map[y][rx] == '+' || Map[y][lx] == '*' || Map[y][rx] == '*')
				rect.top = y * 32 - rect.height;
		}
	}

Но этот код был несовершенен, так как персонаж залазил на блоки, пролетал сквозь них и просто замирал на некотором растоянии от блока. Немножко добавив математики, я просто смотрел вперед перед персонажем и принимал решение. То есть справа я смотрел на 8 пикселей вперед, и когда персонаж двигался вниз, то также смотрел вперед на 8 пикселей. Добавление +1 к движению вправо и движению вниз понадобилось, потому что моя игра имеет отступы по границам экрана, и наш персонаж должен быть на 32 пикселя дальше от границы. -1 аналогично. Код начал работать вполне хорошо.

void Collision(int dir) //Функция обработки столкновения
	{
		if (dir == 1)
		{
			int x = int((rect.left - rect.width - 8)/32);
			int wy = int((rect.top + rect.height)/32);
			int ny = int((rect.top - rect.height)/32);
			if(Map[wy][x] == '+' || Map[ny][x] == '+' || Map[wy][x] == '*' || Map[ny][x] == '*')
				rect.left = (x+1) * 32 + rect.width;
		}
		else if (dir == 2)
		{
			int x = int((rect.left + rect.width) / 32);
			int wy = int((rect.top - rect.height) / 32);
			int ny = int((rect.top + rect.height) / 32);
			if (Map[wy][x] == '+' || Map[ny][x] == '+' || Map[wy][x] == '*' || Map[ny][x] == '*')
				rect.left = x  * 32 - rect.width;
		}
		else if (dir == 3)
		{
			int y = int((rect.top - rect.height) / 32);
			int lx = int((rect.left - rect.width) / 32);
			int rx = int((rect.left + rect.width) / 32);
			if (Map[y][lx] == '+' || Map[y][rx] == '+' || Map[y][lx] == '*' || Map[y][rx] == '*')
				rect.top = (y + 1) * 32 + rect.height;
		}
		else if (dir == 4)
		{
			int y = int((rect.top + rect.height + 8) / 32);
			int lx = int((rect.left - rect.width) / 32);
			int rx = int((rect.left + rect.width) / 32);
			if (Map[y][lx] == '+' || Map[y][rx] == '+' || Map[y][lx] == '*' || Map[y][rx] == '*')
				rect.top = (y - 1) * 32 + rect.height;
		}
	}

Код по своей красоте и восприятию не очень хорош, но он работает. Скриншот рабочей можете посмотреть ниже.

Передвижение персонажа по карте с помощью библиотеки SFML или проходим в разные места - 2

Всем хорошего кода и свежих мыслей.

Автор: rostik_tsekhmistro

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js