Upgrade to Pro — share decks privately, control downloads, hide ads and more …

簡単なAR機能とUI実装を組み合わせてみた記録

 簡単なAR機能とUI実装を組み合わせてみた記録

Mobile勉強会 Wantedly × チームラボ × Sansan #14での登壇資料になります。

こちらは、5/19に株式会社メルカリ様にて開催された「AR 1day Hackathon/お試し会」で実装したサンプルを解説しています。 基本的には.scn形式にした3Dモデルを画面内に表示し、SwiftUIを利用したUI関連実装とSceneKitを組み合わせた形の簡素な物になりますが、何かの参考になれば幸いです。

Sample: https://github.com/fumiyasac/ARDemoSample

Fumiya Sakai

May 21, 2024
Tweet

More Decks by Fumiya Sakai

Other Decks in Technology

Transcript

  1. 自己紹介 ・Fumiya Sakai ・Mobile Application Engineer アカウント: ・Twitter: https://twitter.com/fumiyasac ・Facebook:

    https://www.facebook.com/fumiya.sakai.37 ・Github: https://github.com/fumiyasac ・Qiita: https://qiita.com/fumiyasac@github 発表者: ・Born on September 21, 1984 これまでの歩み: Web Designer 2008 ~ 2010 Web Engineer 2012 ~ 2016 App Engineer 2017 ~ Now iOS / Android / sometimes Flutter
  2. ARモデルを利用した表現とUI実装の組み合わせ例 SwiftUIを活用したUI実装と組み合わせた事例やコードに触れてみよう ① SwiftUI 2.0 SceneKit - SwiftUI Loading 3D

    Objects/Models Using SceneView - SwiftUI 2.0 Tutorials: https://www.youtube.com/watch?v=v8j121DiUfg ② SwiftUI 3.0 Pizza Animation Challenge - Complex Animations - Pizza App UI - Xcode: SwiftUI Tutorials: https://www.youtube.com/watch?v=4fSwN48eSfU ③ SwiftUI 3D Shoe App UI - SceneKit - 3D Objects - Complex UI - Xcode 14 - SwiftUI Tutorials: https://www.youtube.com/watch?v=d4ciSOLvIH8
  3. SwiftUI製の画面でSceneKitを利用した3Dモデル表示 Xcodeで変換した.scn形式の3Dモデル表示部分SceneKitを利用しています UIViewRepresentableを利用してSCNViewをSwiftUIで表示させる方針を取る struct DetailCustomSceneView: UIViewRepresentable { // MARK: -

    `@Binding` Property @Binding var scene: SCNScene? // MARK: - Function func makeUIView(context: Context) -> SCNView { let view = SCNView() view.allowsCameraControl = false view.autoenablesDefaultLighting = true view.antialiasingMode = .multisampling2X view.scene = scene view.backgroundColor = .clear return view } func updateUIView(_ uiView: SCNView, context: Context) {} } // 表示対象SCNScene(SceneKit)をStateとして定義 @State private var scene: SCNScene? // ※ initializer内での調整処理 // 👉 少し拡大&少し手前側に斜めにして見やすく self.scene?.rootNode.rotation = SCNVector4( 1, // X軸 0, // Y軸 0, // Z軸 0.1 * Float.pi // 角度(ラジアン) ) self.scene?.rootNode.scale = SCNVector3Make( 1.28, // X軸 1.28, // Y軸 1.28 // Z軸 ) 3Dモデル表示と調整対応部分 ※標準が水平で見ずらい
  4. SceneKitを利用した3Dモデル表示を回転可能にする SwiftUIのDragGesture処理を応用する事で強引ではあるが実現可能 DragGestureでの変化量を保持しておき、SCNTransactionを利用したAnimation処理と連動する .gesture( // DragGestureと連動して回転する様な形を実現する DragGesture() .updating($temporaryOffsetValue, body: {

    currentValue, outputValue, _ in // MEMO: -64.0をしているのは調整のため outputValue = currentValue.location.x - 64.0 }) ) .onChange(of: temporaryOffsetValue) { // MEMO: 変数「offset」が変更されるので、配置要素が合わせて回転する rotateSceneViewObject(animate: temporaryOffsetValue == .zero) } // Drag処理変化量に合わせて水平方向回転を実施する private func rotateSceneViewObject(animate: Bool = false) { // ① Transition処理を開始する if animate { SCNTransaction.begin() SCNTransaction.animationDuration = commonDurationValue } // 👉 この様な条件分岐にしないと綺麗に回転しなかったんですよねー...😇 scene?.rootNode.eulerAngles.y = Float((temporaryOffsetValue * .pi) / 180.0) // ② Transition処理を実行する if animate { SCNTransaction.commit() } } スライダー要素のModifier処理部分の抜粋 // DragGesture発動時に一時的に格納するための変数 @GestureState private var temporaryOffsetValue: CGFloat = 0
  5. 一覧から詳細画面へ遷移する様に見せる構成のポイント 実は画面の状態に合わせて表示対象の内容を切り替えているだけに過ぎない キーワードになるのは「.matchedGeometryEffect」Modifierの活用: var body: some View { NavigationStack {

    // 全体をZStackにして表示要素を重ねている // 👉 AndroidやFlutter等でよく見る「Hero」Animationの様なイメージ ZStack { // `@State`で定義した変数の状態を元にして表示状態を決定する if selectedMaterial == nil { // 一覧表示時のView全体要素 (全体はScrollView + Grid表示構成) } else { // 拡大時のView全体要素 } } .frame(width: screenWidth) .navigationTitle("3Dモデルを表示して回転させよう♻ ") .navigationBarTitleDisplayMode(.inline) } } @State private var selectedMaterial: MaterialEntity? = nil 表示対象Entityを格納するState値 Animation処理で切替 View要素の表示切り替 え処理に対して連続性 がある形になる様な工 夫を加える。 一覧表示をしている矩形をタップし た際に表示対象のEntityを選出して 適用する事で状態変化を起こす。 .matchedGeometryEffectを利用する方針 矩形とテキスト要素に対して適用する。
  6. .matchedGeometryEffectを利用したAnimation処理 // Grid正方形の表示要素 HStack { VStack { Text(name) .foregroundColor(.white) //

    👉 ① Animation対象となるテキスト要素(遷移元) .matchedGeometryEffect(id: effectTitleID, in: namespace) } .frame(width: standardRectangle, height: standardRectangle) .background( Rectangle() // 👉 ② Animation対象となる矩形要素(遷移元) .matchedGeometryEffect(id: effectShapeID, in: namespace) .onTapGesture { withAnimation(springAnimation) { selectedMaterial = material } } ) Spacer() } // 拡大時のView全体要素 VStack { // (1) ヘッダー表示をするためのView要素を配置する HStack { Text(name) .foregroundColor(.white) // 👉 ① Animation対象となるテキスト要素(遷移先) .matchedGeometryEffect(id: effectTitleID, in: namespace) .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) .padding(.top, 20.0) Spacer() Button(action: { withAnimation(springAnimation) { selectedMaterial = nil } } label: { Text("× 閉じる") }) } .padding(.horizontal, 8.0) // (2) SceneKitを仕込んだView要素を配置する DetailView(scene: materialScene) } // 表示エリアは「横幅いっぱい&縦400px」を確保する .frame(maxWidth: .infinity, maxHeight: 400.0) .background( Rectangle() // 👉 ② Animation対象となる矩形要素(遷移先) .matchedGeometryEffect(id: effectShapeID, in: namespace) ) 一意なID名と@Namespaceで定義する名前空間との紐付けをしている点がポイント(矩形&テキストが対象) LazyVGrid(columns: gridColumns, spacing: 8.0) { ForEach(MaterialFactory.getMaterials()) { material in // … 紐づけるID名を組み立てる + 一覧表示処理(省略) } } 補足: 一覧表示部分の処理イメージ 嬉しいポイント 複数要素にAnimation 処理を適用させたい場 合でも名前空間と一意 なIDがあれば実現可能 であること。
  7. 今回の経験を自分の知見として繋げていくために この時に調べた過程や抱いた疑問等を整理しておくと良さそうに感じました 🤔 具体例に触れた経験や解説を聞いた際に出た疑問やアイデアを記録する ①3Dモデルの管理について 結構重たいファイルなので、これら もGitで管理しているのか? Xcodeプロジェクト内に保持してお く以外の管理方法ってあるのか? 実際の業務ではこの様な3Dモデルは

    どの様なツールで作成されている? ③実務や機能の使われ方 平素でよく利用しているアプリの中 でAR機能を有するものはある? 機能内でARに関連する機能が生まれ たきっかけや着想は? 実務において機能開発において大変 だった点はどの部分か? ②AR関連実装の学び方 実務にキャッチアップするにあたっ てまずはどこから勉強したか? 普段から参考にしているサイトや学 習教材等はあったりする?
  8. まとめ 以前から抱いていた「難しそう」という名の壁を少しだけ突破できた 組み合わせて実現できる「可能性」に目を向ける事で楽しく取り組む事を忘れないで! 1. 簡単な作業をしたり実際に動かしてみることが壁を越える第一歩: 最初からある程度まとまった物を作る事は難しいので、最初はサンプルコードとして提示されているものを動かしてみる事が大 切だと改めて実感しました。また、自分が試した事を言語化して整理する事もしておくと良さそうに思いました。 2. 既存の技術や慣れ親しんだ実装と組み合わせてみる: 今回のUI実装はSwiftUIを利用したAnimation実装やInteraction操作とSceneKitで3Dモデルを表示する方法を組み合わせたものに

    過ぎませんが、簡単な事例を作って応用する際の大事な視点を持つヒントを発見する事にもつながると感じました。 3. 常に「余白」を持つ様にしてみよう: 一朝一夕で簡単に結果につながらずとも、この体験で得た「学び」の機会を応用していくためには、事例から情報を得たり試行 錯誤を繰り返す事も多いので、自分の中に常に余裕と新しい事を知る前のワクワクする気持ちを大切にしたいです。 ゆっくりと時間を持って取り組んでみる事の大切さ