SwiftUI 에서 햅틱을 사용하는 것은 아주 간단하다. 하지만 정말 내가 원하는 햅틱을 커스텀 하기 위해서는 CoreHaptic 이라는 프레임 워크가 필요하다.
시작하기에 앞서, CoreHaptic 을 사용할 경우에 정말 디테일한 햅틱을 조절할 수 있지만, 어떠한 식으로 햅틱을 만들고 싶은 확실한 니즈가 없다면... 시간낭비 일수도 있다. 아마 게임이나 특수한 상황에서 사용자의 특별한 경험을 위해서만 필요한 기능인 것 같다. 단순히 알림을 주거나 피드백을 주는 것이라면 기본 impact 나 notification 에 활용되는 햅틱을 기본으로 잘 제공해주니, 그것을 쓰는것을 추천한다.
개요
심플하게 방법을 설명하면 Engine 을 생성하고, Event 와 Pattern 을 만들어서 Engine 에 전달하고, 시작하면 된다!
사용방법
자 이제 방법을 알아보자. 아주아주 간단하다.
1. engine object 생성
나는 뷰에 바로 생성을 해주었기 때문에 아래와 같이 했다.
import CoreHaptics
import SwiftUI
struct CoreHapticView: View {
@State private var engine: CHHapticEngine?
...
}
그렇다면 여기서 CHHapticEngine 이 무엇일까? 역시 Xcode 에서 친절하게 알려준다. 햅틱서버와 연결해주는 Object 라고 한다.
2. 햅틱 준비
햅틱을 하기전에 이 기기가 햅틱이 지원이 되는 기기인지 확인하는 과정이 필요하다.
func prepareHaptics() {
guard CHHapticEngine.capabilitiesForHardware().supportsHaptics else { return }
do {
engine = try CHHapticEngine()
try engine?.start()
} catch {
print(error.localizedDescription)
}
}
3. 햅틱 커스텀하고 재생하기
지금까지 알아본 바로는 아래와 같은 절차를 거치면 된다.
- 햅틱 이벤트를 생성한다.
- 햅틱의 intensity 를 정의해준다.
- 햅틱의 sharpness 를 정의해준다.
- 햅틱의 parameterCurves 를 정의해준다. (옵셔널, 없어도 된다)
- 2, 3, 4 번으로 pattern 을 만들어준다.
- 만든 패턴을 재생하는 함수를 만들어 action 에 넣어준다.
intensity 와 sharpness 의 차이는 직접 느껴보면 한번에 이해가 되겠지만, 대략 설명하면 intensity 는 정말 진동의 크기고, sharpness 는 얼마나 진동이 쨍한지? 값을 올리면 정말 진동이 날카로운 느낌이 난다. 강도와는 분명히 다르다!
아주 단순하게 다시 이야기하면 햅틱 이벤트를 만든뒤 상세 조정을 한 뒤 재생하면 된다. 백문이 불여일견이라고 했던가, 아래는 예시이다.
사용 예시
버튼에 아래와 같이 함수를 넣어주면 된다. 처음 만들어 주었던 햅틱이 지원이 되는 기기인지 확인, 햅틱 시작부분을 onAppear 에 넣어주기!
Button {
customHaptic1()
} label: {
Text("Custom Haptic 1")
}
.onAppear {
// 햅틱 준비
prepareHaptics()
}
예시 1 : 점점 강도가 강해졌다가 낮아지는 햅틱
stride 는 읽어보면 감이 오겠지만 아래의 코드에서는 0 부터 1 까지 0.1 만큼의 간격으로 넣어주는 방법이다. 따라서 진동이0 부터 1 까지 0.1 간격으로 두두두두두두 나오게 된다.
func customHaptic1() {
guard CHHapticEngine.capabilitiesForHardware().supportsHaptics else { return }
// 이벤트 생성
var events = [CHHapticEvent]()
// 햅틱 이벤트 상세 조정
for i in stride(from: 0, to: 1, by: 0.1) {
let intensity = CHHapticEventParameter(parameterID: .hapticIntensity, value: Float(i))
let sharpness = CHHapticEventParameter(parameterID: .hapticSharpness, value: Float(i))
let event = CHHapticEvent(eventType: .hapticTransient, parameters: [intensity, sharpness], relativeTime: i)
events.append(event)
}
for i in stride(from: 0, to: 1, by: 0.1) {
let intensity = CHHapticEventParameter(parameterID: .hapticIntensity, value: Float(1 - i))
let sharpness = CHHapticEventParameter(parameterID: .hapticSharpness, value: Float(1 - i))
let event = CHHapticEvent(eventType: .hapticTransient, parameters: [intensity, sharpness], relativeTime: 1 + i)
events.append(event)
}
// 햅틱 재생
do {
let pattern = try CHHapticPattern(events: events, parameters: [])
let player = try engine?.makePlayer(with: pattern)
try player?.start(atTime: 0)
} catch {
print(error.localizedDescription)
}
}
예시 2 : 길게 오는 진동
좀더 Digging 이 필요하겠지만 event 뒤에 있는 duration 에 따라 진동의 길이가 정해지는 것 같다.
func customHaptic2() {
// 이벤트와 커브 생성
var events = [CHHapticEvent]()
var curves = [CHHapticParameterCurve]()
// 상세 조정
do {
let sharpness = CHHapticEventParameter(parameterID: .hapticSharpness, value: 0)
let intensity = CHHapticEventParameter(parameterID: .hapticIntensity, value: 0.5)
let start = CHHapticParameterCurve.ControlPoint(relativeTime: 0, value: 1)
let end = CHHapticParameterCurve.ControlPoint(relativeTime: 1, value: 1)
let parameter = CHHapticParameterCurve(parameterID: .hapticIntensityControl, controlPoints: [start, end], relativeTime: 0)
let event = CHHapticEvent(eventType: .hapticContinuous, parameters: [sharpness, intensity], relativeTime: 0, duration: 2)
events.append(event)
curves.append(parameter)
}
// 햅틱 재생
do {
let pattern = try CHHapticPattern(events: events, parameterCurves: curves)
let player = try engine?.makePlayer(with: pattern)
try player?.start(atTime: 0)
} catch {
print(error.localizedDescription)
}
}
마치며
보통 Haptic 도 데이터 형식으로 json 파일로 인코딩하여 사용도 가능한 것 같다. 정말 정말 햅틱이 중요한 게임같은 경우에는 이런식으로 파일데이터로도 관리를 할 것 같다는 생각을 했다.
혹시라도 부족하거나, 틀린부분이 있다면 댓글로 알려주시면 감사하겠습니다 :) 👨🏻💻