Как сделать шанс выпадения unity

Обновлено: 05.07.2024

Некоторые игры имеют постоянное количество объектов на сцене, однако обычно персонажи, пули, сокровища и другие объекты создаются и удаляются во время игры. В Unity, игровой объект (GameObject) может быть создан через функцию Instantiate, которая делает копию существующего объекта. Объект, который мы будем клонировать, должен быть prefab’ом и сохранен в папке Resources.

Сейчас ты напишешь пару скриптов, с помощью которых можно научиться создавать (spawn’инть) игровые объекты, например, для того, чтобы реализовать механику вот такой ловушки:

Подготовка

Тебе понадобятся четыре игровых объекта и скрипт, с которым ты уже знаком:

  1. Камень, несколько копий которого мы будем сбрасывать на Игрока — Stone.
  2. Объект, который соответствует еще не сработавшей ловушке — TrapOn.
  3. Объект, который соответствует уже сработавшей ловушке — TrapOff.
  4. Объект, который будет запускать ловушку — TrapSwitch.
  5. Скрипт OnTriggerSceneLoader.7z — он будет перезагружать уровень, когда Stone упадет (коснется) Игрока.

Stone

По нашему плану нам понадобятся камни, которые будут создаваться над головой Игрока. Выбери 3D-модель, которая тебе понравилась, добавь ей компоненты Rigidbody и два коллайдера: Capsule и Box. Box коллайдер должен быть чуть больше, чем Capsule, а также иметь включенным свойство Trigger.

После этого добавь этому игровому объекту скрипт OnTriggerSceneLoader и укажи в его свойствах название текущей сцены (а еще не забудь добавить ее в Build Settings).

Не забудь сохранить объект Stone, как prefab, в папке Resources!

Программирование

Тебе понадобятся два новых скрипта, чтобы все работало как нужно: Spawner.cs и TrapSwitch.cs

Spawner

Как мы уже говорили, в Unity есть функция Instantiate, которая делает копию существующего объекта:

В нашем случаем наш объект будет физическим, так что ты можешь сразу объявить Rigidbody, также понадобится переменная, в которой ты будешь указывать необходимое количество клонов:

Теперь нужно написать публичный метод, который ты будешь вызывать скриптом TrapSwitch.cs, стандартные методы Update() и Start() нам не понадобятся, так что их можно удалить:

Чтобы выполнить функцию Instantiate несколько раз, используй цикл, например, do while:

Технически, скрипт готов, но, если оставить все как есть, то во время выполнения метода Spawn(), практически, одновременно в одной точке появятся все amount (число) клоны. Из-за того, что наши клоны содержат в себе компоненты Rigidbody и коллайдер, клоны просто разлетятся в разные стороны, что выглядит не очень. При этом, шанс, что хотя бы один клон-камень попадет в Игрока будет очень низок. Поэтому добавь немного рандома и сделай так, чтобы каждый следующий камень создавался выше предыдущего на i:

Теперь все работает как нужно. Продолжим.

TrapSwitch

TrapSwitch будет размещен на объекте с триггером, когда Игрок будет его касаться, TrapSwitch будет запускать метод Spawn() у объекта Spawner. Напомним про коллайдеры и триггеры:

Триггер — это зона срабатывания определенных действий, когда объект входит в эту зону, находится в ней или выходит за ее пределы.

Сейчас тебе понадобиться метод OnTriggerEnter. Он выполняется, когда объект только вошел в зону триггера один раз. Создай такой скрипт:

Ты собираешься взаимодействовать с двумя игровыми объектами и объектом Spawner. Их нужно объявить:

Теперь необходимо убедиться, что объект, который войдет в триггер является Игроком. Это можно сделать, используя тег, как в OnTriggerSceneLoader:

Теперь последовательно напиши все необходимые команды внутрь условия: удаление (уничтожение) объекта TrapOn, включение объекта TrapOff, выполнение метода Spawn() и удаление (уничтожения) себя.

Все скрипты написаны. Поздравляем! Осталось правильно разместить все объекты на сцене и не забыть указать им друг на друга.

Я работаю над игрой, и я хотел бы вызвать событие, основанное на проценте. Итак, согласно моему действию героя, вероятность появления идет вверх или вниз. Вопрос в том, скажем, из-за того, как вы играете переменную "ChanceOFSpawningKremlin" - 75%, я не уверен, как использовать это 75% в функции, чтобы вызвать возникновение, я думал о выборе случайного числа, и если это число между 0 и 75, то причиной возникновения, однако по какой-то причине я не думаю, что лучший способ.

В результате любая помощь для достижения этой цели будет оценена по достоинству.

Классический способ сделать это - выбрать значение от 0 до 1 эксклюзивно равномерно случайным образом, а затем проверить, строго ли это значение, чем ваш шанс. Итак, в вашем случае:

Я забыл, что у Unity есть свой класс Random . И этот класс довольно выигрышный, так как он возвращает значения от 0.0 до 1.0 INCLUSIVE на обоих концах. Однако, пока ваша вероятность не равна 1.0 , вы можете игнорировать это и использовать его следующим образом:

Если ваша вероятность была когда-либо 1.0 вам нужно сделать из нее особый случай, и лечение всегда будет успешным.

Обратите внимание, что в обоих случаях вы используете вероятности как значения от 0.0 до 1.0 , а не проценты. Если вы хотите сохранить свои значения в процентах, тогда разделите их на 100.0 при использовании при проверке вероятности.

Есть скрипт для оружияМы пускаем рейкаст, и когда попадаем им, смотрим на точку попадения

Проверка значения на “старость”

Есть переменнаяОна в бесконечном цикле выдаёт значения типа:

Не перебирает адреса в базе правильно

Я составил скрипт, который перебирает адреса в моей таблице и отмечает отправленные

Как передать данные с laravel в react компонент

Мне нужно передать данные с blade (с бд данные) в react компонентГде-то я прочитал, что можно передать так:

Суть такая. У вражины есть вероятность выпадения предмета при убийстве. Ну например: 0.2. Это значит, что когда мы его убьем, у нас бросятся кости, чтобы проверить выпало что-то или нет (возьмется рандом от 1 до 1000 и если результат в диапазоне от 0 до 0.2 * 1000, то что-то выпало)

Если что-то выпало, то нужно взять рандом еще раз, чтобы определить вероятность выпадения какого-то конкретного предмета. Ну например, есть 1000 предметов с вероятностью выпадения от 0.01 до 0.95. Но выпасть должен какой-то один, причем чем больше его вероятность, тем чаще он будет выпадать.

И вот тут я в ступоре: а как правильно посчитать какой именно предмет выпал? Подскажите формулу. Какой диапазон рандома взять и как преобразовать выпавшее число в конкретный айдишник выпавшего предмета?

Ну а если так: при регистрации предмета я считаю его диапазон, как 1000 * dropProbability, и суммирую это к N. Тогда диапазон будет [Nlast; N - 1] (изначально N = 0)

Затем бросаем рандом от 0 до N и смотрим в какой диапазон попало значение. Предметы с большей вероятностью будут иметь более широкие диапазоны и, соответственно, выпадать чаще.

Ну я бы сделал как-то так

Безымянный | Определение выпавшего лута.

В целом нужно просто зарегистрировать предметы в словарь с вероятностью их выпадения (Больше число - чаще выпадает).
Если скажем зарегать два предмета с вероятностью 1 и 1 то выдать они будут с вероятностью 50\50

Jeners
ну по сути я то же самое описал

Robotex
Ну по сути да. Но моя реализация отличается простотой использования =)

Robotex
> возьмется рандом от 1 до 1000 и если результат в диапазоне от 0 до 0.2 * 1000,


Вскользь интересуюсь, зачем здесь магическое число 1000?
Если 1.0 - понятно, если 100 - тоже, типа проценты. Но 1000?

rcsim
Рандом от 1 до 1000
0.2*1000 = диапазону 0-200
Т.е. 20% от вообще возможного значения рандома.
Т.е. такой подход позволяет уложить в диапазон значения с тремя знаками после запятой
0.001 = 0-1;

Robotex
> возьмется рандом от 1 до 1000 и если результат в диапазоне от 0 до 0.2 * 1000,
> то что-то выпало
Это распространенное решение, но не единственное и не всегда верное. Во первых генератор случайных чисел не гарантирует выпадение определенного числа с равной вероятностью. Это может привести к тому что при малой или даже половинной вероятности ничто не выпадет, или будет выпадать постоянно на определенном промежутке времени.

Обычно делают целочисленный счетчик. Для вероятности 0.2 это 5 (перевернутая дробь) или любое кратное пяти число (N*5) - это интервал для которого будут генерироваться N чисел в диапазоне N*5.

Для N=3; будет три целых числа rnd(N*5). Каждый раз при убийстве этот счетчик уменьшается и когда он будет равен одному из этих трех чисел гарантированно выпадает шмотка с вероятностью 0.2. Если N>1 это может привести к тому что за период N*5 убийств из одного могут выпасть сразу N шмоток, но только один раз. Этого можно избежать если просто перемещать числа от 0 до N*5 и взять первые N как случайные.

Чтобы выбрать шмотку, с начало нужно нормализовать их вероятности. Допустим что ты задал вероятность 1 трем шмоткам, но понятно что три они не выпадут. Поэтому суммируешь все вероятности шмоток и делишь на сумму. Это даст правильные диапазоны. Здесь можно воспользоваться тем же классическим методом, взяв случайное число от 0 до 1, и пройдясь по всем предметам сверить диапазон от K до K+P, где K(0)=0 К(i) = K(i-1)+P, P - нормализованная вероятность.
Или прикрутить такой же интервальный счетчик к каждой шмотке.

Тут можно объединить вероятность выпадения чего либо с выбором.

lootdrop | Определение выпавшего лута.

Суть алгоритма:

Считаешь сумму весов/вероятностей всех предметов, записываешь рандомное число от 0 до этой суммы, идёшь по всем предметам отнимая от числа вес/вероятность предмета. На каком предмете число стало отрицательным - тот и выпал

foxes
> Это может привести к тому что при малой или даже половинной вероятности ничто
> не выпадет, или будет выпадать постоянно на определенном промежутке времени.
Для решения этой проблемы достаточно сделать просто умный рандом. Если 3 (10, 1000) игрок получал только мусор, то на 4 (11, 1001) убрать из пула весь мусор, оставить одну лишь крутоту. Собственно похожим образом сделано в D3, где гарантированно каждый час плейтайма ты получаешь легу или круче, даже если весь час ты бил бочки в городе.
Вообще управлять развесовкой проще, чем к каждой шмотке добавлять счетчики. Формируются пулы в зависимости от крутости шмоток. Условно белый, зеленый, синий и оранжевый. Многовато белого? Бустанем вес, чтобы весь белый пул был в пролете. Давно не получал синевы? Бустанем так, чтобы в финальном пуле остались только оранжевые и синие. Таким же макаром можно и модификаторы мобов делать, где каждый третий ролл у босса роллится только среди синего и оранжевого, а один так вообще гарантированно роллится только в оранжевом пуле.

foxes
> Во первых генератор случайных чисел не гарантирует выпадение определенного числа с равной вероятностью.
Dampire
> Для решения этой проблемы достаточно сделать просто умный рандом.
Вот они, отцы корейского рандома!

Лично я считаю, что врать про вероятность выпадения шмоток — нехорошо (особенно если это лутбоксы, купленные за реальные деньги). А любая модификация стандартного генератора случайных чисел однозначно приводит к такому вранью. Я уж не говорю про проблемы безопасности, возникающие при таких манипуляциях.

Если уж не нравятся длины серий, следующие из стандартного рандома, лучше тогда, вообще, избавиться от него.
Типо не 1/100 шанс выпадения шмотки, а каждый раз падает квестовый предмет, из 100 которых можно собрать шмотку. Если хочется небольшой вариабельности, то можно 50 предметов с шансом 50% или 10 с шансом 10%.

Dampire
А пулы шмоток на 1000 штук, это не те же счетчики? Как по мне памяти может даже больше сесть. Здесь же счетчики можно привязывать к монстру, типам монстра, чтобы ограничить выподаемый лут, ни каких мудреных сортировок и дополнительных условий с удалением массивов. И потом я ни в одном Диабле не видел, чтобы из бочки выпадал шмот, который падает только с басов.

В этой статье разберем как работать с группой физических объектов, рассчитывать и придавать им ускорение в разных направлениях.

В результате попробуем создать, что то похожее на взрывающихся ящиков, где на сцене, нажимая на любой физический объект он взрывается разбрасывая другие объекты которые находятся рядом.

Для начала подготовим сцену, добавим поверхности, стены и сам физический объект в виде ящика. Также добавляем ящику коллайдер BoxCollider2D и компонент Rigidbody2D .


Создаем новый скрипт Box , добавляем в него float переменную explodeForce, в которой будет указана сила взрыва ящика.

Далее запишем новый метод Explode, где будет происходить сам взрыв ящика. Этот метод ( Explode ) будет вызываться через нажатие на ящик.

  1. publicclass Box : MonoBehaviour
  2. [ Range ( 0 , 1000 )]
  3. publicfloat explodeForce = 100 ;
  4. publicvoid Explode ()
  5. >
  6. >

Сохраняем этот скрипт и возвращаемся в редактор. Чтобы не писать целый отдельный обработчик нажатий мыши по ящику, воспользуемся уже готовым инструментом под названием Physics 2D Raycaster . Этот компонент позволяет обрабатывать нажатия на физические объекты, что находятся на сцене. Добавим камере этот новый компонент ( Physics 2D Raycaster ).


и дополнительно создаем новый объект EventSystem , без которого обработчик нажатий ( Physics 2D Raycaster ) не сможет работать.


Теперь мы можем отловить любые действия мыши над любым физическим объектом, для этого достаточно добавить этому объекту компоненте Event Trigger и выбрать нужное действие.

Так и сделаем – выбираем ящик, добавляем новый компонент Event Trigger


после чего выбираем действие, нажав кнопку Add New Event Type .


Для обработки нажатия можно выбрать событие PointerClick и добавить нужное действие, в нашем случае это действие взрыва Explode .


Возвращаемся в скрипт Box и теперь поработаем над физикой взрыва.

Задача заключается в следующем: при нажатии на ящик, первым делом необходимо выполнить сбор всех рядом стоящих ящиков, и в зависимости от расстояния от взрыва, раскидать их в разных направлениях.

Теперь разобьем эту задачу на части:

  1. Получить список всех ящиков на сцене вокруг взрыва.
  2. Рассчитать расстояние и силу взрыва для каждого ящика отдельно.
  3. Приложить рассчитанную силу и направление движения для каждого отдельного ящика с помощью компонента Rigidbody2D .
  4. Уничтожить разорвавшийся ящик.

Для начала в методе Awake скрипта Box получим компонент Rigidbody2D самого ящика.

  1. publicclass Box : MonoBehaviour
  2. [ Range ( 0 , 1000 )]
  3. publicfloat explodeForce = 100 ;
  4. private Rigidbody2D body ;
  5. privatevoid Awake ()
  6. this . body = GetComponent Rigidbody2D >();
  7. >
  8. publicvoid Explode ()
  9. >
  10. >

Переходим к первому пункту задачи – необходимо получить список всех ящиков, стоящих рядом. Для этого воспользуемся специальным статическим методом OverlapCircleAll класса Physics2D , который вернет все коллайдеры, что находятся рядом.

  1. private Rigidbody2D body ;
  2. publicvoid Explode ()
  3. Vector2 position = this . body . position ;
  4. Collider2D [] colliders = Physics2D . OverlapCircleAll ( position, 3 );
  5. >

Для примера, попробуем найти все объекты в радиусе 3 ед . от взрыва. Теперь с помощью цикла переберем все объекты из полученного списка colliders .

  1. private Rigidbody2D body ;
  2. publicvoid Explode ()
  3. Vector2 position = this . body . position ;
  4. Collider2D [] colliders = Physics2D . OverlapCircleAll ( position, 3 );
  5. foreach ( Collider2D collider in colliders )
  6. Rigidbody2D other = collider . attachedRigidbody ;
  7. >
  8. >

Сперва необходимо уточнить, есть ли у выбранного объекта компонент Rigidbody2D , чтобы его получить у компонента Collider2D есть свойство attachedRigidbody .

  1. publicvoid Explode ()
  2. Vector2 position = this . body . position ;
  3. Collider2D [] colliders = Physics2D . OverlapCircleAll ( position, 3 );
  4. foreach ( Collider2D collider in colliders )
  5. Rigidbody2D other = collider . attachedRigidbody ;
  6. if ( other != null && other != this . body )
  7. >
  8. >
  9. >

Здесь мы выполняем даже две проверки: первая проверка на существование объекта Rigidbody2D , а вторая сравнивает, не является ли выбранный объект тем самым ящиком, который мы хотим взорвать. Переходим к следующему пункту задачи – рассчитываем силу и направления толчка. Чтобы вычислить направление от взрыва необходимо найти разность между позициями ящика и точкой взрыва.

  1. publicvoid Explode ()
  2. Vector2 position = this . body . position ;
  3. Collider2D [] colliders = Physics2D . OverlapCircleAll ( position, 3 );
  4. foreach ( Collider2D collider in colliders )
  5. Rigidbody2D other = collider . attachedRigidbody ;
  6. if ( other != null && other != this . body )
  7. Vector2 direction = ( other . position – position ). normalized ;
  8. >
  9. >
  10. >

В переменную direction записываем направление движения ящика от взрыва. Далее необходимо рассчитать дальность нахождения ящика от центра взрыва, так как в зависимости от расстояния будет меняться и сила толчка.

  1. publicvoid Explode ()
  2. Vector2 position = this . body . position ;
  3. Collider2D [] colliders = Physics2D . OverlapCircleAll ( position, 3 );
  4. foreach ( Collider2D collider in colliders )
  5. Rigidbody2D other = collider . attachedRigidbody ;
  6. if ( other != null && other != this . body )
  7. Vector2 direction = ( other . position – position ). normalized ;
  8. float distance = ( other . position – position ). sqrMagnitude ;
  9. >
  10. >
  11. >

В переменной distance получаем квадрат расстояние ящика от центра взрыва, после чего можно приступать к расчету силы толчка.

  1. publicvoid Explode ()
  2. Vector2 position = this . body . position ;
  3. Collider2D [] colliders = Physics2D . OverlapCircleAll ( position, 3 );
  4. foreach ( Collider2D collider in colliders )
  5. Rigidbody2D other = collider . attachedRigidbody ;
  6. if ( other != null && other != this . body )
  7. Vector2 direction = ( other . position – position ). normalized ;
  8. float distance = ( other . position – position ). sqrMagnitude ;
  9. Vector2 force = direction * this . explodeForce * this . body . mass / distance ;
  10. >
  11. >
  12. >

Для получения силы взрыва сперва умножаем направление толчка direction на силу взрыва explodeForce , далее умножаем полученное значение на массу объекта, ведь чем больше масса объекта тем сильнее взрыв, после чего делим все полученное значение на квадрат расстояния distance , так как чем дальше объект тем слабее сила взрыва.

Теперь можно приложить полученную силу толчка к ящику, но просто вызвать метод AddForce у компонента Rigidbody2D будет недостаточно, так как данный метод прикладывает силу к центру объекта, а нам необходимо, чтобы толчок происходил из центра взрыва.

Для того, чтобы приложить силу к физическому объекту относительно его положения на сцене воспользуемся методом AddForceAtPosition компонента Rigidbody2D .

  1. publicvoid Explode ()
  2. Vector2 position = this . body . position ;
  3. Collider2D [] colliders = Physics2D . OverlapCircleAll ( position, 3 );
  4. foreach ( Collider2D collider in colliders )
  5. Rigidbody2D other = collider . attachedRigidbody ;
  6. if ( other != null && other != this . body )
  7. Vector2 direction = ( other . position – position ). normalized ;
  8. float distance = ( other . position – position ). sqrMagnitude ;
  9. Vector2 force = direction * this . explodeForce * this . body . mass / distance ;
  10. other . AddForceAtPosition ( position, force, ForceMode2D . Impulse );
  11. >
  12. >
  13. >

В предпоследнем пункте задачи, в метод AddForceAtPosition необходимо передать точку из которой будет совершен толчок, для этого воспользуемся переменной position, где находится позиция взрыва, далее задаем саму силу толчка force и тип толчка в виде резкого импульса.

После чего завершаем выполнение задачи и удаляем со сцены взорвавшийся ящик.

  1. publicvoid Explode ()
  2. Vector2 position = this . body . position ;
  3. Collider2D [] colliders = Physics2D . OverlapCircleAll ( position, 3 );
  4. foreach ( Collider2D collider in colliders )
  5. Rigidbody2D other = collider . attachedRigidbody ;
  6. if ( other != null && other != this . body )
  7. Vector2 direction = ( other . position – position ). normalized ;
  8. float distance = ( other . position – position ). sqrMagnitude ;
  9. Vector2 force = direction * this . explodeForce * this . body . mass / distance ;
  10. other . AddForceAtPosition ( position, force, ForceMode2D . Impulse );
  11. >
  12. >
  13. Destroy ( this . gameObject );
  14. >

Сохраняем скрипт Box и переходим к тестированию.

Добавляем еще несколько ящиков на сцену.

Чтобы взрывы выглядели более эффектней можно добавить подготовить небольшую анимацию взрыва и добавить ее на сцену.

Заключение

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

Читайте также: