Сломанный Роберт со стороны Сола.

Игра изнутри

Усмотрел я тут несколько статей про «Игра изнутри», решил тоже поделиться некоторыми измышлениями. В общем-то, я вообще в создании игр новичок. В течении жизни несколько раз и пытался, но по итогу все уходило не в создание игры, а в попытку написать движок ( сначала на чистом C и досовской графике, потом узнал об OpenGL, SDL, Ogre, XNA, OpenTK и так далее), но ничем это не закончилось, и движкам этим самое место в прошлом и в мусоре.

А теперь о времени настоящем, предложили мне тут развеяться и поучавствовать в Джеме, а я уже снова почти перешел в состояние абсолютного тлена и задумывался о поисках работы. А работа, как известно, ну, — это хуже всего. Подумал-подумал и решил поучавствовать, тем более сейчас ничего не отвекало и был художник, а значит можно было полностью отдаться коду, что в данном случае, мне кажется, плюс.
Движок, значит, unity3D. Почему он, почему не gamemaker, почему не XNA или еще какой-нибудь из миллиона множества движков. Вопрос на самом деле хороший, но так сложилось, что мне приходилось много общаться с C# по работе, а тут вот тебе — пиши на C# и горя не знай ( хотя горя познать пришлось и не мало ). Ну и еще у меня уже года три-четыре как ооп головного мозга, поэтому я с трудом перевариваю языки, где нельзя два месяца писать архитектуру из 200 бесполезных классов. К тому же сама идея сделать игру за два дня могла немного помочь с излечением этой болезни, не было никакой возможности писать 200 классов.

Что же, с предисловием, думаю, достаточно, перейдем к самой игре. Старт пошел с самого момента объявления темы. Я сразу забил выдумывать что-то особенное и начал с класса PlayerController, который, как можно понять, отвечает за управление игровым персонажем. А что там персонаж должен делать? Ну да, ходить да бить, в начальный момент этого было более, чем достаточно. Коттон оказался достаточно быстрым в выдавании анимаций, поэтому уже в первую ночь орудовал ими. В целом ничего сложного сделано не было, но затык уже появился. После реализации просто движения, перешел к ударам, а где удары, там их комбинации, их анимации, всякие условия, чтобы не бегать, когда бьешь. Под конец первой ночи комбо как бы были, но работали совсем плохо, то зависала анимация, то не влючалась, включалась не та, тайминги не поддвались никакой настройке. Если кто-то знаком с анимациями в юнити, то знает, что они реализованы через машину состояний. Идея работы машины в общем-то стандартная и логичная, но когда сталкиваешься с ней впервые, то вопрос решения перехода между состояними, настройка таймингов и прочее — занятие невероятно муторное. Первый раз я реализовал переход между состояниями ударов через булевские переменные и без таймера, а время выставил через параметры самих анимаций. И это даже работало, но стоило замерджить проект с напарником(«цифры»), то все полетело к чертям. Ушел спать только к часу дня, пытаясь все это выправить, и, хвала триггерам и таймерам из кода, это вышло. Правда, по-моему, самый сложный момент достался именно не мне, а напарнику, который столкнулся с окончательной настройкой этой машины состояний, то есть выставлял все тайминги, все анимации, все переходы между ними. А этих машин было целых две! Еще же и бот.

И вот, на следующий день другие задачи. Если персонаж может ходить — это уже хорошо, но еще лучше, если он может ходить только там, где должен ходить. Опять же решил не замарачиваться и делать в лоб. Раз связано с ходьбой, то ставим триггер в ноги. И пишем триггер, который при столкновении не даст ходить. И тут я уже совершил первую ошибку. Описал логику остановки так, что она работала только для 0 угла, а игра-то в изометрии! ( Вот отсюда-то и пошли баги, из-за которых можно провалиться в объекты на сцене, вокруг них просто тысячи этих триггеров, выставленных руками, а все, что руками, с ошибками, тем более в таком ускоренном режиме ). Логика триггера простая. Есть четыре направления движения, когда ноги задевают триггер, условием вычисляется сторона, которая должна заблокироваться и счетчик блокировки этой стороны увеличивается, а когда выходишь, то уменьшается. ( Кстати, изначально это были булевские переменные, а не счетчики, но уже при расстановке выяснилось, что если они стоят рядом друг с другом, то без счетчика лучше пойти съесть банку бобов и перестать делать игру ). Но когда же АИ? Верно, после реализации блоков для игрока самое время начать делать АИ. Честно говоря, я убил на это кучу времени, они мне даже снились, поэтому не могу сказать какая там была последовательность действий. Класс для врага менялся постоянно. Каждые 15 минут там уже был абсолютно другой код, а времени на него было убито так много, что, возможно, из-за этого и не получилось настроить остальное до конца. А да, важная заметка, дизайном уровня, его заполнением, спавном врагов и написанием игровых триггеров для камеры занимался цифры. Как настоящий геймдизайнер! Я же выступил только в роли совсем кодера, который сидел в своем коде. Вернемся к АИ. Первое — сделал, чтобы он просто ходил куда-то, потом — чтобы ходил за целью по таймеру, потом — чтобы немного тупил при этом, а то забьет кулаками и не заметишь. Далее — чтобы делал комбо, а в конце концов очень-очень долго учил его обходить препятствия. Никакого A*, конечно, все в лоб и очень тупо, но в конце концов это хоть как-то заработало. Забавный момент, если игрок вставал у блока, то боты могли случайно и все таки свалить, поэтому придумался веселый костыль. Если встал у блока, жди яростной атаки от всех ботов вокруг, сделал так, чтобы они молотили тебя почти без остановки. Что же, в какой-то момент бот вел себя более-менее играбельно ( хотя, мне кажется, тут можно твинкать логику до бесконечности и довольным не никогда не останешься). А раз бот играбелен, то пора делать систему повреждений, реакцию на удары и все такое.
Что же, решение опять же было довольно простым. На теле помещался небольшой триггер под названием Body, а спереди помещался триггер под названием Punch. Соответсвенно, когда Punch находился в Body, и при этом удар был именно завершен ( Я выставлял булевкую переменную на 0.05с после окончания анимаций ударов), то наносился урон, выставлялось состояние стана и другие прелести жизни. Да, кстати, в игре может и не заметно, но разные удары могли иметь разный урон! Например, последний удар ногой, который с разворота, наносит целых 3 единицы урона, а не одну!
if (actor.AttackName.Equals(«SuperFoot»))
{
damage = 3;
}
HitPoint -= damage;
А выглядило это как-то так.
Да, кстати, хоть я и хотел в этот раз бежать от написания большого количества классов, но все равно пришлось выделить базовый класс Actor, в котором хранились общие методы и свойства для игрока и бота. И это правильно, как говорится.

И вот наступает последний день. А еще нужно научить персонажа прыгать! Да еще в чертовой изометрии, да еще прыгать по лоджимям и падать в ямы. Я, наверное, опять сидел с этим делом 5 часов, а когда закончил, то времени почти не оставалось, а еще нужно было объединить проекты. ( А с этим всегда куча проблем ).
Вот и получилось, что замерджились мы минут за 20 до конца, половина сломалась, черт знает почему, нервы, сигареты, но в итоге что-то да запустилось.
Да, приложу скриншот машины состояний для игрока, может кому-то интересно.
Ну и в целом, могу приложить и исходный код, если опять же кому-то интересно.
P.S Болит голова, так что, вероятно, будет миллион и более опечаток, если что не злитесь, я потом на свежую голову уберу. 

 

Похожие статьи

  • Как я делал ящики
    (начал писать этот пост давным-давно, да что-то никак не дописывалось и не хотелось его публиковать. В свете последних событий решил-таки дописать, а заодно и вспомнить то...
  • О моём опыте посещения геймдевелоперских мероприятий. (1)
    Я недавно увидел пост NikB о том, как он был на интенсиве по геймдизайну. И захотелось поделиться тем, как я принимал участие в двух геймдизайнерских мероприятиях.
  • AlienCrash — жизнь после конкурса
    После грандиозного успеха игры на конкурсе (целых 3 балла, чё!) я не решился бросить разработку. Короче, чего нового * Изменено управление + На сервере можно выбрать...
  • Наколенник №02 — AndreyMust19. Релиз
    Итак, вот все, что я успел.Скриншот публиковать не буду. Пусть будет сюрприз.Финальная версия на конкурс (для Windows):релизИгра для двух игроков по сети. Поэтому списывайтесь, ...
16 комментариев
Kozinaka
if (actor.AttackName.Equals(«SuperFoot»))
А зачем Equals?
sol
в C# строки — это объект. Равенство объектов определяется методом Equals. == это вариант Equals, то есть тоже самое. Хотя, правильно это было сделать через int или enum, но я сначала подумал, что для int забуду, какое число что значит, а писать emun — показалось слишком долго. Это уже был почти конец джема. И в итоге осталась строка и всего одно условие. 
Kozinaka
Ну так вот и вопрос — зачем платить писать больше? Разве что у тебя там какая-то экзотическая перегрузка.
sol
Больше, чем что? Я в студии пишу, поэтому Equals дописывается, == даже дольше будет печатать.
То, что строка вместо enum или int — это уже да.
sol
Ну и это уже вопрос эстетики, я просто не люблю "==", "<=" и такие символики. Поэтому никогда не перегружаю ==. И такие символики только для стандартный типов и использую. 
сб3
объекты на сцене, вокруг них просто тысячи этих триггеров, выставленных руками
Логика триггера простая. Есть четыре направления движения, когда ноги задевают триггер, условием вычисляется сторона, которая должна заблокироваться и счетчик блокировки этой стороны увеличивается, а когда выходишь, то уменьшается.

Охренеть, чтобы так делали движение влево-вправо, я ещё не слышал. Юнити рулит!

 

Да, приложу скриншот машины состояний для игрока, может кому-то интересно.

А вот скрин ИИ игрока из моей игры на этот джем. Написан был достаточно быстро. У меня сложнее! :)

Xitilon
Э… Почему так много default везде торчит? Что это вообще всё значит? tech invisible, ну, понятно, что торчат провода от старого движка, но как это работает?
сб3

Как это работает, могут понять только сильные духом. Пока лишь один человек, кроме меня, смог. (Тут: http://www.gamedev.ru/projects/forum/?id=199554 ) Причём я ему ничего не объяснял. Сам открыл редактор и ковырял.

Default это переход в том случае, если остальные условия не сработали. Tech invisible location это локация без какого-либо действия, обычно нужна для вычислений.

 

sol
Да это не юнити рулит. Просто мне почему-то показалось, что так сойдет. В принципе, если бы это была не изометрия, то эти тригеры бы спокойно справились со своей целью. А так, для обычных блоков на основе физики там достаточно просто сам блок поставить и не париться.  
ИИ — это пока что моя слабость. В данном случае весь ии был описан в одной функции апдейта.
Xitilon
Я выставлял булевкую переменную на 0.05с после окончания анимаций ударов

Это как - 0.05с?

Классный пост. Мне, как программисту на C#, интересно было почитать.

Пару картинок бы ещё разбавить текст, хоть бы и просто со случайным куском кода. Чтоб атмосферней, чуть более прочувствовать, как это изнутри.

Raseri

Правда, тут Lua.

sol

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


override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
   if (stateInfo.IsName(«KickAttack3»))
  { 
       animator.gameObject.GetComponent<Actor>().AttackName = «SuperFoot»;
  }
  else
 {
      animator.gameObject.GetComponent<Actor>().AttackName = «Attack»;
 }
       animator.gameObject.GetComponent<Actor>().SetAttackDone();
}

Xitilon

Тьфу ты, я просто прочитал «Я выставлял булевкую переменную на 0.05с» как «я присваивал bool time = 0.05c;». Долго тупил, думал, это небось какой-то .NET 5.5, новый суффикс «c» для инициализирования… булевых величин?!

Ну и видимо «с» это «секунд». Это я тоже распарсил позже.

sol
Не, кстати, дальше .4 .Net даже по работе никогда не заходил. Хотя давно пора разобраться, что там новое-то делать можно, но ле-е-ень. Да и в юнити, наверное, не желательно заходить больше возможностей mono. 
Xitilon

Ну да, для кроссплатформенных билдов.

Мне кажется, большая часть виденных мной разработчиков не использует возможности выше .NET 2.0. Ты используешь в работе итераторы, делегаты, LINQ?

sol
Linq нет, не знаю, мне как-то через лямда выражения больше нравится. Остальное да, точнее как, на самом деле много использую только лямбды и делегаты ( куда лямбдам без делегатов ), остальное уже приходит или вместе с технологией или библиотекой, как правило. Но все это я использовал не в юнити, конечно. В этом проекте, если не ошибаюсь, даже лямбда только в 1 месте и то лучше бы ее не было.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.