Статьи

SpriteKit From Scratch: визуальные и аудиоэффекты

В этом руководстве, четвертом выпуске серии SpriteKit From Scratch , мы рассмотрим различные визуальные и звуковые функции, предоставляемые SpriteKit, чтобы добавить больше деталей и разнообразия в ваши игры. Это включает системы частиц, фильтры, освещение и аудио.

Чтобы следовать за мной, вы можете использовать проект, который вы создали в предыдущем уроке этой серии, или загрузить свежую копию с GitHub .

Графика, используемая для игры в этой серии, может быть найдена на GraphicRiver . GraphicRiver — отличный источник для поиска иллюстраций и графики для ваших игр.

В SpriteKit, термин система частиц   ссылается на один узел эмиттера, представленный классом SKEmitterNode . Он определяет положение системы в вашей сцене и все частицы, которые она создает. Излучатель определяет различные виды поведения частиц, которые он генерирует.

Системы частиц лучше всего использовать в играх SpriteKit, где вам нужно генерировать большое количество идентичных или похожих спрайтов, которым не нужно иметь конкретное местоположение или выполнять какие-либо действия.

В этом уроке мы собираемся добавить две системы частиц, когда автомобиль сталкивается с препятствием:

  • кратковременный эффект взрыва
  • эффект дыма, который остается в сцене на неопределенный срок

Хотя системы частиц могут быть созданы программно, это гораздо проще сделать с помощью встроенного редактора Xcode. Все свойства системы частиц могут быть изменены в этом редакторе, а сделанные вами изменения немедленно визуализируются. Это гораздо проще, чем запускать игру после каждого внесенного изменения.

Сначала мы собираемся создать эффект взрыва. Создайте новый файл в своем проекте и выберите iOS> Resource> SpriteKit Particle File template.

Шаблон файла частиц

В появившемся меню выберите « Огонь» в качестве шаблона частиц . Назовите файл Explosion или что-то подобное.

Шаблон огненной частицы

После того, как Xcode создал файл, вы видите, что в вашем проекте есть два новых файла, Explosion.sks и spark.png .

Добавлены два файла

Explosion.sks содержит систему частиц, и это файл, с которым мы будем работать. Второй файл, spark.png , представляет собой простое изображение, используемое шаблоном частиц Fire для создания визуального эффекта. Если вы откроете Explosion.sks , вы увидите оживление огня.

Система огненных частиц

Наиболее важные изменения, которые нам необходимо внести в эту систему частиц, — это перемещение частиц наружу от эмиттера во всех направлениях и непрерывное появление новых частиц.

Чтобы сделать первое изменение, откройте инспектор атрибутов и в разделе « Частицы » измените диапазон углов на 360 ° .

Диапазон изменения угла

Прямо сейчас вы можете видеть, что частицы теперь движутся наружу в форме круга.

Круговой Огонь

Чтобы остановить систему частиц от непрерывного создания новых частиц, мы можем указать максимальное значение. Это значение говорит системе частиц, сколько всего частиц она должна создать. Значение по умолчанию 0 означает, что максимума нет, что приводит к непрерывному созданию новых частиц.

Помимо указания максимального значения, мы также собираемся изменить несколько других свойств, чтобы создать лучший эффект взрыва. В разделе « Частицы » Инспектора атрибутов измените следующие значения:

Пользовательские свойства взрыва

Мы устанавливаем для параметра « Скорость рождения» значение, превышающее свойство « Максимум», поскольку оно определяет, сколько частиц создается в секунду . Мы хотим, чтобы взрыв произошел очень быстро. Таким образом, вместо того, чтобы иметь 1000 частиц, порождаемых в течение полных секунды, что могло бы произойти при коэффициенте рождаемости 1000, мы указываем коэффициент рождаемости 5000. Это означает, что все частицы создаются всего за 0,2 секунды.

Установив Lifetime> Start на 3, частицы живут в течение 3 секунд. Свойство Lifetime Range можно использовать для добавления вариаций времени жизни частиц.

Наконец, мы устанавливаем Speed> Start на 200, чтобы частицы очень быстро вылетали из эмиттера, как это произошло бы при реальном взрыве.

После внесения этих изменений вы можете видеть, что система частиц выглядит совсем иначе и больше похожа на настоящий взрыв.

Взрывная система частиц

Обратите внимание, что, хотя анимация периодически повторяется в редакторе Xcode, система частиц анимируется только один раз при добавлении в вашу сцену.

Когда система частиц взрыва завершена, пришло время перейти к системе частиц дыма. Создайте новый файл Smoke , используя тот же шаблон, который мы использовали для взрыва. Единственное отличие — это шаблон Particle , который мы установили на Smoke .

Шаблон частиц дыма

Единственное изменение, которое нам нужно внести в эту систему частиц, — это движение дыма по кругу, а не просто прямо вверх. Для этого измените свойство Angle> Range на 360 °, как мы делали для системы частиц взрыва. После этого система частиц дыма должна выглядеть примерно так:

Система частиц дыма

Когда обе системы частиц готовы, мы можем добавить их к нашей сцене. Для этого мы загружаем каждый из файлов, которые мы создали как объекты SKEmitterNode а затем добавляем их в сцену как обычный узел. Откройте MainScene.swift и замените реализацию didBeginContact(_:) следующим:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
func didBeginContact(contact: SKPhysicsContact) {
    if contact.bodyA.node == player ||
        if let explosionPath = NSBundle.mainBundle().pathForResource(«Explosion», ofType: «sks»),
            let smokePath = NSBundle.mainBundle().pathForResource(«Smoke», ofType: «sks»),
            let explosion = NSKeyedUnarchiver.unarchiveObjectWithFile(explosionPath) as?
            let smoke = NSKeyedUnarchiver.unarchiveObjectWithFile(smokePath) as?
             
            player.removeAllActions()
            camera?.removeAllActions()
            player.hidden = true
            player.removeFromParent()
             
            explosion.position = player.position
            smoke.position = player.position
             
            addChild(smoke)
            addChild(explosion)
        }
    }
}

Как и в предыдущей реализации didBeginContact(_:) , мы выполняем ту же проверку, что и раньше, чтобы увидеть, является ли какой-либо из узлов, участвующих в столкновении, автомобильным узлом. Затем мы используем необязательную привязку, чтобы получить пути к системным файлам взрывчатых и дымовых частиц. Мы используем эти пути для создания из них объектов SKEmitterNode .

Затем мы удаляем все действия с камер и узлов плеера и скрываем узел плеера, удаляя его со сцены. Мы удаляем машину, чтобы избежать новых столкновений, которые затем приводят к большему количеству взрывов.

Мы также устанавливаем положение узлов-эмиттеров в положение машины и добавляем их в сцену. В результате SpriteKit немедленно начинает анимировать системы частиц, как только они добавляются в сцену.

Создайте и запустите свою игру. Вы должны увидеть систему частиц взрыва, как только автомобиль столкнется с препятствием. За этим следует дым после того, как огонь рассеялся.

Частицы в сцене

В SpriteKit есть специальный тип узла (представленный классом SKEffectNode ), который может использовать объект фильтра Core Image (представленный классом CIFilter ) для визуализации своих дочерних узлов с различными эффектами. Класс SKScene также является подклассом SKEffectNode , что означает, что вы также можете применить фильтр ко всей сцене.

К сожалению, на момент написания этого руководства было несколько проблем, связанных с этими фильтрами и узлами эффектов в iOS 9. В настоящее время, когда эффект включен для узла эффекта, все его дочерние элементы скрываются, что приводит к эффект не виден.

Несмотря на то, что мы не можем внедрить это в нашу игру и посмотреть, как она выглядит, мы все равно можем выполнить код, который будет использоваться для создания эффекта. В этом случае следующий метод является примером добавления и постепенного исчезновения эффекта размытия для всей сцены.

01
02
03
04
05
06
07
08
09
10
11
12
func addBlurFilter() {
    let blurFilter = CIFilter(name: «CIGaussianBlur»)
    blurFilter?.setDefaults()
    blurFilter?.setValue(0.0, forKey: «inputRadius»)
    filter = blurFilter
    shouldEnableEffects = true
     
    runAction(SKAction.customActionWithDuration(1.0, actionBlock: { (node: SKNode, elapsedTime: CGFloat) in
        let currentRadius = elapsedTime * 10.0
        blurFilter?.setValue(currentRadius, forKey: «inputRadius»)
    }))
}

Мы создаем объект CIFilter определенного типа. Если вы хотите взглянуть на некоторые другие доступные вам встроенные фильтры, ознакомьтесь со справочником по фильтру основных изображений . Мы гарантируем, что этот фильтр имеет все входные значения по умолчанию, а затем вручную устанавливаем inputRadius на 0.0 , что означает, что изначально не было размытия.

Затем мы назначаем filter свойству filter текущей сцены и устанавливаем для shouldEnableEffects значение true чтобы включить его. Наконец, мы запускаем пользовательский SKAction который постепенно увеличивает входной радиус фильтра до 10 .

Надеемся, что в будущем выпуске iOS проблемы, влияющие на узлы эффектов, будут исправлены, поскольку они предоставляют способ добавить некоторые уникальные и интересные эффекты в ваши сцены SpriteKit.

SpriteKit также включает в себя отличную систему освещения, которая позволяет сделать ваши сцены более реалистичными. SKLightNode света очень просты в реализации и создаются с помощью класса SKLightNode . Узел источника света определяет определенные свойства, такие как цвет источника света (включая цвет окружающей среды) и его силу на расстоянии.

В нашей сцене мы собираемся создать один белый свет, который будет прикреплен к машине. Этот свет будет освещать препятствия перед автомобилем, а также создавать тени.

Давайте начнем с создания источника света в didMoveToView(_:) вашего класса MainScene .

01
02
03
04
05
06
07
08
09
10
override func didMoveToView(view: SKView) {
    
   …
    
    let light = SKLightNode()
    light.lightColor = UIColor.whiteColor()
    light.falloff = 0.5
     
    player.addChild(light)
}

С помощью этого кода мы создаем новый объект SKLightNode , меняем его свойство lightColor на белое и lightColor его свойство lightColor со значения по умолчанию от 1 до 0,5 .

Как и при настройке обнаружения физических столкновений в SpriteKit, вы должны указать, какие источники света взаимодействуют с какими узлами в сцене с помощью битовых масок. Когда SpriteKit визуализирует источники света в сцене, он использует логический оператор AND в categoryBitMask lightingBitMask и lightingBitMask и shadowCastBitMask каждого другого узла, чтобы определить, как должен выглядеть этот конкретный узел.

В нашей игре мы хотим, чтобы препятствия взаимодействовали со светом, чтобы они отбрасывали тени на сцену. Для этого добавьте следующие две строки в конец метода spawnObstacle(_:) класса MainScene :

1
2
3
4
5
6
7
func spawnObstacle(timer: NSTimer) {
     
    …
     
    obstacle.lightingBitMask = 0xFFFFFFFF
    obstacle.shadowCastBitMask = 0xFFFFFFFF
}

При установке битовой маски со всеми включенными битами препятствия взаимодействуют с каждым источником света в сцене.

Создайте и запустите ваше приложение. Вы увидите, что, когда ваша машина движется по сцене, каждое препятствие имеет динамическую тень, которая всегда направлена ​​в сторону от центра автомобиля.

Тени в сцене

Как видите, источники света в SpriteKit очень просты в использовании и могут добавлять приятные эффекты в ваши сцены.

Наконец, мы рассмотрим аудио узлы в SpriteKit. Аудио узлы используются для добавления звуковых эффектов в сцену. Эти специальные узлы представлены классом SKAudioNode . Поскольку SKAudioNode является подклассом SKNode , вы можете добавлять и SKNode их в любом месте сцены, как обычный узел.

В дополнение к регулярному воспроизведению звука и одинаковому звучанию независимо от того, как устроена ваша сцена (например, фоновая музыка), SpriteKit позволяет использовать позиционное аудио для создания действительно захватывающего эффекта. Это делается путем указания узла listener для вашей сцены, откуда звук будет «слышен».

Аудио узлы являются позиционными по умолчанию. Это означает, что, если вы не хотите использовать эту функциональность в определенных случаях, вы можете установить для positional свойства определенного узла значение false .

Хотя мы не будем реализовывать это в нашей игре, ниже приведен пример метода добавления узла фоновой музыки, который зацикливается, пока он является частью сцены. В методе мы также добавляем узел звука взрыва, который начинает играть, когда мы говорим об этом.

Обратите внимание, что мы импортируем фреймворк AVFoundation вверху. Это необходимо для доступа и работы со свойством SKAudioNode объекта SKAudioNode . Как видите, аудио узлы очень просты в настройке и работе в SpriteKit.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
import AVFoundation
 
func addAudioNode() {
     
    listener = player
     
    let backgroundMusic = SKAudioNode(fileNamed: «backgroundMusic»)
    backgroundMusic.positional = false
     
    let explosion = SKAudioNode(fileNamed: «explosion»)
    explosion.autoplayLooped = false
     
    addChild(backgroundMusic)
    addChild(explosion)
     
    do {
        try explosion.avAudioNode?.engine?.start() // Called when you want to play sound
    } catch {
        // Do something with the error
    }
}

Теперь вам должно быть удобно работать с некоторыми более продвинутыми эффектами в SpriteKit, включая системы частиц, фильтры, свет и звук. Эти комбинированные эффекты могут сильно повлиять на внешний вид вашей игры и на ее увлекательность.

В следующем и последнем учебнике этой серии мы рассмотрим некоторые из лучших практик, которые следует учитывать при работе со SpriteKit. Я также покажу вам, как создавать текстурные атласы и сохранять / загружать сцены.

Как всегда, обязательно оставляйте свои комментарии и отзывы в комментариях ниже.