Блог
Анатолия Борисова

Как повысить свою долю в экспорте программного
обеспечения из России до 1 000 000 рублей в год?

Подарок

для каждого подписавшегося
на нашу рассылку

Как сделать игру и получить работу в game-студии

ПРЕДИСЛОВИЕ

Здравствуй, мой дорогой читатель!
Я устраивался на работу в фирмы, чей хлеб разработка и продаж компьютерных игр, дважды. Во второй раз у меня это получилось :-).
Как я это сделал, вы узнаете, прочитав мою статью.
А вот, что вы не узнаете из нее:

  • как делать игры не программируя. Я не рекламирую подобные конструкторы, наверное, потому что пока не разработал своего подобного продукта;
  • как продвигать и продавать игры. Я не маркетолог и не продажник, хотя небольшой опыт по продаже программных продуктов получил занимаясь системами КонсультантПлюс;
  • как сделать GTA V, но с режимом строительства как в Minecraft.

КУРИЦА ИЛИ ЯЙЦО?

Другими словами сначала убедить себя, что вот эта технология самая крутая и перспективная. Прочитать по ней тонны мануалов, книжек, обучалок, сделать 100500 «Hello, world», а потом искать вакансию. Или стоп… Сходить на сайт работы покрупней, например hh.ru, изучить все вакансии в IT вашего города, если это конечно теоретически возможно за обозримое время. Ощутите разницу: Москва – over 67 000 вакансий, любимый Penza-City – 89 штук. Я пошел по второму пути.
В результате, после первого собеседования получил задание написать игру под Android на С++ с использование OpenGL ES (2.0 предпочтительно). Благо мой древний Galaxy S имел аппаратную поддержку этой версии, потому что эмулятор отказался запускаться под Windows XP. Через неделю труда я получил статичную картинку. Вот она:

Flip Flip Revolution Andriod OpenGL ES 2.0

А будущий работодатель хотел вот такую:

Dance Dance Revolution On Android

Игра по идее повторяет известную Dance Dance Revolution, и подобных игры в Google Play предостаточно, например Finger Dance или Пиано Мастер.
Неделя изучения операционной системы, знакомой как программисту не ближе все того же «Hello, world» на Java, но никак не на C++ дали 39 килобайт кода или примерно 1500 строк под названием Flip Flip Revolution. Файл game.exe делала, к сожалению, успешно только анимацию центрального элемента – желтого квадрата. Застопорившись на цикле анимации множества объектов, при попытке получить «Подсказка в студию» - помощь от работодателя, был отправлен на сайт по разработке игр под iOS. Возможно, за вторую неделю, выделенную на выполнение задания, я бы нашел ответы, но нужно было готовиться к выставке InnoMed 2013, и первая попытка получить работу в game development с треском провалилась.
Через несколько месяцев я нашел вакансию в другой игровой студии моего городка. В задании не было требований по использованию технологий. Будущая работа предполагала программирование на Lua – скриптового языка, используемого во многих игровых проектах, например S.T.A.L.K.E.R., или World of Warcraft.
Передо мной встал выбор – на чем сделать тестовую мини-игру…

ВЫБОР ИНСТРУМЕНТА

Возможно, мне пошло бы на пользу изучение Lua уже в ходе тестового задания и разработка игры в хорошем конструкторе с его поддержкой. Но вот незадача, меня сразу предупредили, что работа будет вестись на собственном внутреннем игровом движке фирмы. Так зачем тратить время на изучение какого-то открытого движка, если эти знания не пригодятся в работе по вакансии. Его ведь еще нужно найти, например популярный Unity не поддерживает Lua.
Основной мой опыт программирования – это языки группы C. По заданию нужно было сделать простую двухмерную игру и поэтому, можно было бы обойтись графическими средствами типичных Windows-приложений, например, Windows Forms. Но память о первой неудаче была еще свежа и злость на самого себя сделала решающий удар. Выбор пал на OpenGL.
Библиотека OpenGL — открытый стандарт, разрабатываемый некоммерческой организацией Khronos Group при участии сообщества. Все крупные производители GPU (nVidia, AMD, Intel) так или иначе влияли на OpenGL. В отличие от Direct3D доступен на очень большом количестве платформ. В частности, OpenGL является основным API для взаимодействия с GPU в Linux и Mac OS.
Как я обозначил выше, графика в игре будет простой, поэтому версия OpenGL может быть довольно старой, а значит, игра без проблем запуститься на большем количестве компьютеров.
Последний шаг в выборе инструмента – бесплатная среда разработки. Для Windows это может быт как Visual Studio Express, QT Creator, Eclipse ,так и Code::Blocks. Последние три IDE (интегрированная среда разработки), как правило, используют в связке с компилятором MinGW.

ВЫБОР ВСПОМОГАТЕЛЬНЫХ БИБЛИОТЕК

В OpenGL нет функций для создания окна ни для работы с вводом с клавиатуры/мыши, ни для работы со звуком, работы с графическими форматами (jpeg, png, bmp, tiff) тоже нет, поэтому для этого нужно было выбрать сторонние библиотеки. Точнее в OpenGL нет функций чтения/записи графических файлов, но поддерживается работа с массивами пикселей.
Посмотрев свой программный код времен университета, я понял, куда двигаться в плане работы по созданию окна приложения и настройки его контекста (режима работы OpenGL). На 2003 год была актуальна библиотека GLUT. Примеры работы с ней я почерпнул из книги Сергея Гайдукова «OpenGL. Профессиональное программирование трехмерной графики на C++», еще до выхода ее в печать. Мы учились вместе на одной кафедре Пензенского государственного университета и познакомились при совместной работе над компьютерной диагностической системой «Кардиовид».
Выяснив, что GLUT почил в бозе, выбор пал на GLFW, так как у нее была более свежая дата последнего релиза по сравнению с freeglut.
Скачать библиотеку можно с официального сайта http://www.glfw.org/. Архив Source package содержит примеры использования, и только так ее можно установить на Linux, если конечно, поставщик вашего дистрибутива не сделал заботливо для вас соответствующий пакет и не положил его в свой репозитарий. Для Windows есть уже готовые бинарные файлы под компиляторы Visual Studio 2010, 2012 и MinGW.
Работодатель прислал графические файлы в формате png и jpeg. В поисках нужной библиотеки я наткнулся на одну крошечную C-библиотечку – SOIL. Возможности у нее, по сравнению с FreeImage, действительно скромные, но больше и не требовалось. Страница библиотеки времен Web 1.0 http://lonesock.net/soil.html и сама библиотека http://www.lonesock.net/files/soil.zip. Для использования под Visual Studio ее нужно скомпилировать, для MinGW в архиве уже есть готовый файл библиотеки (с расширением a).

РЕАЛИЗАЦИЯ ЗАДАНИЯ ОТ БУДУЩЕГО РАБОТОДАТЕЛЯ

Все инструменты скачены и установлены. Можно приступать к программированию.
Эту статью я пишу через три месяца после написания программного кода игры. Сейчас я узнаю, насколько у меня получилось создать хорошо поддерживаемый код, когда я буду объяснять, что в нем происходит. А хорошо поддерживаемый код - это такой код, прочитав который можно с максимальной скоростью понять, что он делает. Особенно, я могу критично на него взглянуть после прочтения книги Д. Босуэлл, Т. Фаучер «Читаемый код или Программирование как искусство», 2012 («The Art of Readable Code (Theory in Practice)» by Dustin Boswell, Trevor Foucher), которую рекомендовал технический директор id Software - Джон Кармак.
Суть игры Waterpipe – вы водопроводчик, поверните трубы на правильный угол и проложите тем самым путь воде.
Задний фон, на котором происходит все действо:

Задний фон игры Waterpipe

Трубы, которые нужно поворачивать:
[Not a valid template]
Создадим проект в Visual Studio. Если вы еще ее не установили, вот пара ссылок на несколько вариантов ее установки:

Выберите Файл->Создать-> Проект… Введите имя Решения (Solution) и укажите тип проекта «Консольное приложение Win32».

Новый проект Visual Studio 2010

Нажмите «Готово» и перейдите к настройка проекта. Необходимо указать пути к сторонним библиотека, их заголовочным файлам. Добавить список этих библиотек в настройки Компоновщика, в его настройках указать точку входа mainCRTStartup.

Путь к библиотекам и заголовочным файлам
Каталоги VC++. Добавляем новый путь в «Каталоги библиотек». Заголовочные файлы SOIL.h, glfw3.h и glfw3native.h я просто скопировал в каталог проекта. Каталог <Имя Вашего проекта> размещен в каталоге <Имя Вашего решения>. По умолчанию они имеют одинаковые имена.

Сторонние библиотеки
Компоновщик->Ввод. Добавляем библиотеки SOIL.lib, glfw3.lib и glfw3dll.lib, opengl32.lib.

Точка входа

Компоновщик->Дополнительно. Укажите точку входа.

Если у Вас установлен MinGW c библиотекой QT 4, то все настройки сводятся к pro-
файлу следующего содержания:

TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt
LIBS += $$PWD/*.a -lopengl32
SOURCES += *.cpp

Настройка проекта закончена, и если Вы выбрали пустой проект, то создайте новый cpp-файл, а так как он содержит функцию main(), я назвал его main.cpp.
Создадим окно программы:

int main( int argc, char *argv[] ) {
  GLFWwindow* window = NULL;

  SetupGLFW(&amp;amp;amp;amp;amp;window);
  SetupGL();

  /* Главный цикл продолжается пока пользователь не закроет окно */
  while (!glfwWindowShouldClose(window)) { 
    /* Обмен переднего и заднего буфера, GLFW работает только с двойной
       буферизацией */
    glfwSwapBuffers(window);
    /* Извлечение и обработка событий через вызов функций зарегистрированных 
       на эти события */
    glfwPollEvents();
  }
  /* Отсоединение контекста */
  glfwDestroyWindow(window);
  /* Очистка ресурсов GLFW */
  glfwTerminate();
  exit(EXIT_SUCCESS);
}

Библиотека GLFW значительно облегчает создание необходимого каркаса. Наша функция SetupGLFW выполняет шаги по связыванию контекста визуализации OpenGL c контекстом устройства Windows. Назначение контекста – помнить настройки и режимы рисования. Так как GLFW кросс-платформенная библиотека, она представляет единообразный способ связывания контекстов как в Windows (не ниже XP), так и Mac OS X и Linux. Рассмотрим ее позже.
Еще одна наша функция SetupGL проводит инициализацию OpenGL, вызывая те ее функции, которые обычно нужно использовать только один раз во всей программе. Функции, необходимые для постоянного обновления кадра, будут расположены в главном цикле программы.
Вернемся к настройке GLFW.

void SetupGLFW( GLFWwindow** window ) {
  GLFWmonitor* monitor;
  const GLFWvidmode* mode;

  glfwSetErrorCallback( error_callback );
  /* Initialize the library */
  if ( !glfwInit() )
    exit( EXIT_FAILURE );
  monitor = glfwGetPrimaryMonitor();
  if ( !monitor ) {
    glfwTerminate();
    exit( EXIT_FAILURE );
  }
  glfwWindowHint( GLFW_RESIZABLE, GL_FALSE );
  glfwWindowHint( GLFW_VISIBLE, GL_FALSE );
  /* Create a windowed mode window and its OpenGL context */
  *window = glfwCreateWindow( WINDOW_W_PX, WINDOW_H_PX, "WaterPipe", NULL, NULL );
  if ( !window ) {
    glfwTerminate();
    exit( EXIT_FAILURE );
  }
  mode = glfwGetVideoMode( monitor );
  glfwSetWindowPos( *window, ( mode-&gt;width - WINDOW_W_PX ) / 2, ( mode-&gt;height - WINDOW_H_PX ) / 2 );
  /* Make the window's context current */
  glfwMakeContextCurrent( *window );
  glfwSetKeyCallback( *window, *key_callback );
  //IsMousePressedInGameFiled = false;
  glfwSetMouseButtonCallback( *window, mouse_button_callback );

  // some info about 3D API
  printf( "GL_VERSION: %s\n", glGetString( GL_VERSION ) );
  printf( "GL_VENDOR: %s\n", glGetString( GL_VENDOR ) );
  printf( "GL_RENDERER: %s\n", glGetString( GL_RENDERER ) );
  int major = 0;
  int minor = 0;
  major = glfwGetWindowAttrib( *window, GLFW_CONTEXT_VERSION_MAJOR );
  minor = glfwGetWindowAttrib( *window, GLFW_CONTEXT_VERSION_MINOR );
  if ( major &gt; 1 ) {
    const GLubyte *glslVersion = glGetString( GL_SHADING_LANGUAGE_VERSION );
    printf("GLSL Version: %s\n", glslVersion);
  }

  glfwShowWindow( *window );
}

Вот что нам нарисует SetupGL. Результат ее работы мы увидим только после вызова glfwSwapBuffers.

OpenGL. Первое окно

Изменяя параметры glClearColor можно вывести любой из 16 581 375 цветов доступных к отображению на типичном ПК.
Добавим задний фон.
Для этого поместим код загрузки текстуры в функции InitTexture() и вызовем ее до главного цикла, а код ее отображения в RenderScene(window) и будем вызывать ее в главном цикле.

void InitTexture() {
  // устанавливаем выравнивание пикселей текстуры в памяти
  glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
  // используем библиотеку SOIL для получения текстуры из файла back.jpg
  back_identif = SOIL_load_OGL_texture(
                   BACK_JPG,
                   SOIL_LOAD_RGB,
                   SOIL_CREATE_NEW_ID,
                   0
                   );

  if( back_identif == 0 ) {
    printf( "SOIL loading error: '%s'n", SOIL_last_result() ) ;
  }
  // устанавливаем параметры текстуры
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
  // устанавливаем взаимодействие текстуры с объектом в буфере
  glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
}

OpenGL. Первая текстура

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

static void key_callback( GLFWwindow* window, int key, int scancode, int action, int mods ) {
  // обработка кнопки Esc – закрываем окно
  if ( key == GLFW_KEY_ESCAPE &amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp; action == GLFW_PRESS )
    glfwSetWindowShouldClose( window, GL_TRUE );
}

void mouse_button_callback( GLFWwindow* window, int button, int action, int mods )
{
  if ( button != GLFW_MOUSE_BUTTON_LEFT )
    return;

  if ( action == GLFW_PRESS ) {
    glfwGetCursorPos(window, &amp;amp;amp;amp;amp;mx, &amp;amp;amp;amp;amp;my);
    printf("mx: '%f' my: '%f'n", mx, my);
  }
}

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

Pipes[i].id = SOIL_load_OGL_texture (
                    Pipes[i].file,
                    SOIL_LOAD_RGBA,
                    SOIL_CREATE_NEW_ID,
                    0
                    );

Готовая игра на OpenGL
В коде функции RenderScene для анимации следует применять вызов glEnable(GL_ALPHA_TEST) и glAlphaFunc.
Логику игры Вы можете посмотреть в единственном файле main.cpp, который содержит всю игру. Если возникнут вопросы по ней, я отвечу в комментариях. На данный момент, мне не нравиться, как она портит код отрисовки в функции RenderScene, и я планирую ее переделать, посветив этому отдельную статью. Еще в игре используются только 3 текстуры трубы, вместо 7 заданных, которые вращаются, чтобы показать все их возможные положения, при этом падение света на них будет показано в неправильном направлении.

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

Исходный текст тестового задания: мини-игра Waterpipe

 

 

Проект для QT4 и MinGW тестового задания: мини-игра Waterpipe. С улучшенной анимацией

3 ответов

  1. Лиза
    Разработка игры меня всегда привлекала, но пока что я не рискнула заняться данным процессом, потому что этот процесс сложный и надо не только уметь хорошо программировать, но и рисовать тоже надо уметь. Все таки в наше время пользователей привлекают игры с отличной графикой и снижать ее качество - равносильно убить игру.
  2. Артур
    разработка игры конечно же представляет для меня большой интерес и по возможности я попробую сделать что то свое. Ну а пока что у меня просто не хватит навыков и я искренне завидую тем людям, которые могут создавать игры. Они настоящие профессионалы и могут гордиться тем что создали продукт, от которого нет отбоя.
  3. Разработка игр,это очень прибыльное дело.Но есть много но,нужно придумать тему игры,и начинать разработку,дело кропотливое и займет не один месяц.

Оставить комментарий