Исходные тексты игр (240). Простой движок для написания игр на языке C под Linux Что нужно знать

Я - тот ещё фрукт. Все мои личные игровые проекты, которыми я занимался в последнее время, были написаны на «vanilla» C. Больше никто так не делает, поэтому, полагаю, вам может быть интересно узнать, почему я сделал такой выбор.
Написанное дальше содержит мнение о языках программирования, которое вам может не понравиться. Я предупреждал .

Что мне нужно от языка

Некоторые требования не подлежат обсуждению или поиску компромиссов. Во-первых, язык должен быть надёжным. Я не могу позволить себе тратить своё время на поиск ошибок, которых я не совершал.

Я написал большое количество игр на Flash, и вот, Flash умер. Я не хочу тратить время на портирование старых игр на современные платформы, я хочу писать новые игры. Мне нужна платформа, в долговечности которой я буду уверен.

Чего я точно хочу, так это избежать зависимости от определённой ОС. В идеале - иметь возможность разрабатывать и для консолей. Поэтому важно, чтобы язык позволял легко портировать написанное на нём, а также имел хорошую поддержку кроссплатформенных библиотек.

Чего я хочу от языка

Самым важным, но не обязательным требованием является простота. Изучение особенностей языка и и его чудного «умного» API я нахожу безумно скучным. Идеальный язык можно однажды изучить и никогда больше не пользоваться его документацией.

Поиск и исправление багов истощает творческий потенциал. Я хочу оставлять меньше багов, потому желаю строгую типизацию, качественные сообщения об ошибках и статический анализ кода. Хочу упростить поиск ошибок, а, следовательно, хорошие отладочные средства и динамический анализ.

Меня не интересует красивая реалистичная картинка, зато я забочусь о производительности. Меньшая требовательность к ресурсам расширяет спектр возможностей, доступных для реализации. Особенно интересно наблюдать за тем, что происходит с современным мощными компьютерами, когда разработчик о производительности не думает.

Ещё больше меня волнует скорость компилятора. Я не какой-нибудь буддийский мастер концентрации, и ждать больше 10 секунд - расточительство. Хуже того, это выбивает из потока. Вроде только глянул Twitter, а 5 минут куда-то пропали.

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

Альтернативы

C++ продолжает оставаться наиболее распространенным языком для разработки игр, и на то есть свои причины. До сих пор большинство моих заказных проектов пишется на нем, и это мне не нравится.

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

C# и Java имеют схожие проблемы. Это многословные и сложные монстры, а мне нужен маленький простой зверёк. Оба языка направляют программиста прямиком в пучину ООП, а я против этого. Как и в большинстве высокоуровневых языков, многие сложные вещи тут скрываются так, что ничего не мешает случайно выстрелить себе в ногу.

Мне очень нравится Go. Во многих аспектах это изобретённый заново C, с поправкой на то, что до представления его публике он продумывался несколько лет. Я хотел бы использовать Go, но есть один огромный подводный камень - сборка мусора. Разработка игр на Go сомнительна, ведь сборщик мусора будет приостанавливать весь игровой мир, чего разработчик не может позволить. Также тут всё не очень хорошо с игровыми библиотеками. И хотя всегда можно приспособить для этого дела библиотеку на C, причём без особых проблем, это всё равно порождает много лишней работы. Кроме того, у меня есть сомнения насчёт перспектив. Go был бы неплох для веба, но это стремительно меняющаяся среда. Особенно это почувствовалось со смертью Flash.

JavaScript мне абсолютно не нравится. Он предоставляет столько свободы, что мне непонятно, как люди умудряются писать сколько-нибудь сложные проекты на нём. И не хочу даже пытаться его пробовать.

Haxe выглядит гораздо перспективнее остальных языков в этом списке. У него нет проблем с библиотеками. Если я снова начну писать под веб, то обязательно познакомлюсь с ним поближе. Несколько беспокоит относительная молодость языка, будет ли он жить? Больше мне добавить нечего, с Haxe я успел лишь немного поиграться, не углубляясь.

Джонатан Блоу пишет свой язык. Язык, которым он сам захотел бы пользоваться. Я восхищаюсь его решением, иногда и сам загораюсь идеей поступить так же. Но не выкинуть же все существующие библиотеки. А реализовать полную с ними совместимость - не так просто. Да и вообще это трудно, я бы предпочёл дальше писать игры, а не языки программирования.

Почему C - лучший выбор для меня

Хоть C и опасен, зато надёжен. Это очень острый нож, способный отрубить пальцы так же легко, как нарезать овощи. Но он прост, и научиться правильно его использовать труда не составит.

Он быстрый. А когда дело доходит до компиляции, я и представить себе не могу, что какой-то язык способен сделать это быстрее.
Есть возможность писать код так, чтобы он работал везде. И обычно это относительно несложно. Трудно представить, что однажды это изменится.

Есть отличная поддержка библиотек и инструментов.

Хоть меня это и несколько печалит, но C до сих пор остаётся лучшим языком для меня.

Я совсем не хочу сказать что-то вроде: «Эй, вы тоже должны писать на C». Я осознаю, что мои предпочтения весьма специфичны. Кроме того, по количеству, написанного мной на разных языках, код на «vanilla» C занимает лидирующую позицию, так что это уже часть моей зоны комфорта.

Так что да, C для меня - лучший выбор.

От переводчика

Перевод местами достаточно вольный, но не в ущерб смыслу или содержанию.

Подготовил перевод специально к пятнице, в этот день он особенно уместен, на мой взгляд. Хоть автор оригинального текста и пишет, кажется, на полном серьезе… вы сами всё понимаете, если прочитали. Как истину прочитанное воспринимать не стоит.

С предложениями, пожеланиями и замечаниями, как обычно, в ЛС.

Видеоигры на протяжении уже более 30 лет активно развиваются, начиная от геймплея и графических возможностей, вплоть до продвинутых систем искусственного интеллекта и восприятия пользователями подаваемого разработчиками материала. Киберспорт, в свою очередь, является полноценным видом спорта с многомиллионными призами и миллиардными оборотами. А сама индустрия видеоигр неустанно набирает обороты, являясь одной из наиболее кассовых и массовых ответвлений сферы развлечений. А ко всему этому человечество пришло от примитивнейшей реализации «тенниса», запускавшейся на осциллографе.


Я, как первокурсник, решил в рамках изучения базовых консольных возможностей C/C++ возможно запрограммировать "классическую" ASCII-игру, требующую от игрока скорости мышления, непрерывного взаимодействия с игрой, а также обладающую несложной графической частью, которую игрок мог бы интерпретировать в некое подобие трехмерного изображения.


Несмотря на визуальную примитивность подобных решений, они безусловно вызывают у игроков ностальгию по олдскульным играм.

Вдохновление

На заре появления на рынке мобильных телефонов с монохромными дисплеями выделилась игра «Space Impact», прошивавшаяся в аппаратах Nokia. Она отлично вписалась в ограниченные возможности тогдашних сотовых. После введения простых игр в мобильные телефоны – продажи аппаратов резко возросли, а мобильный гейминг постепенно стал популярным, и сейчас вполне конкурирует со индустрией «взрослых» ААА-проектов.


«Space Impact» и послужила основой для моей первой игры ввиду своей легкости исполнения и популярности в начале 2000-х.

Особенности «Space Invader»

В первую очередь – легковесность программы. Исполняемый файл занимает менее 100 кб и будет работать, как задумано, практически на любом компьютере под OC Windows с пакетом Visual C++.


Во-вторых, переносимость - исходный код можно за несколько минут переделать под POSIX-системы, тем самым обеспечив работоспособность на UNIX и Mac операционных системах, лишь заменив несколько функций и пересобрав программу на соответствующем компиляторе.


Третье преимущество - программа портативна, не требует никаких дополнительных файлов. Все дополнительные файлы при необходимости создаются автоматически в каталоге местонахождения исполняемого файла.


Наипростейший интуитивно понятный интерфейс, не требующий описания или инструкций по использованию.


  • Подпункт «Новая игра» - запускает новый игровой сеанс.
  • Подпункт «Продолжить» - загружает последнюю сохраненную игру из бинарного файла в директории исполняемого файла и запускает игровой сеанс с использованием полученных данных.
  • Пункт «Помощь» - инструкции по работе с приложением и его описание. Пролистываются по нажатию стрелок на клавиатуре.
  • Пункт «Зал славы» - список лидеров игровых сессий, загружается из бинарного файла в директории исполняемого файла и форматированно выводится в консоль.
  • Пункт «Выход» - выход из приложения.
  • Сдвигающийся влево «мир» с динамической скоростью. «Мир» содержит рандомные поля сверху и снизу толщиной в 1 или 2 символа. Также между полями рандомно появляется «космический мусор», отображаемый символом «¤», являющийся препятствием для игрока.
  • Прижатый к левому краю окна «космический корабль». Корабль смещается с помощью стрелок на клавиатуре вверх и вниз, по нажатию пробела выпускает снаряд, уничтожающий «космический мусор» по касанию.
  • «Приборная панель» вверху консоли, отображающая пройденный километраж, текущую скорость и количество оставшихся попыток.
  • Как работает «Space Invader»?

    При запуске приложения возникает заставка-анимация. Она состоит из 6 заранее отформатированных символьных массивов, сменяющихся через каждые 200мс.


    Начальная заставка – итоговый слайд


    Далее идет обращение в функцию главного меню с параметром 1(целое число). Функция отображает меню с выделенным угловыми скобками пунктом меню, номер которого совпадает с входным параметром. Пунктов в меню 4, соответственно входной параметр может различаться от 1 до 4. При нажатии стрелки вниз происходит рекурсивное обращение с инкрементированным входным параметром в том случае, если входной параметр меньше 4, с параметром 1, если входной параметр равен 4. При нажатии Space или Enter происходит обращение к функции, соответствующей выделенному пункту меню.


    void StartMenu(int switcher) { system("cls"); switch (switcher) { case 1: cout << "\n\n\n << ИГРАТЬ! >> << "\n\n\n ИГРАТЬ!\n\n << ПОМОЩЬ! >> << "\n\n\n ИГРАТЬ!\n\n ПОМОЩЬ!\n\n << ЗАЛ СЛАВЫ >> << "\n\n\n ИГРАТЬ!\n\n ПОМОЩЬ!\n\n ЗАЛ СЛАВЫ\n\n << ВЫХОД >>"; break; } int choice = _getch(); if (choice == 224) choice = _getch(); if (choice == 72) if (switcher != 1) StartMenu(switcher - 1); else StartMenu(4); if (choice == 80) if (switcher != 4) StartMenu(switcher + 1); else StartMenu(1); if (choice == 13 || choice == 32) { if (switcher == 1) GameMenu(1); if (switcher == 2) Help(0); if (switcher == 3) TopChart(); if (switcher == 4) _exit(0); } }


    Главное меню (вход в функцию выполнен с параметром 1)


    При обращении к функции, соответствующей пункту «Игра» запускается функция, аналогичная по функционалу, но выбор есть только из 2-х пунктов. Соответственно, входной параметр будет 1 или 2, и при нажатии любой из стрелок (вверх или вниз) нам необходимо лишь сменить цифру на «противоположную». Наиболее оптимизированным будет вариант отнимания входного параметра от 3 (3 – 1 = 2, 3 – 2 = 1).


    void GameMenu(int switcher) { system("cls"); if (switcher == 1) cout << "\n\n\n\n\n << НОВАЯ ИГРА! >> << "\n\n\n\n\n НОВАЯ ИГРА!\n\n << ПРОДОЛЖИТЬ! >>"; int choice = _getch(); if (choice == 224) choice = _getch(); if (choice == 72 || choice == 80) GameMenu(3 - switcher); if (choice == 27) StartMenu(1); if (choice == 13 || choice == 32) Game(switcher); }


    Дополнительное меню (вход в функцию выполнен с параметром 1)


    Теперь к основному – процессу игры. При выборе подпункта «Новая игра» - запускается новый игровой сеанс. Создается двумерный массив, размерностью 14 строк на 50 столбцов. Первая строка выделяется под приборную панель. Первый прибор – количество пройденных километров, оно равно количеству обновлений консоли (изначально консоль обновляется раз в 80мс, с каждым обновлением этот параметр декрементируется, пока не достигнет значения 25).


    int odometerBuf = odometer, odometerDigitLength; for (odometerDigitLength = 0; odometerBuf != 0; odometerBuf /= 10, odometerDigitLength++);//вычисление количества цифр на одометре for (int i = odometerDigitLength, odometerBuf = odometer; i >= 0; i--, scr[i] = odometerBuf % 10 + "0", odometerBuf /= 10);//прорисовка одометра на приборную панель scr = "К"; scr = "М";//дописывание "КМ" odometer++;//наращение одометра

    Второй прибор, текущая скорость, являет собой формулу - 1000/скорость обновления консоли. Скорость измеряется в километрах в секунду. Таким образом, изначально корабль движется со скоростью 12км/с, и через некоторое время достигает отметки в 40 км/с.


    speed = 1000 / timer;//обновление спидометра int speedBuf = speed; for (int i = 42; speed != 0; i--, scr[i] = speed % 10 + "0", speed /= 10);//прорисовка спидометра на приборную панель scr = "К"; scr = "М"; scr = "/"; scr = "С";//дописывание "КМ/С"

    Третий прибор отображает количество оставшихся попыток, изначально их 3. Попытки отображаются символом «&».


    for (int i = 50; lifes > 0; i--, lifes--, scr[i] = "&");


    Приборная панель в начале игрового сеанса


    Следующие 2 строки, как и последние 2 – являются полями игры. Декорации полей выбираются случайным образом, могут состоять из 3-х символов или пробела. Крайние строки всегда полностью заполнены, а вторая и предпоследняя содержит символы, отличные от пробела лишь в тех местах, где эти символы «растут» из других.


    char borderSymbols = { "†", "‡", "¤", " " }; for (int aboveBelow = 0; aboveBelow < 50; aboveBelow++)//прорисовка верхнего и нижнего полей (2 + 2) { scr = borderSymbols; if (scr == "‡") scr = "¤"; scr = borderSymbols; if (scr == "‡") scr = "¤"; }


    Приборная панель и поля в начале игрового сеанса


    В центральной строке игрового пространства, прижатым к левому краю, прорисовывается космический корабль, которым и предстоит управлять игроку.


    scr = "\\"; scr = "\\";//прорисовка корабля scr = "3"; scr = "="; scr = "="; scr = "/"; scr = "/";

    Управлять ним можно стрелками вверх и вниз.


    if (_kbhit())//если клавиша была нажата { control = _getch();//переменная примет ее значение if (control == 224) control = _getch(); } if (control == 72)//при движении корабля вверх if (scr == "\\" || scr == "\\" && scr == "¤" || scr == "\\" && scr == "¤")//если корабль врезался в верхнее поле - игра окончена if (lifes > 1) { cout << "\a"; lifes--; weaponPos = 7; GameStart(scr, lifes, &timer); Sleep(1000); } else GameOver(odometer); else { for (int i = 2; i < 13; i++)//корабль смещается на элемент выше for (int j = 0; j < 49; j++) if (scr[i][j] == "3" || scr[i][j] == "\\" || scr[i][j] == "=" || scr[i][j] == "/") { scr[j] = scr[i][j]; scr[i][j] = " "; } weaponPos--; } if (control == 80)//при движении корабля вниз if (scr == "/" || scr == "/" && scr == "¤" || scr == "/" && scr == "¤")//если корабль врезался в нижнее поле - игра окончена if (lifes > 1) { cout << "\a"; lifes--; weaponPos = 7; GameStart(scr, lifes, &timer); Sleep(1000); } else GameOver(odometer); else { for (int i = 12; i > < 49; j++) if (scr[i][j] == "3" || scr[i][j] == "\\" || scr[i][j] == "=" || scr[i][j] == "/") { scr[j] = scr[i][j]; scr[i][j] = " "; } weaponPos++; }


    Расположение корабля при двукратном нажатии стрелки вверх


    Отойдя от визуализации, стоит отметить, что вместе с кораблем мы смещаем вверх или вниз элемент массива, отвечающий за «дуло» корабля. Соответственно, из которого можно выпускать снаряды по нажатию пробела.


    Однако, ни поле, ни снаряды не двигаются, потому, чтобы «оживить» игровой процесс, по истечению таймера перерисовки экрана «мир» будет смещаться на один столбец влево, а снаряды на один столбец вправо. Корабль же остается на месте, пока игрок не захочет иного. Но после перерисовки теперь возникает один пустой столбец – заполним его случайным образом полями и «космическим мусором», который можно сбивать снарядами или боковыми отбойниками корабля.


    for (int i = 1; i < 14; i++)//все "космические" элементы смещаются на элемент влево for (int j = 0; j < 49; j++) { if (scr[i][j] == "\\" && scr[i] == "¤" || scr[i][j] == "=" && scr[i] == "¤" || scr[i][j] == "/" && scr[i] == "¤") if (lifes > 1) { cout << "\a"; lifes--; weaponPos = 7; GameStart(scr, lifes, &timer); Sleep(1000); } else GameOver(odometer); if (scr[i][j] != "3" && scr[i][j] != "\\" && scr[i][j] != "=" && scr[i][j] != "/" && scr[i][j] != "-" && scr[i] != "-") scr[i][j] = scr[i]; if (scr[i][j] == "¤") scr[i] = " "; } for (int i = 1; i < 14; i++)//все снаряды смещаются на элемент вправо for (int j = 48; j > < 12; i++)//рандомное появление космического мусора { if (rand() % 10 == 1) scr[i] = "¤"; }

    При движении корабля вверх или вниз в этот момент космический мусор будет уничтожен без урона кораблю, все благодаря отбойникам



    Если в данный момент не увернуться от мусора стрелкой вниз – корабль будет разбит


    Снаряды, выпускаемые кораблем, сконструированы так, что сметают все на пути своего следования. Следовательно, выпустив один снаряд, расчищается коридор в одну строку.


    Впрочем, хоть поля состоят из материалов, схожих на космический мусор, их нельзя разрушить отбойниками. Соприкосновение любой части корабля с полями приводит к неминуемому краху. Также, как и попадание космического мусора в лицевую часть корабля. Если в такие моменты на приборной панели есть обозначение еще хотя бы одной «попытки» - игра как будто начинается сначала, но сохранив набранный счет и потеряв одну «попытку». Скорость же сбрасывается до начального значения в 12км/с.


    Если же игрок «разбился», а попыток не осталось – игровой сеанс заканчивается, а игроку предлагается ввести его имя, чтобы сохранить свой результат в «зале славы».



    Предложение игроку ввести имя, чтобы сохранить свой результат в «зале славы»


    Приложение обрабатывает 2 файла:

    • «TopChart.bin» - двоичный файл для хранения таблицы лидеров. Данные хранятся в структурах (ник игрока, его счет, дата завершения игрового сеанса). Данные дозаписываются в конец файла при окончании игры. При вызове пункта «Зал славы» файл открывается для чтения с возможностью редактирования. Далее объявляется динамический массив структур, в который переписываются данные из файла, после чего массив сортируется и форматированно выводится в консоль (максимально возможное число результатов – 12, если массив содержит 13 результатов – последний отбрасывается после сортировки). Далее файл перезаписывается массивом структур результатов, после чего массив уничтожается.
    • «CurrentSave.bin» - двоичный файл для хранения сохраненной игры. Вызывается для чтения при запуске подпункта «Продолжить» пункта «Игра». Может содержать данные для восстановления одного незавершенного игрового сеанса: номер строки, в которой содержится нос корабля, количество пройденных километров, количество оставшихся попыток, расположение обьектов на экране. С помощью этих данных формируется игровой сеанс, в точности повторяющий незавершенный. Во избежание нечестной игры, при загрузке сеанса из файла – файл удаляется. При нажатии Escape во время игры данный файл создается, и в него записываются все необходимые данные для успешного дальнейшего продолжения игрового сеанса.

    Пункт главного меню «Помощь» - функция, принимающая параметр от 0 до 22, отображающая последующие 12 строк по 50 символов от входного параметра. Управление осуществляется рекурсивно с помощью стрелок вверх и вниз.


    void Help(int switcher) { system("cls"); cout << "ПРОКРУТКА: СТРЕЛКИ ВВЕРХ/ВНИЗ | ВЕРНУТЬСЯ: ESCAPE\n"; char arr = { " УПРАВЛЕНИЕ В МЕНЮ Передвигаться по пунктам – СТРЕЛКИ ВВЕРХ/ВНИЗ Выбрать пункт – ПРОБЕЛ или ENTER Вернуться в предыдущее меню – ESCAPE УПРАВЛЕНИЕ В ИГРЕ Передвигаться вверх/вниз – СТРЕЛКИ ВВЕРХ/ВНИЗ Сделать выстрел – ПРОБЕЛ Вернуться в меню, сохранив игру – ESCAPE БРИФИНГ Вы – пилот космического корабля, попавшего в космическую бурю. Вам необходимо не разбиться и пролететь как можно большее расстояние. Корабль оборудован динамическим управлением. Чем быстрее вы летите – тем острее поворачивает судно. Корабльавтоматически постепенно разгоняется до 40 км/с. Вы можете сбивать космический мусор с помощью магнитной пушки, встроенной в судно, а также боковыми отбойниками. При управлении кораблем на щитке приборов отображается пройденная дистанция, текущая скорость и количество оставшихся «ячеек отката» (отображаются символом «&»), изначально их 3. Если решите прекратить игру – просто нажмите ESCAPE. Игра сохранится, и вы сможете ее продолжить даже после перезапуска приложения с помощью пункта «ПРОДОЛЖИТЬ!». В главном меню можно посмотреть таблицу почетных пилотов. Добейтесь своего права там оказаться! АВТОРСТВО Svjatoslav Laskov – AUTHOR Igor Marchenko – COACH National Technical University «Kharkiv Polytechnic Institute» 2016" }; for (int i = 0, buf = switcher; i < 13; i++) { for (int j = buf * 50; j < buf * 50 + 50; j++) cout << arr[j]; if (i != 12) cout << endl; buf++; } int controller = _getch();//получить значение нажатой клавиши if (controller == 224)//если была нажата стрелка controller = _getch();//то определить какая именно if (controller == 72)//если стрелка вверх if (switcher > < 22) Help(switcher + 1); else Help(22); if (controller == 27)//если Escape StartMenu(2); }

    Пункт главного меню «Выход» - осуществляет выход из приложения.


    Руководство пользователя

    УПРАВЛЕНИЕ В МЕНЮ
    o Передвигаться по пунктам – СТРЕЛКИ ВВЕРХ/ВНИЗ
    o Выбрать пункт – ПРОБЕЛ или ENTER
    o Вернуться в предыдущее меню – ESCAPE
    УПРАВЛЕНИЕ В ИГРЕ
    o Передвигаться вверх/вниз – СТРЕЛКИ ВВЕРХ/ВНИЗ
    o Сделать выстрел – ПРОБЕЛ
    o Вернуться в меню, сохранив игру – ESCAPE
    БРИФИНГ
    Вы – пилот космического корабля, попавшего в космическую бурю. Вам необходимо не разбиться и пролететь как можно большее расстояние.


    Корабль оборудован динамическим управлением. Чем быстрее вы летите – тем острее поворачивает судно. Корабль автоматически постепенно разгоняется до 40 км/с.


    Вы можете сбивать космический мусор с помощью магнитной пушки, встроенной в судно, а также боковыми отбойниками.
    При управлении кораблем на щитке приборов отображается пройденная дистанция, текущая скорость и количество оставшихся «ячеек отката» (отображаются символом «&»), изначально их 3.


    Если решите прекратить игру – просто нажмите ESCAPE. Игра сохранится, и вы сможете ее продолжить даже после перезапуска приложения с помощью пункта «ПРОДОЛЖИТЬ!».


    В главном меню можно посмотреть таблицу почетных пилотов. Добейтесь своего права там оказаться!

    Итог

    Лично я получил уйму удовольствия от разработки столь мелочного проекта, тем более, что стандартная консоль предназначена для вывода информации, и никак не для игр. Из-за этого фреймрейт получился очень ограничен независимо от компьютера, на котором приложение запускается.


    Хотелось еще разделить приложение на несколько потоков для более корректной реакции на нажатие клавиш пользователем, но треды я реализую уже в следующем проекте на С++.


    Исходный код

    #include "conio.h" #include "windows.h" #include "ctime" #include using namespace std; struct player//определение структуры, хранящей данные о результатах какого-либо завершенного игрового сеанса { char name; int score; int mday; int mon; int year; }; struct save//определение структуры, хранящей данные о незавершенном игровом сеансе { int weaponPos; int timer; int odometer; int lifes; char scr; }; void ScreenOutput(char scr)//функция поэлементного вывода массива в консоль { system("cls"); for (int i = 0; i < 14; i++) { for (int j = 0; j < 50; j++) cout << scr[i][j]; if (i != 13) cout << endl; } } //блок прототипов функций void StartMenu(int switcher);//функция, вызывающаяся из главного меню, содержит пункты "ИГРА" и "ПРОДОЛЖИТЬ" void GameMenu(int switcher);//функция главного меню void GameStart(char scr, int lifes, int *timer);//функция, определяющая начальный символьный массив при запуске нового игрового сеанса void Game(int var);//функция игровго сеанса void GameOver(int score);//функция, спрашивающая имя игрока, и записывающая его результат в бинарный файл void Help(int switcher);//функция помощи игроку void TopChart();//функция "ЗАЛ СЛАВЫ" - отображает список лидеров void Help(int switcher) { system("cls"); cout << "ПРОКРУТКА: СТРЕЛКИ ВВЕРХ/ВНИЗ | ВЕРНУТЬСЯ: ESCAPE\n"; char arr = { " УПРАВЛЕНИЕ В МЕНЮ Передвигаться по пунктам – СТРЕЛКИ ВВЕРХ/ВНИЗ Выбрать пункт – ПРОБЕЛ или ENTER Вернуться в предыдущее меню – ESCAPE УПРАВЛЕНИЕ В ИГРЕ Передвигаться вверх/вниз – СТРЕЛКИ ВВЕРХ/ВНИЗ Сделать выстрел – ПРОБЕЛ Вернуться в меню, сохранив игру – ESCAPE БРИФИНГ Вы – пилот космического корабля, попавшего в космическую бурю. Вам необходимо не разбиться и пролететь как можно большее расстояние. Корабль оборудован динамическим управлением. Чем быстрее вы летите – тем острее поворачивает судно. Корабльавтоматически постепенно разгоняется до 40 км/с. Вы можете сбивать космический мусор с помощью магнитной пушки, встроенной в судно, а также боковыми отбойниками. При управлении кораблем на щитке приборов отображается пройденная дистанция, текущая скорость и количество оставшихся «ячеек отката» (отображаются символом «&»), изначально их 3. Если решите прекратить игру – просто нажмите ESCAPE. Игра сохранится, и вы сможете ее продолжить даже после перезапуска приложения с помощью пункта «ПРОДОЛЖИТЬ!». В главном меню можно посмотреть таблицу почетных пилотов. Добейтесь своего права там оказаться! АВТОРСТВО Svjatoslav Laskov – AUTHOR Igor Marchenko – COACH National Technical University «Kharkiv Polytechnic Institute» 2016" }; for (int i = 0, buf = switcher; i < 13; i++) { for (int j = buf * 50; j < buf * 50 + 50; j++) cout << arr[j]; if (i != 12) cout << endl; buf++; } int controller = _getch();//получить значение надатой клавиши if (controller == 224)//если была нажата стрелка controller = _getch();//то определить какая именно if (controller == 72)//если стрелка вверх if (switcher > 0) Help(switcher - 1); else Help(0); if (controller == 80)//если стрелка вниз if (switcher < 22) Help(switcher + 1); else Help(22); if (controller == 27)//если Escape StartMenu(2); } void StartMenu(int switcher) { system("cls"); switch (switcher) { case 1: cout << "\n\n\n << ИГРАТЬ! >>\n\n ПОМОЩЬ!\n\n ЗАЛ СЛАВЫ\n\n ВЫХОД"; break; case 2: cout << "\n\n\n ИГРАТЬ!\n\n << ПОМОЩЬ! >>\n\n ЗАЛ СЛАВЫ\n\n ВЫХОД"; break; case 3: cout << "\n\n\n ИГРАТЬ!\n\n ПОМОЩЬ!\n\n << ЗАЛ СЛАВЫ >>\n\n ВЫХОД"; break; case 4: cout << "\n\n\n ИГРАТЬ!\n\n ПОМОЩЬ!\n\n ЗАЛ СЛАВЫ\n\n << ВЫХОД >>"; break; } int choice = _getch(); if (choice == 224) choice = _getch(); if (choice == 72) if (switcher != 1) StartMenu(switcher - 1); else StartMenu(4); if (choice == 80) if (switcher != 4) StartMenu(switcher + 1); else StartMenu(1); if (choice == 13 || choice == 32) { if (switcher == 1) GameMenu(1); if (switcher == 2) Help(0); if (switcher == 3) TopChart(); if (switcher == 4) _exit(0); } } void GameMenu(int switcher) { system("cls"); if (switcher == 1) cout << "\n\n\n\n\n << НОВАЯ ИГРА! >>\n\n ПРОДОЛЖИТЬ!"; else cout << "\n\n\n\n\n НОВАЯ ИГРА!\n\n << ПРОДОЛЖИТЬ! >>"; int choice = _getch(); if (choice == 224) choice = _getch(); if (choice == 72 || choice == 80) GameMenu(3 - switcher); if (choice == 27) StartMenu(1); if (choice == 13 || choice == 32) Game(switcher); } void GameStart(char scr, int lifes, int *timer) { for (int i = 0; i < 14; i++)//очищение от мусора for (int j = 0; j < 50; j++) scr[i][j] = " "; for (int i = 50; lifes > 0; i--, lifes--, scr[i] = "&"); *timer = 80; char borderSymbols = { "†", "‡", "¤", " " }; for (int aboveBelow = 0; aboveBelow < 50; aboveBelow++)//прорисовка верхнего и нижнего полей (2 + 2) { scr = borderSymbols; if (scr == "‡") scr = "¤"; scr = borderSymbols; if (scr == "‡") scr = "¤"; } scr = "\\"; scr = "\\";//прорисовка корабля scr = "3"; scr = "="; scr = "="; scr = "/"; scr = "/"; } void GameOver(int score) { system("cls"); player newPlayer;//объявляние структуры newPlayer.score = score;//инициализацие поля набранного счета cout << "Поздравляем Вас!\nВы продержались " << score << " километров.\n\n(Пожалуйста, не используйте кириллические символы)\n(Используйте не более 6 символов)\nОставьте свое имя и станьте примером\nдля подражания будущим игрокам: "; cin.getline(newPlayer.name, 7);//инициализацие поля имени time_t timeCur; time(&timeCur); struct tm * timeCurStruct = localtime(&timeCur); newPlayer.mday = timeCurStruct->tm_mday;//инициализацие даты завершения игры newPlayer.mon = timeCurStruct->tm_mon; newPlayer.year = timeCurStruct->tm_year; FILE *topChart; fopen_s(&topChart, "TopChart.bin", "ab+"); fwrite(&newPlayer, 1, sizeof(player), topChart);//дозапись результата в файл fclose(topChart); TopChart(); } void TopChart() { FILE *topChart; fopen_s(&topChart, "TopChart.bin", "rb+"); system("cls"); if (topChart == NULL)//если произошла ошибка при открытии файла { system("cls"); cout << "Нет ни единого результата."; Sleep(1000); system("cls"); cout << "Нет ни единого результата.."; Sleep(1000); system("cls"); cout << "Нет ни единого результата..."; Sleep(1000); cout << "\nНажмите любую клавишу, чтобы вернуться."; _getch(); StartMenu(3); } fseek(topChart, 0L, SEEK_END); int playerAmount = ftell(topChart) / sizeof(player); player *temp = new player; fseek(topChart, 0L, SEEK_SET); for (int i = 0; i < playerAmount; i++)//копирование содержиомого файла в структкры fread(&temp[i], 1, sizeof(player), topChart); fclose(topChart); for (int i = 1; i < playerAmount; i++)//сортировка структур по спаданию итоговых счетов if (temp[i].score > temp.score) { player tempAlone; strcpy(tempAlone.name, temp[i].name); tempAlone.score = temp[i].score; tempAlone.mday = temp[i].mday; tempAlone.mon = temp[i].mon; tempAlone.year = temp[i].year; strcpy(temp[i].name, temp.name); temp[i].score = temp.score; temp[i].mday = temp.mday; temp[i].mon = temp.mon; temp[i].year = temp.year; strcpy(temp.name, tempAlone.name); temp.score = tempAlone.score; temp.mday = tempAlone.mday; temp.mon = tempAlone.mon; temp.year = tempAlone.year; if (i > 1) i -= 2; else i = 0; } if (playerAmount > 12) playerAmount = 12; cout << "№ " << "Имя" << "\t" << "Счет" << "\t" << "Дата" << endl;//вывод таблицы лидеров в консоль for (int i = 0; i < playerAmount; i++) { cout << i + 1 << ")" << "\t" << temp[i].name << "\t" << temp[i].score << "\t"; if (temp[i].mday / 10 == 0) cout << "0" << temp[i].mday; else cout << temp[i].mday; cout << " "; switch (temp[i].mon) { case 0: cout << "января"; break; case 1: cout << "февраля"; break; case 2: cout << "марта"; break; case 3: cout << "апреля"; break; case 4: cout << "мая"; break; case 5: cout << "июня"; break; case 6: cout << "июля"; break; case 7: cout << "августа"; break; case 8: cout << "сентября"; break; case 9: cout << "октября"; break; case 10: cout << "ноября"; break; case 11: cout << "декабря"; break; } cout << " " << 1900 + temp[i].year << endl; } fopen_s(&topChart, "TopChart.bin", "wb+"); for (int i = 0; i < playerAmount; i++)//запись таблицы лидеров в бинарный файл fwrite(&temp[i], 1, sizeof(player), topChart); fclose(topChart); delete temp; _getch(); StartMenu(3); } int main() { setlocale(LC_ALL, "Rus");//задание кодировки system("mode con cols=51 lines=14");//задание размеров окна консоли system("title Space Invader");//задание описания окна консоли system("color 0A");//задание цвета консоли (0-задний фон; А-передний фон) HANDLE hCons = GetStdHandle(STD_OUTPUT_HANDLE);//получение хендла CONSOLE_CURSOR_INFO cursor = { 100, false };//число от 1 до 100 размер курсора в процентах; false\true - видимость SetConsoleCursorInfo(hCons, &cursor);//применение заданных параметров курсора int timer = 200; cout << " (____/(__) \\_/\\_/ \\___)(____)\n\n\n\n\n\n\n\n\n\n\n __ __ _ _ _ __ ____ ____ ____\n ()((\\/)(\\ / _\\ (\\(__)(_ \\";//вступительная заставка Sleep(timer); system("cls"); cout << " \\___ \\) __// \\((__) _)\n (____/(__) \\_/\\_/ \\___)(____)\n\n\n\n\n\n\n\n\n __ __ _ _ _ __ ____ ____ ____\n ()((\\/)(\\ / _\\ (\\(__)(_ \\\n)(/ /\\ \\/ // \\) D () _)) /";//вступительная заставка Sleep(timer); system("cls"); cout << " / ___)(_ \\ / _\\ / __)(__)\n \\___ \\) __// \\((__) _)\n (____/(__) \\_/\\_/ \\___)(____)\n\n\n\n\n\n\n __ __ _ _ _ __ ____ ____ ____\n ()((\\/)(\\ / _\\ (\\(__)(_ \\\n)(/ /\\ \\/ // \\) D () _)) /\n (__)\\_)__) \\__/ \\_/\\_/(____/(____)(__\\_)";//вступительная заставка Sleep(timer); system("cls"); cout << " ____ ____ __ ___ ____\n / ___)(_ \\ / _\\ / __)(__)\n \\___ \\) __// \\((__) _)\n (____/(__) \\_/\\_/ \\___)(____)\n\n\n\n\n __ __ _ _ _ __ ____ ____ ____\n ()((\\/)(\\ / _\\ (\\(__)(_ \\\n)(/ /\\ \\/ // \\) D () _)) /\n (__)\\_)__) \\__/ \\_/\\_/(____/(____)(__\\_)";//вступительная заставка Sleep(timer); system("cls"); cout << "\n ____ ____ __ ___ ____\n / ___)(_ \\ / _\\ / __)(__)\n \\___ \\) __// \\((__) _)\n (____/(__) \\_/\\_/ \\___)(____)\n\n\n __ __ _ _ _ __ ____ ____ ____\n ()((\\/)(\\ / _\\ (\\(__)(_ \\\n)(/ /\\ \\/ // \\) D () _)) /\n (__)\\_)__) \\__/ \\_/\\_/(____/(____)(__\\_)";//вступительная заставка Sleep(timer); system("cls"); cout << "\n\n ____ ____ __ ___ ____\n / ___)(_ \\ / _\\ / __)(__)\n \\___ \\) __// \\((__) _)\n (____/(__) \\_/\\_/ \\___)(____)\n __ __ _ _ _ __ ____ ____ ____\n ()((\\/)(\\ / _\\ (\\(__)(_ \\\n)(/ /\\ \\/ // \\) D () _)) /\n (__)\\_)__) \\__/ \\_/\\_/(____/(____)(__\\_)";//вступительная заставка cout << "\a"; Sleep(10 * timer);//задержка заставки StartMenu(1); return 0; } void Game(int var) { int weaponPos;//позиция строки дула в массиве int timer;//задержка между перерисовками экрана int odometer;//количество перерисовок экрана, они же итоговые очки int lifes;//количество жизней char control = "&";//переменная управления кораблем int shotPause = 4;//задержка между выстрелами (указывать на одну перерисовку больше) int speed;//скорость корабля char scr; if (var == 1) { weaponPos = 7;//позиция строки дула в массиве odometer = 1;//количество перерисовок экрана, они же итоговые очки lifes = 3;//количество жизней GameStart(scr, lifes, &timer); } else//при восстановлении игрового сеанса из сохранения { FILE *saveBin; fopen_s(&saveBin, "CurrentSave.bin", "rb"); if (!saveBin) { system("cls"); cout << "Нет сохранения."; Sleep(1000); system("cls"); cout << "Нет сохранения.."; Sleep(1000); system("cls"); cout << "Нет сохранения..."; Sleep(1000); Game(1); } fread(&weaponPos, 1, sizeof(int), saveBin); timer = 80; fread(&odometer, 1, sizeof(int), saveBin); fread(&lifes, 1, sizeof(int), saveBin); fread(&scr, 14 * 50, sizeof(char), saveBin); fclose(saveBin); remove("CurrentSave.bin"); } while (true) { int odometerBuf = odometer, odometerDigitLength; for (odometerDigitLength = 0; odometerBuf != 0; odometerBuf /= 10, odometerDigitLength++);//вычисление количества цифр на одометре for (int i = odometerDigitLength, odometerBuf = odometer; i >= 0; i--, scr[i] = odometerBuf % 10 + "0", odometerBuf /= 10);//прорисовка одометра на приборную панель scr = "К"; scr = "М";//дописывание "КМ" odometer++;//наращение одометра speed = 1000 / timer;//обновление спидометра int speedBuf = speed; for (int i = 42; speed != 0; i--, scr[i] = speed % 10 + "0", speed /= 10);//прорисовка спидометра на приборную панель scr = "К"; scr = "М"; scr = "/"; scr = "С";//дописывание "КМ/С" if (_kbhit())//если клавиша была нажата { control = _getch();//переменная примет ее значение if (control == 224) control = _getch(); } if (control == 13 && shotPause == 4 || control == 32 && shotPause == 4)//при нажатии на курок если пушка перезаряжена { scr = "-"; shotPause = 0; } if (shotPause < 4)//перезарядка shotPause++; if (control == 27)//при выходе { FILE *saveBin; fopen_s(&saveBin, "CurrentSave.bin", "wb"); fwrite(&weaponPos, 1, sizeof(int), saveBin); fwrite(&odometer, 1, sizeof(int), saveBin); fwrite(&lifes, 1, sizeof(int), saveBin); fwrite(&scr, 14 * 50, sizeof(char), saveBin); fclose(saveBin); GameMenu(2); } if (control == 72)//при движении корабля вверх if (scr == "\\" || scr == "\\" && scr == "¤" || scr == "\\" && scr == "¤")//если корабль врезался в верхнее поле - игра окончена if (lifes > 1) { cout << "\a"; lifes--; weaponPos = 7; GameStart(scr, lifes, &timer); Sleep(1000); } else GameOver(odometer); else { for (int i = 2; i < 13; i++)//корабль смещается на элемент выше for (int j = 0; j < 49; j++) if (scr[i][j] == "3" || scr[i][j] == "\\" || scr[i][j] == "=" || scr[i][j] == "/") { scr[j] = scr[i][j]; scr[i][j] = " "; } weaponPos--; } if (control == 80)//при движении корабля вниз if (scr == "/" || scr == "/" && scr == "¤" || scr == "/" && scr == "¤")//если корабль врезался в нижнее поле - игра окончена if (lifes > 1) { cout << "\a"; lifes--; weaponPos = 7; GameStart(scr, lifes, &timer); Sleep(1000); } else GameOver(odometer); else { for (int i = 12; i >= 2; i--)//корабль смещается на элемент вниз for (int j = 0; j < 49; j++) if (scr[i][j] == "3" || scr[i][j] == "\\" || scr[i][j] == "=" || scr[i][j] == "/") { scr[j] = scr[i][j]; scr[i][j] = " "; } weaponPos++; } for (int i = 1; i < 14; i++)//все "космические" элементы смещаются на элемент влево for (int j = 0; j < 49; j++) { if (scr[i][j] == "\\" && scr[i] == "¤" || scr[i][j] == "=" && scr[i] == "¤" || scr[i][j] == "/" && scr[i] == "¤") if (lifes > 1) { cout << "\a"; lifes--; weaponPos = 7; GameStart(scr, lifes, &timer); Sleep(1000); } else GameOver(odometer); if (scr[i][j] != "3" && scr[i][j] != "\\" && scr[i][j] != "=" && scr[i][j] != "/" && scr[i][j] != "-" && scr[i] != "-") scr[i][j] = scr[i]; if (scr[i][j] == "¤") scr[i] = " "; } for (int i = 1; i < 14; i++)//все снаряды смещаются на элемент вправо for (int j = 48; j >= 0; j--) if (scr[i][j] == "-") if (j != 48) { scr[i] = "-"; scr[i][j] = " "; } else scr[i][j] = " "; char borderSymbols = { "†", "‡", "¤", " " }; scr = " ";//рандомное заполнение новых элементов краев scr = borderSymbols; if (scr == "‡") scr = "¤"; scr = " "; scr = borderSymbols; if (scr == "‡") scr = "¤"; for (int i = 3; i < 12; i++)//рандомное появление космического мусора { if (rand() % 10 == 1) scr[i] = "¤"; } ScreenOutput(scr);//вывод экрана if (control != "&")//"обнуление" управляющей переменной control = "&"; if (timer > 25)//ускорение корабля timer--; Sleep(timer);//задержка перерисовки } }


    Загрузить исполняемый файл.exe можно тут.

    Создание игры для Linux с помощью Weaver Framework

    Это статья о Weaver Framework, небольшом фреймворке для создания двухмерных игр под Линукс. Weaver написан на языке программирования Си, т.е. для создания игры с его помощью вам потребуется хотя бы начальные знания этого языка программирования.

    Для начала нужно сказать, что фреймворк всего содержит около 70 функций для манипулирования звуком, картинками, создания графических примитивов, детектора столкновений и кое-чего еще. В ходе этой статьи будут описаны почти все функции.

    Сайт проекта находится по адресу http://weaver.nongnu.org/ , там же можно получить уже собранный пакет в формате.deb либо тарбол с исходным кодом, для не debian-based систем. Итак, приступим.

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

    weaver

    В ходе создания проекта Будет создана иерархия директорий, предназначенных для хранения графических, звуковых и исходного кода файлов. Для начала, чтобы потренироваться, создадим проект test. Он будет использоваться для изучения возможностей weaver.

    2.1. Создание пустого проекта

    weaver test

    Несколько слов о содержимом директории проекта.

    test

    |-fonts

    |-images

    |-music

    |-sounds

    | |-weaver

    LICENSE

    Makefile

    В принципе, названия говорят сами за себя. Упомяну лишь о /src , здесь содержатся как непосредственно файлы проекта (game.c , game.h - основные), так и (в директории /weaver ) файлы самого фреймворка необходимые для удачной сборки проекта.

    Рассмотрим файл /src/game.c подробнее. Он является основным файлом проекта и именно в нем находиться главный цикл программы.

    int main(int argc, char **argv)

    Awake_the_weaver(); // Initializing Weaver API

    // Main loop

    For(;;){

    Get_input();

    If(keyboard){

    Break;

    Weaver_rest(10000000);

    May_the_weaver_sleep();

    Return 0;

    Функции

    awake_the_weaver()

    may_the_weaver_sleep()

    разрешают и запрещают использование функций из фреймворка. Между ними находится главный цикл программы.

    get_input() служит для захвата состояния клавиатуры и мышки. Следом за этим идет участок кода. отвечающий за выход из программы по нажатию любой клавиши.

    weaver_rest(n) предназначена для приостановки программы на некоторое количество наносекунд, эта же функция отвечает и за FPS, чем ниже число n, тем выше FPS.

    Для сборки проекта набираем в консоли команду make и ждем удачного завершения.

    gcc -Wall -O2 -g $(freetype-config --cflags) -c src/weaver/display.c

    gcc -Wall -O2 -g $(freetype-config --cflags) -c src/weaver/keyboard.c

    gcc -Wall -O2 -g $(freetype-config --cflags) -c src/weaver/vector2.c

    gcc -Wall -O2 -g $(freetype-config --cflags) -c src/weaver/vector3.c

    gcc -Wall -O2 -g $(freetype-config --cflags) -c src/weaver/vector4.c

    gcc -Wall -O2 -g $(freetype-config --cflags) -c src/weaver/weaver.c

    gcc -Wall -O2 -g $(freetype-config --cflags) -c src/weaver/sound.c

    gcc -Wall -O2 -g $(freetype-config --cflags) -c src/weaver/image.c

    gcc -Wall -O2 -g $(freetype-config --cflags) -c src/weaver/font.c

    gcc -Wall -O2 -g $(freetype-config --cflags) -c src/game.c

    gcc -Wall -O2 -g $(freetype-config --cflags) -g -o test display.o keyboard.o vector2.o vector3.o vector4.o weaver.o sound.o image.o font.o game.o -lX11 -lXext -lm -lvorbisfile -lasound -lpng -lfreetype $(freetype-config --cflags)

    Удача. Запустим проект:

    ./test

    Вот и все! Нажатие любой клавиши закрывает игру.

    2.2. Работа с клавиатурой

    Состояние клавиатуры содержит глобальная переменная keyboard . Узнать нажата ли нужная нам клавиша можно таким нехитрым образом:

    if (keyboard[])

    // что-нибудь происходит

    Если нужно чтобы программа реагировала на сочетание клавиш, используем Логическое-И:

    if (keyboard[] && keyboard[])

    //что-нибудь да произойдет!

    Теперь изменим программу так, чтобы выход производился по нажатию Ctrl-Q.

    Изменим

    if(keyboard)

    Break;

    на

    if(keyboard && keyboard[Q])

    Break;

    Рискнем собрать и запустить игру. Теперь для выхода нам необходимо нажать комбинацию клавиш Ctrl-Q.

    2.3. Рисование графических примитивов

    Для рисования примитивов служат функции:

    draw_circle()

    draw_ellipse()

    draw_line()

    draw_point()

    draw_rectangle()

    Для заливки примитово используются функции

    fill_circle()

    fill_ellipse()

    fill_rectangle()

    Их название, как и названия большинства функций weaver, наглядны и не нуждаются в подробном описании. Описания требуют лишь параметры, но ссылка на Reference Guide будет дана в конце статьи.

    Не будем стоять на месте и нарисуем желтый квадрат с координатами (x=50, y=150) и размером 125x125. В основной цикл добавим такую запись:

    draw_rectangle(50, 150, 125, 125, YELLOW);

    где YELLOW - цвет нашего квадрата.

    Собираем проект и смотрим что получилось.

    Не то, чего мы ожидали, правда?

    Дело в том, что задавая цвет в рисовании квадрата, мы задаем цвет его границы, поэтому нам необходимо залить наш квадрат с помощью функции fill_rectangle(). Что мы и делаем, добавляя после команды рисования квадрата следующую строку:

    fill_rectangle(50, 150, 125, 125, YELLOW);

    Пробуем и смотрим!

    На этот раз у нас все вышло. Теперь попробуем объединить наши знания об использовании клавиатуры и рисовании примитивов. Но перед этим немного оптимизируем нашу программу, создав сам квадрат как переменную.

    rectangle является структурой, с 4 параметрами ось x (параметр x), ось y (параметр y), ширина (параметр w) и высота (параметр z).

    Создадим же переменную типа rectangle, добавив после инициализации weaver (напомню, это awake_the_weaver()):

    rectangle rect1;

    Затем инициализируем ее параметры:

    rectangle rect1;

    rect1.x=50;

    rect1.y=150;

    rect1.w=rect1.z=125;

    Зададим обработку для нажатия клавиш <стрелка-влево> и <стрелка-вправо> клавиатуры.

    If (keyboard)

    Rect1.x-=10;

    If (keyboard)

    Rect1.x+=10;

    А затем и рисование самого квадрата:

    draw_rectangle(rect1.x, rect1.y, rect1.w, rect1.z, YELLOW);

    Отмечу, что инициализация параметров переменной должна находлиться до главного цикла отрисовки, т.е. до for(;;).

    Соберем и запустим наш проект.

    И опять не то, что мы хотели бы видеть?

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

    fill_screen()

    принимающая в качестве параметра цвет заливки. Добавим ее перед рисованием квадрата и снова посмотрим, что получится.

    Удача! Перейдем к загрузке и рисованию изображений.

    Единственный возможный в weaver формат изображения - . png , его и будем использовать. Нужно помнить, что все изображения должны находиться в директории /images .

    Для начала объявим и инициализируем указатель на тип surface, представляющий изображения:

    surface *face=new_image("face.png");

    Функция new_image() принимает один аргумент - название графического файла.

    Для отображения графического файла существует функция

    draw_surface()

    принимающая в качестве аргументов указатель на изображаемый графический файл (surface *origin ), указатель на то, где будет изображаться (surface *destiny ) и координаты (x, y ).

    Графические файлы можнно рисовать на других графических файлах, чтобы рисовать просто на экране, в качестве параметра destiny нужно указывать window, это и есть указатель на текущее окно рисования.

    Уберем лишние упоминания о квадрате, соберем проект и запустим его.

    Итог наших стараний:

    Для более удобной манипуляции рисунком создадим структуру, состоящую из указателя на картинку и ее координат на экране.

    struct pict{

    Surface *fc;

    Int x;

    Int y;

    Заново создадим и инициализируем изображение:

    face.fc=new_image("face.png");

    face.x=100;

    face.y=100;

    Заставим его отображаться:

    draw_surface(face.fc, window,face.x, face.y);

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

    2.5.Вывод текста на экран.

    Этот раздел вас наверняка разочарует, но weaver не умеет выводить текстовые сообщения на экран. В проекте есть модули отвечающие за вывод текста, но они не используются. Почему? Видимо автор еще не готов к этому;). Но он сам предложил решение проблемы. Для вывода текста используется своеобразная палитра - набор букв, размещенный на изображении, откуда буквы по мере надобности берутся и выводятся на экран. Этот прием использовался и раньше в старых (и не очень) играх.

    Воспользуемся им и мы. Для этого загрузим картинку с уже набранным буквами в нашу игру.

    surface *font=new_image("font2.png");

    Итак, у нас есть картинка с размещенными на ней несколькими буквами. Как же брать из нее нужные на буквы? Для вывода некотрой части изображения используется функция blit_surface(). Параметр *src принимает указатель на исходный графический файл, *dest принимает указатель на поверхность для изображения, (x_src, y_src) - координаты, откуда будет начинаться вырезанное изображение, width - ширина нового изображения, height - его высота, (x_dest, y_dest) -координаты целевого пповерхности, куда будет выводиться вырезанное изображение.

    Добавим вызов функции и посмотрим, что получиться.

    blit_surface(font, window, 0, 0, 90, 100, 500, 500);

    Не так это и страшно, но остался один вопрос - на рисунке красные буквы на темно-синем фоне, но выводятся только буквы красного цвета. Это связано с тем, что, по-умолчанию, темно-синий цвет (#00029a) считается прозрачным и не выводится. Если вас не устраивает именно этот цвет, то можно его поменять, просто задав другое значение переменной transparent_color.

    2.5.Звук

    К сожалению, на формат звуковых файлов тоже наложено ограничение - weaver проигрывает лишь файлы формата Ogg Vorbis. Для проигрывания звуков есть две функции: play_music() и play_sound(). Разница между ними в том, что play_music() будет повторять музыку до тех пор, пока не будет вызвана функция stop_music(). Функция play_sound() проигрывает музыкальный файл лишь один раз и управлять воспроизведение этой функции мы не в состоянии. Очевидно, что play_music() лучше использовать для воспроизведения звукового фона игры, а play_sound() для воспроизведения звуковых эффектов.

    Попробуем воспроизвести звук. Для play_sound() файл должен располагаться в директории sound, а для play_music() в директории music соответственно.

    Добавим перед главным циклом:

    play_sound("sound.ogg");

    Затем, как обычно, соберем и запустим игру. Et voilà, слышен звук выстрелов.

    2.6.Определение столкновений

    Для определения столкновений между объектами служит набор функций, имеющий в названии префикс collision_* . Использовать их так же легко как и все остальное. Для примера создадим два прямоугольника:

    rectangle rect1, rect2;

    rect1.x=rect1.y=50;

    rect1.w=rect1.z=25;

    rect2.x=rect2.y=150;

    rect2.w=rect2.z=50;

    Один из них будет двигаться:

    if (keyboard)

    Rect1.x-=10;

    if (keyboard)

    Rect1.x+=10;

    if (keyboard)

    Rect1.y-=10;

    if (keyboard)

    Rect1.y+=10;

    И финальная часть, проверка на столкновение и реакция на это:

    if (collision_rectangle_rectangle(&rect1, &rect2))

    Break;

    Естественно, что для наглядности следует отображать наши прямоугольники на экране.

    Теперь мы достаточно знаем, чтобы попробовать написать собственную небольшую игру. Это будет сильно упрощенный Марио, без преград, но с врагами.

    Начнем!

    3.1. Проект будет называться YAM (Yet Another Mario)

    weaver yam

    Это все хорошо, но с чего же начать? Для начала неплохо было бы придумать интерфейс игры, затем найти необходимые ресурсы для графического оформления игры. Что это? Это могут быть спрайты персонажей, фоновый рисунок, палитра букв.

    О интерфейсе. Его у нас не будет, по сути игра будет представлять собой один короткий уровень, пройти его нужно будет с одной попытки.

    Теперь о спрайтах. Подробнее о них вы можете почитать в Википедии. Есть большое количество сайтов, где можно найти как рипнутые из игр спрайты, так и авторские, сделанные «по мотивам». Нам нужны как минимум два типа спрайтов, для Марио и для его противника. Погуглив, я нашел несколько сайтов с базами данных спрайтов, на одном из них спрайты были размещены в едином изображении, на другом предоставлялись отдельными картинками. Выберем, где они разбиты, это нам понадобиться для более удобного создания анимации персонажей.

    3.2. Так как мы желаем чтобы при нажатии на клавишу движения перемещалась не просто статичная картинка, а было хоть какое-то подобие движения, нам нужно анимировать персонажей. Делается это с помощью ряда похожих изображений, где различия лишь в деталях.

    У нас есть 3 вида спрайтов для анимации Марио.

    Принцип анимации, я думаю вам знаком, это просто последовательное изображение нескольких картинок.

    Создаем структуру, где будем хранить данные о персонаже:

    struct character{

    Surface *fc;

    Int x;

    Int y;};

    Инициализируем переменную для Марио:

    Mario.x=mario.y=100;

    Mario.fc=new_image("mw1.png");

    Mario.fc=new_image("mw2.png");

    Mario.fc=new_image("mw3.png");

    Ну и наконец начем анимацию персонажа, но так как экран обновляеться каждые 0.01 секунды, то изображения будут сменяться очень часто. Это можно исправить либо уменьшив значение FPS, передав функции weaver_rest() значение больше текущего, либо используюя счетчик кадров, который будет решать какой именно спрайт будет отображаться в данном кадре. Будем использовать метод со счетчиком.

    Перед главным циклом создадим переменную counter, которая и будет счетчиком

    int counter=0;

    А в главном цикле будет находиься проверка счетчика и в зависимости от его значения будет рисоваться тот или иной спрайт анимации:

    If (counter<=10){

    Counter++;

    If (counter>10 && counter<=20){

    Draw_surface(mario.fc, window, mario.x, mario.y);

    Counter++;

    If (counter>20 && counter<=29){

    Draw_surface(mario.fc, window, mario.x, mario.y);

    Counter++;

    If (counter>29)

    Counter=0;

    Готово, можно собирать проект и запускать.

    Точно так же анимируем и противника Марио.

    Теперь, чтобы у Марио была хоть какая-нибудь возможность победить, реализуем возможность его прыжка. С этим все просто, при нажатии на клавишу изменяется значение координат по вертикали.

    После этого можно начать обрабатывать столкновения Марио и его противника. Для этого добавим в структуры, описывающие персонажа игры, прямоугольник, за пересечением которых и будем отслеживать.

    Теперь структура выглядит так:

    struct character{

    Surface *fc;

    Int x;

    Int y;

    Rectangle col_det;};

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

    Теперь можно приступить и к реализации ответа на пересечение прямоугольников, для этого мы используем уже знакомую функцию collsion_rectangle_rectangle():

    if (collision_rectangle_rectangle(&mario.col_det, &bowser.col_det))

    Break;

    Вот и все, при столкновении персонажей игра будет завершаться. Легко и просто.

    3.3. Осталось добавить последние штрихи к нашей игре, т.е. фон и звуковые эффекты

    Для добавления звуковых эффектов и фоновой музыки воспользуемся функциями play_music()/stop_music() и play_sound().

    Управление простое - стрелка влево - стрелка вправо - движение, стрелка вверх - прыжок

    Вот и готова наше убогая пародия на великолепную игру. Если возникнет желание, можете добавить облака, кусты, ну и, конечно же, грибочки.

    Как видите, написать игру, используя лишь Си достаточно легко, гораздо труднее сделать это хорошо.

    Напоследок небольшой список полезных ссылок

    http://weaver.nongnu.org / - веб сайт фреймворка Weaver, там вы найдете как подробную документацию, так и примеры кода.

    Если вы желаете повторить примеры из статьи - вот ссылка на архив с исходными текстами нашей игры: http://narod.ru/disk/16196657001/yam.tar.gz.html .

    118.0 Kb новый

    2D-игра написана на JavaScript ES6 HTML5, 17-уровней.

    Прислал: xAtom | JavaScript | Любая ОС

  • Змейка(WinAPI) 255.8 Kb

    Игра 2D, написана на WinAPI, в среде CodeBlocks 17.12(unicode) на C++14.

    Прислал: xAtom | Visual C++ | Windows NT/2000/XP/Vista/7

  • xzGame - крестики нолики 3.9 Kb

    крестики нолики со вкусом))
    сами увидите обязательно прочитете тхт файл

    [email protected]
    по любому вопросу

    Прислал: kalandar | C/C++ | Windows NT/2000/XP/Vista/7

  • 501

    Пример простой Raycast графики с возможностью перемещения по карте, и вращения камеры, на ассемблере компилятор FASM, работает в реальном режиме. Используется 13h видео режим BIOS 320х200, 256 цветовой режим.

    Используются прерывания BIOS
    - INT 10h
    - INT 16h

    Возможности графической оболочки:
    - Заливка экрана
    - Рисование спрайтов
    - Рисования прямоугольников (простых линий)
    Особенности графической оболочки
    - Небольшой вес, простота
    - Использование видео буфера для создания фрейма

    В данной версии можно вращать камеру, отрисовка сцены происходит за счет бросания луча, чем луч длиннее тем препятствие дальше, чем оно дальше тем оно меньше.

    Плюсы этого исходника:
    - Почти полностю рабочий raycast
    - Возможность вращения камеры
    -Возможность перемещения в пространстве.
    Минусы:
    - Низкая производительность нужна оптимизация

    Исходник игры (source):
    http://catcut.net/CAPB
    Канал ютуб:
    https://www.youtube.com/ТипаПрограммист
    Сайт проекта:
    http://neosoft.pp.ua

    Прислал: Типа Программист | Assembler | Любая ОС

  • Исходники программы ConfigIL2 5265.1 Kb

    Исходники программы для настройки параметров игры Ил-2. Язык программирования VB6. В архиве все неоходимые файлы и ресурсы, в том числе подключаемые библиотеки.

    Прислал: Alik044 | Visial Basic | Windows NT/2000/XP/Vista/7

  • City Race 12255.4 Kb

    Игра каких-то даунов из команды Gravity Games, игра не очень, вся баганутая. В этой 2Д гонке есть повреждение машин, турбо и т.п. Игра создана на Construct 2.

    Прислал: adminds | документ | Любая ОС

  • Гусеничный робот 1637.6 Kb

    2D-игра Гусеничный робот, 12-уровней игра создана в среде Eclipse для Android.

    Прислал: xAtom | Java | Другая ОС

  • Ассемблер клеточный автомат, игра 501

    Клеточный автомат, или игра "жизнь" на ассемблере, компилятор FASM, работает в реальном режиме. Используется 13h видео режим BIOS 320х200, 256 цветовой режим. Данный код работает на голом железе в реальном режиме.


    http://catcut.net/tihx


    http://catcut.net/7Nqw

    [email protected]

    Сайт проекта:
    http://neosoft.pp.ua

    Прислал: Типа Программист | Assembler | Любая ОС

  • Ассемблер игра шашки 501

    Заготовка для создания игры шашки на ассемблере, компилятор FASM, работает в реальном режиме. Используется 13h видео режим BIOS 320х200, 256 цветовой режим.
    В этом исходнике нету условий победы, поражения, а так-же нету дамок.

    Для управления:
    W,S,D,A - Перемещение по полю игры.
    Enter - Выбрать шашку,после чего снова в той клетке куда она должна походить.

    Исходник можно скачать здесь:
    http://catcut.net/5ZGy
    Так-же есть канал проекта, где иногда появляються видео, исходники новых программ:
    https://www.youtube.com/channel/UCTVn_Azy0WTDGAh7OYNReJg?view_as=subscriber

    И да у канала есть свой сервер с иходниками, где в основном исходники на ассемблере (почти все мусор), а так-же есть на С++, операционная система на Си, и программа на Паскале:
    http://catcut.net/7Nqw
    При желании добавить свой исходник на сервер, пишите мне на почту:
    [email protected]
    (Да да анонимность не мой конек...)

    Сайт проекта:
    http://neosoft.pp.ua

    Прислал: Типа Программист | Assembler | Любая ОС

  • Приключение шарика 1537.6 Kb

    2D-игра Приключение шарика, 25 уровней. Для Android, игра создана при помощи Eclipse.

    Прислал: xAtom | Java | Другая ОС

  • Простая игра на ассемблере, кто вытянет спичку последним. 501

    Простая игра на двоих проиграет тот кто тянет спичку последним.
    (Игра простая, но преимущество, возможен ее запуск без ОС)

    Исходник можно скачать здесь:
    http://catcut.net/YMqw
    Так-же есть канал проекта, где иногда появляються видео, исходники новых программ:
    https://www.youtube.com/channel/UCTVn_Azy0WTDGAh7OYNReJg?view_as=subscriber

    И да у канала есть свой сервер с иходниками, где в основном исходники на ассемблере (почти все мусор), а так-же есть на С++, операционная система на Си, и программа на Паскале:
    http://catcut.net/7Nqw
    При желании добавить свой исходник на сервер, пишите мне на почту:
    [email protected]
    (Да да анонимность не мой конек...)

    Прислал: Типа Программист | Assembler | Другая ОС

  • Простая графическа игра на ассемблере. 501

    Простая игра на ассемблере, компилятор FASM, работает в реальном режиме. Используется 13h видео режим BIOS 320х200, 256 цветовой режим. Примитивная коллизия по ширине, высоте.

    Суть игры не дать попасть кометам в ваш космический корабль, для этого вы можете двигаться только влево или вправо, до рамок, со временем игра ускоряется, игра продолжается пока игрок не проиграет.

    Исходник можно скачать здесь:
    http://catcut.net/KMqw

    Так-же есть канал проекта, где иногда появляються видео, исходники новых программ:
    https://www.youtube.com/channel/UCTVn_Azy0WTDGAh7OYNReJg?view_as=subscriber

    И да у канала есть свой сервер с иходниками, где в основном исходники на ассемблере (почти все мусор), а так-же есть на С++, операционная система на Си, и программа на Паскале:
    http://catcut.net/7Nqw
    При желании добавить свой исходник на сервер, пишите мне на почту:
    [email protected]
    (Да да анонимность не мой конек...)

  • Вопрос: Что посоветуйте для создания игр с нуля?


    Доброго времени. Не так давно был вдохновлен игрушками вроде Papers, Please и Hotline Miami. Есть своя идея для создания игры. Но дело в том что не знаю на чем лучше писать. Игру задумал с видом сверху, не 3D, для ПК. По работе пишу на 1С, но с-ка не для игр, хотя если очень постараться то можно, также изучал самые азы Java. Я понимаю, что сначала лучше попробовать сделать что-нибудь просто, типа крестики-нолики или змейку, чтобы получить базовые знания.
    На просторах интернета предлагают использовать C++, но также предлагают использовать C# + XNA. У кого есть опыт в создании игр, посоветуйте литературу или дайте какие то советы по поводу программирования игр с нуля.

    Ответ:

    Сообщение от MonteKristo

    посоветуйте литературу

    К сожалению на русском очень мало литератуты, тем более качественной. Переводят далеко не всё, далеко не сразу, а бывает, что не качественно. Бывает, что переведут книгу через два года после выхода оригинала, а уже вышла новая версия оригинала и первая устарела. Поэтому я изучаю английский и перевожу с помощью Lingvo и translate.google.com актуальные книги

    Искать книги по созданию игр на разных языка и технологиях можно здесь:

    Но не в коем случае не начинайте с книг из категорий: Progressing и Mastering

    Что какой раздел означает можно узнать если кликнуть "i". Можно ещё бесплатно скачать примеры после регистрации. Я это показал на рисунке:

    Вопрос: Создание игры на языке Си?

    Ответ: Добавлено через 30 минут

    Сообщение от HighPredator

    Вообще на си можно написать все. Было бы желание, мозги и время.

    Желание есть, мозгов нету, время буду искать >< Благодарю!

    Сообщение от McFair

    Так же можно самому начать без движка еще изучать Opengl, создать пару тетрисов, и глядишь поймешь что к чему.

    Чувствую придется писать движок самому

    Сообщение от McFair

    7.Не совсем понятно, типо часть рисунка вырезать и наложить на куб?

    Да, отрисовать объект и оттекстурировать его, наложив изображение.

    Сообщение от cyber-satyr

    GTK, например, кросс. А так, для венды юзай winapi, там чисто сишый интерфейс.

    Благодарствую! Это я и имел в виду

    Сообщение от McFair

    8.Больше зависит от игры,целей и стиля программирования, все нужно учитывать, если брать один и тот же проект на си и шарп то конечно си намного выиграет, но в шарпе скорость разработки на 2\3 помоему повысится.

    Неплохим выбором наверное будет совмещение их. Основу писать на шарпе, а на Си докручивать фитчи?)

    Вопрос: Создание игры ММОРПГ


    Здравствуйте. Недавно создавал тут тему, где узнавал про создание игры 2д.
    Все же я решил узнать, в каком направлении мне учиться.
    Вот 2 скриншота:
    http://kape.cc/uploads/posts/2011-05...a630f08343.jpg
    http://mmohuts.com/wp-content/galler..._07.jpg?bb7a3b
    И видео:

    Мне нужно сделать игру подобного рода. В общем, локация - системой сетки. Локация поделена на некоторые кубики
    Я понимаю, то нужна графика, звуки, звуковые библиотеки, но для начала возьмем звуковые библиотеки.
    Забудьте про графика (она допустим есть), есть звуки.
    Хочу спросить, удовлетворяет ли связка с++ + OpenGL моим требованиям и созданию похожей игры, а именно: передвижение объекта по клеткам с анимацией, столкновение объектов, взятие графики со спрайтов + скелетная анимация (голый персонаж + привязка предметов - графики, к телу персонажа (одежда)).

    Ответ: 8Observer8 , никогда не любил физику, пока не понял, что она так необходима... Но сейчас, я понял, что Box2D для моей задуманной игры точно не нужен... Смотрел документацию на SFML, после, сегодня потыкал предыдущую наработку и смог сделать чтобы взаимодействовал с стенами, сколько счастья было)) Просто потратить пришло около 1-2 часа чтобы понять как это сделать, но это того стоит.
    Кстати хочу попробовать сделать хотя бы первый уровень бомбермена)

    Добавлено через 10 минут
    P.S. параллельно изучаю c++

    Вопрос: Посоветуйте литературу по созданию игр от начала до конца


    Посоветуйте литературу по созданию игр от начала до конца. С всема елементами, на с++ можна. Толька новую книгу издательства 2010-2015 года

    Ответ: А вы что для рисования будете использовать: DirectX или OpenGL? Я смотрел есть общие книги по теории игр --> . Какого плана вы будете игры писать: 2D, 3D, RPG, стрелялки, гонки, шахматы, го? У всех этих игр специфики разные

    Вопрос: создание игры


    Здравствуйте, задумал сделать простенькую игру (rpg). Делать буду просто "Для себя". Игра будет статичная (bmb картинки персонажей, объектов и т.д, возможно gif) Когда начал рыться в интернете в поисках того как это вообще делается, нашел кучу готовых компонентов для delphi (например delphix) и поэтому у меня возник вопрос, стоит ли использовать что то подобное? Или лучше все самому вырисовывать на canvas?

    Ответ:

    MrDmitry ,
    Статичная игра? Это что-то новое в программе строения.
    Если она и в правду такая то вам хватит TImage.
    А если всё таки это динамичная игра то вам потребуется TPaintBox.

    Мало кто знает но в мести с Delphi поставляются примеры программ в том числе и 3 мини-игры!

    В Windows 7, нажмите пуск | Все программы | Embarcadero RAD Studio XE5 | Samples.

    У меня эта папка
    C:\Users\Public\Documents\RAD Studio\12.0\Samples\

    Что касается компонентов и движков.
    О компонентах сразу забудьте. Не существует компонента который сделает за вас игру. Игра пишется руками и головой.

    Что касается движков. Движок ускоряет разработку.
    В движке вы найдёте кучу готового кода. Движок надо подбирать под игру.

    От движка советую посмотреть математическую и геометрическую библиотеку. Библиотеку для работы с континентом и мультимедиа. Какие форматы файлов поддерживает программа?
    Поддержка камеры. Поддержка спецэффектов.

    Вы не сказали 2D или 3D игра.
    Наличие отсутствие поддержи спрайтов, билбордов. Вывода текста.

    Добавлено через 14 минут и 34 секунды
    Думаю вам будет интересно. Есть сайт посещенный созданию игр.

    Тут можно и подсмотреть и позаимствовать как идеи для игры так и идеи по реализации.

    Вопрос: Создание своего языка в Visual Studio


    ищу понятные пошаговые примеры/уроки по созданию своего языка в Visual Studio.

    Нашёл пример языка, но мне нужно понять что, как и для чего, т.к. мой асм мало похож на оок.

    --
    Нужно вообще:
    "кросскомпилятор" из своего асма в студии, с подсказками кода, в коды для процессора Z80 (пока не разбирался со способом загрузки на "железку" или в эмулятор, это следующий этап).

    Требования:
    редактор - сама ВС (2013);
    ассемблерный Z80 код на экране, без лишних модулей;
    выбор языка при создании проекта (где список языков C#, F#, VB.NET и прочих).

    Ответ: а как "сказать" студии 2013 чтобы она подсвечивала и подсказывала?

    Вопрос: Создание игры на движке C++


    Народ, помогите с созданием игры на движке с++ . Doodle jump, Буду благодарен аналогу. Проблема в том что начал только не давно изучать. И ни как не могу написать правильную программу. Видимо я еще конкретный Нуб. Благодарен заранее!

    Поделиться: