1
4

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 5 years have passed since last update.

【Swift Playgrounds】ARを作るPART.1「スタート地点」を読み解く

Last updated at Posted at 2019-12-07

この記事は何か?

iPadアプリケーションSwift Playgroundsは、Swiftプログラミングのスキルを習得できます。
「ブック」と呼ばれるコンテンツを実践することで、Swiftプログラミングやコンピュータの基礎から応用までを学びます。
そして、習得したスキルからオリジナルのコンテンツを開発できるテンプレートのブックがあり、「ARを作る」では拡張現実コンテンツを簡単に構築できます。
この記事では、「ARを作る」の最初のページである「スタート地点」を解説します。
「ARを作る」テンプレートから、独自の拡張現実空間を構築してiPadで遊ぶことができます。

IMG_80046717937D-1.jpeg

実行環境

  • iPad Pro 11.5インチ(2018モデル)
  • iOS 13.2.3
  • Swift Playgrounds(バージョン3.1)
  • ARを作る(Swift 5.1)

ブックの構成

「ARを作る」ブックは、次の4つのページで構成されています。

  1. スタート地点(この記事で解説)
  2. 簡単なシーン(解説はこちら
  3. APIの概要(解説はこちら
  4. 蝶を捕まえる(解説はこちら

また、それぞれのページには共通のSharedCodeというファイルがあります。メインファイルからSharedCodeに定義された機能を利用できます。

メインファイル

最初のページなので、メインファイルのコードは極めてシンプルです。
以下の作業を行なっています。

  • レモンを作って、シーンに追加する
メインファイル
let lemon = Model.lemon

scene.add(lemon)

ページを実行すると、iPadのカメラが平面(サーフェス)の検出を開始します。
平面を検出すると、任意の箇所をタップしてレモンを配置できます。
さらに、「シーンを開始する」ボタンをタップすることで、コードで指定した挙動が動き出します。

このファイルにコードを自由に追記することで、独自のAR空間を構築できます。

SharedCodeファイル

このファイルは、すべてのステージに含まれています。
コメントにもあるように、このファイルに加えた編集内容は「ARを作る」ブックに含まれる全てのページに反映されます。
以下、ソースコードを少しずつ、分解してみていきます。

SharedCodeファイル
// 共有コード
// このファイルに書いたコードは、このプレイグラウンドブックのすべてのページで利用できます。
import SceneKit

public var scene = Scene()

public var allModels: [ImaginaryNode] = [Model.alarmClock, Model.arrow, Model.cactus, Model.ear, Model.eye, Model.flyingBug, Model.foot, Model.hand, Model.lemon, Model.lightbulb, Model.lightning, Model.moon, Model.nose, Model.mouth, Model.raincloud, Model.snail, Model.star, Model.sun]
public var origin = Point(x: 0, y: 0, z: 0)
var xOffset = -48.cm
var modelsPlacedCount = 0

// allModels配列のすべてのモデルをシーンの原点の周囲に配置します。
public func placeAllModels() {
    let model = allModels[modelsPlacedCount]
    scene.place(model, at: Point(x: origin.x - xOffset, y: origin.y, z: origin.z - 30.cm))
    model.animate()
    xOffset += 8.cm
    if modelsPlacedCount + 1 < allModels.count {
        modelsPlacedCount += 1
        model.play(.ring, loops: false, completion: placeAllModels)
    }
}

準備

まず、3D空間を扱うためのフレームワークSceneKitを導入しています。
そのあと、シーンのオブジェクトを生成して、変数sceneに割り当てています。

import SceneKit

public var scene = Scene()

主要な変数の定義

ここで定義している変数オブジェクトは、以下の4つです。

  • allModels(ARコンテンツとして配置できる3Dモデルのコレクション)
  • origin(3D空間の原点)
  • xOffset(原点からX軸方向への移動距離)
  • modelsPlacedCount(配置した3Dモデルの総数)
public var allModels: [ImaginaryNode] = [Model.alarmClock, 
                                         Model.arrow, 
                                         Model.cactus, 
                                         Model.ear, 
                                         Model.eye, 
                                         Model.flyingBug, 
                                         Model.foot, 
                                         Model.hand, 
                                         Model.lemon, 
                                         Model.lightbulb, 
                                         Model.lightning, 
                                         Model.moon, 
                                         Model.nose, 
                                         Model.mouth, 
                                         Model.raincloud, 
                                         Model.snail, 
                                         Model.star, 
                                         Model.sun]

public var origin = Point(x: 0, y: 0, z: 0)
var xOffset = -48.cm
var modelsPlacedCount = 0

3Dモデルには、生き物から身体の一部、天体に置物などいろいろ用意されています。種類ごとに整理しておくと...

  • arrow(矢印)
  • alarmClock(目覚し時計)
  • lightbulb(電球)
  • star(星)
  • sun(太陽)
  • moon(月)
  • lightning(稲妻)
  • raincloud(雨雲)
  • ear(耳)
  • eye(目)
  • foot(足)
  • hand(手のひら)
  • nose(鼻)
  • mouth(口)
  • cactus(サボテン)
  • lemon(レモン)
  • snail(カタツムリ)
  • flyingBug(空飛ぶ虫)

placeAllModels()関数の定義

この関数を実行すると、allModels配列のすべての3Dモデルをシーンの原点の周囲に配置します。

placeAllModels()関数
public func placeAllModels() {
    let model = allModels[modelsPlacedCount]
    scene.place(model, at: Point(x: origin.x - xOffset, 
                                 y: origin.y, 
                                 z: origin.z - 30.cm))
    model.animate()
    xOffset += 8.cm
    if modelsPlacedCount + 1 < allModels.count {
        modelsPlacedCount += 1
        model.play(.ring, loops: false, completion: placeAllModels)
    }
}

もっと詳しくコードを見てみます。
最初に、モデルのコレクションから指定したインデックスのモデルをひとつだけ取り出して、定数modelに割り当てています。

let model = allModels[modelsPlacedCount]

ここでインデックスとして使用されているmodelPlacedCountは、シーンに配置済みの3Dモデルの総数です。既定値は0でした。
つまり、初めてplaceAllModels()関数が呼び出された時点では、定数modelModel.alarmClock(レモン)が割り当てられるはずです。

次に、place(_:at:)メソッドを使って、この3Dモデルをシーンに追加します。
さらに、配置した3Dモデルのanimate()メソッドを呼び出して、それぞれ特有の動作をさせています。

scene.place(model, at: Point(x: origin.x - xOffset, 
                             y: origin.y, 
                             z: origin.z - 30.cm))
model.animate()

なお、3Dモデルは3次元座標が指定されています。
実行したiPadから見て、X軸方向に48cm、Z軸方向に30cm移動した地点です。

ここからは、モデルを配置した後のコードです。
X軸方向のオフセットである変数xOffset8cmだけ増分しています。

xOffset += 8.cm

既定値が-48cmだったので、この時点では-40cmに変化します。
もう一度、placeAllModels()が呼び出されると、3Dモデルは8cmだけ隣に配置されるはずです。

最後に、モデルのコレクションの全てをシーンに追加しています。

if modelsPlacedCount + 1 < allModels.count {
    modelsPlacedCount += 1
    model.play(.ring, loops: false, completion: placeAllModels)
}

ここでは、__配置した3Dモデルの数__と__コレクションにあるモデルの総数__を比較しています。
allModels.countに達していない場合、modelsPlacedCount+1します。

modelsPlacedCount += 1
model.play(.ring, loops: false, completion: placeAllModels)

play(_:loops:completion:)メソッドにある最後のパラメータcompletion:に注目します。
このパラメータには、placeAllModelsメソッドが渡されています。
つまり、コレクションにあるすべてのモデルがシーンに追加されるまで、placeAllModels()関数が呼び出されることになります。
したがって、メインファイルでplaceAllModels()メソッドを一度実行するだけで、シーンには用意されたすべてのモデルが配置できます。

placeAllModels()関数を使ってみる

既存コードをコメントアウトして、placeAllModels()関数を呼び出します。

メインファイル
// let lemon = Model.lemon
//scene.add(lemon)

placeAllModels()

ページを実行すると、iPadから見て右から左に向かって、3Dモデルが配置されていきます。
サーフェスを検出したり、モデルの配置場所をタップすることはありません。
3Dモデルが追加される高さは、ちょうどiPadと同じくらいです。
「リン〜、リン〜、...」というリズムに合わせて、空間にひとつずつ3dモデルが出現します。
これはplay(_:loops:completion:)メソッドの挙動です。
completion:部分が完了ハンドラになっていることがわかります。

コードを読んでわかったこと

「ARを作る」ブックのガイダンスにような内容になっていることが感じ取れました。
また、シーンについては次のようなベクトルがあることがわかりました。

  • X軸: 画面左へ + <---> - 画面右へ
  • Y軸: 画面上へ + <---> - 画面下へ
  • Z軸: 画面前へ + <---> - 画面奥へ

このブックは、少なくとも以下の機能が利用できることがわかりました。

Scene型オブジェクト

  • add(_:)メソッド(検出した平面上の任意の場所にモデルを配置する)
  • place(_:at:)メソッド(コードで指定した場所にモデルを配置する)

Model型オブジェクト

  • animate()メソッド
  • play(_:loops:completion:)メソッド

さらに

続きのページ「簡単なシーン」の解説はこちら

1
4
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
1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?