Программирование игр всегда было одним из моих интересов с тех пор, как я написал простую игру Pong! еще в колледже с помощью Processing. Но моя первая работа оказалась в веб-разработке, и с тех пор это все, чем я занимаюсь. Проникнуть в веб-разработку легче, чем в разработку игр (у меня нет формального образования в области компьютерных наук), и, следовательно, после двух лет работы в качестве члена общества я m все еще находится в веб-разработке.
Но мой интерес к разработке игр никогда не пропадал, и недавно я взялся за C++ и библиотеку SFML, чтобы посмотреть, чему я могу научиться и что я могу сделать.
Разобравшись, как настроить SFML for XCode, я вник в суть и начал программировать. Это были адские 10 дней, и вот некоторые вещи, которые, я думаю, стоит отметить, потому что они доставили мне неприятности. И всегда стоит помнить о вещах, которые доставляют вам неприятности.
Мои цели с моей первой программой были просты. Создайте спрайт, который перемещается по экрану в зависимости от действий пользователя (влево, вправо, вверх, вниз). Если пользователь продолжает удерживать кнопку, спрайт ускоряется. Если пользователь отпускает кнопку, спрайт продолжает двигаться с той скоростью, которой он достиг при ускорении. Существует также постоянная сила сопротивления, действующая в направлении, противоположном движению спрайта, которое в конечном итоге остановит его, когда на него больше не воздействуют.
Достаточно просто.
Заголовки и защита заголовка?
Одной из первых вещей, которые мне пришлось решить, было то, как структурировать файлы заголовков. Заголовочные файлы и вся концепция предварительных объявлений не существуют в Java и Javascript (два языка, которые я использовал в прошлом), поэтому работа с заголовочными файлами была для меня совершенно новым опытом, особенно когда дело касалось организации проекта. . Здесь я столкнулся с бинарным выбором (если есть еще способы сделать это, кто-нибудь дайте мне знать):
Сохраню ли я их вместе с исходным файлом, чтобы каждая пара сохранялась как единое целое? Или мне хранить все заголовки в одном месте?
Code::Blocks организует свои проекты, разделяя свои файлы на деревья source
и header
, а сама библиотека SFML имеет заголовки, хранящиеся в одной папке в иерархической структуре. Хотя кодирование в современных IDE стало проще, чем когда-либо (вы перемещаете файл, и IDE обновляет все сотни ссылок за вас!), я по-прежнему хотел, чтобы все мои заголовки находились в одном месте и на них можно было ссылаться из любой точки мира. одно корневое расположение. Поэтому я поместил их все в каталог header
в корне моего проекта.
Еще одна вещь, которая меня сбила с толку, это охранники головы. Я понял, что это такое, и я понял, как их использовать, но я позволил IDE сделать работу по созданию охранников при создании новых файлов заголовков (охранники были названы в соответствии с путем к файлу внутри папки src
; пример: MOVE_COMPONENT_GRAPHIC_H
), в результате чего после того, как я переместил и переименовал некоторые файлы, защита заголовков не обновлялась (среда IDE не такая умная), так что в итоге я получил два файла заголовков с одной и той же защитой.
И что произошло, когда я включил оба в свой
main.cpp
?
Второй не импортировался, и я не мог понять, почему мою структуру нельзя было использовать в текущей области видимости. Мне потребовался добрый час, чтобы понять, что ни с IDE, ни с компилятором все в порядке. Я просто не принял во внимание защиту заголовка.
Я по-прежнему позволяю IDE генерировать для меня защиту (нет смысла в среде IDE, если она не выполняет тяжелую работу), но я всегда дважды проверяю, чтобы убедиться, что никакие другие файлы не имеют такой же защиты. Быстрый текстовый поиск по всему проекту помогает.
нан, нан, нан, нан, нан…
Сначала я не знал, что в SFML есть свои собственные классы Vector (я не использовал RTFM, просто перешел к коду; дети, не делайте этого. Всегда RTFM!), и поэтому пошел дальше и написал один из моих собственных. Класс 2D-векторов, в котором есть элементы x и y, а также обычные методы для векторных вычислений, включая метод нахождения величины вектора и метод его нормализации. Теперь вот как вы нормализуете вектор. Как видите, если величина равна нулю, вас ждут некоторые проблемы.
Теперь в Java деление на ноль выбрасывается как Exception. В NodeJS деление на ноль дает Бесконечность, но для C++ деление на ноль дает нан соответствующего типа (да, нан для двойного числа — это не тот же тип, что и нан для числа с плавающей запятой, точно так же, как 0 и 0.0 — разные типы). Я не знал, что это произойдет, и я забыл учесть векторы нулевой величины.
Итак, что случилось?
Ничего такого. Буквально ничего не произошло. Как и в случае, никакие вычисления не будут работать, потому что я вычисляю с помощью nan повсюду, а nan, вычисляемый с любым числом, дает вам nan. Мне потребовалось несколько распечаток значений, чтобы понять это, хотя задним числом это должно было быть смехотворно очевидным.
Перетащите
Еще одна идиотская вещь, которую я сделал, заключалась в написании функции перетаскивания. Вот первая версия этого.
void scionofbytes::apply_drag(scionofbytes::Mover& mover) { scionofbytes::Vec2D drag = mover.velocity.scale(-1); opposite_velocity.normalize(); opposite_velocity.scale(scionofbytes::DRAG_COEFFICIENT); mover.velocity.subtract(drag); };
Кто-нибудь видит, что я сделал неправильно здесь? Кто-нибудь?
Я подожду.
…
…
…
Хорошо, если вы еще не поняли, я вычел вектор сопротивления, когда должен был добавить его.
Видите ли, я уже вычислил силу сопротивления как вектор в направлении, противоположном вектору скорости. И, вычитая этот вектор вместо того, чтобы добавлять его к скорости, я просто увеличивал скорость.
Это похоже на вычитание отрицательного числа: на самом деле вы добавляете значение хода. Видеть?
Во всяком случае, это были лишь некоторые из вещей, с которыми я боролся в своих ранних исследованиях C++ и SFML. И все они даже не были связаны с программированием. Буду выкладывать свои приключения по мере их прохождения.
Ваше здоровье!