Сегодня я покажу как написать игру "Жизнь". Сразу хочу обратить внимание на то, что я не буду использовать ООП. Так же по совету опытных программистов я решил, что не стоит игру нагружать потоками, синглтонами и прочей ерундой. Если будет большое желание - я сделаю отдельный урок по разработке маленького параллельного игрового движка.
Игра "Жизнь" Суть игры элементарна! Есть массив клеток. Каждая клетка может находиться в состоянии "1" или "0".
Если возле пустой клетки находится три живых клетки - зарождается жизнь.
Если же у живой клетки есть 2-3 соседа, то она продолжает жить (в противном случае погибает либо от одиночества, либо от "тесноты"). Вот и всё.
Код Игру можно было легко сделать и в консоли, но мне показалось, что получится "круче" если будет графика. Я использовал SFML, дабы не зацикливать ваше внимание на нюансах OpenGL/DirectX. Всё, что нас сейчас интересует, - это игра. Перед тем, как взглянуть на мой код, попробуйте самостоятельно написать такую игру. Если у вас получится, то можете гордиться собой.
На этапе настройки SFML я не буду останавливаться, ведь всё написано здесь. Итак. Имеем следующее:
int main() { srand(time(NULL)); //Для рандома sf::RenderWindow window(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), WINDOW_TITLE); //Создаём окно window.setFramerateLimit(FPS); while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); } window.clear(); ... window.display(); } return 0; }
По условию игры есть массив клеток, где каждая клетка может становится либо "0", либо "1". Ничего не напоминает? Да-да! Это тоже самое, что true или false. Следовательно у нас есть массив булевых значений:
Код
bool Generation[MAP_WIDTH][MAP_HEIGHT];
Но нам одного массива недостаточно, потому что мы каким-то образом должны "запоминать" это поколение клеток, чтобы его нормально обновлять:
Код
bool OldGeneration[MAP_WIDTH][MAP_HEIGHT];
Что-то в этом есть... Не так ли? Но наши поколения пусты! Что же нам делать? Верно! Нам нужно его создать. Следовательно создадим функцию Generate:
Может быть кто-то из вас испугался циклов for... Но ничего здесь страшного нет! Мы всего лишь "пообщаемся" с каждой ячейкой массива =) Что касаемо rand()%10 - здесь мы генерируем число в диапазоне от 0 до 9. Следовательно rand()%N вернет число в диапазоне от 0 до (N-1). Если сгенерирована единица, то мы ставим в ячейку клетку. В противном случае в ячейке никого нет. В OldGeneration мы зафиксировали поколение, чтобы нормально обновлять Generation. Теперь нужно написать функцию обновления поколения:
Описание функций UpdateOldGeneration и GetCellsCount(x,y) я приведу ниже. Могу лишь сказать, что UpdateOldGeneration копирует Generation в OldGeneration, а GetCellsCount возвращает количество живых клеток возле OldGeneration[x][y] (с проверками на выход из массива). В функции Update мы всего лишь закодировали правила игры:
Цитата
Если возле пустой клетки находится три живых клетки - зарождается жизнь. Если же у живой клетки есть 2-3 соседа она продолжает жить (в противном случае погибает либо от одиночества, либо от "тесноты").
Вот описание UpdateOldGeneration:
Цитата
void UpdateOldGeneration() { for(int y = 0; y < MAP_HEIGHT; y++) { for(int x = 0; x < MAP_WIDTH; x++) { OldGeneration[x][y] = Generation[x][y]; } } }
"Ну вот! Опять какая-то функция!" - скажите вы. Но не беспокойтесь, мы скоро закончим : ) GetCell всего лишь возвращает значение определенной клетки, если её координаты находятся в диапазоне массива:
Можно я использую ваш материал на своем сайте с ссылкой на эту статью? http://kfni.ho.ua/sfml.html И да, извини что влезу, но #define не желательно уже использовать const int WINDOW_WIDTH = 640; http://kfni.ho.ua/ - мой сайт по урокам SFML
Сообщение отредактировал TreeLoys - Суббота, 24 Января 2015, 17:47
Можно я использую ваш материал на своем сайте с ссылкой на эту статью? http://kfni.ho.ua/sfml.html И да, извини что влезу, но #define не желательно уже использовать const int WINDOW_WIDTH = 640;
Здравствуйте, мне очень понравился ваш урок, в связи с чем у меня возник вопрос: Возможно у вас есть схожие уроки/примеры простых игр? Или не совсем простых, но снабжённых комментариями наподобие данного урока?
Вот мой вариант "Жизни", дабы отличаться сделан в консоли:
template<size_t I, size_t J, typename T> BYTE GetCellsCount(BYTE x, BYTE y, array<array<T, J>, I> const &arr) { BYTE Result {}; Result += GetCell(x-1, y, arr); Result += GetCell(x-1, y-1, arr); Result += GetCell(x, y-1, arr); Result += GetCell(x+1, y-1, arr); Result += GetCell(x+1, y, arr); Result += GetCell(x+1, y+1, arr); Result += GetCell(x, y+1, arr); Result += GetCell(x-1, y+1, arr); return Result; }
template<size_t I, size_t J, typename T> int sumOrganism(array<array<T, J>, I> const &arr) { int Result {}; for(auto const i: arr) { for(auto const j: i) { Result += j; } } return Result; }
Ozzy659, уроки больше не пишу, потому что они никому не нужны) По коду: зачем использовать шаблоны там, где они не нужны? Нужно бороться за читабельность кода...
ЦитатаOzzy659 ()
#include <windows.h>
os_specific_shit. Никакой кроссплатформенности... Sleep можно сделать с помощью фишек из стандарта С++11 (усыпить текущий поток на N мили\нано\...секунд)
Сообщение отредактировал Saitei - Воскресенье, 15 Февраля 2015, 17:52
уроки больше не пишу, потому что они никому не нужны)
Очень жаль.
ЦитатаSaitei ()
Sleep можно сделать с помощью фишек из стандарта С++11
std::this_thread::sleep_for(std::chrono::milliseconds(Х)); А Sleep использую ибо все равно подключать windows.h из-за функции управления консолью SetConsoleCursorPosition и SetConsoleTextAttribute, хотя возможно стоит попробовать отказаться от их использования, но боюсь знаний по С++ набранных за неделю мне может не хватить)