Применение 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 


					  
Похожие статьи:
Бомбосховище у театрі Маріуполя витримало удар, тим часом росіяни обстріляли притулок для матерів із дітьми у Сєвєродонецьку. DOU...
Всем привет! Меня зовут Виктор и я работаю менеджером проектов в компании Cogniance. Делюсь дюжиной интересных материалов...
Відбулися перші успішні випробування безпілотника українського виробництва. Вітчизняний БпЛА «полетить» вже...
Компанія Rozetka вже місяць не виплачує у повному обсязі зарплати розробникам, дизайнерам і маркетологам....
Домінік Піоте пішов з поста CEO інноваційного парку Unit.City. Натомість там створили дорадчу раду. Про те,...
Яндекс.Метрика