Ответы пользователя по тегу Разработка игр
  • Header в header'е и можно ли делать игру на одних Header'ах?

    @Mercury13
    Программист на «си с крестами» и не только
    Такой механизм называется «одна единица компиляции» и вполне имеет право на жизнь: на одном процессоре время полной сборки будет наименьшим, потому так распространяют некоторые библиотеки (SqLite, Google Test). Правда,
    игра — обычно масштабный проект, и чем больше будет кода, тем дольше нужно компилировать, чтобы проверить изменения.

    Потому код обычно разбрасывают по единицам компиляции (*.cpp) в соответствии с его внутренней логикой, и к каждой единице (кроме точки входа) приписывают хедер (*.h), который показывает, что эта единица делает. Повторяю, только описывает, что делает — весь код в CPP. И в большинстве случаев самый длинный этап сборки — линковка (особенно с оптимизацией по ходу линковки aka LTO).

    Единственное, что в 80% случаев не получается закинуть в CPP — это шаблоны.

    Чтобы один хедер не подключался несколько раз — есть include guard. Правда, если вы не продумали зависимость между хедерами, может попасться циклическое включение — это вредно, но лишь потому, что программа может не скомпилироваться.

    Чтобы не было циклических включений, для сохранения скорее всего потребуются два хедера: один отвечает за собственно процедуру сохранения и подключается к одному-единственному модулю — системе меню. Второй — за какие-то общие функции, которые позволяют сохраняться в абстрактный поток плиточному фону, снаряду, монстру… Называется как-нибудь SaveUtils.h и подключается повсюду.

    (Примечание. И SqLite, и Google Test разрабатывались по традиционной схеме, с проектом из кучи CPP. SqLite собирается в один большой *.c автоматикой, и я даже качал традиционный код — на 5-метровом файле некоторые версии Embarcadero вылетали с нехваткой памяти. В Google Test есть файл all.cpp или что-то подобное, в котором #include остальных CPP — пользователь подключает в проект all.cpp, и библиотека отнимает минимум его времени.
    Ответ написан
  • Реально ли 2d игра на С++ без граф.библиотек и движокв?

    @Mercury13
    Программист на «си с крестами» и не только
    Без DirectX или OpenGL никуды, это самые низкоуровневые API, дающие как скорость, так и мало-мальскую совместимость.
    Советую использовать тонкую обёртку над всем этим добром вроде SDL: думаю, интереснее будет писать игру, чем решать проблемы с Alt-Tab.
    А так, если задаться целью, можно небольшой движок сделать за неделю-две. Остальное лучше оставить на багофиксы и наполнение.
    Не советую работать с гексами, и вот почему. Гексы сразу же подразумевают, что игра пошаговая. ИИ замучитесь писать!

    И ещё. Систему анимации-то можно за это время написать, но сделать хороший редактор анимаций сложнее. Каждый кадр — отдельная картинка, и всё?
    Ответ написан
    1 комментарий
  • Что использовать, int, float или double в современном игровом движке?

    @Mercury13
    Программист на «си с крестами» и не только
    Предварительные расчёты (например, координаты моделей) проводить в типе достаточной точности (и, разумеется, на процессоре). Окончательные — в привычном float.
    И, понятное дело, для открытых миров придётся писать свои «велосипеды» по отсечению далёких горизонтов, и Unity в стандартной поставке не уверен, что подойдёт.

    Ещё раз. Координаты участников игры записываются в double. Координаты вершин модели — во float. Мы на процессоре проводим начальные преобразования координат, убеждаемся, что координаты заслуживают того, чтобы их рендерить, преобразуем во float и гоним на видяху.
    Ответ написан
    2 комментария
  • Как написать структура классов платформера?

    @Mercury13
    Программист на «си с крестами» и не только
    1. Перед нами серьёзный проект: есть и сеть, и подкачка, и поиск путей. Если вы будете писать платформер с нуля, вероятно, «лишние» компоненты вы писать не будете.
    2. Это компоненты, а не классы. Структура классов в геймплее будет более густая, а в рендеринге и физике — менее.
    Например, пишем 2D-рендеринг. Я бы делал Renderer, Tileset, TileLayer, Sprite, Particle…

    В зависимости от желания и целей, лучше воспользуйтесь чужим движком или напишите свой простенький. Куча вопросов отпадёт.
    Ответ написан
    Комментировать
  • Как правильно настроить характеристики врагов и оружия (урон, здоровье и тп), чтобы соблюдался баланс?

    @Mercury13
    Программист на «си с крестами» и не только
    Да, это называется игровой баланс. Дело сложное и мутное, единого подхода нет.

    Рубиться компьютер на компьютер — помогает в маргинальных случаях и свидетельствует о бедном геймплее. Сложных героев наподобие Ио или Оракула из Доты попробуй отработай. (Хотя все последние герои сложные, начиналось всё ≈2007 с героев поддержки, но потом всяких наделали: и танков, и дамагеров.)

    Все начальные подсчёты приблизительные, и примерно такие.

    Вот у нас есть гипотетическая Лунная Наездница, которая не имеет длительных дисейблов (зато догонит почти любого), каждые 6 секунд даёт магию на 250hp и раз в секунду даёт удар на 50hp. Вот и получается: за кулдаун нюка она сносит 300 hp. Другими словами, больше урона она наносит руками, чем магией. Сколько должен наносить гипотетический Гном-снайпер, который вообще не имеет магии и бьёт только своим ружьём (с такой же скоростью)? Получается, что 90hp. Многовато, но можно дать Снайперу фору другими методами. Поскольку первые уровни Доты — это «перестрелка на Угре» из-за спин крипов, можно дать ему большую дальность, и он зачморит Наездницу. Можно дать подствольник, который будет эффективен против крипов с первых же уровней.

    Особые сложности начинаются, когда в игру входят полубоевые персонажи (дисейблеры, киллеры и прочие). Один держит, второй палит — сколько секунд держать? И, считаю, только под Valve решились основные косяки с Дотой.

    Поскольку игра длится в пространстве и времени, надо понимать, как та или иная местность, тот или иной этап игры помогает тому или иному герою. Сможет ли игрок выстоять «не в своей тарелке» и пережить чужой «золотой век»? Какие нужно для этого принять решения? Это больше творчество, чем наука.

    Но прежде, чем собирать баланс, надо… 1) Собрать приблизительное видение игры; 2) Сделать прототип; 3) Собрать общую картину баланса — и только тогда можно раздавать характеристики разным персонажам.
    Ответ написан
    Комментировать
  • Как нарисовать изгиб мостика под нагрузкой?

    @Mercury13
    Программист на «си с крестами» и не только
    В первом приближении (деформации малы, балка лёгкая) — половина параболы.
    Если точно — решить можно только численно.
    Ответ написан
    Комментировать
  • Есть ли игры в открытом мире с круглой землей (картой)?

    @Mercury13
    Программист на «си с крестами» и не только
    В первую очередь авиасимуляторы. Знаю, что из-за масштабов Земли им приходится справляться с ошибками float: если Земля 12000 км в поперечнике, то единица младшего разряда — полметра.
    Первый X-COM имел круглую землю, но полёты были по локсодроме. А вот в опенсорсном ремейке 2014 года полёты уже по большому кругу.
    Симуляторы бога от Питера Молиньё: Populous, Black and White. Возможно, и другие симуляторы богов (например, Spore — не проверял).

    Я говорю именно про шарообразную карту, а не трубу/тор.

    А так — я бы предостерёг от шарообразной земли с открытым миром. 1) Расстояния (глобальная карта с ускоренным временем или без времени не в счёт). 2) Навигация по миру (в том числе понятие «север»), проекция миникарты. 3) В какой-то момент нужно закругляться и выкатывать игру, а в какой-то — выдавать аддон. Тяжеловато будет. Кстати, во всех упомянутых играх мир или замкнутый, или намного больше, чем одна планета.

    P.S. Моё определение открытого мира: игра, дающая относительную свободу действий и передвижения, но локальный участок, где орудует игрок/группа, намного меньше мира. В замкнутом мире, наоборот, участник орудует практически по всему «миру», оперативно переходя с одного участка на другой. Есть и пограничные случаи: огромное побережье нового Hitman — открытый мир или замкнутый?
    Ответ написан
    1 комментарий
  • Почему единицы измерения разрешений экранов мониторов и смартфонов разные?

    @Mercury13
    Программист на «си с крестами» и не только
    > но этот экран в 10 раз меньше экрана моего ноутбука
    Если только по площади — если пропорции сходные, 15" больше 5,5" в 7,43 раза.

    И там, и тут единицы измерения — пиксели. Просто у смартфонов больше пикселей на дюйм.
    Почему так?
    • Мы смотрим на экран с меньшего расстояния.
    • Смартфонные API молоды и потому изначально рассчитывались на переменный DPI. Имеющиеся программы не пострадают.
    • И в то же время из-за маленьких экранов и больших пальцев плотная компоновка элементов управления не требуется.
    • Видимо, HiDPI-экран настольных размеров без битых пикселей будет стоить запредельно дорого.

    Иногда размеры меряют в «диалоговых единицах» и «аппаратно-независимых пикселях» (например, 1px в вебе именно что «аппаратно-независимый пиксель», который может быть 2 или 3 реальных пикселя). Но это уже офтоп.
    Ответ написан
    1 комментарий
  • Как сделать управляемый интерфейс?

    @Mercury13
    Программист на «си с крестами» и не только
    Дело тут вот в чём. Оконные ОС работают в событийном режиме: пришло событие «перерисовать», мы рисуем; пришло событие «щёлкнуть», мы щёлкаем. Но поскольку учебным программам надо быть предельно простыми, куда удобнее написать: нарисовать, дождаться нажатия клавиши — как во времена ДОС, когда процессорное время было полностью вашим от точки входа до передачи управления, когда был прямой доступ к видеопамяти… Для консольных программ всю эту событийщину скрыли, хитрым образом на уровне ОС засинхронизировав два потока: один отвечает за поддержание консольного окна, второй — собственно программа. А Pascal.ABC пришлось наладить простейшую эмуляцию, сделав «видеопамять» в виде внутреннего буфера.

    Так что придётся подключать события — см. pascalabc.net/downloads/pabcnethelp/scr/PABCUnits/... — и смотреть, какие в этой абстракции найдутся дыры. А они, по Спольскому, будут обязательно: TCP — это протокол гарантированной доставки, но кто её гарантирует, когда физически выдернули провод…
    Ответ написан
    9 комментариев
  • Нужен ли бэк-энд для одиночной F2P игры?

    @Mercury13
    Программист на «си с крестами» и не только
    Рейтинг: рассчитывается по своим формулам и никак не стандартизируешь.
    Покупки: на это бэк-энд не нужен (хотя при желании можно тянуть информацию со своего сервера, а не с API покупок).
    Так что бэк-энд будет небольшой, ибо 99% работы на клиенте, но будет.
    Ответ написан
  • На чем сейчас разрабатывают игры?

    @Mercury13
    Программист на «си с крестами» и не только
    Почему скриптовой язык идет вместе с компилируемыми? Разные задачи?

    1. Для упрощения внутренней логики игры. Например, программа — монолитное однопоточное приложение, но каждый объект ведёт себя как сопрограмма. Качающийся маятник можно реализовать на состоянии и на сопрограммах…
    // На состоянии
    функ Маятник.ПровестиТакт
      ++кадр;
      если (кадр >= 10)
        кадр = 0;
      УстановитьКадр(кадр);
    
    // На сопрограммах
    функ Маятник.Жизнь
      для i=[0..10)
        УстановитьКадр(i)
        НовыйТакт


    2. Чтобы геймдизайнеры и прочие непрофессионалы (в программировании, естественно, непрофессионалы) могли свободно корректировать код.

    3. Часто игру пишут на готовом движке. А в нём зашит какой-то скриптовый язык — это самый простой способ написать игровой движок, законченный и пригодный к применению программистом куда меньшей квалификации. Так, например, устроен Wintermute Engine (старый, но хорошо известный движок для квестов).

    Читал, что плюса, C#, Lua.

    • C++: язык с давними традициями в геймдеве. Выразительный код (при достаточном профессионализме) плюс контроль за деструкторами — так что не будет просадок FPS из-за мусорщика. Выбор №1, если нужно написать игру с нуля.
    • C#: очень простой в изучении язык общего назначения. Библиотеки для Windows, скорее всего, у пользователя есть изначально. Куча движков, библиотек и фреймворков. Даром, что мусорный — если игра не слишком сложная, просадок FPS не будет (хотя последнего босса Hyper Light Drifter я долго громил — в критические моменты игра тормозила).
    • Lua: наиболее известный из скриптовых языков, которые можно реально присоединять к Си-программе, и скрипт будет вызывать функции, написанные на обычном компилируемом языке.
    Ответ написан
    Комментировать
  • Насколько необходимы навыки художника для геймдизайнера?

    @Mercury13
    Программист на «си с крестами» и не только
    Нужно постольку, поскольку художник понимает ваши почеркушки. В смысле, какие-то знания не помешают, но не самоцель.
    Куда важнее (из художественного)…
    • Понимание стилей искусства, как исторических, так и современных.
    • Умение быстро сфотошопить концепт-рисунок, который и будет техзаданием художнику.
    • Умение внятно, словами и зарисовками, объяснить художнику, где он не прав.
    • По портфолио понять, брать художника в проект или не стоит.
    • Умение собрать из «всякого хлама в интернете» временную картинку, чтобы понять, не будет ли лучше другая метафора или другой стиль.

    Бывают игры, вышедшие из графического стиля. Например: Myst, Syberia, Transistor. В них геймдизайнер — в первую очередь художник. Но важно и сделать надлежащий геймплей — то, чего, по-моему, не хватает Transistor.
    Ответ написан
    Комментировать
  • Как организована многопоточность в играх жанра RTS?

    @Mercury13
    Программист на «си с крестами» и не только
    Без параллельности можно обойтись — и поначалу лучше обойдитесь.
    Если уж хочется помногопоточить — я бы разбил на два потока, интеллект и графику.

    У каждого юнита три одинаковых структуры, renderInfo, bufferedInfo и aiInfo. Там могут быть координаты, курс, HP, ссылка на следующего — всё, что угодно. Ссылка на следующего — это важно, ведь юниты могут появляться и исчезать.

    Поток интеллекта работает с aiInfo; где-то здесь есть и код мультиплеера. Закончив шаг, поток захватывает мьютекс и для всех юнитов (понятие «все» определяется по aiInfo) даёт bufferedInfo = aiInfo.

    Поток прорисовки захватывает тот же мьютекс и для всех юнитов («все» по bufferedInfo) даёт renderInfo = bufferedInfo. А затем, освободив мьютекс, делает с этим renderInfo что хочет.
    Под мьютексом будем сидеть очень мало: ни прорисовки, ни мультиплеера там нет.

    Поскольку юнит исчезает из памяти только тогда, когда на него пропадают ссылки во всех трёх структурах, надо делать или объектный пул (т.е. юнит, будучи созданным, остаётся навсегда, но впоследствии этот блок памяти можно будет задействовать на другой юнит), или какие-нибудь «мусорные» указатели наподобие std::shared_ptr.

    Такая система аналогична тройной буферизации в прорисовке. Только в видяшной тройной буферизации асинхронно действуют кадры монитора и кадры рендерера; у нас — такты игры и кадры рендерера.

    Разбивать интеллект на потоки откровенно тяжело, и в игре с мультиплеером я просто не знаю, как. RTS обычно передают по сети короткие пакеты наподобие «выделить юнитов в квадрате (X,Y) — (X,Y)», «добавить к выделению юнита 1234» и «отправиться в точку (X, Y)». Эти команды, будучи выполненными на разных компьютерах, выполняются одинаково.

    Если объединить эти две конструкции — повторяемые расчёты и асинхронный рендеринг — возможны неточности, и, надеюсь, они будут приемлемыми — на экране (renderInfo) юнит попадает в отмеченный квадрат, а в aiInfo уже нет, посколько сдвинулся.

    Отдельный вопрос — компенсация пинга в играх по интернету. Не думал пока над этим. Наверно, придётся иметь officialAiInfo и lagCompensatedAiInfo…

    ЗЫ. Подсказывают, что такт игры в такой ситуёвине длится порядка 0,1 с, т.е. может продлиться несколько кадров. Тогда во всех этих …info надо описать движение так, чтобы в любой момент получить промежуточное x(t), y(t), yaw(t)… Для танка — bodyYaw(t) и turretYaw(t), для человечка — фазу анимации…

    ЗЗЫ. Если игра трёхмерная и вид хоть как-то настраивается, одна из команд игры будет выглядеть «выделить юнитов в квадрате (X,Y) — (X,Y) с матрицей преобразования A».

    ЗЗЗЫ. Чтобы игра была повторяемой (важно для мультиплеера), все расчёты, влияющие на течение игры, проводить в фиксированной запятой. Все переменные, с этим связанные, чем угодно, хоть нулём, но инициализировать.
    Ответ написан
    Комментировать
  • Движение персонажа под углом?

    @Mercury13
    Программист на «си с крестами» и не только
    Расскажу, как поступал я. Писал на Java ME, так что особо сложных идей не было.

    У каждой плитки была алгоритмически заданная поверхность (pavement). В более новых версиях движка приделали и потолок — впрочем, неважно.
    Для простоты введём несколько ограничений. Все они, разумеется, обходятся усложнённой логикой.
    • Ни на чём, кроме плиток, персонаж стоять не может. Тогда можно обойтись флагом: isSupported: true/false.
    • Поднимаясь по склону, персонаж не ударится головой в потолок. Кстати, у нас было два разных геймдиза и два разных подхода — когда один увидел этот баг, пошёл жаловаться, второй взял ограничение на карандаш и не делал таких уровней.
    • Неровности достаточно велики, чтобы можно было проверить ноги слева и ноги справа — и понятно, на какой высоте ему стоять.
    • Соотношение «площадь ног / размер хитбокса» достаточно велико, чтобы, пока персонаж падает, его гарантированно вытолкнуло из стены.
    В зависимости от скорости персонажа и крутизны склонов подберём epsilon — диапазон поиска поверхности.
    Предположим, наш персонаж стоит на чём-то и двигаем его вправо.
    1. Сдвинем его туда, где он должен быть.
    2. Попробуем подстроить высоту, взяв правую сторону ног и отыскав новую поверхность в диапазоне (y — epsilon, y + epsilon). То же самое с левой стороной ног. То, что выше — и есть наш новый y. Если не получилось — УПИРАЕТСЯ (см. шаг 3).
    3. Если на шаге 2 персонаж не упирается в стену, проверим на упор весь хитбокс. Если упирается — вытолкнем его из стены, снова подстроим высоту по принципу 2.
    Ответ написан
    2 комментария
  • Правильно ли я понимаю идею скролинга игрового мира?

    @Mercury13
    Программист на «си с крестами» и не только
    1. Проще изменять координаты объекту Camera или Viewport. И уже на ходу, при рендеринге, вычислять someObject.x - viewport.x.
    Ведь отображение объектов — малая часть игровой логики: объектам надо жить своей жизнью, взаимодействовать друг с другом и героем, заранее отсекаться, если они далеко от камеры.
    А если — вдруг — будут несколько окошек, например, для игры вдвоём, можно сделать несколько viewport’ов.

    2. Это лишь простейший алгоритм скроллинга (т.н. «привязанная камера»):
    viewport.x = clamp(player.x - viewport.width / 2, 0, world.width - viewport.width);

    clamp(x, y, z) = max(min(x, z), y); при y>z можно (но не обязательно) наладить своё хитрое поведение, взяв, например, (y+z) / 2.
    Если игра динамичная, а окно обзора невелико, можно усложнить поведение камеры.
    У меня есть на этот счёт прекрасный перевод.
    https://habrahabr.ru/post/272933/
    https://habrahabr.ru/post/273397/

    P.S. Платформеры писал профессионально, правда, не на холсте HTML 5, а на Java ME.
    Ответ написан
    2 комментария
  • Как решить проблему поворота вектора в сторону заданных координат?

    @Mercury13
    Программист на «си с крестами» и не только
    sin(x^y) = x×y / (|x|·|y|)       (1-й курс института, но легко выводится и самостоятельно)
    cos(x^y) = (xy) / (|x|·|y|)      (школа)

    x, y — векторы в 2D
    x^y — угол между векторами
    x×y — косое произведение векторов
    (xy) — скалярное произведение векторов
    |x| — евклидова длина (модуль, 2-норма) вектора

    Отсюда x^y = atan2( (xy), x×y ).
    Возможно, где-то упустил знак и у меня получился не x^y, а y^x.
    Ответ написан
  • Как создать эффект камеры?

    @Mercury13
    Программист на «си с крестами» и не только
    У меня по этому поводу целый перевод есть.
    Первая часть: https://habrahabr.ru/post/272933/
    Вторая часть: https://habrahabr.ru/post/273397/

    Именно по поводу того, как камерой вести персонажа.
    Ответ написан
    1 комментарий
  • Как реализовать игру с графикой "Псевдо-3D"?

    @Mercury13
    Программист на «си с крестами» и не только
    Здесь и в Doom совершенно разные технологии.

    Здесь честное 3D, но со спрайтами. Гугли «billboard opengl» (или подставь ту 3D-библиотеку, которой пользуешься).

    В эпоху Doom трёхмерные чипы были недоступны, всё программно, силами процессора. Потому трёхмерщина была несколько ограниченная: полы всегда горизонтальны, стены вертикальны, использовалась двухточечная проекция, стены рисовались по столбцам, а полы — по строкам. Чтобы взглянуть вверх-вниз, эта проекция прокручивалась вверх-вниз. Сейчас так писать стоит только для машин, где нет трёхмерных чипов (какие-нибудь калькуляторы и читалки).
    Ответ написан
    Комментировать
  • Для чего в игры вводят свитки идентификации (Diablo2, PoE)?

    @Mercury13
    Программист на «си с крестами» и не только
    Наверно, ещё с жанра roguelike, где неопознанная шмотка была реально страшной: встречаются вредные; надел такую — прилипла, носи, пока не расколдуешь.
    По слухам, первый Diablo даже пошаговым был, как обычный «рогалик». Потом переделали.
    Ответ написан
    Комментировать
  • Как рассчитать глубину полигона (метод z-буфера)?

    @Mercury13
    Программист на «си с крестами» и не только
    Это уже не z-буферизация, а алгоритм художника. Возьмите, например, z-координату центра.
    Ответ написан
    Комментировать