iOS マーカーからマーカーレスへ

このチュートリアルは、画像トラッカブルに追加されたノードを ArbiTrack に転送する方法を紹介します。

上級者向けチュートリアル

このチュートリアルは、マーカーの基礎ArbiTrack の基礎を終了済みであることを前提にしています。まだ終了していない場合は、先にこれらのチュートリアルを実施することを強く推奨します。

このチュートリアルは、マーカーの基礎チュートリアルで使用した Lego Marker と Kudan Cow アセットを使用します。これらのアセットをまだ入手していない場合は、マーカーの基礎にあるダウンロード リンクから入手してください。

画像トラッカブルの作成

最初に、Lego Marker 画像を使用して画像トラッカブルを作成します。次に、Kudan Cow 画像を使用して画像ノードを作成し、画像トラッカブルに追加します。

また、画像ノードが一意な名前になるようにコード行を追加する必要があります。この名前は、後でトラッカーで検索する際に使用します。

ViewController.m
// 画像で画像ノードを初期化
// PNG 画像なので、ここではファイル拡張子を含めない
ARImageNode *imageNode = [[ARImageNode alloc] initWithBundledFile:@"Kudan Cow"];

imageNode.name = @"Cow";

ビュー コントローラーを ArbiTrack デリゲートとして登録

ArbiTrack がいつ開始されたのかを知る必要があります。幸いにも、そのためのデリゲートがあります。次のコードを setupContent メソッドの最後に追加します。

ViewController.m
// ArbiTrack を初期化
ARArbiTrackerManager *arbiTrack = [ARArbiTrackerManager getInstance];
[arbiTrack initialise];
 
// ビュー コントローラーを ArbiTrack デリゲートとして追加
[arbiTrack addDelegate:self];

このコードは、ArbiTrack が開始してワールド空間に配置されると、デリゲート メソッド arbiTrackStarted を呼び出します。

ターゲット ノードの設定

ここでは、ターゲット ノードをやや異なる方法で使用します。ArbiTrack の基礎チュートリアルでは、ノードを作成してターゲットとして使用しました。このチュートリアルでは、ArbiTrack が画像トラッカブルと同じ位置でトラッキングを開始するようにします。そのため、トラッカブルのワールド空間を ArbiTrack のターゲット ノードとして使用する必要があります。次のコードを setupContent メソッドに追加します。

ViewController.m
// 画像トラッカブルのワールド空間をターゲット ノードとして使用
// これにより、ArbiTrack がトラッカブルの位置でトラッキングを開始
arbiTrack.targetNode = imageTrackable.world;

ArbiTrack のデリゲート メソッドの実装

次に、ArbiTrack が開始時に何をすべきかを指示します。そのためには、ビュー コントローラーで arbiTrackStarted デリゲート メソッドを実装します。次のようなコードを使用します。

ViewController.m
// ArbiTrack が開始したらデリゲート メソッドが呼び出される
- (void)arbiTrackStarted
{
  
}

マーカーとマーカーレス トラッキングを切り替えるには、マーカーの検出時に表示される画像ノードを取得して、ArbiTrack のワールド空間に転送する必要があります。また、画像が正しい位置と向きであることを確実にする必要があります。そうでないと、トラッキング タイプを切り替えたときに画像がジャンプします。これは望ましくありません。

次のコードを arbiTrackStarted メソッドに追加します。

ViewController.m
// ArbiTrack が開始したらデリゲート メソッドが呼び出される
- (void)arbiTrackStarted
{
  // 名前を使用して Lego トラッカブルをトラッカー内で検索
  ARImageTrackable *imageTrackable = [[ARImageTrackerManager getInstance] findTrackableByName:@"Lego Marker"];
    
  // 名前を使用して Cow ノードを Lego トラッカブルのワールド空間内で検索
  ARImageNode *cowNode = (ARImageNode *)[imageTrackable.world findChildWithName:@"Cow"];
  
  ARArbiTrackerManager *arbiTrack = [ARArbiTrackerManager getInstance];
    
  // 親を変更しても完全な方向性が保持されるように Cow ノードの向きを変更
    cowNode.orientation = [arbiTrack.world.orientation.inverse multiplyByQuaternion:cowNode.fullOrientation];
    
  // Cow ノードをトラッカブルのワールド空間の子から削除して ArbiTrack に追加
  [imageTrackable.world removeChild:cowNode];
  [arbiTrack.world addChild:cowNode];
}

これで、ArbiTrack が開始すると、画像ノードの親が変更され、マーカーレスにトラッキングされます。ただし、ArbiTrack にいつ開始すべきかを知らせる必要があります。

ArbiTrack の開始

ArbiTrack にいつ開始すべきかを知らせるには、ArbiTrack の基礎チュートリアルのタッチ入力への応答と同じメソッドを使用します。次のようなコードを使用します。

ViewController.m
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
  
}

次に、ArbiTrack が開始していない場合にのみ開始するように、タップしたときに ArbiTrack が実行中かどうかを知る必要があります。さらに、マーカーをトラッキングしていなければ、マーカー トラッキングからマーカーレス トラッキングに切り替えることはできないため、トラッカブルが検出されたかどうかも知る必要があります。

これらのステップを実装すると、touchesBegan メソッドは次のようになります。

Objective-C
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
  // ArbiTracker と画像トラッカブルへの参照を取得
  ARArbiTrackerManager *arbiTrack = [ARArbiTrackerManager getInstance];
  ARImageTrackable *imageTrackable = [[ARImageTrackerManager getInstance] findTrackableByName:@"Lego Marker"];
  
  // 現在実行中でない場合にのみ ArbiTrack を開始
  if (!arbiTrack.isTracking)
  {
      // マーカーが検出されていない場合はメソッドを終了して ArbiTrack への切り替えは行わない
      if (!imageTrackable.isDetected)
      {
          return;
      }

      [arbiTrack start];
  }
}

つまり、画面をタップしたときに、ArbiTrack がまだ実行されておらず、マーカーが検出されている場合は、マーカーレス トラッキングを開始します。これにより、arbiTrackStarted メソッドがトリガーされて、画像ノードを介して転送されます。

なぜ arbiTrackStarted メソッドを実装するのか、皆さんは不思議に思っているかもしれません。たしかに、メソッドを実装しなくても、画面をタップしたときに同じコードを実行すれば済みます。これは、ArbiTrack が最初に開始したときに、ジャイロスコープからデータをすぐに取得できないためです。このため、ワールド空間の変換が数フレーム分更新されません。この時点で画像ノードを転送しようとすると、向きが変わり、画面上でジャンプします。

arbiTrackStarted メソッドは、ArbiTrack が開始され、かつ値がある場合にのみ呼び出されます。そのため、このメソッドが呼び出される場合、ワールド空間は更新済みであり、コンテンツを安全に転送できることを確認できます。

この時点でアプリをビルドして実行すると、マーカーを検出することができ、画面をタップするとマーカーレス トラッキングに切り替わります。トラッキング方法の変更時に視覚的フィードバックを得るため、何らかの形式のテキストを追加したり、IBOutlet を使用してタッチ コントロールをボタン押下として実装することもできます。

ここまで順調に進んでいますが、現時点ではまだ、画面を一度しかタッチできず、いったん ArbiTrack に切り替えたら ArbiTrack を停止してマーカー トラッキングに戻ることができません。トラッキング方法の切り替えを実装し、必要に応じて画像ノードを転送する必要があります。

トラッキング方法の切り替え

これは簡単に実装できます。マーカー トラッキングに戻るには、ArbiTrack への切り替えと逆のことをすればよいだけです。画像トラッカーには更新が必要なワールド空間がないため、touchesBegan メソッドで転送を実行できます。

ArbiTrack が実行中かどうかをチェックする if 文がすでにあるため、else 文を追加するだけで済みます。

ViewController.m
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
  // ArbiTracker と画像トラッカブルへの参照を取得
  ARArbiTrackerManager *arbiTrack = [ARArbiTrackerManager getInstance];
  ARImageTrackable *imageTrackable = [[ARImageTrackerManager getInstance] findTrackableByName:@"Lego Marker"];
  
  // 現在実行中でない場合にのみ ArbiTrack を開始
  if (!arbiTrack.isTracking)
  {
      // マーカーが検出されていない場合はメソッドを終了して ArbiTrack への切り替えは行わない
      if (!imageTrackable.isDetected)
      {
          return;
      }

      [arbiTrack start];
  }
  
  // ArbiTrack が実行中の場合
  else
  {
    // ArbiTrack のワールド空間内で Cow ノードを検索
    ARImageNode *cowNode = (ARImageNode *)[arbiTrack.world findChildWithName:@"Cow"];
    
    // マーカー上で平らになるようにノードの向きを調整
    cowNode.orientation = [ARQuaternion quaternionWithIdentity];
    
    // ArbiTrack のワールド空間から Cow ノードを削除して画像トラッカブルのワールド空間に再度追加
    [arbiTrack.world removeChild:cowNode];
    [imageTrackable.world addChild:cowNode];
    
    // ArbiTrack を停止
    [arbiTrack stop];
  }
}

アプリを再度ビルドして実行すると、マーカーのトラッキング中に画面をタップして ArbiTrack に切り替えることができ、再度タップするとマーカー トラッキングに戻ることができます。