Неповторимый рандом
Доброго времени суток!
Вероятно все вы знаете, что в RpgMaker есть переменные и возможность присваивать им случайное значение в некотором диапазоне.
Это бесспорно удобная функция, которую можно использовать для большого количества игровых ситуаций.
Но что делать, если нам нужно, чтобы все случайные значения были уникальными и не повторялись? Например, мы делает игру-викторину с уникальными вопросами или некий текстовой квест со случайными событиями, которые не должны повторяться. Конечно, можно стандартными средствами мейкера, переключателями и проверками условий, но это муторно в настройке и крайне неудобно, когда такого рандома должно быть больше 10 вариантов.
И тут на помощь нам приходит команда скрипт и тот факт, что в мейкере можно выполнять код на
JavaScript; так как
данный урок для MV (и MZ) в Ruby для Xp-Vx-Vx Ace, вероятно делается схожим подходом, но с
другим синтаксисом.
Уточним условия примера. У нас игра-викторина, при которой 9 вопросов должны задаваться в любом порядке, но только по одному разу…то есть не повторяться (число вопросов может быть любым!).
Достигнуть подобного можно, храня перечень номеров вопросов в массиве, который будет хранится в определенной переменной мейкера и, после выбора случайного номера вопроса, данный номер будет удаляться из массива и отфильтровываться.
Для этого выполним несколько простых действий (номера переменных могут быть любыми):
1)В инициализирующем событии, в команде «Скрипт», напишем такой код:
let randomNumbersList = [1, 2, 3, 4, 5, 6, 7, 8, 9];
$gameVariables.setValue(1, randomNumbersList);
где:
«randomNumbersList» - массив с номерами вопросов;
«$gameVariables.setValue()» - сохранение массива в определенной переменной мейкера.
2)В событии, в котором мы выбираем номер, в команде «Скрипт», напишем такой код:
let randomNumbersList = $gameVariables.value(1);
let randomIndex = Math.floor( Math.random() * randomNumbersList.length );
let randomNumber = randomNumbersList[randomIndex];
$gameVariables.setValue(2, randomNumber);
delete randomNumbersList[randomIndex];
let uniqueRandomNumbersList = randomNumbersList.filter( elem => elem );
$gameVariables.setValue(1, uniqueRandomNumbersList);
где:
«$gameVariables.value()» - получение массива с номерами вопросов из переменной мейкера и сохранение его во временную переменную «randomNumbersList»;
«randomIndex» - получение случайного индекса массива, под которым хранится номер вопроса;
«randomNumber» - получение номера вопроса при помощи случайного индекса массива;
«$gameVariables.setValue()» - здесь это сохранение выбранного номера вопроса в определенной переменной мейкера;
«delete randomNumbersList[randomIndex]» - удаление номера вопроса, выбранного по случайному индексу, из массива с вопросами.
И вот тут важный момент! После удаления элементов, в массиве остаются пустые места. Поскольку мы не хотим для выбора вопроса вводить какие-то доп. проверки, нам нужно исключить из массива вопросов эти самые пустые места. Как? Фильтрацией, а именно:
«randomNumbersList.filter( elem => elem )»
Данный фрагмент кода возвращает массив с номерами вопросов, но уже без пустых мест. Ну и дальше мы привычным способом сохраняем его в переменную мейкера, причем в ту же, в какую писали при инициализации:
«$gameVariables.setValue»
Как видите, несколько строчек кода позволяет удобным способом сделать нам рандом неповторимым и уникальным.
Ну а сами номера вопросов в нужном событии мы используем в условии по переменной, в которой сохранили номер.
И с каждым вызовом скрипта из
пункта №2 массив с вариантами ответов будет уменьшаться...пока не достигнет нуля и массив будет пустым, весь. Поэтому количество ответов / случайных событий / для чего вы это используете нужно контролировать, как минимум учитывая в отдельной переменной сколько раз этот рандом уже использовали (в нашем случае - сколько ответов уже дали).
Вот и все, спасибо за внимание!
P.S: допускаю, что такой урок уже был – но поиском я не нашел…в случае, если такое уже было – смело снимайте с конкурса.
update-1 от 10.11.2021
Спасибо
Lekste, он напомнил, как важно правильно называть переменные.
Изменен код, в фильтрации "index" заменен на "elem", так как первая переменная фильтра - элемент массива.
И
Lekste прав, есть еще один вариант кода фильтрации, без удаления элемента из массива, а именно:
randomNumbersList.filter( (elem, index) => index != randomIndex );
где
"elem" - элемент массива, первый параметр фильтра;
"index" - собственно индекс элемента;
в условии фильтрации указываются, что возвращаются все индексы, кроме равного случайно выбранному.
При использовании такого подхода
удаление при помощи:
"delete randomNumbersList[randomIndex];"
уже не нужно!