Страницы

среда, 21 июня 2017 г.

Маленькие хитрости в разработке игр

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

1. «Прокрутка» по диапазону значений

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

Как это можно было сделать и как это делают чаще всего:
public int selectedWeapon;
public const int MAX_WEAPONS = 8;

public void Increment() {
 selectedWeapon++;
 if (selectedWeapon >= MAX_WEAPONS) {
  selectedWeapon = 0;
 }
}

public void Decrement() {
 selectedWeapon--;
 if (selectedWeapon < 0) {
  selectedWeapon = MAX_WEAPONS;
 }
}
Намного элегантнее это можно сделать с помощью операции вычисления остатка от деления:
public int selectedWeapon;
public const int MAX_WEAPONS = 8;

public void Increment() {
 selectedWeapon = (selectedWeapon + 1) % MAX_WEAPONS;
}

public void Decrement() {
 selectedWeapon =  (MAX_WEAPONS + selectedWeapon - 1) % MAX_WEAPONS;
}
Что же здесь произошло?! Дело в том, что мы всякий раз получаем лишь остаток от деления на на наше максимальное значение, в данном случае 8. Таким образом, при достижении максимального значения, остаток будет  равен 0.
С вычитанием операция несколько сложнее, так как мы уходим в отрицательную сторону:
(-1) % 8 = -1
Это решается простым добавлением максимального значения, так что остаток берется от:
8 + 0 - 1 = 7
7 % 8 = 7

2. Случайное значение из диапазона, за исключением заданного

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

Предположим, что персонажи хранятся в массиве. Тогда выбор случайного будет выглядеть так:
GameCharacter[] characters = new GameCharacter[16];
Random r = new Random();

public GameCharacter GetRandomCharacter(int playerCharIndex) {
 int enemyIndex = (playerCharIndex + 1 + r.Next(characters.Length - 1)) % characters.Length;
 return characters[enemyIndex];
}
Таким образом, мы совершаем выборку случайного значения, из диапазона всех значений после выбранного и до следующего его появления.
Для примера приведенного выше, если вызывать метод с параметром 5, модуль по числу 16 будет браться из диапазона значений (6; 20).

3. Быстрые вычисления

Несмотря на то, что вычислительные мощности компьютеров и даже мобильных устройств сегодня позволяют не задумываться о повышении производительности на низком уровне (хотя все еще остаются области, где нужно экономить самые крохи процессорного времени и памяти, например при написании шейдеров), следует быть знакомым с простейшими приемами, связанными с принципами работы процессора, к примеру:
  1. Деление с помощью умножения. Операция деления значительно затратнее, нежели умножение. Будет полезно взять в привычку, вместо деления, к примеру на 10, умножать на 0.1. По скромному мнению автора, это как минимум повышает читабельность сложных формул в коде.
  2. Так же деление/умножение на степени двойки можно совершать с помощью операции битового смещения:
    37 << 4 = 37 * 2 * 2 * 2 * 2 = 592
    136 >> 3 = (((136 / 2) / 2) /2 = 17
    Двоичные операции значительно быстрее десятичных!
  3. Операция побитового умножения «И» (&) может быть использована для проверки числа, является ли оно степенью двойки:
    (x & (x - 1)) == 0
Конечно, хороший компилятор может проанализировать и заменить многие вычисления на их быстрые аналоги, поэтому всегда нужно знать меру и не портить читабельность кода, в угоду сомнительной предварительной оптимизации.

4. Кодирование нескольких значений в одно поле

Этот прием крайне полезен для экономии памяти и упрощении обозначения каких либо связанных значений, например координат.
Предположим, что у нас есть карта, хранящаяся в виде двумерной сетки из множества ячеек. Тогда каждая ячейка будет иметь X и Y координату в этой сетке.
Таким образом, карту можно хранить в виде двухмерного массива, а обращаться к каждому элементу по двум индексам. Иногда такой подход оправдан, но с ростом сложности проекта, два значения начинают создавать проблемы. А если нужно хранить больше значений? (в одном проекте мне нужно было использовать адрес «между двумя клетками» – 4 значения)

Трюк заключается в записи двух чисел подряд в одно, с помощью операции битового смещения.
если нашему приложению хватает 65536 возможных вариантов для каждого значения, то два значения можно записать в переменную типа Integer.
Integer – хранит в себе 4 байта информации или 32 бита. В двоичной системе это вот столько:
1111 1111 1111 1111 1111 1111 1111 1111
65536 – это собственно половина Integer – 2 байта.

Для того чтобы сохранить числа X:43 (0010 1011) и Y:6435 (0001 1001 0010 0011), необходимо совершить следующие действия:
  1. Сместить 43 на 16 бит влево: получаем 0000 0000 0010 1011 0000 0000 0000 0000 или 2818048 в десятичной системе
  2. Добавить к полученному числу вторую координату: 2818048 + 6435 = 2824483. Однако, так как мы добавляем предыдущее значение в разряды заполненные нулями, можно воспользоваться операцией побитового «ИЛИ» ( | )
  3. Готово! Мы получили число 0000 0000 0010 1011 0001 1001 0010 0011. Можете проверить этот процесс в калькуляторе в режиме «Программист».
Для того чтобы раскодировать полученное значение, необходимо совершить следующие действия:
  1. Сместить код на 16 бит вправо. Так младшие два байта исчезнут, останутся только старшие два, которые будут соответствовать значению X:43
  2. Значение Y можно найти двумя способами:
    1. Самый примитивный, приходящий в голову людям, незнакомым с двоичной арифметикой – сместить значение X обратно, на 16 бит влево и вычесть полученное число из кода. Этот вариант дает возможность вычислить лишь последнюю часть кода.
    2. Более быстрым и "правильным" будет использование маски – побитового умножения «И» (&).
      Если совершить это действие над кодом с аргументом в 1111 1111 1111 1111 (65535), то мы получим именно последние два байта, соответствующие координате Y: 2824483 & 65535 = 6435.
      Для упрощения подобной операции рекомендуется использовать шестнадцатеричную запись числа 65535 – 0xFFFF. Это легко запомнить, ибо один байт заполненный единицами будет записан как 0xFF, три байта – 0xFFFFFF и т.д.

При всем при этом, существует проблема связанная с тем, что Integer – тип знаковый. И старший бит предназначен для хранения знака (0 – число положительное, 1 – отрицательное). В связи с этим, при декодировании происходит ошибка. Исправить этот эффект можно вычитая при кодировании из первой части кода половину максимально возможного кодируемого значения (для приведенного выше примера – это 65536 / 2 = 32768), а при декодировании, прибавления его к результату.

Пример кода:
public int Code (int x, int y) {
 return ((x - 32768) << 16) | y;
}

public int DecodeX (int code) {
 return (code >> 16) + 32768;
}

public int DecodeY (int code) {
 return code & 0xFFFF;
}
Следует учитывать, что полученный код может иметь отрицательное значение и потому не может быть использован, как индекс в массиве. Возможные решения:
  1. Хранение данных в коллекции ключ-значение
  2. Отказ от использования значений, приводящих к отрицательному коду (сокращение возможных значений вдвое)
  3. Использование типов данных бОльшего размера, для хранения кода, например long

вторник, 8 апреля 2014 г.

GameDev в Университете ч.2

Еще две недели занятий по дисциплине «Разработка Игр»!

В отличие от первой недели, последние четыре занятия были посвящены разработке видео-игр в разных ипостасях:

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

GameMaker. Fish Game. Изучаем основы логики игр.

пятница, 21 марта 2014 г.

GameDev в Университете ч.1

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

За неделю, проведено два вводных занятия, одно из которых полностью посвящено теоретической части, второе - практической.

Несмотря на то, что тема разработки игр очевидно интересна многим, я не ожидал, что выбранная аудитория будет настолько сильно страдать от нехватки мест. Оба занятия посетило около полусотни человек. За три дня, на соответствующую группу в ВКонтакте подписалось более 80 человек.

Твит от одного из слушателей, зашедшего "на огонек", во второй день

суббота, 14 декабря 2013 г.

Семинар Unity3D

В первые за полгода прочел семинар, на котором рассказал о нововведениях в Unity 4.3, в частности о появившемся нативном 2D.
В ходе почти пятичасового экспромта, была разработана крайне неадекватная игра. Изначально, задались целью разработать игру про «злых птичек» в классическом понимании этого жанра, однако вышло чуть иначе..

Ссылки (играть обязательно со звуком!) :
Играть онлайн (web-player): http://mrimsh.ucoz.ru/some2d/index.html
Скачать игру (win): http://yadi.sk/d/Xv2bPYp4EKRod
Скачать игру (mac): http://yadi.sk/d/5UHqTSstEKRee
Скачать игру (linux): http://yadi.sk/d/zNf9fpZHEKawn
Скачать проект: http://yadi.sk/d/gjEtk6PZEKRmP

Фото:


понедельник, 4 ноября 2013 г.

Инди – больше чем просто мода

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

Дело даже не в том, что гиганты типа Gameloft (что еще со времен J2ME именуют «клонолофтом») или Mobage (насколько мне известно, это всего лишь издатель, но ситуация похожая) чаще всего выпускают однообразные игры. Нет, бывают и очень даже оригинальные игры, разнящиеся геймплеем..однако, не в последнюю очередь благодаря вопросу монетизации, совершенно разные игры становятся заложниками клише крохотных элементов геймплея.
Столько игр, но в каждой есть что-то, что я уже видел где-то..ах да, среди ваших же игр, Gameloft!
Перелистывая страницы на AppStore или Steam, я зачастую оставляю без внимания игры большинства крупных издательств/разработчиков. Максимум – посмотрю скриншоты, почитаю описание и комментарии, чтобы в лишний раз убедиться, что в итоге столкнусь с чем то похожим и приевшимся. Немудрено, что я так часто нахожу утешение в очень старых играх.
О, старые игрушечки! Столько необычного... Сейчас, в большом геймдеве так не принято.
Вот тут-то инди-разработчики и проявляют себя во всей красе! Известное дело – в последнее время термин Indie стал мейнстримом, частенько несущим негативный окрас, из-за разработчиков, что твердят о своей независимости, но предлагают все тот же геймплей наполненный теми же ключами, с которыми мы знакомы уже много лет. Но среди толп этих волков в овечьей шкуре, нередко попадаются и истинные бриллианты, способные удивить чем-то необычным: симулятор енота, игра про кошмарный сон, где ты мерзнешь и голодаешь или игра про игроков в настолку?! Они смогли удивить меня! А это что-то да значит.
Shelter
Don't starve
Knights Of Pen And Paper

среда, 25 сентября 2013 г.

Age Of Glory. Dungeon step

Прошло несколько дней и проект стал обретать некоторые формы.

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

На данный момент есть код генерирующий уровни. Каждый уровень (пещера) это: случайное количество комнат, случайного размера, в случайных местах случайно соединенные случайными коридорами. Хотя все стены сейчас однообразны, не очень сложно будет улучшить код, чтобы генератор не менее процедурно декорировал все стены и создавал комнаты с различными тематиками (банная/бойня/бойлерная и т.п. и все в одном подземелье!).

Генератор, кстати, написан на основе замечательного туториала: http://gamedev.tutsplus.com/tutorials/game-design/create-a-procedurally-generated-dungeon-cave-system/

Работающий код и все ресурсы можно скачать https://github.com/MindEdg3/AgeOfGlory

 

воскресенье, 22 сентября 2013 г.

AgeOfGlory. init commit.

Решил я научиться моделить недавно. Maya, студенческая лицензия, все дела.. Осваиваю уроки один за другим.

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

В связи с этим я ставлю себе цель: Модели для игры! В свое свободное время, с крайней низким приоритетом, я буду делать классическую пещерную RPG, что уведена в героическую тематику, чуть ли не до пародийной степени. И название соответствующее: Age of Glory.

А вот и скетч, что задаст мне направление:
Скетч Age of Glory нарисованный за несколько минут.
Да я пока не умею рисовать, но у меня еще есть время)
Здесь видно Героя управляемого игрока, с видом от первого лица. Он вооружен простенькой секирой и сражается с Жабо-человеком, которого встретил на развилке в запутанной пещере.
Над Жабо-человеком висит окошко с минимумом информации. Больше можно получить нажав на кнопочку со знаком вопроса. Ниже кнопки-стрелочки для переключения текущей цели. В данном случае они не работают.
Справа панель игры с кнопкой выхода в меню, картой покрытой туманом войны в местах, где игрок еще не побывал, линейки его текущих/максимальных здоровья и маны, кнопки для открытия окон Персонажа, Инвентаря, Магии и Журнала. Так же на панели можно увидеть лог последних действий в игре:

  • Жабо-человек атаковал игрока на 7 ед
  • Настал ход игрока
Таким образом игра является пошаговой, что традиционно для подобных игр.

Говорят начать что-то делать – значит сделать это наполовину. Ну вот, игра наполовину готова! Осталось еще немного.

Скрывать мне нечего, буду все держать в открытом виде на GitHub: https://github.com/MindEdg3/AgeOfGlory