Что это: Пошаговый пример сборки игры про аномалии (выживание, случайное появление, исправление, победа/поражение) на компонентах NeoxiderTools.
Как использовать: Выполните шаги в разделах 1–5; нужные компоненты перечислены ниже.
- TimerObject (2 экземпляра): один — время до победы (например выжить 6 часов), второй — интервал появления аномалий.
- Selector + SelectorItem на каждом объекте-аномалии.
- VisualToggle или ToggleObject для визуала аномалии (опционально).
- NeoCondition по Selector.CountActive для поражения (например, 4 активных аномалии → проигрыш).
- GM / EM для победы и поражения (опционально).
- RandomRange — для сценария «от 0 до N аномалий на уровень» (опционально).
Цель: игрок должен продержаться, например, 6 часов игрового времени.
- Создайте пустой GameObject (например,
TimerVictory). - Добавьте компонент TimerObject.
- Настройте:
- Duration =
21600(6 × 3600 секунд). - Count Up =
true. - Looping =
false. - Time Scale = множитель скорости игры (например,
24, если 1 реальная минута = 24 игровых минуты). - Auto Start =
true(или запускайте по событию старта игры).
- Duration =
- В On Timer Completed привяжите вызов победы (например, GM.Win() или свой UnityEvent перехода на экран победы).
Цель: через случайные интервалы «включать» следующую аномалию.
- Создайте GameObject (например,
TimerAnomalySpawn). - Добавьте TimerObject.
- Настройте:
- Use Random Duration =
true. - Random Duration Min / Max = желаемый диапазон в секундах (например, 5–30).
- Looping =
true. - Time Scale = тот же множитель, что и у таймера победы (чтобы интервалы были в игровом времени).
- Auto Start =
true(или при старте игры).
- Use Random Duration =
- В On Timer Completed привяжите вызов Selector.SetRandom() у вашего Selector с аномалиями. Таймер сам будет вызывать команду выбора следующей аномалии по истечении интервала.
- Создайте родительский объект (например,
AnomalyManager). - Сделайте дочерними объекты-аномалии (каждый — префаб или объект с визуалом и, при необходимости, VisualToggle / ToggleObject).
- На каждый дочерний объект добавьте компонент SelectorItem. Индекс проставится автоматически при обновлении списка детей.
- На родителя
AnomalyManagerдобавьте Selector:- Включите Auto Update From Children (по умолчанию включено).
- Включите Use Random Selection.
- Включите Notify Selector Items Only: на дочернем объекте с
SelectorItemселектор вызывает толькоSelectorItem.SetActive(сам компонент не дергаетGameObject.SetActiveна этом объекте — только реактивное поле и UnityEvent). ПрямойGameObject.SetActiveселектор использует только для элементов безSelectorItem, если они есть в списке. - Флаг Control Game Object Active в инспекторе — это общий «разрешить прокидывать выбор в элементы». Для сценария «только через SelectorItem» оставьте его включённым, иначе селектор не вызовет ни
GameObject.SetActive, ниSelectorItem.SetActive(останутся только индекс иOnSelectionChanged*).
- На каждом SelectorItem:
- В On Activated привяжите показ аномалии (например, вызов VisualToggle.SetActive(true) на том же объекте).
- При «исправлении» аномалии игроком вызовите ExcludeFromSelector() (например, кнопкой или другим событием), чтобы этот индекс больше не участвовал в случайном выборе до сброса.
- При необходимости сброса пула (новая игра или новый уровень) вызовите Selector.IncludeAllIndices().
Если одновременно активно слишком много аномалий — поражение; игрок должен «чинить» аномалии, чтобы вернуться в безопасное состояние.
- Используйте NeoCondition (или аналог проверки по полю).
- В качестве объекта проверки укажите ваш Selector.
- Настройте условие по полю CountActive (например, «больше или равно 4»).
- При выполнении условия вызовите поражение (например, GM.Lose()).
Чтобы игрок мог чинить аномалии кликом/клавишей:
- На каждом объекте-аномалии добавьте коллайдер (Collider/Collider2D) подходящей формы.
- Добавьте компонент InteractiveObject (
Scripts/Tools/InteractableObject/InteractiveObject.cs):- Включите Use Mouse Interaction (по умолчанию включено) или Use Keyboard Interaction (клавиша, например
E). - Убедитесь, что
Include Trigger Colliders In Mouse Raycastвключён, если используетеIs Trigger-коллайдер.
- Включите Use Mouse Interaction (по умолчанию включено) или Use Keyboard Interaction (клавиша, например
- В событии On Interact Down InteractiveObject привяжите вызов SelectorItem.ExcludeFromSelector() (на том же объекте или через ссылку), а также, при необходимости, скрытие визуала (например, VisualToggle.SetInactive).
- При вызове ExcludeFromSelector соответствующая аномалия перестанет выбираться Selector.SetRandom(), а CountActive уменьшится, что отодвигает поражение.
- TimerObject «победа»: duration = 21600 (6 ч), countUp, не looping; On Timer Completed → победа.
- TimerObject «спавн»: useRandomDuration (например, 5–30 с), looping; On Timer Completed → Selector.SetRandom().
- При необходимости оба таймера могут использовать один и тот же Time Scale, чтобы и время до победы, и интервалы появления аномалий шли в одной «игровой» шкале времени. Это опционально: таймер победы и таймер спавна можно ускорять по-разному или оставить один из них в реальном времени.
Если на уровне (например, «замок») нужно включать случайное число аномалий от 0 до 5, а при переходе на следующий день уровень сбрасывается и аномалии не должны повторяться в течение дня:
- RandomRange: добавьте компонент RandomRange на объект (например, уровень или менеджер). Mode = Int, Min = 0, Max = 5. При старте уровня вызовите Generate() (из стартового события сцены или таймера).
- NeoCondition по RandomRange: создайте NeoCondition с условием «объект — RandomRange, поле ValueInt, оператор GreaterOrEqual, порог 1». При On True включите таймер спавна аномалий (например, вызов TimerAnomalySpawn или активация GameObject с таймером). Так таймер будет работать только если выпало хотя бы 1 аномалия.
- Ограничение числа активных аномалий: можно добавить второе условие по Selector.CountActive (например, «меньше ValueInt») и вызывать Selector.SetRandom() по таймеру только когда CountActive < ValueInt (через NeoCondition или отдельную логику).
- Для нового дня используйте сам Selector: при переходе на следующий день или перезагрузке уровня вызовите Selector.IncludeAllIndices() и, если нужно, Selector.ResetAll(). Так пул аномалий начнётся заново без отдельного вспомогательного компонента.
- Если в течение одного дня аномалии не должны повторяться, исключайте их по мере исправления через SelectorItem.ExcludeFromSelector() или напрямую через Selector.ExcludeIndex(int). В конце дня очищайте исключения вызовом IncludeAllIndices().