50
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Core Haptics - カスタムハプティックパターンの作成と再生

Last updated at Posted at 2020-02-28

Core Hapticsは、iOS 13で新たに追加された「ハプティック(触覚)パターンを作成し、再生する」ためのフレームワークです。ついにTaptic Engineを開発者が制御するためのAPIが公開されたというわけです。1

従来手法(UIFeedbackGenerator)との違い

Taptic Engineが初めて搭載されたのはiPhone 6sで、ハプティックパターンの再生自体は、iOS 10で追加されたUIFeedbackGeneratorにより以前から可能でした。

UIFeedbackGeneratorは抽象クラスで、UIImpactFeedbackGenerator, UINotificationFeedbackGenerator, UISelectionFeedbackGeneratorの3種類の具象クラスがあります。それぞれの実装例を以下に示します。

  • UIImpactFeedbackGenerator
let impactFeedbacker = UIImpactFeedbackGenerator(style: .heavy)
impactFeedbacker.prepare()
impactFeedbacker.impactOccurred()
  • UINotificationFeedbackGenerator
let notificationFeedbacker = UINotificationFeedbackGenerator()
notificationFeedbacker.notificationOccurred(.success)
  • UISelectionFeedbackGenerator
let selectionFeedbacker = UISelectionFeedbackGenerator()

@IBAction func sliderChanged(_ sender: UISlider) {
    selectionFeedbacker.selectionChanged()
}

上記のコード例からもわかる通り、いずれの場合もあらかじめ決められたタイプのハプティックパターンを再生するというものでした。

Core Hapticsの場合は、このハプティックパターンをカスタマイズできる点が従来手法と大きく違う点です。

Core Hapticsの実装

まずはCore Hapticsの全体感を掴むため、基本的な実装の流れを見てみましょう。

1. インポート

CoreHapticsをインポートします。

import CoreHaptics

2. エンジンをスタートする

エンジン(CHHapticEngine)を初期化し、

let engine = try! CHHapticEngine()

start()メソッドを呼んでスタートします。

try! engine.start()

3. ハプティックパターンを生成する

ハプティックイベント(CHHapticEvent)を生成し(詳細は後述)、

let audioEvent = CHHapticEvent(eventType: .audioContinuous, parameters: [
    CHHapticEventParameter(parameterID: .audioPitch, value: -0.15),
    CHHapticEventParameter(parameterID: .audioVolume, value: volume),
    CHHapticEventParameter(parameterID: .decayTime, value: decay),
    CHHapticEventParameter(parameterID: .sustained, value: 0)
], relativeTime: 0)

let hapticEvent = CHHapticEvent(eventType: .hapticTransient, parameters: [
    CHHapticEventParameter(parameterID: .hapticSharpness, value: sharpness),
    CHHapticEventParameter(parameterID: .hapticIntensity, value: intensity)
], relativeTime: 0)

複数のハプティックイベントを組み合わせてパターン(CHHapticPattern)を生成します。

let pattern = 
    try! CHHapticPattern(events: [audioEvent, hapticEvent], parameters: [])

4. ハプティックパターンを再生する

CHHapticEnginemakePlayer(with:)メソッドにパターンを渡して、プレイヤー(CHHapticPatternPlayer)オブジェクトを生成します。

let player = try! engine.makePlayer(with: pattern)

CHHapticPatternPlayerstart(atTime)メソッドを呼んでハプティックを再生します。

try! player.start(atTime: CHHapticTimeImmediate)

ハプティックイベント(CHHapticEvent)

Core Hapticsとはカスタムハプティックパターンをつくって再生できるフレームワークです。そして、そのパターン(CHHapticPattern)は複数のハプティックイベント(CHHapticEvent)から構成され2

hapticpattern.png

ハプティックイベントは発生タイミング、イベントタイプ、長さ、そして複数のイベントパラメータ(CHHapticEventParameter)より規定されます。

つまり、カスタムなハプティックパターンをつくる重要な構成要素であるCHHapticEventを規定する各種プロパティとCHHapticEventParameterを理解することがCore Hapticsを使いこなす鍵となります。

というわけで、以下でCHHapticEventを規定する各種要素について順番に解説していきます。

ハプティックイベントタイプ(CHHapticEvent.EventType)

CHHapticEventは次のようなイニシャライザを持ち、第1引数にイベントタイプ(CHHapticEvent.EventType)を渡せるようになっています。

init(eventType type: CHHapticEvent.EventType, 
     parameters eventParams: [CHHapticEventParameter], 
     relativeTime time: TimeInterval)

これはその名の通りハプティックイベントのタイプを決めるもので、次の4種類が定義されています。

static let audioContinuous: CHHapticEvent.EventType
static let audioCustom: CHHapticEvent.EventType
static let hapticTransient: CHHapticEvent.EventType
static let hapticContinuous: CHHapticEvent.EventType

audio〜はオーディオによるフィードバック、haptic〜は触覚によるフィードバックです。

hapticタイプの方は"Transient"と"Continuous"とがありますが、"Transient"とは「一時的な」という意味で、hapticTransientは短いインパルス的なハプティックを、hapticContinuousは任意の長さを持ち、ループするハプティックを示します。

CHHapticEventの発生タイミング(relativeTime)

上述したCHHapticEventのイニシャライザは、第3引数にイベントを開始する時間(relativeTime)を指定できるようになっています。

名前に"relative"とある通り、絶対時間ではなく、ハプティックパターン内における相対時間(単位は秒)で指定します。

let hapticEvent = CHHapticEvent(
    eventType: type, parameters:params, 
    relativeTime: 0.1)  // 0.1秒後に開始

CHHapticEventの長さ(duration)

CHHapticEventはハプティックイベントの長さを指定するdurationプロパティを持ちます。

var duration: TimeInterval

Continuousなイベントタイプの場合は、本プロパティに0.0より大きい値をセットしておかないと再生時にクラッシュします。3

最大値は30秒です。

イベントパラメータ(CHHapticEventParameter)

前述のCHHapticEventのイニシャライザの第2引数に配列で渡すのが、イベントパラメータ(CHHapticEventParameter)です。

let audioEvent = CHHapticEvent(
    eventType: type, 
    parameters: [param1, param2],   // [CHHapticEventParameter]
    relativeTime: 0)

イニシャライザは次のように定義されており、

init(parameterID: CHHapticEvent.ParameterID, value: Float)

第1引数にパラメータの種類を示すID(CHHapticEvent.ParameterID)、第2引数にパラメータの値(Float)を渡します。

let pitch  = CHHapticEventParameter(parameterID: .audioPitch, value: -0.15)
let volume = CHHapticEventParameter(parameterID: .audioVolume, value: 0.5)

CHHapticEvent.ParameterIDには多くの種類があるので、ここでは主なものを紹介します。

haptic用

イベントタイプがhapticTransient, hapticContinuousなハプティックイベント専用のパラメータIDが次の2つです。

static let hapticIntensity: CHHapticEvent.ParameterID
static let hapticSharpness: CHHapticEvent.ParameterID
  • hapticIntensity:ハプティックの強さを0.01.0で指定
  • hapticSharpness:ハプティックの鋭さを0.01.0で指定

audio用

イベントタイプがaudioContinuous, audioCustomなハプティックイベント専用のパラメータIDが次の4つです。

static let audioVolume: CHHapticEvent.ParameterID
static let audioPitch: CHHapticEvent.ParameterID
static let audioPan: CHHapticEvent.ParameterID
static let audioBrightness: CHHapticEvent.ParameterID
  • audioVolume:音量を0.01.0で指定
  • audioPitch:音のピッチを-1.0(低い)〜1.0(高い)で指定
  • audioPan:音の位置(定位)を-1.0(左)〜1.0(右)で指定。デフォルトは0.0(中央)
  • audioBrightness:音の高周波成分を0.01.0で指定。デフォルトは1.0(高周波成分を減らさない)

共通

audio, haptic共通で"Continuous"なイベントタイプに使えるパラメータのIDとして、次のようなものがあります。それぞれハプティックイベントの波形を細かく調整するためのパラメータです。

static let attackTime: CHHapticEvent.ParameterID
static let decayTime: CHHapticEvent.ParameterID
static let releaseTime: CHHapticEvent.ParameterID
static let sustained: CHHapticEvent.ParameterID

AHAP

AHAP (Apple Haptic and Audio Pattern)はハプティックパターンを定義するJSONライクなファイルフォーマットです。次のような構造でパターンを定義できます。

ahap.png

以下にAHAPファイルの例を示します。4

{
  "Version": 1.0,
  "Metadata":
    {
      "Project" : "Haptic Sampler",
      "Created" : "5 June 2019",
      "Description" : "An effect that builds in sharpness and intensity."
    },
  "Pattern":
  [
    {
      "Event":
      {
        "Time": 0.0,
        "EventType": "HapticContinuous",
        "EventDuration": 1.7,
        "EventParameters":
        [
          { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 },
          { "ParameterID": "HapticSharpness", "ParameterValue": 0.5 }
        ]
      }
    },
    {
      "ParameterCurve":
      {
        "ParameterID": "HapticIntensityControl",
        "Time": 0.0,
        "ParameterCurveControlPoints":
        [
          { "Time": 0, "ParameterValue": 0.0 },
          { "Time": 1.1, "ParameterValue": 0.5 },
          { "Time": 1.7, "ParameterValue": 0.0 }
        ]
      }
    },
    ...(略)
  ]
}

この定義のうちEventキーはCore HapticsのCHHapticEventに相当し、その配下のTimeキーはrelativeTimeプロパティ、EventTypeキーはeventTypeプロパティ、EventDurationキーはdurationEventParametersキーはeventParametersプロパティに相当します。またEventParametersキー配下の配列に入る要素はCHHapticEventParameterに相当します。こうしてみれば、AHAPというフォーマットはハプティックパターン(CHHapticPattern)の実装をそのままJSONに落とし込んだだけであり、難しくはありません。

JSONフォーマットなので、AHAPは通常のテキストファイルとして閲覧・編集でき、Xcodeプロジェクトへの追加方法も他のリソースと同様です。

Core Hapticsは、このAHAPファイルからハプティックパターンを読み出して、再生することができます。

AHAPファイルからの再生

AHAPファイルを読み込んでハプティックパターンを再生する実装方法は非常にシンプルです。

エンジン(CHHapticEngine)を初期化し、

let engine = try! CHHapticEngine()

開始します。

try! engine.start()

あとはCHHapticEngineplayPattern(from:)メソッドを呼び、引数にAHAPファイルのURLを渡すだけです。

try! engine.playPattern(from: URL(fileURLWithPath: path))

以上でAHAPファイルからハプティックパターンを再生することができます。

サンプルコードのダウンロード

本記事は2019年9月に発売した書籍「iOS 13の新機能をざっくり把握する本」からの転載です。同書籍は現在は100円で販売しています。

またCore Hapticsは実際に手元で試してみないと文章だけでは伝わりにくいと思います。上記記事やWEBに転がっている情報で十分実装できるとは思いますが、サクッと試してみたい方はBOOTHにてサンプルも100円で販売しているので投げ銭がてらよろしければご利用ください。

カスタムパターンを生成するサンプルと、AHAPファイルを再生するサンプルが入っています。

IMG_0367.PNG
  1. Core Hapticsはオーディオによるフィードバックも扱うので、厳密にはTaptic Engineを制御するだけではありません。

  2. 実際にはハプティックパターンを構成する要素としては他にCHHapticParameterCurveCHHapticDynamicParameterといったものがあります。

  3. ただし、sustainedパラメータにfalse(0.0)をセットしておくと、Continuousなイベントタイプに対してduration0.0でも再生可能です。

  4. Appleの"Haptic Sampler"というサンプルコードに付属しているInflate.ahapというファイルから抜粋したものです。

50
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
50
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?