Getting Started With Core Haptics
In this Core Haptics tutorial, you’ll learn how to create and play haptic patterns, synchronize audio with haptic events and create dynamic haptic patterns that respond to external stimuli. By Andrew Tetlaw.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Getting Started With Core Haptics
35 mins
- Getting Started
- Adding Your First Haptic Experience
- Exploring the Events That Make up the Pattern
- Managing Energy Usage
- Designing a Haptic Experience
- Feeding the Crocodile
- Playing Different Patterns
- Syncing Audio Events
- Setting a Reset Handler
- Ramping Intensity Up and Down — Pineapple Splashdown
- Controlling Intensity With a Parameter Curve
- Updating Pattern Parameters in Real Time
- Making the Player Dynamic
- Where to Go From Here?
Apple introduced Core Haptics in iOS 13. It’s a new API that can generate custom, high-resolution tactile and audio feedback, making vibrations and rumbles feel like an old-fashioned gimmick. Get ready to immerse yourself in a world of haptic experiences.
In this tutorial, you’ll enhance an app with new haptic experiences. You’ll learn how to:
- Create haptic patterns and play them.
- Synchronize audio with haptic events.
- Create dynamic haptic patterns that change in response to external stimuli.
This tutorial is fully hands-on!
Getting Started
Download the starter project using the Download Materials button at the top or bottom of this tutorial. Open the starter project. Build and run, and… you have a game.
You’ve got the cute graphics, your physics simulation and animations are slick and your sound effects put the player right in the jungle. But you have an intense feeling that something’s missing. You’re itching to feel the *snip*-iness as you slice the vine. And what about the *ker-splosh*-iness of that pineapple landing in the water and, of course, the *nom-nom-crunch-munch*-iness when the croc eats it?
It’s time to get tactile with Core Haptics!
Adding Your First Haptic Experience
The best way to start is to quickly make a simple haptic experience and test it. Your first haptic experience will add a little *snip* that players feel when they cut the vine.
The standard process of creating a haptic experience is to:
- Check device compatibility.
- Create a haptic engine object.
- Create a pattern of haptic events.
- Make a pattern player.
- Start the haptic engine.
- Play the pattern.
- Stop the haptic engine.
To avoid adding even more code to GameScene.swift, expand SnipTheVine ▸ Classes then find and open Haptics.swift. It’s an empty file for you to use. Add the following:
import CoreHaptics
class HapticManager {
// 1
let hapticEngine: CHHapticEngine
// 2
init?() {
// 3
let hapticCapability = CHHapticEngine.capabilitiesForHardware()
guard hapticCapability.supportsHaptics else {
return nil
}
// 4
do {
hapticEngine = try CHHapticEngine()
} catch let error {
print("Haptic engine Creation Error: \(error)")
return nil
}
}
}
Here’s what this code does:
-
hapticEngine
holds a reference toCHHapticEngine
. - The initializer is failable, so you can check if it’s
nil
in the game scene and use that to indicate that haptics are unavailable. - The first thing you do in the initializer is check if haptics are available. Call
CHHapticEngine.capabilitiesForHardware()
to obtain an object that you can use to test support with a simple check ofsupportsHaptics
. - Finally, you create an engine object. The
CHHapticEngine
initializer can throw, so you need to wrap it in ado/catch
block and returnnil
if it throws an error.
do-catch
blocks for a lot of the API. This also means you need to make sure you have a fallback for any haptic experience.Now, push on to get to the point where you can test your first haptic as soon as possible.
Still in Haptics.swift, add this extension to the class:
extension HapticManager {
private func slicePattern() throws -> CHHapticPattern {
let slice = CHHapticEvent(
eventType: .hapticContinuous,
parameters: [
CHHapticEventParameter(parameterID: .hapticIntensity, value: 0.35),
CHHapticEventParameter(parameterID: .hapticSharpness, value: 0.25)
],
relativeTime: 0,
duration: 0.25)
let snip = CHHapticEvent(
eventType: .hapticTransient,
parameters: [
CHHapticEventParameter(parameterID: .hapticIntensity, value: 1.0),
CHHapticEventParameter(parameterID: .hapticSharpness, value: 1.0)
],
relativeTime: 0.08)
return try CHHapticPattern(events: [slice, snip], parameters: [])
}
}
slicePattern()
returns your first Core Haptics pattern. It creates two haptic events and a haptic pattern that uses them. You’ll explore the details of events and patterns shortly, but for now, forge on!
Finally, add a method to HapticManager
to play the pattern:
func playSlice() {
do {
// 1
let pattern = try slicePattern()
// 2
try hapticEngine.start()
// 3
let player = try hapticEngine.makePlayer(with: pattern)
// 4
try player.start(atTime: CHHapticTimeImmediate)
// 5
hapticEngine.notifyWhenPlayersFinished { _ in
return .stopEngine
}
} catch {
print("Failed to play slice: \(error)")
}
}
This code does the following:
- You call
slicePattern()
to, as the name implies, grab your haptic slice pattern. - Then you call
start()
on the haptic engine. - Make a pattern player with your slice pattern.
- Next, play the pattern, calling
start(atTime:)
withCHHapticTimeImmediate
to play it immediately. - Call
notifyWhenPlayersFinished(finishedHandler:)
and return.stopEngine
to stop the engine once it finishes playing.
OK, you’re nearly there, but you need to make some updates to GameScene.swift. Start by opening the file and adding the following property at the top of the class:
private var hapticManager: HapticManager?
Then add the following to the top of didMove(to:)
:
hapticManager = HapticManager()
In checkIfVineCut(withBody:)
, add this line above the line where you run the slice sound action:
hapticManager?.playSlice()
Build and run and slice that vine! Can you feel the gameplay improving already?
Exploring the Events That Make up the Pattern
OK, that was a lot all at once. Now, take a moment for a deeper look at what you did there.
Focusing on the pattern, you can see that it’s made up of events:
let slice = CHHapticEvent(
eventType: .hapticContinuous,
parameters: [
CHHapticEventParameter(parameterID: .hapticIntensity, value: 0.35),
CHHapticEventParameter(parameterID: .hapticSharpness, value: 0.25)
],
relativeTime: 0,
duration: 0.5)
let snip = CHHapticEvent(
eventType: .hapticTransient,
parameters: [
CHHapticEventParameter(parameterID: .hapticIntensity, value: 1.0),
CHHapticEventParameter(parameterID: .hapticSharpness, value: 1.0)
],
relativeTime: 0.08)
A haptic event, represented by CHHapticEvent
, can be one of two types: .hapticTransient
or .hapticContinuous
. A transient event is instantaneous, like a single drum beat, and a continuous event is like a rumble. You can also create audio events, but more on that later.
Each event has two properties that control the haptic sensation that CHHapticEventParameter
represents. Each has a parameter ID and a value between 0
and 1
.
A parameter with the ID .hapticIntensity
represents the strength of the sensation; the higher the value, the stronger it is. A parameter with the ID .hapticSharpness
represents a physical quality that, at the high end of the scale, creates a precise mechanical feel. At the low end, it has a more rounded, organic feel.
The event’s relativeTime
represents the number of seconds from the start of the pattern where the event occurs. For continuous events, there’s also a duration
property to define how long the event plays.
Your first haptic effect is a low, soft rumble for 0.25 seconds starting immediately, with a sharp, intense beat that occurs 0.08 seconds from the start. You can represent the pattern in a diagram:
Next, you’ll learn how you can use haptic events more efficiently.