В этом разделе обучения на примере игры «Головоломка из лампочек с тегами» вы узнаете, как определить, что игрок решил головоломку. После этого можно будет создать предмет и отключить взаимодействие с головоломкой.
Определение момента решения
В этом разделе показано, как определить, когда игрок решает головоломку, подобрав верную комбинацию лампочек. После решения головоломки нужно активировать устройство «Генератор предметов», чтобы создать предмет.
Выполните следующие действия, чтобы отследить момент решения головоломки:
- Добавьте редактируемое поле
ItemSpawnerтипаitem_spawner_deviceв классtagged_lights_puzzle. Оно будет представлять устройство «Генератор предметов».@editable ItemSpawner : item_spawner_device = item_spawner_device{} - Затем определите, в каком состоянии должны быть лампочки, чтобы решить головоломку. Так как представление состояния лампочек является массивом значений типа
logic, комбинация состояний лампочек, решающая головоломку, должна быть представлена массивом значений того же типа, что позволит нам сравнивать их. В классеtagged_lights_puzzleсоздайте редактируемое поле, представленное массивом значений типаlogicс названиемSolvedLightsState. В этом примере игрок должен включить все лампочки, чтобы решить головоломку, поэтому инициализируйте каждый элемент значениемtrue, что будет соответствовать включённому состоянию.@editable SolvedLightsState : []logic = array{true, true, true, true} - Сохраните файл в Visual Studio Code.
- Чтобы обновить устройство Verse на уровне, нажмите на Создать сценарии Verse на панели инструментов в UEFN.
- Чтобы открыть панель Сведения, выберите tagged_lights_puzzle в разделе Окно сборки.
- На панели «Сведения» задайте свойство Генератор объектов для устройства «Генератор объектов», расположенного на уровне.
- Далее необходимо написать код, который будет проверять, совпадает ли текущее состояние
LightsStateсSolvedLightsState. В классtagged_lights_puzzleдобавьте новый методIsPuzzleSolved(). Этот метод должен успешно выполняться, если головоломка решена, а также завершаться ошибкой, если она ещё не решена: это позволит нам получить необходимый результат проверки. Это означает, что метод должен быть помечен в качестве выражения с возможной ошибкой со спецификаторомdecides. Поскольку метод будет иметь спецификаторdecides, он также должен иметь спецификаторtransacts, что будет означать, что для действий, выполняемых внутри этого метода, может быть выполнен откат (как если бы эти действия никогда не выполнялись), если сам метод завершится с ошибкой.IsPuzzleSolved()<decides><transacts> : void = Logger.Print(“Проверяем, решена ли головоломка”)В языке Verse для принятия решения используются категории «успех»/«ошибка». Подробнее о том, как происходит обработка ошибок в Verse, описано в разделе Ошибки.
- Когда устройство должно проверять, решена ли головоломка? Проверку лучше всего выполнять сразу после изменения массива
LightsState, которое происходит внутри методаToggleLights(). Как только в выражении сforзавершится обновление массиваLightsState, вызовите методIsPuzzleSolved[], чтобы определить, решена ли головоломка и, при необходимости, создать предмет (вызвав методItemSpawner.Enable()). ПосколькуIsPuzzleSolved[]является выражением с возможной ошибкой (именно поэтому при вызове метода используются квадратные скобки[]вместо круглых()), вызывать его следует в контексте, допускающем ошибку. В этом примере такой контекст создаётся при помощи выражения сif, поэтому вы можете выполнять те или иные операции при соблюдении желаемых условий, в зависимости от того, решена ли головоломка.ToggleLights(LightIndices : []int) : void = for: LightIndex : LightIndices IsLightOn := LightsState[LightIndex] Light := Lights[LightIndex] do: Logger.Print("Перевожу лампочку с индексом {LightIndex} в положение {if (IsLightOn?) then "Выкл." else "Вкл."}") NewLightState := if (IsLightOn?): Light.TurnOff() ложь else: Light.TurnOn() истина if (set LightsState[LightIndex] = NewLightState): Logger.Print("Обновлено состояние лампочки с индексом {LightIndex}") if (IsPuzzleSolved[]): Logger.Print("Головоломка решена!") ItemSpawner.Enable() - Далее необходимо сделать так, чтобы метод
IsPuzzleSolved()определял, решена ли головоломка. Поскольку методIsPuzzleSolved()является контекстом, допускающим неоднозначность, вы можете использовать выражения с неоднозначным результатом внутри метода без необходимости дополнительно использовать другой такой контекст, например выражение сif. В этом случае необходимо проверить, совпадает ли состояние каждой из лампочек с состоянием решённой головоломки. Чтобы проверить, равны ли два значения типаlogic, вы можете использовать оператор равенства=, который применяется в выражениях с неоднозначным результатом. Если два сравниваемых значения не совпадают, выражение заканчивается ошибкой, из-за чего метод также завершается с ошибкой и возвращается к контексту вызова (в данном случае это выражение сifв методеToggleLights()).IsPuzzleSolved()<decides><transacts> : void = Logger.Print(“Проверяем, решена ли головоломка”) for: LightIndex -> IsLightOn : LightsState IsLightOnInSolution := SolvedLightsState[LightIndex] do: IsLightOn = IsLightOnInSolution - Сохраните изменения в Visual Studio Code.
- На панели инструментов UEFN нажмите на Создать сценарии Verse, чтобы скомпилировать код.
- Нажмите Играть на панели инструментов UEFN, чтобы протестировать уровень.
При тестировании уровня с настройками из этого примера для решения головоломки и создания предмета достаточно нажать на каждую кнопку один раз.

Блокировка взаимодействия с кнопками после решения головоломки
После решения головоломки игрок уже не должен иметь возможности взаимодействовать с кнопками и включать/выключать лампочки. В этом разделе мы разберём, как отписаться от события, чтобы обработчик событий больше не вызывался при взаимодействии игрока с кнопками.
При вызове Subscribe() для события устройства вызов функции будет иметь результат типа cancelable. Вызов Cancel() для переменной cancelable отменяет подписку функции, обрабатывающей событие, что приведёт к тому, что функция больше не будет вызываться при срабатывании события.
Выполните следующие действия, чтобы заблокировать взаимодействие с кнопками после решения головоломки:
- К классу
tagged_lights_puzzleдобавьте поле, представленное массивомButtonSubscriptionsдля переменных типаcancelable. В нём будет храниться результат подписки на события каждой из кнопок. Инициализируйте поле как пустой массив.var ButtonSubscriptions : []cancelable = array{} - Поскольку это последнее выражение в цикле
for, возвращаемые значения для всех успешных итераций будут добавляться в массив, имеющий тип, соответствующий типу результата, возвращаемого выражением. Как уже объяснялось ранее, вызов событияSubscribeвозвращает результат типаcancelable; это позволяет соотнести ToggleLights с результатамиforв массивеcancelable([]cancelable). Это соответствует типуButtonsSubscription, поэтому вы можете присвоить результат выраженияforмассивуButtonsSubscription. Добавьте этот код в функциюOnBeginпослеSetupPuzzleLights:OnBegin<override>()<suspends> : void = SetupPuzzleLights() set ButtonSubscriptions = for: ButtonIndex -> Button : Buttons LightIndices := ButtonsToLights[ButtonIndex] do: Button.InteractedWithEvent.Subscribe(button_event_handler{Indices := LightIndices, PuzzleDevice := Self}.OnButtonPressed) - Наконец, помните, что кнопки необходимо отключить, чтобы игрок больше не мог изменять
LightsState. Для этого мы убираем привязку для обработчиковInteractedWithEventу каждой кнопки с помощью вызова подписки типаcancelable.ButtonSubscriptionsбыли сохранены при создании обработчиков событий в функцииOnBegin. Осталось лишь выполнить итерацию по массиву и вызвать отмену для каждойButtonSubcription:ToggleLights(LightIndices : []int) : void = for: LightIndex : LightIndices IsLightOn := LightsState[LightIndex] Light := Lights[LightIndex] do: Logger.Print("Перевожу лампочку с индексом {LightIndex} в положение {if (IsLightOn?) then "Выкл." else "Вкл."}") NewLightState := if (IsLightOn?): Light.TurnOff() ложь else: Light.TurnOn() истина if (set LightsState[LightIndex] = NewLightState): Logger.Print("Обновлено состояние лампочки с индексом {LightIndex}") if (IsPuzzleSolved[]): Logger.Print("Головоломка решена!") ItemSpawner.Enable() for (ButtonSubscription : ButtonSubscriptions): ButtonSubscription.Cancel() - Сохраните изменения в Visual Studio Code.
- На панели инструментов UEFN нажмите на Создать сценарии Verse, чтобы скомпилировать код.
- Нажмите Играть на панели инструментов UEFN, чтобы протестировать уровень.
Если используются значения по умолчанию, для решения головоломки, создания предмета и отключения кнопок достаточно нажать на каждую кнопку один раз.
Что дальше
В последнем разделе этого урока вы найдёте полный сценарий, а также идеи для самостоятельной работы над этим примером.