- 投稿日:2019-07-11T23:53:19+09:00
【Swift】 UITextFieldの左側に余白を設ける
前置き
今回はほぼ前置きですが。
自作アプリもだいぶイメージができてきて、UITextFieldのplaceHolderにも文字を入れたりして、全体的な見た目を確認したりしてたんですが、どうも文字列が左端ぴったりに設定されて余白が全然ない感じがよろしくない。
左端から少し余白を設けて(要はpadding)、見た目よくしたかったんですが、どうも調べてみるとUITextFieldのカスタムクラスを作って、
textRect(forBounds bounds: CGRect)
のような関数を設定する、といった手法ばっかりで、おおかた画面イメージも出来てきてるところに今からそんな修正を入れるのも正直面倒。
というわけで、もう少し調べてみると、さらっと解決できる方法がありました。少し左側に余白ができているのがお分かりいただけると思います。
ソース
実装というより、おまじないのように書いていたある記述が悪さをしていただけ、というハナシなのですが。
ViewController.swifttextField.borderStyle = .none textField.layer.cornerRadius = 5 textField.layer.masksToBounds = trueUITextFieldの枠線の角に丸みを持たせるために、こういう書き方をしていたのですが、これの一番上の行、
textField.borderStryle = .none
という記述があると、textField内の文字列が左にきっちり詰めた状態になるようです。
なので、対処としては、この1行を消してあげるだけです。
(上で画像を貼っているとおり、角の丸みの設定はきちんと残っています)ViewController.swift// textField.borderStyle = .none textField.layer.cornerRadius = 5 textField.layer.masksToBounds = trueもし同様の事象で対処法を探っている方がこれを読まれているのであれば、
textField.borderStyle = .roundedRect
ように明示的に.borderStyle
の設定を.none
以外に変えてみるのは、対処の一つとして試してみてもよいかもしれません。まとめ、ポイント
今回は何か新しいやり方を知った・気づいた、というより、「いつもこう書いてるから」という理由で盲目的にソースをコピペして必要な部分だけを書き換えるようなやり方をしていると、実はそのロジックが悪さをしていることに気づきにくい、という教訓を得た感じでした。
- 投稿日:2019-07-11T16:03:49+09:00
BitriseのWorkflowをSlackから起動してiOSのデバイス登録をしよう!
TL;DR
- Slackのslash commandの使い方が(なんとなく)わかるよ
- BitriseのWorkflowをSlackから起動する方法が(なんとなく)わかるよ
- fastlaneでデバイス登録する方法が(なんとなく)わかるよ
- fastlaneで証明書更新する方法が(なんとなく)わかるよ
前提
- Bitriseの使い方がある程度わかる
- fastlaneの使い方がある程度わかって, matchで証明書を管理している
手順
- fastlaneにデバイス登録, 証明書更新をするlaneを作る
- Bitriseにデバイス登録, 証明書更新するWorkflowを作る
- BitriseからWebhook URLを取得する
- Slackのslash commandを登録する
まずはlaneを作ろう
Fastfileに下記の様な内容を登録
Fastfile# このlaneをBitriseから起動 lane :add_device_and_update_certificates do add_device( name: ENV["DEVICE_NAME"], # Slackから環境変数で指定 udid: ENV["UDID"] # Slackから環境変数で指定 ) update_certificates end # デバイス登録をする lane :add_device do |options| if options[:name] && options[:udid] # こちら参照->https://docs.fastlane.tools/actions/register_devices/ register_devices( devices: { options[:name] => options[:udid] } ) else UI.error "Usage: fastlane add_device name:'New device name' udid:'UDID'" end end # 証明証を更新する lane :update_certificates do # こちら参照->https://docs.fastlane.tools/actions/match/ match( type: "development", app_identifier: "tihimsm.app", force_for_new_devices: true, # デバイスが追加されていた場合証明書を更新する readonly: false ) end一つのlaneにregister_devicesとかmatchとか直接書いてもいいんだけど, 一応単品で使える様にlane分けてます.
BitriseにWorkflowを設定しよう
↓みたいな感じでfastlane使えるWorkflowを作ります
そして↓のようにlane名(add_device_and_update_certificates)を指定します
fastlaneもBitriseも適宜公式ドキュメント見て確認してくださいね.
BitriseからWebhook URLを取得しよう
codeタブのINCOMING WEBHOOKSのブロックからURLを取得します.
SETUP MANUALYを選択し, プルダウンからSlackを選択して, 表示されたURLをコピーしときます.
Slack commandを作ろう
SlackのサイドバーのAppsにある「+ボタン」をクリックします.
Appsの中から「Slash Commands」を選択しましょう.
「Add Configuration」から進み, 適当なコマンド名をつけちゃいます.
Slash Commandを作成すると↓のような画面が表示されるので, URL覧に先ほどBitriseから取得したURLを入力しましょう.
あとはアイコンの設定や, コマンドのヒントなどは任意で設定してください.あとはSlackでコマンドを叩きましょう
こんな感じの長ったらしいコマンドをSlackで入力します.
/test_command workflow: add_device_and_update_certificates|b: develop|ENV[DEVICE_NAME]: デバイス名|ENV[UDID]: UDID
- workflow: BitriseのWorkflow名
- b: githubのブランチ名
- ENV[DEVICE_NAME]: デバイス名が入ってfastlaneに渡されます
- ENV[UDID]: UDIDが入ってfastlaneに渡されます
詳しくは公式ドキュメントをご覧ください.
まとめ
いかがでしたでしょうか?
デバイス登録&証明書更新はiOS開発の面倒な作業の一つですが, コマンド一つで簡単にできるようになります.複数証明書を更新したい場合などは
update_certificates
の中でループさせるなどすれば, 一気に複数証明書を更新も可能です.もっと良い方法知ってる方たくさんいると思うので, CI周りの特殊な訓練を受けている方々のご意見お待ちしております.
- 投稿日:2019-07-11T16:03:49+09:00
BitriseのworkflowをSlackから起動してiOSのデバイス登録をしよう!
TL;DR
- slackのslash commandの使い方が(なんとなく)わかるよ
- Bitriseのworkflowをslackから起動する方法が(なんとなく)わかるよ
- fastlaneでデバイス登録する方法が(なんとなく)わかるよ
- fastlaneで証明書更新する方法が(なんとなく)わかるよ
前提
- Bitriseの使い方がある程度わかる
- fastlaneの使い方がある程度わかって, matchで証明書を管理している
手順
- fastlaneにデバイス登録, 証明書更新をするlaneを作る
- Bitriseにデバイス登録, 証明書更新するworkflowを作る
- BitriseからWebhook URLを取得する
- slackのslash commandを登録する
まずはlaneを作ろう
Fastfileに下記の様な内容を登録
Fastfile# このlaneをBitriseから起動 lane :add_device_and_update_certificates do add_device( name: ENV["DEVICE_NAME"], # slackから環境変数で指定 udid: ENV["UDID"] # slackから環境変数で指定 ) update_certificates end # デバイス登録をする lane :add_device do |options| if options[:name] && options[:udid] # こちら参照->https://docs.fastlane.tools/actions/register_devices/ register_devices( devices: { options[:name] => options[:udid] } ) else UI.error "Usage: fastlane add_device name:'New device name' udid:'UDID'" end end # 証明証を更新する lane :update_certificates do # こちら参照->https://docs.fastlane.tools/actions/match/ match( type: "development", app_identifier: "tihimsm.sugoiyapp", force_for_new_devices: true, # デバイスが追加されていた場合証明書を更新する readonly: false ) end一つのlaneにregister_devicesとかmatchとか直接書いてもいいんだけど, 一応単品で使える様にlane分けてます.
Bitriseにworkflowを設定しよう
↓みたいな感じでfastlane使えるworkflowを作ります
そして↓のようにlane名(add_device_and_update_certificates)を指定します
fastlaneもBitriseも適宜公式ドキュメント見て確認してくださいね.
BitriseからWebhook URLを取得しよう
codeタブのINCOMING WEBHOOKSのブロックからURLを取得します.
SETUP MANUALYを選択し, プルダウンからSlackを選択して, 表示されたURLをコピーしときます.
slack commandを作ろう
SlackのサイドバーのAppsにある「+ボタン」をクリックします.
Appsの中から「Slash Commands」を選択しましょう.
「Add Configuration」から進み, 適当なコマンド名をつけちゃいます.
Slash Commandを作成すると↓のような画面が表示されるので, URL覧に先ほどBitriseから取得したURLを入力しましょう.
あとはアイコンの設定や, コマンドのヒントなどは任意で設定してください.あとはSlackでコマンドを叩きましょう
こんな感じの長ったらしいコマンドをslackで入力します.
/test_command workflow: add_device_and_update_certificates|b: develop|ENV[DEVICE_NAME]:デバイス名|ENV[UDID]:UDID
- workflow: Bitriseのworkflow名
- b: githubのブランチ名
- ENV[DEVICE_NAME]: デバイス名が入ってfastlaneに渡されます
- ENV[UDID]: UDIDが入ってfastlaneに渡されます
詳しくは公式ドキュメントをご覧ください.
まとめ
いかがでしたでしょうか?
デバイス登録&証明書更新はiOS開発の面倒な作業の一つですが, コマンド一つで簡単にできるようになります.複数証明書を更新したい場合などは
update_certificates
の中でループさせるなどすれば, 一気に複数証明書を更新も可能です.もっと良い方法知ってる方たくさんいると思うので, CI周りの特殊な訓練を受けている方々のご意見お待ちしております.
- 投稿日:2019-07-11T16:01:35+09:00
【Swift】iOSアプリでよく見る『既に開いているタブをタップして、一番上までスクロール』の実装方法
概要
iOSアプリでよく見るこの動きをサクッと実装します
- tabbarの既に開いているタブアイコンをタップすると、一番上までスクロールする
- ただしUINavigationControllerでの「戻る遷移」を邪魔しない
アプリの構造
- タブバー(UITabBarController)が複数の画面(UIViewController)を持っている
- 各UIViewControllerはUINavigationControllerで遷移する
注意点
概要の通りだが、
UINavigationControllerで遷移する場合、タブをタップ時のデフォルトの動きは
『UINavigationControllerのrootに戻る』であり、
このときはスクロールさせないようにしたい実装
class SampleTabBarController: UITabBarController, UITabBarControllerDelegate { // shouldSelectでは、現在開いている画面がnavigationControllerのルートかを判定するまで。 var shouldScroll = false func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { self.shouldScroll = false // 表示しているvcがnavigationControllerルートのときはスクロールさせる // ルート以外は、navigationControllerの戻る機能を優先しスクロールさせない if let navigationController: UINavigationController = viewController as? UINavigationController { let visibleVC = navigationController.visibleViewController! if let index = navigationController.viewControllers.index(of: visibleVC), index == 0 { shouldScroll = true } } // 遷移を許可するためのtrueを返す return true } // didSelectで、選択されたタブが、前回と同様なら、shouldSelectの結果(shouldScroll)も考慮し、スクロールさせる var lastSelectedIndex = 0 func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) { guard self.shouldScroll else { return } if self.lastSelectedIndex == tabBarController.selectedIndex { if let navigationController: UINavigationController = viewController as? UINavigationController { let visibleVC = navigationController.visibleViewController! if let scrollableVC = visibleVC as? ScrollableProtocol { scrollableVC.scrollToTop() } } } self.lastSelectedIndex = tabBarController.selectedIndex } }各タブのViewControllerたちの実装
// スクロールを持つプロトコル protocol ScrollableProtocol { func scrollToTop() } // ↑のように protocolで縛ることで、各画面のスクロールが // scrollViewだろうが、tableViewやcollectionViewだろうが、 // tabbarControllerは意識せずに実行できる class AAAViewController: ScrollableProtocol { // scrollViewを持つViewController @IBOutlet weak var scrollView: UIScrollView! func scrollToTop() { scrollView.setContentOffset(CGPoint(x:0, y:0 - scrollView.contentInset.top), animated: true) } } class BBBViewController: ScrollableProtocol { // tableViewを持つViewController @IBOutlet weak var tableView: UITableView! func scrollToTop() { let indexPath = IndexPath(row: 0, section: 0) tableView.scrollToRow(at: indexPath, at: .top, animated: true) } } class CCCViewController: ScrollableProtocol { // collectionViewを持つViewController @IBOutlet weak var collectionView: UICollectionView! func scrollToTop() { let indexPath = IndexPath(row: 0, section: 0) collectionView.scrollToItem(at: indexPath, at: .top, animated: true) } }実装の解説と、改善したいと思っているところ
UINavigationControllerで遷移する場合、タブをタップ時のデフォルトの動きは
『UINavigationControllerのrootに戻る』であり、このときはスクロールさせないようにしたいこの動きがやや面倒で
今回の実装ではTabBarControllerのshouldSelect
とdidSelect
で判定を行っている。
didSelect
の発火時では、すでにUINavigationControllerのルートに移動してしまっているため、
UINavigationControllerで遷移先のVCに居たとしても常にスクロールが走ってしまう。一方で
shouldSelect
ではtabBarController.selectedIndex
がまだ
「今回選択されたタブ」ではなく「前回選択されたタブ」であるため
『既に開いているタブが選択された』が判定できない。この辺をきれいに改善する方法があれば教えてほしいです。
参考
- 投稿日:2019-07-11T14:26:49+09:00
初めて開発した個人Toy Project!!
非常に単純な支出管理アプリケーション
Spending Management - SPEM!
旅行,小遣い,その他いろいろのお金を
複雑に管理する必要なく簡単に
目標のタイトルと金額を設定して管理してみてください
皆様の支出をSPEMがお手伝いいたします。
- 投稿日:2019-07-11T13:38:10+09:00
Bitrise で並列ビルド (iOS/Android)
概要
- React Native のビルドとデプロイに Bitrise を利用している
- iOS のビルド時間が長いせいで、 Android が完了する前に45分超えてしまう
- そのため、 iOS/Android でビルドするワークフローを並列実行したい
- 使っている技術が別物だから、並列実行しても問題ないのではないか
結論
並列実行は GUI ではサポートされていないが、 Bitrise CLI を使うと、超単純に並列ビルドが可能。
- ローカルに下記のような
bitrise.yml
を入れてGitリポジトリに入れるbitrise.yml--- format_version: '6' default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git project_type: react-native workflows: common: steps: - yarn@0.0.8: inputs: - command: install ios: steps: - certificate-and-profile-installer@1.10.1: {} - # 色々 android: steps: - install-missing-android-tools@2.3.5: {} - # 色々
- Bitrise にて、ワークフローエディタを開き、下記を入力
#!/bin/bash set -ex bitrise run common --config bitrise.yml bitrise run ios --config bitrise.yml & bitrise run android --config bitrise.yml & wait全体としてはこんな感じ。
これで、ビルド時間が3分の2くらいになった。
- 投稿日:2019-07-11T11:40:40+09:00
スマホのシェア率を見れるサイトのまとめ
スマホのシェア率なんてググればすぐ出てくるだろーって思ったら意外と時間かかったのでまとめました。
特にandroidの機種ごとのシェア率は全然見つからないですね…
サイト名 OS カテゴリ 集計期間 対象 集計方法 apple iOS バージョン 1日? 世界 App Store(アクセス解析?) スマタブinfo iOS/android バージョン 直近3ヶ月? 日本 独自調査 DeviceAtlas iPhone+android 機種 四半期 国別 提携サイトのアクセス解析 statista 記事により異なる android android バージョン 1週間 世界 Google Play ストアのアクセス解析 statcounter iOS/android バージョン/メーカー 最小で日単位 世界/国別 提携サイトのアクセス解析 webrage iPhone+android 機種 1ヶ月? 日本 アクセス解析 BCN+R android メーカー 半期 日本 販売台数 BCN+R iPhone+android 機種 1ヶ月 日本 販売台数 apple
https://developer.apple.com/support/app-store/
シンプルな円グラフでわかりやすいです。スマタブinfo
http://smatabinfo.jp/os/android/index.html
iOSはバージョンごと、androidはAPKレベルごとに見れます。
また実測値は見れませんが、androidの機種ごとのシェアが5段階のランクで見れるのがいいですね。DeviceAtlas
https://deviceatlas.com/blog/most-popular-smartphones#japan
OS関係なく全機種のシェア率の上位12機種が見れます。
日本はiphoneのシェア率が高いので実質ihponeの機種ごとのランキングになってます。statista
https://www.statista.com/search/?q=share&category=418&subCategory=481
ほとんどの記事が有料会員専用なのであまり使えないかもしれません…
集計方法は記事によって異なります。android
https://developer.android.com/about/dashboards/index.html
集計期間が2018/10/20~2018/10/26とちょっと古いです。
他に画面サイズと密度ごとのシェア率も見れます。statcounter
http://gs.statcounter.com/
一番情報量が多く使い勝手がいいサイトでした。
OSごと、バージョンごと、などなどたくさんの統計を2009年1月から最小は日単位で見ることができます。
元となるデータも200万サイト月間100億PVと申し分ないです。webrage
https://webrage.jp/techblog/sp_share/
日本でのシェア率ランキングです。
iPhoneはiphoneで一括りにされてます。BCN+R
https://www.bcnretail.com/market/contents_type=49
androidのシェア率を全国の主要家電量販店・ネットショップのPOSデータから集計しています。
アクセス解析ではなく販売台数なので注意。
目当ての記事探すのがちょっと大変かも。
- 投稿日:2019-07-11T11:34:22+09:00
CocoaPods 1.7でCDNを使ったらSpecs更新が爆速だった
tl;dr
CocoaPodsをv1.7系にしてCDNをsourceにして爆速!
CocoaPods v1.7
CocoaPodsのv1.7系が5/23にリリースされています。
あまりCocoaPodsは追っていなかったのですが、Specsの更新が爆速になってて感動したのでまだの人は使ったほうがいいです!CDNサポート
v1.7からsourceとしてCDNがサポートされ
1.7.2からFinalizeされNetlifyでホストされているようです。Specs.gitのかわりにcdnをsourceに指定しましょう。
- source 'https://github.com/CocoaPods/Specs.git' + source 'https://cdn.cocoapods.org/'でも使ってみた感じだとbeta1で案内されていたjsdelivr.netの方が早かったです。
CDNソースだと必要なSpecだけ取得できる
以下のPodを利用している環境で試しました。
Crashlytics DeallocationChecker Fabric Mockingjay Periphery SwiftFormat SwiftGen SwiftLint URITemplateゼロからSpecs.gitを更新すると手元で2分かかるところが・・・
CDNだと利用しているPodのSpecだけDLされますResolving dependencies of `Podfile` CDN: jsdelivr-cocoa Relative path downloaded: Specs/5/e/d/SwiftFormat/index.txt, save ETag: W/"2eb-6gklZWWeegEzBbq63V1uA8xQgu8" CDN: jsdelivr-cocoa Relative path: Specs/5/e/d/SwiftFormat/index.txt modified during this run! Returning local CDN: jsdelivr-cocoa Relative path downloaded: Specs/5/e/d/SwiftFormat/0.13/SwiftFormat.podspec.json, save ETag: W/"1ea-z72pQ1Xtukf7S9Ip+lU60dloSNg" CDN: jsdelivr-cocoa Relative path downloaded: Specs/5/e/d/SwiftFormat/0.16/SwiftFormat.podspec.json, save ETag: W/"1ea-4yhY9JtA+tyAFcmG52nHey4IYGc" CDN: jsdelivr-cocoa Relative path downloaded: Specs/5/e/d/SwiftFormat/0.16.3/SwiftFormat.podspec.json, save ETag: W/"1ee-EDv9/GnjwZJZpIb/nCzuPNfAy+o" CDN: jsdelivr-cocoa Relative path downloaded: Specs/5/e/d/SwiftFormat/0.16.1/SwiftFormat.podspec.json, save ETag: W/"1ee-/+ETQMcayrjdya79gkc0dwEHyko" 略 bundle exec pod install --repo-update --verbose 1.75s user 1.37s system 78% cpu 3.955 total約4秒で終わってしまいました!
なんてこった30倍速!みんな使おう!
CircleCI使ってる人向け
CircleCIではSpecs更新を高速化するために
cocoapods-specs.circleci.com/fetch-cocoapods-repo-from-s3.sh
が案内されています。
執筆時点のこのスクリプトの中身は従来のSpecs.gitを更新する内容になっています。速いといっても4分ぐらいかかるので、上述のCDNからの必要Specだけの取得に比べると雲泥の差ですし
今後スクリプトの中身がCDN sourceに対応したのだとしても所要時間に意味のある差は生まれないと思いますので利用をやめて素朴な[bundle exec] pod install [--repo-update]するだけで良いと思います。
- 投稿日:2019-07-11T00:39:06+09:00
UnityでARKit2 - 第四回 -
UnityでARアプリ開発をやってみたい 第四回
- 第一回: UnityでARKit2
- 第二回: UnityでARKit2 第二回
- 第三回: UnityでARKit2 第三回
- 第四回: UnityでARKit2 第四回 --> この記事
1. この記事でやること
BitBucketから拝借してきたunity-arkit-pluginのExampleにあるUnityARWorldMapをちゃんと読もうの回です。
2. UnityARWorldMapを理解したい
UnityARWorldMapはUnityARKitPluginに用意されているExampleのUnityプロジェクトです。
ここではUnityARWorldMapがどういうことをやっているのかを理解することを目的としています。
まずは構成要素からチェックしていきます。Hierarchy
UniryARWorldMapのHierarchyは以下のようになっています。
- Directional Light
- 通常の光源です
- Random Cube
- AR空間においておくCubeです
- このCubeにはX,Y,Z方向がわかるようにコンポーネントがぶら下がっています
- Camera Parent
- Main Camera
- GameView用のメインカメラです
- なんで階層化しているのかわからない...
- Hit Cube Parent
- Hit Cube
- AR空間に配置するCubeです
- UnityARTestHitExample.csがあてられています
- なんで階層化しているのかわからない...
- Generate Planes
- AR空間上で平面を認識するものです
- UnityARGeneratePlane.csがあてられています
- PointCloudParticleExample
- AR空間で特徴点を表すために追加されているParticleです
- ParticleのPrefabをつかっています
- PointCloudParticleExample.csがあてられています
- Canvas
- Layout
- Save button
- Load button
- 画面上に表示される補助ボタンです
- OnClickイベントを割り当てたりすることができます
- EventSystem
- イベントドリブンでゲームを構成するためのコンポーネント、なにか画面に見えるわけではないです
- WorldMapManager
- WorldMapManager.csがあてられています
- ARCameraManagerと連携してWorldMapを管理します
- ARKitControl
- ARセッションをコントロールしています
- ARCameraManager
- カメラマネージャーです、ARアプリの基盤的役割を果たします
Unity Scriptの読み方
Inspectorの設定値
ARCameraManagerを例に取ります
Componentとして追加されているスクリプトはUnityARCameraManager.csです。
UnityARCameraManager.csのようにファイル名を指定し、このファイル名と同じ名前のクラスを内部に定義するのがルールです。Unityのinspectorビューで設定できる項目がいくつかありますが、これはこのクラスのPubilcなメンバが自動的にUIに反映されるようになっており、これらの設定値はUnity UIで与えるようになっています。
public class UnityARCameraManager : MonoBehaviour { public Camera m_camera; private UnityARSessionNativeInterface m_session; private Material savedClearMaterial; [Header("AR Config Options")] public UnityARAlignment startAlignment = UnityARAlignment.UnityARAlignmentGravity; public UnityARPlaneDetection planeDetection = UnityARPlaneDetection.Horizontal; public bool getPointCloud = true; public bool enableLightEstimation = true; public bool enableAutoFocus = true; public UnityAREnvironmentTexturing environmentTexturing = UnityAREnvironmentTexturing.UnityAREnvironmentTexturingNone; [Header("Image Tracking")] public ARReferenceImagesSet detectionImages = null; public int maximumNumberOfTrackedImages = 0; [Header("Object Tracking")] public ARReferenceObjectsSetAsset detectionObjects = null; private bool sessionStarted = false; // 以下略 }publicの
Camera m_camera
ではCameraクラスのインスタンスを指定する必要があり、ここにMain Cameraを割り当てています。
Headerを指定すると、Inspectorビューでパラメーターのグループ名として表示されます。
publicのパラメータがenumの場合はInspectorではドロップダウンで表示されます。
例えばUnityARAlignment
はこのenumがUnityARSessionNativeInterface.csに定義されています。
デフォルトで値を入れておけばInspectorビューでの初期値となります。StartとUpdate
各スクリプトにはStart()メソッドとUpdate()メソッドが用意されていることが多いです。
Startは最初に1度だけ実行され、Updateは毎フレームで実行されます。
フレームはゲーム画面を常に最新に保つためのクロックのようなものです。UnityARCameraManagerの場合、Start()メソッド内でUnityARSessionNativeInterfaceのセッションを取得し、セッションを開始します。
UnityARSessionNativeInterfaceはUnityでARを取り扱う上でのベースとなるクラスで、このセッションとしてすべての処理を管理します。
Update()メソッドでは各フレームにおけるカメラの物理的なポジション情報をデバイスから取得し、メンバのm_cameraにセットする。これにより、カメラのいち情報を内部で保持することができます。(別の処理で参照するなど)SetCameraは初期だと誰も使っていなさそうですね、外部からカメラを再割り当てする感じかな。
スクリプトを読んで全体像を理解する カメラ編
ARCameraManagerのCameraメンバとして設定されているMainCameraに割り当てられているスクリプトをチェックします。
- UnityARVideo
メンバとしてMaterialが割り当てられています。YUVMaterialが割り当てられます。
いろいろ書いてあってややこしい。これはiOSのカメラからの情報をUnityCameraに展開するスクリプトなので、理解は難しいです。
このカメラ映像がUnityCameraに割り当てられる際にじゃまにならないよう、Cameraの設定でClear Clear FlagsはDepth onlyにする必要があります。
- UnityARCameraNearFar
SetCameraNearFar(iOS側:外部IF)経由でUnityCameraのNearとFarを設定します。
これらの値は、3D空間にものを描画するときの手前側と奥側を設定するものです。イメージとしては、AR空間に置かれた物体にカメラを向けながらじりじりと近づいたとき、ある瞬間から画面に映らなくなるような処理に使います。ここまでに出てきた
UnityARVideo, UnityARCameraNearFar, UnityARCameraManagerの3つのスクリプトはARアプリ制作における必須アイテムです。スクリプトを読んで全体像を理解する
UnityARKitControl
次はUnityARKitControlについてスクリプトをチェックします。
UnityARKitControlにはpublicメンバがないので独立スクリプトです。
Start処理はシンプルで以下のパラメーターのセットを行います。
- runOptions
- alignmentOptions
- planeOptions
このスクリプトでは主にOnGUIで処理が行われます。OnGUIは先のUpdateに似た事前に定義されたタイミングで呼び出されるメソッドです。
Updateは毎フレームごと、つまりユーザーのオペレーションがあろうがなかろうが、一定間隔で実行されます。一方で、OnGUIはイベントが発生するたびに呼び出されるメソッドです。ユーザーが操作したときなどのイベント発生毎に呼び出されます。OnGUIでは主にARSessionのStopとStartがコントロールされています。
WorldMapManager
次はWorldMapManagerについてスクリプトをチェックします。
WorldMapManagerは先程のUnityARCameraManager
をメンバに持っています。まずStartメソッドではARFrameUpdatedEventにイベントが登録されています。
これはその名の通りフレームごとに発生するイベントです。
登録されたイベントはOnFrameUpdateです。
OnFrameUpdateではちょっとしたログの登録しかされないので、ここは本体ではないです。
WorldMapManagerにはSave()とLoad()が用意されており、これらはCanvasのSave buttonとLoad buttonに割り当てられています。
そのためこれらのボタンがクリックされるとこのメソッドに入ります。
まずSaveメソッドでは、ARWorldMapをpathに保存します。ARWorldMapについてはARKit2.0のReadMeを見ると色々書いてあります。
WhatsNewInARKit2_0.mdを見てください。
このARWorldMapはARKit2.0で追加された、AR空間の共有を実現するために非常に重要な仕組みです。
SaveSerializedとLoadSerializedはSaveとLoadの別パターンで、ARWorldMapをシリアライズしてByte列で保存するという違いがある。Canvas (Buttons)
Canvas中のSave ButtonとLoad ButtonはWorldMapManagerのSaveとLoadが割り当てられています。
Generate Planes
Generate PlanesにはUnityARGeneratePlane.csのスクリプトが割り当てられています。
- UnityARGeneratePlane
publicでGameObjectが割り当てられています。デバッグ用のplanePrefabです。
デバッグ用というのは、このPrefabを開くとわかるように、境界線付き平面だからです。
通常のアプリには不要な線が表示されているので、Debugです。
Start処理ではまずUnityARAnchorManagerのインスタンス生成をしています。UnityARAnchorManagerは平面のアンカー情報を保持するクラスで、イベントドリブンでアンカー情報を更新します。ARGeneratePlaneではこのクラスからアンカーを教えてもらって、以後の処理をします。
次にUnityARUtilityでplanePrefabを初期化します。これで、先のDebug用Prefabがセットされました。あとはARUtility側でイベント更新に合わせてPrefabが画面上に表示されます。
HitCubeParent/HitCube
HitCubeにはBoxColliderがセットされています。ColliderはUnity空間上での物理量同士の接触を発生させるためにセットするパラメーターです。
これがないと物体を物体が通り抜けるようになってしまいます。Meshも定義されているので視認可能な物体ということになります。HitCubeには次のスクリプトが割り当てられています。
- UnityARHitTestExample.cs
- publicなメンバとして以下の3つが用意されています。
- Transform
- HitCubeParentがセットされています
- LayerMask
- float maxRayDistance
- Unity上でのデバッグ用なので無視してOKこのスクリプトではUpdate処理が主役です。Update処理では画面がタッチされたポジションを取得します。
このポジションを使って、HitTestWithResultTypeを呼び出し、UnityARSessionNativeInterfaceがうまいことやって、ヒット位置を検出し、m_HitTransformに位置と回転をセットします。これにより、Cubeが配置されます。おしまい
これでUnityARWorldMapの全体像チェックはおしまいです。