Применение GameplayKit Randomization и State Machine в iOS-проектах
В предыдущей статье было описано, как применять игровой 2D-движок SpriteKit для быстрого создания простых анимаций в iOS. В новой статье я хочу поделиться, как использовать GameplayKit в неигровых приложениях.
GameplayKit — это набор инструментов, который Apple представляет для быстрого конструирования игровых процессов и алгоритмов. Рассмотрим инструменты, которые применимы даже в UIKit/Appkit-проектах.
Randomization
Так называется инструмент, позволяющий применять различные алгоритмы рандома, которые довольно часто приходится использовать в играх. Здесь не будет обсуждаться генерация рандомных чисел для создания секретных ключей шифрования, так как даже в самой документации у Apple указано, что эти сервисы рандомизации не являются криптографически устойчивыми, и для таких целей рекомендуется применять совсем другие инструменты.
Раньше чаще всего многие применяли метод random() или arc4random(), построенный на ARC4-алгоритме и генерирующий числа между 0 и 4294967295. После выхода Swift 4.2 появились новые методы для генерации рандома:
let randomInt = Int.random(in: 0..<10) let randomDouble = Double.random(in: 5.71838...6.15249) let randomBool = Bool.random()
Обычно этих инструментов достаточно, если нам нужно просто сгенерировать случайное число, не задумываясь о последствиях. В таких случаях вы никак не сможете влиять на алгоритм рандомизации, последовательность и частоту выпадения определенных значений. А если попытаться влиять на этот процесс, производя генерацию несколько раз, чтобы получать нужный range значений, и еще это нужно делать на каждый кадр, то производительность работы может сильно пострадать. Такая ситуация обусловлена тем, что в современных играх часто происходит одновременная генерация нескольких случайных чисел за один кадр и, таким образом, чтобы поддерживать 60 кадров в секунду, придется несколько десятков, а иногда и сотен раз в секунду инициализировать генерацию и обработку случайных чисел.
Такой подход имеет и проблемы в создании последовательности одинаковых чисел у двух и более пользователей, особенно если эти люди используют различные платформы, код которых написан на других языках программирования.
Именно для этого и применяется Randomization из GameplayKit, позволяя сделать генерацию более детерминированной.
Random Source
Собственно, весь процесс рандома состоит из объекта-суперкласса GKRandomSource, который является источником рандомных чисел (Random Source), а также наследования от протокола GKRandom.
Сам протокол GKRandom представляет минимальный интерфейс для генерации случайных чисел и состоит всего из 4 методов:
let randomSource = GKRandomSource.sharedRandom() // возвращает случайное значение Int32.min и Int32.max // диапазон чисел от -2 147 483 648 до 2 147 483 647 randomSource.nextInt() // возвращает случайное значение Int между 0 и 9 randomSource.nextInt(upperBound: 10) // возвращает случайное Float значение в диапазоне от 0.0 до 1.0 randomSource.nextUniform() // возвращает случайное Bool randomSource.nextBool()
GameplayKit предлагает один базовый и 3 альтернативных Random Source, которые являются детерминированными и могут быть сериализованы с использованием NSCoding, чтобы, к примеру, была возможность сохранить текущее состояние последовательности.
- GKRandomSource — базовый генератор случайных чисел, от которого наследуются все последующие Random Source классы.
- GKARC4RandomSource — генератор случайных чисел, реализующий уже привычный в iOS алгоритм ARC4 (arc4random). Особенность также состоит в том, что у этого источника есть метод dropValues(_:), который помогает отбросить определенное количество первых последовательностей, чтобы было сложнее предугадать вероятное следующее значение.
let arc4 = GKARC4RandomSource() // Минимальное рекомендуемое количество отбрасываемых значений в последовательности arc4.dropValues(768) // Генерация случайного числа от 0 до 10 arc4.nextInt(upperBound: 11)
- GKLinearCongruentialRandomSource — генератор чисел, реализующий алгоритм линейного конгруэнтного генератора, который быстрее, но менее случайный, чем стандартный ARC4. Основное преимущество его в том, что этот алгоритм есть в стандартных библиотеках некоторых языков программирования. Поэтому иногда его можно применять для создания одинаковой последовательности случайных чисел на разных платформах. К примеру, в Java этот алгоритм используется в java.util.Random. Также его стоит применять в том случае, если вы действительно делаете десятки или сотни генераций в секунду, иначе разница в производительности будет практически незаметна.
let linearCongruential = GKLinearCongruentialRandomSource() // Генерация случайного числа от 0 до 10 linearCongruential.nextInt(upperBound: 11)
- GKMersenneTwisterRandomSource — генератор случайных чисел, реализующий алгоритм вихрь Мерсенна, разработанный японскими учеными, который является более случайным, но и менее производительным, чем ARC4. Реализован в стандартных библиотеках: C++, Python, Ruby, PHP.
let mersenneTwister = GKMersenneTwisterRandomSource() mersenneTwister.nextInt(upperBound: 11)
Очень удобно, что все эти источники имеют одинаковый интерфейс и не нужно каждый раз изучать специфику использования каждого в отдельности.
За счет того, что все эти классы наследуются от GKRandomSource, который является суперклассом для всех представленных алгоритмов, это позволяет создавать сразу все генераторы независимыми друг от друга и в то же время детерминированными. При этом мы можем легко производить репликацию с сохранением последовательности каждого из алгоритмов.
Random Distribution
Еще одним важным преимуществом рандомизации через GameplayKit является возможность формировать Random Source вместе с Random Distribution (методом случайного распределения).
Всего нам представлено 3 класса для Random Distribution:
- GKRandomDistribution — распределение, где равномерная вероятность генерации любого числа в указанном диапазоне приблизительно равнозначна. Таким образом, исключается предвзятость в отношении любого возможного результата. Что приятно, этот класс имеет удобный интерфейс, чтобы сразу инициализировать аналог
6-гранного кубика, или20-гранного кубика, или даже100-гранного кубика.
// Это также можно сделать через GKRandomDistribution.d6() letПохожие статьи:
В этом году в международном хакатоне NASA SpaceApps Challenge 15 310 участников из 61 страны разрабатывали 1178 проектов. Украина приняла участие...
Українська компанія Ajax Systems оголосила про вихід на ринок Бразилії. Про це компанія повідомила DOU. Це розширення є частиною...
У березні стало відомо, що український стартап Preply залучив $35 млн інвестицій від бізнес-ангелів. Напередодні цієї події...