Пересування персонажа по карті за допомогою бібліотеки SFML або проходимо в різні місця

Навіть не знаю, чи допоможе комусь дана стаття чи ні, але так як в момент написання своєї міні-ігри я не знайшов відповіді на це питання, вирішив написати рішень.

Місяців 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;
}
}

Код за своєю красою і сприйняття не дуже гарний, але він працює. Скріншот робочої можете подивитися нижче.



Всім гарного коду і свіжих думок.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.