Я тебя очень уважаю за твои труды, но лучше было начать давать шарп применительно к юнити, лично моё мнение
СЕРЬЕЗНО ? Вы решили объяснять людям как писать игры на С++ ? И "вскользь" пройтись по ООП? Вы же сами прекрасно понимаете, что "основ" программирования не уместить в двух-четерёх листах А4....
Боюсь, те кто в теме и так уж как-нибудь поймут, как им игру написать. А те кто нет - тем проще визуальные конструкторы юзать.
Без обид.
А во-вторых, я - злостный юнити-хейтер :)))
У меня вопрос. Можно ли каким то образом сделать массив из функций? Просто перед каждым уровнем надо сделать описание, а оно же разным будет и чтобы не пользоваться switch, можно было бы просто вызывать функции в зависимости от номера уровня. Я где-то читал, что можно сделать указатель на функцию, но больше этого ничего не помню. Подскажи, пожалуйста, как тут удобнее сделать.
void Function1()
{
}
void Function2()
{
}
typedef void (*FunctionRef)();
int main()
{
FunctionRef pointer1 = &Function1;
FunctionRef pointer2 = &Function2;
std::vector< FunctionRef > pointers;
pointers.push_back( pointer1 );
pointers.push_back( pointer2 );
(*pointers[0])();
return 0;
}
Все, проблему окончательно решил и у меня возник к тебе вопрос. Что должно происходить с противником, когда его убили. У меня есть метод в классе, который этим занимается. Так вот он обнуляет ХП и отрисовывает на месте противника пробел, и выставляет координаты противнику куда подальше (у меня эти координаты находятся за полем игры, куда никто не заходит). Это нормально, что я так делаю или это как-то можно более грамотно реализовать?
Так курсовой проект задали реализовать игру именно в консоли, причем применяя MVC в обязательном порядке. Надеялся на Ваши уроки, думал, что поболее вникну в указатели и объекты - и справлюсь, но нет... Причем само описание паттерна вполне понятно, а вот на реализации встрял, не то что с нуля, а даже просто переделать мозгов не хватает.
Нет, уроки то я читаю. Теория нужна. Но вот как реализовать саму игру пытаюсь сам. Потому что иначе это будет просто бездумное списывание.
По поводу перемещения. Как оно происходит у меня прямо по шагам:
1. Высчитываю новую позицию противника (float).
2. Проверяю перешел ли он в новую клетку (if ((int)NewPosition_X == Position_X))
3. Если да, то просто ему прописываю новую координату.
4. Если нет, то тут идет несколько действий:
5. Отрисовываю пробел на старой позиции противника
6. В схему уровня (которая двухмерный массив) ставлю пробел в клетку старой позиции
7. Прописываю новую координату (точно также как в п.3)
8. На новой позиции отрисовываю противника.
9. В схему уровня ставлю значок противника в клетку новой позиции
Так вот у меня вопрос. Так ли я все делаю. Оно работает, но тот баг не дает мне покоя. Если они стоят, то он не возникает, но как они двигаются, то иногда проскакивает эта временная "неуязвимость".
Можно тебя совета попросить, а то у меня мелкий баг появился, а код я пишу сам, стараясь в твой не смотреть. Можешь словами описать то, как у тебя перемещаются противники вбок. Ну сам процесс по шагам. Вот у меня просто есть специальный класс для противников. Я создал массив из объектов этого класса. Так вот, чтобы каждый противник двинулся, я создал функцию, которая циклом перебирает каждый объект и двигает его (с помощью метода класса) вбок на определенное расстояние. Движение происходит так: если он двигался в своей клетке - просто меняем координату. Если он двинулся в другую клетку, то мы ставим пробел на то место где он был, меняем координату, отрисовываем его в новой координате.
В принципе, все работает, я даже сделал нормальную стрельбу и противники уничтожаются, но есть мелкий баг. Иногда при стрельбе попадаются противники, которым насрать на снаряды, которые попадают в них. Они их как будто проглатывают их. Это происходит буквально долю секунды и как только этот противник перемещается (вбок или вниз), то эта "неуязвимость" пропадает. Мелочь, но довольно досадная. И поймать ее очень сложно.
Я думаю это связано с кривым перемещением.
Во-первых отрисовка в клетки не должна влиять на позицию объектов, просто должно происходить отрезание дробной части координат для получения row и column :)
Во-вторых лучше посмотреть как это реализовано у меня, ибо какой смысл тогда в уроках? :)
Привет, извини за долгое отсутствие, пару месяцев занят был и что-то забыл про эти уроки. Теперь пора вернутся и делать игры. Так вот, собественно, у меня возник вопрос по поводу реализации классов в этой игре.
Ты сделал класс Game для реализации всей игры, но есть ли в этом смысл, если объект этого класса будет всего один? Не проще ли разбить игру на несколько более мелких классов, чтобы уже на каждый класс было больше одного объекта. Например, класс для уровня, класс для врага и т.п.
Смысл в правильной архитектуре. :) Классов должно быть столько, сколько существует сложных отдельных модульных задач :) Ну и я приверженец древовидной архитектуры с главным корневым классом-объектом( Как Game в данном случае ). Остальные объекты должны находится внутри Game, а в них уже могут быть свои объекты, ну и так далее :)
В этом уроке происходит только ознакомление с классами, поэтому тут используется такая упрощенная архитектура из нескольких классов :)
Сделал большую часть.
Осталось сделать второго игрока, чтобы пришельцы делали что-нибудь интересное(например, при смерти) и бонусы. Но в игре и без того уже немало наворочено :)
Из особенностей:
* Стрелять стрелочкой вверх. Пробел не стал, т.к. когда добавится второй игрок, будет не круто.
* Код инвейдера отделен от общих файлов. Можно сказать что получилось подобие движка. Надо будет почитать как в плюсах либы создаются и вынести отдельно, а потом переписать марио и подъемелья. :)
* Экраны. Отображаются либо пока не истечет таймер, либо по нажатию на кнопку. Соответственно есть экран лого/загрузки, экран перехода на новый уровень и окончания игры.
* Пришельцы:
- ярко зеленый: два попадания (потом сделаю, чтобы при смерти создавал других)
- фиолетовый: 3 попадания (потом добавлю, чтобы по кд появлялись новые пришельцы рядом с ней)
- красный: 5 попаданий. Просто танк.
* Через определенный промежуток времени выбирается случайный пришелец и стреляет.
Сорцы тут:
https://github.com/deadly-cat/cpp-space-invaders-console
Экзешник тут:
https://drive.google.com/file/d/0B-5kzMyUwusILUx4OWlqbW5oWDg...
Для ленивых на время теста есть кнопка N. Начинает работать через 2 секунды после старта уровня и сразу запускает следующий уровень.
Ну вообще в планах еще две части :)
Девятая - про инкапсуляцию и полиморфизм
Десятая - про подключение графического движка
Как будет время - займусь :)
Очень вдохновился этим уроком и решил сделать более менее полноценную игру. И вот наконец доделал, прошу оценить)
Папка с игрой - https://yadi.sk/d/rJzd5loiooEVzQ , в ней экзешник и мануал по игре, советую сначала прочитать его, там довольно много полезного.
Давно меня так не удивляли... Пожалуй, что лучшая работа по этому уроку за всё время :)
Жду теперь Часть 9 ;)
Ну вот смотри, если вернуться к прошлому конкретному примеру, где ты сказал, что эта переменная должна хранится в классе Game. Но ведь эта переменная должна использоваться в более мелких классах. Например, в классе нашего персонажа, где и создаются выстрелы. То есть в файл, где описан класс нашего персонажа, нужно подключить файл, где описан класс Game, чтобы использовать эту переменную. Так?
Во все дочерние классы ты спокойно можешь передавать указатель на Game и использовать его там для обратной связи
Все, понял, спасибо. Завтра буду переписывать код так, чтобы у меня был один большой класс Game. И у меня опять вопрос)
У меня есть большой класс Game. Соответственно он должен содержать всякие другие классы помельче. Точнее объекты этих классов. Это значит, что нужно в отдельных файлах, эти классы расписать и подключить эти файлы потом к файлу, где мы описываем класс Game. Но проблема в том, чтобы описать эти мелкие классы, понадобятся переменные из большого класса. Что делать в таком случае?
Зачем? Классы нужно разбивать так, чтобы они по-максимуму были самодостаточными и независимыми. Если что-то нужно для настройки, то это можно передать в конструктор при создании.
Вот смотри. Конкретный пример. Вот есть массив указателей на объект, в частности на выстрелы. И вот наш персонаж стреляет. Значит должен создаваться объект и его адрес должен передаваться в этот массив, а точнее в элемент массив с каким-то номером. Вот как у меня определяется этот номер. Существует глобальная переменная, которая по началу равна нулю. Персонаж делает первый выстрел и в нулевом элементе массива теперь хранится адрес о первом выстреле. После этого эта переменная увеличивается на один, чтобы следующий выстрел хранился уже в первом элементе массива.
Так вот эта переменная, по сути, является глобальной. И как я понял, у тебя в программе такие переменные являются членами класса Game, да?
Ох, это сложно. Но есть очень похожая ошибка, и если с ней разобраться то и та не будет проблемой. Касается она заголовочного файла со всякими константами и прочими переменными, которые используются по всей программе. Если это константа, то проблем нет. Но если это переменная, то как раз вылезает ошибка о множественном определении, потому что этот заголовочный файл встроен почти во все другие файлы программы.
Просто если я ставлю static на таких переменных (в прошлой игре так и делал и это работало), то в этой это вызывает обнуление этих переменных когда не надо.
Как вообще должны быть организованны эти переменные и константы? Где их хранить и как использовать?
Константы - можно глобально, можно в классах, по необходимости :)
Переменные - желательно внутри классов, глобальные - только по острой необходимости или для отладки
Проблема в том, что даже первый у меня не работает, потому что лежит в заголовочном файле рядом с определением класса, и поэтому выдает ошибку, что массив несколько раз определяется.
Нет, я не про это. Просто проблему с массивом указателей по другому не решить? Ну, не создавай большой класс Game.
Ну я про то, что он не в воздухе должен висеть типа
static GameObject* s_globalObjects[256];
А аккуратно лежат внутри какого-нибудь класса:
GameObject* m_objects[256];
P.S. Работать то будут оба варианта, но второй - правильный с точки зрения архитектуры.
То есть, если у меня нет класса, который описывает всю игру, то по другому мне уже не сделать?
Вот еще один вопрос возник по той же тематике. Пилю я игру уже по следующему уроку. Там у меня есть заголвочный файл для снарядов, которые выстреливают игрок и мобы. В нем описан класса снаряда. Его методы описаны в файле исходного кода. Все гладко.
Но где создавать массив указателей, где будут хранится указатели на объекты этого класса?
Если создать в файле исходного кода, то пользоваться этим массивом в других файлах будет нельзя. Если создать в заголовочном, то будет выдавать ошибку из-за многократного определения.
Мне помогло только то, что я определил этот массив как static. Но правильно ли это?
Я закончил. Подошел ответственно. Сделал 10 уровней. 10 различных противников, 5 бонусов. Полировал от багов пару дней. Вроде таких явных уже не осталось. Посмотри, пожалуйста.
https://wdfiles.ru/901q
Огромный плюс за старания ))) К сожалению уровни довольно долгие, поэтому пока на работе особо поиграть не могу :) Выглядит круто ))
А, все понял, спасибо. А почему мы обязательно создаем ссылки на функцию. Можно ли сделать указатель на функцию и создать просто массив таких указателей вместо контейнера?
Это и есть указатель :)
Просто "FunctionRef pointer" писать приятнее, чем "void (*pointer)()";
Я почти понял. Это контейнер из STL. Мы создаем ссылки на функции и закидываем их в контейнер. Но я не понял вот эту строчку:
typedef void (*FunctionRef)();
И соответственно, что за тип FunctionRef. И смысл последней строчки:
(*pointers[0])();
Что мы с помощью этого делаем?
typedef void (*FunctionRef)(); - объявлем свой тип данных FunctionRef, который является указателем на функцию возвращающую void и не принимающую аргументов.
Все, разобрался. То есть мы в начале создаем массив указателей. Потом туда закидываем динамически выделенный указатель на объект. И дальше на протяжении программы работаем с объектом только через указатель, так?
А вот как бы данную прогу выполнить в стиле Model-View-Controller?
Не будет времени хоть приблизительно наметить где какие классы и методы будут располагаться при применении данного паттерна?
Вообще, надо подобие танчиков в MVC, но следующая прога слишком заморочена, так как после еще и придется разные алгоритмы и диаграммы по проге составлять.
Думаю, если уж с этой разберусь, то что-то немногим сложнее как по маслу пойдет.
Эээм... Ну это ИМХО уже слишком - наворачивать MVC для примитивного консольного приложения :) Оверинженеринг какой-то :)
Мотивация не та, но мысль правильная.То, что у класса лишь один экземпляр - это не плохо само по себе. Есть даже такой архитекрутный паттерн, как "Singleton". (Готов поспорить с желающими назвать его антипаттерном).
Модуляризацией приложения можно улучшить абстракцию элементов программы, что упростит поддержку (модернизацию) и позволит проводить их независимое тестирование.
Но чтобы понять, какие проблемы такой подход решает, их нужно встретить самостоятельно.
Ну и монолит пишется компактнее и лучше читается. Новичкам это важнее.
Я как Singleton использую в 99% случаев только главный корневой класс-объект :) И то, только с возвратом экземпляра без автоматического создания :) Это отличный паттерн при правильном использовании :)
Спрошу на всякий) Как дела на работе?) А то я все жду возможности запилить своих героев с блекджеком)
А мне твои посты очень нравятся! Шикарные оформление и изложение! Если еще дополнительно почитывать какую-нибудь книженцию по си, то посты вообще на "ура" заходят)
Молодец автор, пили дальше, ждем)
Да в игрострое полно важных частей ))) Я то пытаюсь только основы подать :) Может будет время - после этого курса еще какие-нибудь статьи выпущу :))
Ну вообще в планах еще две части :)
Девятая - про инкапсуляцию и полиморфизм
Десятая - про подключение графического движка
Как будет время - займусь :)
Я решил заморочиться и написал движок на основе твоих уроков. И назвал его Consolity :D
Исходники здесь: https://github.com/deadly-cat/Consolity
Так же создал на его основе игру Galaxy Warrior
Исходники здесь: https://github.com/deadly-cat/cpp-galaxy-warrior
Exe тут: https://drive.google.com/file/d/0B-5kzMyUwusIaGhKd0Y2eTRrSGc...
Управление:
Игрок 1: стрелки - движение, 0 на нампаде стрелять.
Игрок 2: wasd - движение, space стрелять.
Скрины:
У меня вопрос по архитектуре есть.
Как реализовывать логику объектов и их взаимодействие с миром?
Вот например сейчас у меня в Game большой метод апдейт с кучей кейсов в свиче. Логичнее кажется вынести эту логику в конкретные классы объектов. Но при этом в них помимо дельты придется передавать еще и объект мира, что выглядит не слишком красиво. Хотя как по мне здоровенный свич выглядит менее красивым.
Тот же вопрос касается и взаимодействия пользователя с объектом. Где лучше хранить обработку действий пользователя (нажатия кнопок, движения мыши и т.д)?
Хотя бы вкратце проясни этот момент, пожалуйста.
Конечно логичнее :) Для этого используется наследование :)
От GameObject наследуемся классами Ship, Bullet и Alien и у каждого в update будет своя логика :)
А еще такой вопрос, который мучает на протяжении третьего урока.
При движении персонажа(корабль, марио, герой) вправо остается шлейф. Как бы от него избавиться? В консоли надо шрифт какой-то выставить или что?
Поставьте в свойствах консоли "Точечные шрифты" и размер шрифта 8x12
Добрый день, спасибо за урок, пишу сам по Вашей архитектуре, подскажите пожалуйста, хочу сделать так что бы враги атаковали игрока тоже могли стрелять, не получается хоть убей, заранее спасибо!!!!
Добрый день! Об этом как раз рассказывается в следующем уроке:
https://pikabu.ru/story/kursyi_sozdaniya_kompyuternyikh_igr_...
Добрый вечер, Tinaynox. Было сложно, но я смог (правда только первые четыре дз)! Вот https://yadi.sk/d/caLopD6d3SqqY7 .
А вот еще вопрос, почему когда мы создаем класс Game, в нем мы два раза пишем private: , почему одного не достаточно?
А дочерний класс определяется также как обычный, просто объект этого класса является частью другого? Если да, то опиши, пожалуйста как использовать ссылку на родительский класс внутри дочернего. А то на словах я не очень понял.
Хорошо, тогда к какому модулю мне отнести остатки от класса GameObject?
Совсем торможу уже - вроде все ок получается, и правда. Спасибо за пояснение.
остатки от класса GameObject
Что значит "остатки"? GameObject не нужно трогать, нужно просто чтоб все объекты теперь жили в классе Model, который бы в свою очередь находился в Controller
Так у меня не укладывается пока в голове, как это аргументировать, что в одном сегменте model or view or controller будет несколько классов.
И как эти классы должны быть друг с другом связаны.
Наследованием?
Да как угодно :)) MVC предполагает разделение на Модули, а не на Классы :)) А в Модуле может столько классов, сколько нужно
Но более насущным является то, что преподавателя не устроила
инициализация в Contoller (а я принял за него класс Game)
void Game::initialize() должно производиться в каком-то доп.классе или же в самой Model (касс GameObject). Можешь что посоветовать, а то прям горит...
А, точно, совсем забыл. Спасибо за разъяснение. Вместо контейнера можно обычным массивом воспользоваться?
Спасибо. Но это уже чисто для своего развития.
А по курсовому преподаватель сказал,
что никаких лишних "ххх.h" быть не должно.
Чисто MVC. То есть даже файл "level.h" частично
отправить во View, а частично в Model.
А именно, если я правильно понял, то сам уровень
из символов и пробелов (массив срок) в модель GameObject,
а уже преобразование этих символов в символы, которые
будут отображены в консоли - во View - то есть renderSystem...
А как грамотно выполнить в данной проге меню, продолжать ли в случае проигрыша или выходить?
Когда while(1) в main() добавил - то появились лаги.
Нужно ввести какой-то enum, который будет указывать текущее состояние игры и исходя из него в рендере рисовать либо саму игру, либо диалог с вопросом :) Ну и также исходя из него обрабатывать нажатия кнопок :)
Хотелось бы вот что спросить, насколько я понимаю, "чужие" могут и выходить за рамки правой или левой границы, при этом отрисовываться они будут, дабы не выходить за рамки массива, в старых координатах. Считаю это не совсем честно, так как в этом случае мы просто напросто не сможем в них попасть (а возможен , наверное, и такой исход, что самый последний "чужой" как раз и уйдёт за границу).
// к сожалению понятия не имею, как можно присылать скрины, так как сам понимаю, что объясняю на достаточно сложном языке)
Ну и хотелось бы лишний раз поблагодарить за то, что находите своё личное время на благо развития человеческой цивилизации )
Разрешите поинтересоваться: как там разборки с работой? :) Дело в том, что мне совершенно не интересно писать игры, но Вашу теорию С++ я читаю с большой охотой!
Можешь ли ты рассказать как попасть в ГеймДев контору? Рассказать чуть детальнее чем именно вы там занимаетесь, и нужно ли в ГеймДев идти целеустремленно, или можно просто перепрыгнуть например с .net разработки, или с низкоуровневой разработки на С
Геймдев направление делится на несколько веток:
1. ААА-игры - всякие там ХалфЛайфы, Варкрафты, Доты, Лиги Легенд :) Качество игр - высокое. Соответственно как и уровень программистов. :)
2. Бизнес-игры - трешовые игры вроде Веселого Фермера или какие-нибудь социальные "стратегии", призванные выкачать все бабло из пользователя :) Качество игр, как правило - посредственное. В такие компании берут и джуниоров и мидлов - устроится на работу плюс-минус легко, особенно с любым программистским опытом работы. :)
3. Детские-игры - очень узкая, низкооплачеваемая сфера геймдева. Самое сложное - это найти такую контору :) Устроится в принципе легко :)
4. Инди-игры - тут полный хаос и неопределенность :) Повезет найти хорошего инвестора или написать гениальную игру - значит повезло :)
Ну для новичка соответственно подходят варианты 2, 3 и 4 :)
Наткнулся на данный пост несколько дней назад и хочу задать пару вопросов перед тем как создавать игру по данным урокам, надеюсь на ваши ответы.
Программированием увлекся не так давно (опыта никакого). Есть поверхностные знания по C. Сейчас решил изучить С++. В качестве базового изучения выбрал "Принципы и практика использования c++" автор Бьёрн Страуструп.
Собственно сами вопросы:
1. Хорош ли выбранный мною автор?
2. Хотел бы узнать ваше мнение о том, книги каких авторов следует принять во внимание в начальном ознакомлении с С++ и последующем укреплении знаний?
3. Ну и при каком наборе знаний можно приступить к созданию игры по вашему курсу?
2. Да вот не знаю даже, в основном новичкам рекоммендуют Шилдта :)
3. При нулевом :) Главное - желание и энтузиазм ))
Да, но только на шарп) Мне в ваших постах гораздо интереснее система построения игровой механики в коде
Ну следующая часть пока не знаю когда )) У меня тут подработка на фрилансе появилась, поэтому пока времени нет =\
Огромное спасибо за труд!
Раньше писал в основном на Шарпе. А за последний месяц так захотелось научиться на плюсах кодить, аж жуть берёт. Пытался раньше как то читать гайды\маны по играм. Но никак не мог понять принцип игрового алгоритма, взаимодействие различных движков. Материал читается легко, концептуальные вещи доступно изложены. Очень надеюсь на продолжение.
p.s.
Кто считает, что это всё азбучные истины, и слишком легкие примеры: вам никто не запрещает делать намного сложнее и эффектнее.
Скажи, а ты бы не мог скинуть все посты одним архивом? Или поделить все предыдущие уроки на страницы и засунуть в pdf`ку? Думаю, было бы круто)
Коммент получился в стиле сам спросил сам ответил, оказалось нужно просто заполнять пустые элементы пробелами, это вероятно просто условности новых версий VS ?
Не уверен о чём речь, лучше залить код куда-нибудь на гитхаб, чтобы можно было посмотреть в чём может быть дело
То есть, надо просто до посинения бомбить корабль?
У меня тут идея: лучше, если будут уязвимыми определенные сектора определенное время, например квадрат 3х3 или 2х2, ну с ответным огнем будет интересно по ним стрелять.
С этой приблудой можно будет изменять лвл (сложнее - время уязвимости меньше).
П.С. Перечитываю коммент.. Это я щас как супир-ультра-стартапер выгляжу?
Dansken, спасибо за новую часть! Довольно большое количество вопросов появляется и чтоб не разводить здесь ток-шоу "Вопрос - ответ" прошу посоветовать пару-тройку книг/учебников для новичков. Уверен, что 90% вопросов отпадут.
P.S. Реально хочу научиться =)
Вот так чтоб читать как книгу - не подскажу.. На своем веку именно "интересных" книг "для чтения" по C++ я не видел.. А если как технический справочник, то любая известная думаю подойдет - Страуструп, Шилдт и т. п.
Круто) Буду очень ждать.
А пока не подскажешь на какой движок стоило бы обратить внимание, чтобы попробовать свои силы?