Внутренняя архитектура игры. Прежде чем накосяпорю дальше, нужны дельные советы по уже имеющимся косякам?
Вопрос собственно такой, не совсем понимаю как правильно/красиво сделать отслеживание столкновений (коллизий) игровых объектов и где размещать логику обработки возникающих коллизий? Сейчас все это внесено в класс Ball, но получается дикая мешанина, а другого приемлемого варианта что то не вижу. Возможно просто пора спать. Буду рад любому хорошему развернутому ответу/комментарию по сути вопроса.
Не смотрел ни в код ни в результат, но я бы написал некий абстрактный класс InteractiveObject, в котором бы реализовывались коллизии со сценой, а ball и другие интерактивные объекты унаследовались от него.
Пашенька, не факт что в данном случае так будет лучше, но ваш вариант натолкнул меня на пару идей, спасибо.
Уточню в чем мои сомнения. Есть много блоков, есть много шариков, возможен вариант когда будет много ракеток, ну и витаминок тоже будет не одна. Шарики могут взаимодействовать со всеми, в том числе и сами с собой, витаминки только с шариками и ракеткой, ракетка только с шариками и витаминками, плюс ракетка витаминки и шарики взаимодействуют с границами мира. Это все что касается определения пересечения объектов. Основное непонимание это не то как проверять а то где где проверять пересечения многих ко многим (например шариков и блоков) и где разместить логику обработки возникших коллизий. Вижу несколько вариантов:
- в самих объектах. Позволит немного экономить на расчетах, так как не все объекты требуют проверки на пересечение всех 4-х граней, а для пересечения шариков с шариками вообще математика другая. Так же позволит разнести по объектам логику обработки ситуаций с пересечением, которая тоже разная у разных объектов.
- в некой отдельной абстракции (нечто типа недофизического движка), от которой как вы и сказали, наследовать объекты. Но в таком случае куда засунуть логику действия элемента в случае возникновения коллизий.
- вынести всю логику нахождения коллизий в отдельную сущность (класс/модуль/библиотека) а всю логику обработки данных коллизий в отдельный класс Game. Позволит оставить сами объекты "чистыми" (то есть будут содержать только данные необходимые для их отображения (координаты, размеры, спрайты картинок и анимаций)
Роман, используйте готовые схемы готовых движков. Это конечно не идеал, но как пример сойдет.
Например, тот же юнити:
- взаимодействия между объектами идут не по типам, а по слоям (есть матрица взаимодействий)
- объект реализует сущность физической модели - т.е. содержит инфу о BoundingBox, положении и прочем
- вся физика считается отдельной сущностью - физической моделью, делает это по таймеру
- физмодель дергает соответствующие методы у сущностей, которые провзаимодействовали, и там уже пишется соответственно уникальная логика
- для ПОВЕДЕНИЯ по физике (отскоки и прочее) - есть отдельная сущность - и ее тоже реализует (если так можно говорить о компонентной системе) объект.
- реализцаии сущности физмодели (коллайдер и ригидбади) хоть и принадлежат де-факт объекту, но сделаны не через наследование, если что
GavriKos, попытался немного осмыслить сказанное вами и вот что получилось применительно к данной игрульке:
1. мячи, ракетки, блоки, витаминки - объекты от соответствующих классов, содержащие данные о физических параметрах (координаты, размеры, вектора скорости и так далее, все то что умеет физдвидок) а также примешиваем к объектам коллайдеры (упругость, трение, неуничтожимость, физическая прозрачность и т.д.)
2. все они собираются в некие абстрактные слои
3. То, как слои могут между собой взаимодействовать указываем в некой матрице. Применительно к ЖС, это некий json, в котором указанно при возникновении каких физических событий и для каких объектов/слоев какие события вызывать
4. вся физика реализована как отдельная библиотека/модуль/класс. Ей на обработку в игровом цикле кидаем данные слои с объектами и данную матрицу взаимодействий
5. физ.движек рассчитывает состояния объектов и генерирует события согласно матрице из п.3, передавая в качестве аргументов участвующие во взаимодействии объекты
6. в обработчиках генерируемых физ движком событий прописываем как именно должно влиять данное событие на участвующие в нем объекты, включая изменение координат и самих объектов (если оно чем либо отличается от стандартного, предоставляемого физдвижком из коробки, или дополняет/изменяет его). Именно тут мы дергаем соответствующие методы объектов. Так же в данных обработчиках при необходимости мы можем генерировать уже игровые события (например уменьшение количества жизней/шариков, при вылете последнего шарика из мира, или изменение состояния ракетки при поедании витаминки)
7. отображение всех слоев возлагается на отдельную сущность? Кто хранит данные необходимые для отображения объектов (текстуры, спрайты, логику/алгоритмы отрисовки), сами объекты или все это выносится отдельно?
8. обработка игровых событий возлагается на отдельную сущность?
Правильно ли я понял описанную вами модель и экстраполировал ее на JS?
Роман, 1. Ну допустим
2. Нет. Слой - это свойство объекта.
3. Можно обычный двумерный массив
4. Типа того
5. Типа того
6. Вот тут не понял. Обработчики на каком уровне абстракции находятся? Я бы делал обработчики на уровне объектов
7. Конечно на отдельную. Причем никак не связанную с физикой
8. См. пункт 7.
6. Вот тут не понял. Обработчики на каком уровне абстракции находятся? Я бы делал обработчики на уровне объектов
Да, на уровне объектов. Например у объекта ball ставится обработчики onBlockCollision и onCollision которые будут вызываться при столкновении с блоками и вообще при любом столкновении соответсвенно. В обработчик передаются в качестве параметров:
- объект с которым произошло пересечение
- json с рассчитанными параметры пересечения (углы, грани с которыми произошло пересечение и т.д.)
7. Конечно на отдельную. Причем никак не связанную с физикой
я тут немного дополнил 7 пункт: Кто хранит данные необходимые для отображения объектов (текстуры, спрайты, логику/алгоритмы отрисовки), сами объекты или все это выносится отдельно?
2. Нет. Слой - это свойство объекта.
поясните пожалуста как тогда прописываются взаимодействие между слоями (например слой с боундбоксами и слой с упругостью), просто не до конца понимаю суть такого разбиения
Роман, весьма энергичный паренёк на ютабе, который всякие интересности накидывает, включая игры, на либе правда, но всё же. Раньше иногда натыкался, посматривал, может зайдёт.
Хотя там вряд ли про архитектуру, там скорее про работает и хорошо, всё же лайв режим)
lastuniverse.ru:3000
В FF немного подтормаживает (даже пустая сцена). В хроме летает.
Пока это лишь заготовка. В ней всего 1 уровень, механизм жизней не реализован, так что жизнь тоже пока лишь одна, механизмы различных типов шаров так же не реализован до конца, так что шары отличаются только размерами и внешним видом, ну и блоки тоже все одинаковые с единичной прочностью. Зато реализованы почти все виды витаминок а вся графика кроме фона - моего авторства))))
Bavashi, хмм, это странно, я умудряюсь даже боковиной к стене прижимать в том же хроме. Думаю тут дело не в скорости, а в том, что палка просто выставляется по координате Х курсора. Таким образом если быстро двигать, то может и перескочить позицию мяча. Сейчас попробую подумать как это обойти)
Bavashi, ну что же, для исправления этого косяка пришлось грубо игнорировать стандартную физику phaser-а, но теперь можно подбивать мяч боковиной весла. Правда если просто мотать мышкой от края экрана до края экрана вправо-влево шарик почти никогда не прорвется вниз)))
Bavashi, физика варианта игры в самом вопросе, сделана мной, на голом канвасе, этот момент учитывала (там строились отресзки траекторий и производился расчет пересечений). Физика же в phaser-е по всей видимости является несколько более упрощенной (видимо для скорости вычислений) так что мне пришлось дорабатывать.
И вот еще пару моментов:
1. Сейчас для расчета коллизий мячика и весла используется следующий алгоритм:
- если диапазон координат X мячика перекрывает диапазон координат X весла, то используется физика phaser-а
- иначе если диапазоны координат н весла и мячика перекрываются а диапазон координат X мячика перекрывает диапазон координат (старая координата весла-новая координата весла) то рассчитывается отбой боковиной.
При таком подходе мячик может вести себя неадекватно только из-за физдвижка phaser-а.
2. я за 2 дня тестов на достаточно слабом компе ни разу не получил мячика внутри весла. Если есть возможность записать на видео данный момент, сделайте это. Это мне поможет понять суть данного процесса.
Bavashi, еще раз перечитал ваш комментарий. Понял в чем дело. Вы как раз описываете ситуацию, возникающую когда диапазоны координат Y весла и мячика перекрываются а диапазон координат X мячика перекрывает диапазон координат (старая координата весла-новая координата весла). В данной ситуации, происходит расчет отбой мяча боковиной, но не внутренней, а именно той которой попали. Единственное НО заключено в том, что само весло просто устанавливается в новую координату X, что вполне может давать визуальный эффект - мяч в весле. Прямо сейчас даже не соображу, как от этого визуального эффекта избавится.
Могу порекомендовать книгу Графика на javascript Рафаэлло Чекко - там в одной главе делается игра space invaders и разбирается, в том числе, ваш вопрос