В любом столкновении участвуют два объекта GameObject, верно? Я хочу знать, как мне решить, какой объект должен содержать мой OnCollision*
?
В качестве примера, давайте предположим, что у меня есть объект Player и объект Spike. Моя первая мысль - поставить скрипт на плеер, который содержит такой код:
OnCollisionEnter(Collision coll) {
if (coll.gameObject.compareTag("Spike")) {
Destroy(gameObject);
}
}
Конечно, та же самая функциональность может быть достигнута, вместо этого имея скрипт на объекте Spike, который содержит такой код:
OnCollisionEnter(Collision coll) {
if (coll.gameObject.compareTag("Player")) {
Destroy(coll.gameObject);
}
}
Хотя оба они действительны, для меня более логично было иметь сценарий на проигрывателе, потому что в этом случае, когда происходит столкновение, на проигрывателе выполняется действие .
Однако, что заставляет меня сомневаться, это то, что в будущем вы можете захотеть добавить больше объектов, которые убьют игрока при столкновении, таких как враг, лава, лазерный луч и т. Д. Эти объекты, вероятно, будут иметь разные теги. Итак, сценарий на проигрывателе станет:
OnCollisionEnter(Collision coll) {
GameObject other = coll.gameObject;
if (other.compareTag("Spike") || other.compareTag("Lava") || other.compareTag("Enemy")) {
Destroy(gameObject);
}
}
Принимая во внимание, что в случае, когда скрипт был на Spike, все, что вам нужно было бы сделать, это добавить этот же скрипт ко всем другим объектам, которые могут убить Player, и назвать скрипт как-то так KillPlayerOnContact
.
Кроме того, если у вас есть столкновение между игроком и врагом, то вы, вероятно, хотите выполнить действие на обоих . Так в таком случае, какой объект должен обрабатывать столкновение? Или оба должны обрабатывать столкновения и выполнять различные действия?
Я никогда не создавал игры какого-либо разумного размера, и мне интересно, может ли код стать грязным и сложным в обслуживании по мере роста, если в начале вы ошиблись. Или, может быть, все способы действительны, и это не имеет значения?
Любое понимание высоко ценится! Спасибо за ваше время :)
источник
Tag.SPIKE
вместо этого?Ответы:
Я лично предпочитаю проектировать системы таким образом, чтобы ни один объект, участвующий в столкновении, не обрабатывал его, а вместо этого некоторый прокси-сервер обработки столкновений обрабатывал его с помощью функции обратного вызова
HandleCollisionBetween(GameObject A, GameObject B)
. Таким образом, мне не нужно думать о том, какая логика должна быть, где.Если это не вариант, например, когда вы не контролируете архитектуру столкновения-отклика, все становится немного неясным. Обычно ответ «это зависит».
Поскольку оба объекта будут получать коллбэки, я поместил код в оба, но только код, который имеет отношение к роли этого объекта в игровом мире , и в то же время пытался поддерживать максимально возможную расширяемость. Это означает, что если какая-либо логика имеет одинаковый смысл в любом объекте, предпочтительнее поместить ее в объект, что, вероятно, приведет к меньшему количеству изменений кода в долгосрочной перспективе при добавлении новой функциональности.
Иными словами, между любыми двумя типами объектов, которые могут сталкиваться, один из этих типов объектов обычно оказывает одинаковое влияние на большее количество типов объектов, чем другой. Помещение кода для этого эффекта в тип, который влияет на большее количество других типов, означает меньше обслуживания в долгосрочной перспективе.
Например, объект игрока и объект пули. Возможно, «получение урона при столкновении с пулями» имеет логический смысл как часть игрока. «Наносить урон тому, что поражает», имеет логический смысл как часть пули. Однако пуля может нанести урон большему количеству объектов, чем игроку (в большинстве игр). Игрок не будет получать урон от блоков местности или неигровых персонажей, но пуля все еще может наносить урон. Так что я бы предпочел в этом случае использовать обработку столкновений для нанесения ущерба пуле.
источник
TL; DR Лучшее место для установки детектора коллизий - это место, где вы считаете, что это активный элемент в коллизии, а не пассивный элемент в коллизии, потому что, возможно, вам потребуется дополнительное поведение для выполнения в такой активной логике (и, следуя хорошим правилам ООП, таким как SOLID, ответственность лежит на активном элементе).
Подробно :
Посмотрим ...
Мой подход, когда у меня возникают одни и те же сомнения, независимо от игрового движка , состоит в том, чтобы определить, какое поведение является активным, а какое - пассивным по отношению к действию, которое я хочу вызвать при сборе.
Обратите внимание: я использую различные варианты поведения в качестве абстракций различных частей нашей логики для определения ущерба и - в качестве другого примера - инвентаря. И то, и другое - отдельное поведение, которое я бы добавил позже к Игроку, и не загромождает мой пользовательский Игрок с отдельными обязанностями, которые будет сложнее поддерживать. Используя аннотации, такие как RequireComponent, я бы гарантировал, что поведение моего плеера имело также поведение, которое я сейчас определяю.
Это только мое мнение: избегайте выполнения логики в зависимости от тега, но вместо этого используйте различные варианты поведения и значения, например, перечисления для выбора .
Представьте себе это поведение:
Поведение для определения объекта как стека на земле. РПГ игры используют это для предметов в земле, которые можно подобрать.
Поведение для указания объекта, способного нанести урон. Это может быть лава, кислота, любое токсичное вещество или летящий снаряд.
В этом смысле считают
DamageType
иInventoryObject
перечисления.Эти поведения не активны , так как находясь на земле или летая, они не действуют сами по себе. Они ничего не делают. Например, если у вас есть огненный шар, летящий в пустом пространстве, он не готов наносить урон (возможно, другие действия следует считать активными в огненном шаре, например, огненный шар, поглощающий его силу во время путешествия и уменьшающий его мощность урона в процессе, но даже когда потенциальное
FuelingOut
поведение может быть разработано, это все же другое поведение, а не то же самое поведение DamageProducer), и если вы видите объект в земле, это не проверяет целых пользователей и думает, что они могут выбрать меня?Для этого нам нужно активное поведение. Аналогами будут:
Одно поведение, чтобы получить урон (Это потребует поведения, чтобы справиться с жизнью и смертью, поскольку снижение жизни и получение урона обычно являются отдельными действиями, и потому что я хочу сохранить эти примеры как можно более простыми) и, возможно, противостоять урону.
Этот код не проверен и должен работать как концепция . Какова цель? Ущерб - это то, что кто-то чувствует , и связан с повреждением объекта (или живой формы), как вы считаете. Это пример: в обычных играх RPG меч повреждает вас , в то время как в других RPG, реализующих его (например, Summon Night Swordcraft Story I и II ), меч также может получать урон, ударяя по твердой части или блокируя броню, и может нуждаться в ремонт . Таким образом, поскольку логика повреждения относится к получателю, а не к отправителю, мы помещаем только соответствующие данные в отправителя, а логику в получателе.
То же самое относится и к владельцу инвентаря (другое требуемое поведение будет связано с тем, что объект находится на земле, и возможностью удалить его оттуда). Возможно, это подходящее поведение:
источник
Как вы можете видеть из множества ответов здесь, в этих решениях присутствует достаточная степень личного стиля и суждения, основанные на потребностях вашей конкретной игры - ни один абсолютно лучший подход.
Я рекомендую подумать об этом с точки зрения масштабирования вашего подхода по мере развития игры , добавления и повторения функций. Приведет ли логика обработки коллизий в одном месте к более сложному коду позже? Или это поддерживает более модульное поведение?
Итак, у вас есть шипы, которые наносят урон игроку. Спроси себя:
Очевидно, что мы не хотим увлекаться этим и создавать сверхпроектированную систему, которая может обрабатывать миллион случаев, а не только один случай, который нам нужен. Но если это одинаковая работа в любом случае (т.е. поместите эти 4 строки в класс A против класса B), но одну лучше масштабировать, это хорошее правило для принятия решения.
Для этого примера, в частности, в Unity, я бы склонялся к тому, чтобы поместить логику нанесения ущерба в шипы , в форме чего-то вроде
CollisionHazard
компонента, который при столкновении вызывает метод получения ущерба вDamageable
компоненте. Вот почему:Damageable
компонент (если вам нужно, чтобы некоторые из них были невосприимчивы только к шипам, вы можете добавить типы повреждений и пустьDamageable
компонент справится с иммунитетами)источник
Это действительно не имеет значения, кто обрабатывает столкновение, но будьте последовательны, чья это работа.
В своих играх я пытаюсь выбрать то,
GameObject
что «делает» что-то с другим объектом, как тот, который контролирует столкновение. IE Spike наносит урон игроку, поэтому он справляется со столкновением. Когда оба объекта «делают» что-то друг с другом, пусть каждый из них выполняет свое действие над другим объектом.Кроме того, я бы предложил попытаться спроектировать ваши коллизии так, чтобы коллизии обрабатывались объектом, который реагирует на коллизии с меньшим количеством вещей. Спайку нужно будет знать только о столкновениях с игроком, что делает код столкновения в скрипте Spike красивым и компактным. Объект игрока может столкнуться со многими другими вещами, которые сделают сценарий раздутого столкновения.
Наконец, попробуйте сделать ваши обработчики столкновений более абстрактными. Спайку не нужно знать, что он столкнулся с игроком, ему просто нужно знать, что он столкнулся с чем-то, чему он должен нанести урон. Допустим, у вас есть сценарий:
Вы можете создать подкласс
DamageController
в своем игроке GameObject, чтобы обработать ущерб, а затем вSpike
коде столкновенияисточник
У меня была такая же проблема, когда я только начинал, так что вот так: в данном конкретном случае используйте врага. Вы обычно хотите, чтобы Столкновение было обработано Объектом, который выполняет действие (в данном случае - противником, но если у вашего игрока было оружие, тогда оружие должно справиться со столкновением), потому что, если вы хотите настроить атрибуты (например, урон противника) вы определенно хотите изменить его в сценарии врага, а не в сценарии игрока (особенно если у вас есть несколько игроков). Аналогично, если вы хотите изменить урон любого оружия, которое использует Игрок, вы определенно не хотите менять его у каждого врага в вашей игре.
источник
Оба объекта будут обрабатывать столкновение.
Некоторые объекты будут немедленно деформированы, разбиты или разрушены в результате столкновения. (пули, стрелы, водяные шары)
Другие просто отскочили бы и откатились бы в сторону. (камни) Они все еще могут получить урон, если вещь, которую они ударили, была достаточно твердой Скалы не получают повреждений от ударов человека, но они могут быть от кувалды.
Некоторые мягкие объекты, такие как люди, будут получать урон.
Другие будут связаны друг с другом. (магниты)
Оба столкновения будут помещены в очередь обработки событий для актера, действующего на объект в определенное время и место (и скорость). Просто помните, что обработчик должен иметь дело только с тем, что происходит с объектом. Даже обработчик может поместить другой обработчик в очередь для обработки дополнительных эффектов столкновения, основанных на свойствах субъекта или объекта, над которым выполняется действие. (Бомба взорвалась, и в результате взрыва теперь приходится проверять столкновения с другими объектами.)
При создании сценариев используйте теги со свойствами, которые будут переведены в реализации интерфейса вашим кодом. Затем обратитесь к интерфейсам в вашем коде обработки.
Например, меч может иметь тег для Melee, который переводится в интерфейс с именем Melee, который может расширять интерфейс DamageGiving, который имеет свойства для типа урона и величину урона, определяемую либо с помощью фиксированного значения или диапазонов, и т. Д. И бомба может иметь тег Explosive, чей интерфейс Explosive также расширяет интерфейс DamageGiving, который имеет дополнительное свойство для радиуса и, возможно, скорость распространения повреждения.
Тогда ваш игровой движок будет кодировать интерфейсы, а не конкретные типы объектов.
источник