- 投稿日:2021-01-29T23:26:23+09:00
Flutterのバージョンを切り替える
こちらの記事を参考にしました
https://qiita.com/sekitaka_1214/items/60c4c36585f2a216ffd0
- 投稿日:2021-01-29T23:23:17+09:00
休学中にアプリを作ったらappleにスパムアプリと言われた話
今年度大学を1年間休学しました。その間に作ったアプリをリリースしたのでやってきたことを振り返ろうかな〜と思いこの記事を書きました。
作ったトークアプリ
『ChillTalk』というトークアプリをつくりました。1つのルームにユーザーが集まり、移動をすることでいろんなグループの話が聞け、話相手を見つけられるのが特徴です。自分の付近にいるユーザーの声だけが聞こえます。
3月
まず、わたしは機械工学部なのでプログラミングは少しいじっただけでまともに勉強したことがありませんでした。高校を選ぶ頃はものづくりが好きだったり、宇宙飛行士に憧れ機械系に進みました。しかし、もともとガジェットやアップルなどのテック企業が大好きだったこともありソフトウェアへの関心が強くなり仕事ではハードではなくソフトを作りたいと考えるようになりました。そんなとき、今やっている研究も楽しくなく来年度を続けるのかと考えるととても気が重くなり、休学を考えました。
一年後には学生に戻れて学生じゃ無くなるわけじゃないし、休学金もかかりません。ソフトウェアの勉強して、なにか作ってみたいと思い休学しました。4 - 5 月
まだやることがあったので4月は学校に行っていました。
休学し始めた頃は、インスタのARフィルターを作っていました。
あとは、LINEスタンプとかも作りました。6 - 7 月
ここで、真剣に1年なにやろうか考えました。そこで思いついたのがアプリ制作でした。
今までは自分で作って満足していたけどあまり人に使ってもらうことは考えていませんでした。今度はみんなに使ってもらえるアプリ作りたいと思いました。そこで考えたのがトークアプリです。
twitterのようなくだらない話をしたり、いきなり知らない人と絡められる、そんな気軽に人としゃべれる場所が欲しいなあと思いました。トークアプリすでに多くあります。しかし、ほとんどが1対1で会話するものです。いきなり2人きりで喋るのはきつい。話す内容を考えるのにハードルが高い。そんな感じで今まで1回も使ったことがありませんでした。
そこで考えたのが1つのルーム内にユーザーを集めてその中を移動して会話相手(グループ)を見つけるというものです。映画の話をしたかったらマップ上の映画館に行けばいいし、誰かと話をしたかったらぶらぶらマップ内をあるいて話し相手を見つけれる、というものです。マンダロリアンの新作について誰かと話したいし、友達つくりたい。これならルームに入るだけで話し相手を見つけられるんじゃないか。
早速作り始めました。8月
早速Xcodeで作りはじめました。
音声通話まわり
WebRTCという仕組みを使えば通話ができるらしい。そして、WebRTCを行う場合の通信方法がいくつかある。
P2P : ユーザー同士が直接通信する。ユーザー側の処理が増えるので接続数には制限がある。
MCU : サーバーを介して通信する。サーバー側の処理が増えるので接続数が増やせる。
SFU : サーバーを介して通信する。WebRTCに使われる。2,3人の通信だとP2P品質がいいとのこと。今回つくりたいのは大人数の会話を行いたいのでその場合はSFUを使うといいらしい。
そして、NTT CommunicationsがSkyWayというWebRTCアプリのSDKを提供していた。これを使おう!!移動まわり
ジョイスティックを使って移動したい。けど実装難しくない!?時間かかりそう。とりあえず十字キーにすることにしました。
ゲームみたいにスムーズに移動したいけどXcodeだとできない。毎フレームごとわずかな移動を連続させると処理が重くなりカクカクするし...とりあえず「x座標に1移動する」みたいな処理になりました。
そして、他のユーザーに近づくとその人のSkyWayのトークルームに入室に、会話でできるようにしました。本来実装したかったユーザー間の距離による音量減衰はできませんでした。とりあえずテスト版が出来た
思ってたのと違う!! 移動方法も移動もスムーズじゃない。
よく考えれば、自分がやろうとしていることはシステム的にはゲームなんじゃないかと気づきました。
ゲーム開発ならUnityをつかうことになるけど、せっかくSwift勉強してXcode使えるようにそれを一切使わずに他の開発するのはもったいないと思いました。けれど、このアプリのアイデアはウケるし気がしたしみんなに使ってもらいたい。どうしてもこのアプリを作りたいと強く思いUnityを勉強しました。9月
Unityの本を買ってきてサンプルのアプリを一通り作りました。思っていたより簡単である程度のことはすぐにできました。
スムーズに移動できる! ジョイスティックはアセットストアにあったものを使いました。(アセットストア便利すぎw)
リアルタイム同期にはPUNを使いました。簡単に説明すると、Photon(PUN)はユーザー間の通信をリレーするサーバーを提供してくれるサービスです。SDKを入れて設定するだけで簡単にリアルタイム通信を行うことができました。また、Photonが提供しているPUN VoiceをというボイステャットができるSDKを使用しました。そして、ユーザーのオブジェクトごとにスピーカーを設定することで音量減衰を実装できました。
今までXcodeでやっていたことはなんだったんだろう?と思うレベルで、すぐにアイデアを実装できました。行き詰まったとき
ときどきアプリが思ったように動かなかったり、エラーが連続したりしてモチベーションがなくなるときが結構ありました。
そんなときはいろいろ作ったりして遊んでいました。LEDテープをディスプレイ裏に貼ってArduinoとPCをつなげて画面の色と同期したり
これはAR,VR関係の人達に反響があって嬉しかったです。
Google PlayStoreでリリースしたのでダウンロードしてくださいーー!
ということで「Speed Display」リリース
— Brian (@Brian3546) November 30, 2020
位置情報のアクセスを許可してねhttps://t.co/XUNNfYFSac pic.twitter.com/MXeL30YqSa冬あたり
SNSみたいにフォロー機能をつけて、フォローした人がアプリを立ち上げたらフォロワーに通知がいく機能や、フォローしているルーム内の座標を取得してそこにワープ出来たらいいなと思い実装を始めました。しかし、これが地獄の始まりでした。誰をフォローしたかなどを保存するのにはサーバーが必要です。そのあたりはFirebaseを使いました。swiftで使ったこともあってかサーバーへの保存・読み取りは簡単でした。しかし、フォローリストをサーバーとリアルタイムに同期させたり、フォロー一覧のスクロールビューを作るのにものすごい時間がかかり、それに伴いモチベーションも低下し気づいたら1ヶ月経っていました。
そして、ある程度出来たのがこちらなんとか形になった!
数人の友人にTestFlightで使ってもらいました。
アイコンの移動や同期はうまくいきましたが、重大な問題が発覚しました。
スマホのスピーカーを使用して通話するとマイクがスピーカーの音を拾ってしましい、ハウリングが発生しました。
話すたびにハウリングし、まともに会話が出来ませんでした。
自分がイヤホンするとハウリングは発生しないのですが相手のマイクが自分の声を拾い、数秒後に声が返ってきます。非常に話しづらい...
(この原理を利用して、相手の話をやめさせる機械があるそうですw)PUN Voiceのマニュアルをみてみるとスマホとスピーカーを使用しての会話は非推奨とのこと。ハウリングを無くす機能もありませんでした。他の通話アプリはどうしているのかLINEを使ってテストしました。そしたら、相手が話している時は自分のマイクがミュートされていました。反対に自分が話しているときには相手の音声はオフになって聞こえなくなっていました。なるほど、意外とシンプル。これなら実装出来そう。
自分の音量が一定値を超えたら相手の音声をオフにし、音量が一定値以下になったら相手の音声をオンにする。というシンプルなものを作ってみました。
これでテストしてみたら、だいぶハウリングが無くなりました。しかし、遅延の存在を忘れていました。自分が話終えたあとすぐに相手の音声をオフにしていたので、相手のマイクから自分の声が聞こえてきます。自分が話し終えた後も遅延分音声をオフにしている必要があるのです。
遅延分も考慮して実装してみると、確かにハウリングは無くなりました。しかし、話し終えた後に相手の声が聞こえるまでの時間があり、素早い会話ができません。例えると、一球一球丁寧にキャッチボールをしているような感じでした。また、遅延も一定ではありません。相手の話ているのに音声がオンになっていなかったり使いづらかったです。遅延分の制御がとても大変でした。ここでまた2,3週間かかってしまいました。そんな時、本来なくてもいい機能に時間を費やし過ぎていることに気がつきました。思いついた機能を付けたすことで、コードもエラーも多くなりました。確かにあったほうがいい機能かもしれないが、まだユーザーもいない状態で肝の機能以外で時間を費やすことは無駄だと思いました。とりあえず最低限の機能を持ったものをリリースしようと決めました。ここから一気に進捗が捗りました。
・アプリデザイン
・サインイン関係
・アイコン写真の設定
・バグの修正そして、ハウリング問題を解決するためにもう一度PUN Voiceのマニュアルを読んでみると、ハウリングキャンセリング設定も見つけました!数ヶ月前には無かったはずなのに!!!設定のチェックマークをオンにすると、たしかに綺麗にハウリングが消えている!スピーカーつかっても普通に会話できる!時間かけて作ったアルゴリズムはなんだったんだ。と複雑な気分になりました。
その時に本当にこの機能が必要なのか考え結果、とりあえずアプリのコンセプト部分だけでいいからリリースすることを優先するようにしました。
こうして一通りアプリが完成しました。
AppStoreとの戦い
いよいよストアの審査へ提出します。GooglePlayは問題ないと思っていたのですが、AppStoreがとても心配でした。
というのも上記にあった車のスピードをHUD表示するアプリをAppStoreに申請した際、何回か修正と提出を繰り返した後にこう言われました。
Guideline 4.2 - Design - Minimum Functionality
We understand that there are no hard and fast rules to define useful or entertaining, but Apple and Apple customers expect apps to provide a really great user experience.
要するに、シンプルすぎるからだめ。
ここまできていきなり殴られた気分になりました。(まあ、このアプリは3,4日で作ったのでそんなに傷は深くないですが)
こんなことが今回もある可能性があると思うととても心配でした。いざトークアプリを提出しました。
何回かリジェクトされ、バグの修正やアプリ情報を追加している時、こんなメッセージが来ました。
Guideline 4.3 - Design
メッセージのやりとりはもう見れず細かい文章はもう覚えていないのですが、「マッチングアプリはもうたくさんあるからいらないよ。他と似たようなアプリならばスパムとみなして、アカウント消すよ。アプリのデザインを一からやり直して」というような事を言われました。
調べてみるとちらほら同じ4.3リジェクトをくらっている人がいましたが、このリジェクトされた場合はもう審査は通らないという感じでした。
落ち込みます。半年間作ってきたものをスパムと言われ、リリースできないなんて一体自分は何をしてきたのだろう。この日は一日中落ちこんでいました。それでも、どうしてもリリースしたかったのでメールを書きました。
The matching feature is just a part of the app.
It was created to be used with people you know, such as classmates, workmates, and friends.The best feature of this app is that it allows you to move around the room, listen to different conversations, and participate in the ones that interest you. This feature has been well received by many people and we believe it is much desired.
There is no other app in the App Store that has this feature. This is a unique feature that only this app has. For these reasons, I don't think it's the same as other matching apps or talk apps.
I am responsible for being the developer of this app. While the app is being used by users, I will always check and strictly respond to reports from users to maintain the quality of the app.
・自分が作りたかったのは、このアプリにくれば誰とでも何人とでも話ができるというものです。それはどのアプリにもない独自性のある機能である事。
・個人開発者ではあるがユーザーの秩序を保ち利用規約を守らせる事。
を書きました。少しオーバーに書き過ぎたかもw
この熱量が少しでも伝われ!!と思いかきました。なんとかなるかもしれないと。やったぁーー!!!!!!
あっさりと審査通りました。この時は本当に嬉しかったです。
地獄から一気に元気になりました。
GooglePlayでも審査が通り、こうして無事リリースできました。この一年で学んだこと
・アプリ開発初心者がいきなりリアルタイム同期(SNS)のアプリを作るのはハードルが高い
・やらない事を決める
・100%の完成はしない
・ある程度完成したらみんなに使ってもらい、ユーザーの声を聞いて機能の追加を考えていく
・困ったらエラーログをしっかり読む
・アプリ制作たのしい
・ものづくりたのしいこれからもアプリを作ってくうえで一番気を付けたいことは、100%を目指したらいつまでも終わらないことです。そんあんことは前から分かったつもりでいたけれど、なんだかんだこり始めてしまうものです。twitterにもアプリ起動後のロード時間はあるし、iPhoneにもいろいろバグがあります。世の中を見てみるとみんな完璧ではないことに気づきました。
決して動作が速く、バグもない完璧な物が評価いいわけではありません。みんなが気に入るのは根幹にあるアイデアなのかなと思いました。これからはもっとアイデアに重点を置いて短い期間でアプリを作っていきたいと思います!
最後まで読んでいただきありがとうございました。ぜひ「ChillTalk」を使ってみてください!
GooglePlayはこちら
AppStoreはこちらtwitterもフォローお願いします!
https://twitter.com/Brian3546
- 投稿日:2021-01-29T23:14:25+09:00
機種変後のiPhoneでバイブレーション機能が動作しなくなった際の対応
はじめに
iPhoneを機種変し、事前バックアップから復元をしたところバイブレーション機能が正常に動作しなくなりました。ググっても対応が出てこなかったので、その際の対応を記録しておきます。
※Appleサポートに電話して確認した内容のメモ書きです。なお、docomo、au、softbankと言ったキャリアのショップで購入した場合でも、Appleサポートに連絡することになります。最寄りのAppleストアに行くか、電話で問い合わせましょう。
iPhoneの構成を知っておく
iPhoneの構成は大まかに言って下記です。
バイブレーション機能が動作しなくなった場合に、疑われるのも以下の3つです。①データ部
②ソフトウェア部
③ハードウェア部被疑個所を特定する
手順1:設定の確認
iPhoneの設定でバイブレーション機能をOFFにしている可能性がありますので、確認します。ONになっている場合でも、いったんOFFにし、再度ONにしてください。これで動作するようになった場合はここで終わりです。ダメな場合は手順2に進みましょう。
参考サイト:
http://mobareco.jp/a83093/
https://appllio.com/iphone-how-to-set-vibration手順2:iPhoneを工場出荷時の状態に戻す
まずはiPhoneを工場出荷時の状態に戻します。
※このときPCでしかバックアップを取得していない場合はiCloudにもバックアップを取得してください。
参考サイト:
https://support.apple.com/ja-jp/HT201252データを復元せずに、新しいiPhoneとして使い始めてください。
この状態でバイブレーション機能が動作しない場合は②ソフトウェア部、③ハードウェア部に異常がある可能性があります。Appleサポートに連絡をして現状を伝え、ソフトウェアのチェックを受けてください。IMEIを聞かれますので、事前にメモしておくと良いでしょう。
※iPhoneの「設定」>「一般」>「情報」から確認できます。②と③の場合はAppleサポートの指示に従ってください。私はハードウェア部の故障だったので、交換対応をお願いしました。
バイブレーション機能が動作する場合は手順3に進みましょう。
手順3:データを復元する
バックアップからデータを復元して、もとのiPhoneの状態に戻します。ここでバイブレーション機能が動作しなくなる場合は、①データ部に異常があることになります。
再度iPhoneを工場出荷状態に戻して、バックアップデータの何が問題なのかを確認します。
流れとしては以下になります。
a:iCloudにバックアップをとる。
b:データを復元する際に、「写真」データを復元しない等、一部のデータを除いて復元する。
c:バイブレーション機能が動作するか確認する
d:動作しない場合は再度工場出荷状態に戻し、a~cを繰り返す。
e:動作する場合は除いたデータが異常データなので、そのデータはバックアップから復元しない。
例:写真データであれば、Googleドライブ等のクラウドストレージにアップロードしておき、そこからダウンロードする等、バックアップ以外を経由して復元する。終わりに
この問題はわりと多い?(Appleサポートに電話で質問した際には、写真データの欠落が原因でバイブレーション機能が動作しなくなる例が多いと聞きました)ようです。本記事を参照し、対応に困る人が少しでも減れば幸いです。
- 投稿日:2021-01-29T17:45:21+09:00
アプリの一部をWebViewにするメリット・デメリット
前提条件
- iOSとAndroidの両方で同じアプリを開発する
- Webでも同じようなアプリを提供する
メリット
- iOSとAndroidで共通のWebページが使える
→アプリ側の工数と、アプリ側とWeb側を合計した全体の工数が減る- Webページのコーディングを並行して別の人に振れる
→スケジュールを短縮できる- アプリよりWebエンジニアのほうが人を集めやすい
デメリット
- Webページを実装する必要がある
アプリ独自のWebページを作るのでなく、レスポンシブデザインにできるといい- WebViewが多いとリジェクトされる(iOS)
→プッシュ通知を付けるだけで通ることもあるよう- スマホ固有の機能(カメラやNFC読み取りなど)はWebViewにしにくい
- 機能にもよるが、ネイティブのほうが使い勝手がいい
- 通信する分だけネイティブよりパフォーマンスが落ちる
- オフラインで使えない
- 認証周りを工夫する必要がある
→ログインが必要なアプリの場合、Webページを開いたときにログイン状態になっていないといけないまとめ
WebView化は工数削減の手段になり、すでにWebアプリがあると特に有効です。
参考リンク
- 投稿日:2021-01-29T16:15:39+09:00
iOSをレビューする前に、プロジェクトのロード方法に注意してください
今回の主な話題:iOS監査の悲劇(拒否)を回避する方法、メモリリークの調査、ilcpp.soライブラリファイルのサイズ、il2cppコードサイズを縮小する、Strip byte Code。
アセット管理
Q1: 私たちのプロジェクトはAssetBundleパッケージング方式を使用し、WWWでローカルロードを行い、プログレスバーもあり、ロードされているデータと全体的なデータを指摘するヒント枠もあります。iOSレビューに送信した後、拒否されました。ヒントは4.2.3でした。
Guideline 4.2.3 – Design – Minimum Functionality
Your app did not include sufficient content in the binary for the app to function at launch, and we were required to download or unpack additional resources before we could use it.
We were required to download or unpack additional resources to continue using your app; however, the size of the download was not disclosed, and we were not prompted to choose to download additional resources. If your app requires additional resources, you must disclose the size of the download and prompt users before doing so.
内部で何もダウンロードされていないことが確認されていますが、ローカルリソースのみがロードしていて、ヒントも与えました。なぜ拒否されたのか分かりません。
いくつかの考えすべき事項があります。
1)圧縮リソースを使用しないでください。パッケージ内のリソースには非圧縮またはLZ4圧縮形式を使用してください。パッケージ本体のサイズが敏感な場合はLZ4を使用してください。LZ4を使用する場合、アセットをロードするときに解凍必要があることに注意ください。これはAssetのローディングに時間がもっとかかることを引き起こします。フレームレートが敏感なときにアセットをロードする場合は、LoadAssetAsyncの使用を考慮する必要があります。
2)iOSでは、WWWの代わりにLoadFromFileを使用してください。WWWはより多くのメモリを必要とします。特にwww.bytesを使用する場合、下図のように、それが役に立たなくても、LoadFromFileの2倍になります。(WWWを使用するとダウンロードする必要があることが検出された場合があるとある報告されました、まだ確認していません。)
3)当社はもともと7zを使用してリソースを圧縮していましたが、却下された後、圧縮なしに変更され、スムーズにレビューに合格しました。
メモリ
Q2:妙な問題に遭いました。ダンジョンを繰り返し出入りすると、合計メモリは増加し続けます(1回あたり約10MB)。この時点では、各リソースメモリ、Monoメモリ、およびLuaメモリは基本的に変更されていません。つまり、Unityによってカウントされたあるメモリはありますが、詳細情報には含まれていません。 この部分は何でしょうか?
問題主が提供するスクリーンショットから判断すると、AssetBundleの常駐が原因である可能性があります。毎回シーンを出入りすると、アセットのローディングはAssetBundleで行いますか?AssetBundleの数は変更されましたか?そして、3回目、4回目以降、メモリも毎回10MBずつ増加しますか?AssetBundleを使用してロードする場合、実際にはAssetBundleでロードされた一部のメモリは、Detailedモデルで観察されません。この方面から調べることをお勧めします。
IL2CPP
Q3: IL2CPPの体積を減らすことについて質問します。公式ドキュメントによって、https://docs.unity3d.com/Manual/IL2CPP-BytecodeStripping.html
私の理解は次のとおりです。
しかし、実際にテストしたところ、正しくではないようです。
2018.2.7f1、左側の図にStrip Engine Codeは開きましたが、右側のほうに開きませんでした。
いくつかの問題があります。1)Strip Engine Codeを開いた後、いくつかのxx_Module cppファイルが欠落していることがわかりますが、これらのcppはC#とDllから転換してくるすべきですが、Byte Code StripのステップでStripする必要があります。Strip Engine Codeパラメーターの影響を受けないはずです。(公式ドキュメントがこのパラメーターはEngine Native CodeのStripのみに影響ありますと言っています。)どうする?
2)LinkステップのStripは私自身の推測です。XCodeに余り精通していませんから、Linkする時に不要なSymoblsを削除するためにどちらのCompileとLinkパラメーターは使えますが良くわかりません。この部分について何か正確な結論ありませんか?
3)Package Managerでは、不要なEngine Builtin Moudeleを明確にDisableします。例えば、AIやXRがIL2CPP の時に引き続きcppソースコードを生成します。常識に反しているように感じます。この部分は最終的にあるステップで削除されますか?
4)Strip Engine Codeは開いていますか?この前に、裁断が引き起こす問題を回避するために(特にホットアップデート)、開けないことにしましたが、現在、体積を減らすために開きたくなりました。何か注意すべき点がありませんかを聞きたいです。
ソースコードを調べました、https://github.com/Unity-Technologies/UnityCsReference
CodeStrippingUtils.csおよびAssemblyStripper.csにStrip Engine Codeに関するコンテンツがあります。このオプションをオンにすると、ModulesにStripを行います。(この一歩は完全には理解していません。おそらく、先にRoot Assemblyが使用するClassをフィルタして、これらのClassが使用するModuleを記録します。また、これらのModule自身のプロパティにも関連しています。)ここで、具体的に保存されたModuleとClassはファイルUnityClassRegistration.cppに記録されます。
私の理解は以下とおります。
Strip Engine Codeが影響するのは「Byte Code StripステップでUnity Engine CodeにStrip処理をするか」どうかということです。PackageManager内の設置をMonoの形式でパッケージしてみます。Strip Byte CodeでもStrip Assembliesでも、依然としてDisableのbuilt-in Moduleに対応するdllファイルを生成します。ここのDisableはパッケージ化のStrip操作と関係ありませんと推測します。
上記はただUnity buildに対して私個人的な理解であります。最も重要な問題は、Strip Engine Codeを開きますかどうかということです。私の前回のプロジェクトでは開きました。プロジェクトにリフレクションを使用していなかったので、目前には問題ありません。以後、問題があれば直したら大丈夫です。Unityの公式ドキュメントから、「開くとコードの量を減らすことができますが、問題がある場合閉めてください。」問題主に自分のプロジェクトによって決めることをお勧めします。
アセット管理
Q4: 最近、Android端末プロジェクトのパッケージかをIL2CPPの形式に変更したいですが、エクスポートされたapkのil2cpp.soファイルが40MBと大きく、libmono.soが作成した3MBのサイズよりもはるかに大きいことがわかりました。ですからapkもはるかに大きくなりました。何か解決策がありませんか?
Libli2cpp.soにはロジックコードが含まれています。libil2cpp.soとlibmono.soのサイズを直接比較することは無意味です。一般的に、IL2CPP方式のパッケージ化がパッケージを大きくする原因はC#コードがコンパイルされてC++コードに変更すると、コードの量が急激に増えるためです。この点について、Unityの新しいバージョンも常に最適化しています。
問題主が使用しているUnityのバージョンはなんでしょうか?Strip Engine Codeオプションが有効になっていますか?
Unityのバージョンが新しいほど、この点についての最適化効果が高いと言われています。 公式もこれに言及しました:https://docs.unity3d.com/Manual/dotnetProfileLimitations.html
アセット管理
Q5: 私のプロジェクトは、プレハブを非同期でロードするときにピークがあります。詳しく確認すると、この関数が原因であることがわかりました。これはなぜですか?どうすれば避けられますか?
これは、アセットを非同期ロードする時の時間コストです。アセットのローディングには常に時間コストがあります、必ずしも回避する必要はありません。アセットの非同期ローディングに頻繁ローディング問題があるかどうかを確認することを問題主にお勧めします。存在する場合には、できる限りに改善してください。
UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、パフォーマンス分析と最適化ソリューション及びコンサルティングサービスを提供している会社でございます。
UWA公式サイト:https://jp.uwa4d.com
UWA公式ブログ:https://blog.jp.uwa4d.com
- 投稿日:2021-01-29T16:10:30+09:00
【Swift】 SpriteKit 入門 ③
SpriteKitの使い方を解説します。
SpriteKit 入門 ② で画像をドラッグして動かせるようになりました。今回は自機(画像)から弾を発射するようにしていきましょう。
(環境:Xcode12.1 Swift5.3)弾を発射する
今回使用するのは
SKShapeNode
SKAction
TimeInterval
の3つです。
弾などの図形を描画する際には
SKShapeNode
を使用します。SKAction
で図形にアニメーションを追加することができます。一定時間毎に弾を発射させたいので、TimeInterval
も使用しています。それではMyScene.swiftファイルにコードを追加していきましょう。MyScene.swiftvar image: SKSpriteNode! var beforeDragPosition: CGPoint? var shoot: SKShapeNode? var lastUpdateTime : TimeInterval = 0 //省略 func repeatShoots(){ self.shoot = SKShapeNode.init(rectOf: CGSize.init(width: 30, height: 30), cornerRadius: 3) if let shoot = self.shoot { shoot.position = image.position shoot.fillColor = SKColor.red shoot.lineWidth = 3 shoot.strokeColor = SKColor.white shoot.run(SKAction.repeatForever(SKAction.rotate(byAngle: CGFloat(Double.pi), duration: 1))) shoot.run(SKAction.sequence([SKAction.move(by: CGVector(dx: 0, dy: self.size.height), duration: 3),SKAction.removeFromParent()])) self.addChild(shoot) } } override func update(_ currentTime: TimeInterval) { if (self.lastUpdateTime == 0) { self.lastUpdateTime = currentTime } if (lastUpdateTime + 0.3 <= currentTime) { self.repeatShoots() lastUpdateTime = currentTime } }
repeatShoots()
では時期から弾を発射させる処理を記述しています。図形の形をSKShapeNode.init()
で設定します。ここでは四角にしていますが、円を描画する場合は次のように記述します。self.shoot = SKShapeNode.init(circleOfRadius: 15)以下の部分では弾のアニメーションを設定しています。アニメーションの設定には
SKAction
を使用します。複数のアニメーションは配列として追加することができます。shoot.run(SKAction.repeatForever(SKAction.rotate(byAngle: CGFloat(Double.pi), duration: 1))) shoot.run(SKAction.sequence([SKAction.move(by: CGVector(dx: 0, dy: self.size.height), duration: 3),SKAction.removeFromParent()]))また
update(_ currentTime: TimeInterval)
を override して一定時間毎にrepeatShoots()
を呼び出すようにしています。override func update(_ currentTime: TimeInterval) { if (self.lastUpdateTime == 0) { self.lastUpdateTime = currentTime } if (lastUpdateTime + 0.3 <= currentTime) { //0.3秒毎にrepeatShoots()を呼び出す self.repeatShoots() lastUpdateTime = currentTime } }次回は敵を表示させて衝突判定を実装していきましょう。
参考
この記事は以下の情報を参考にして執筆しました。
- 投稿日:2021-01-29T14:56:23+09:00
LiDARスキャナを使って現実世界をボクセルにしてみた
iPhone 12 Pro/Pro MAXや、iPad Proに搭載されているLiDARを使って、現実世界をボクセルにしてみたので、その方法を説明します。
Appleの公式サンプル「Visualizing a Point Cloud Using Scene Depth」を改変して作ります。
仕上がり
仕上がりは印象派の絵のようになりました。
もう少し鮮明な動画はこちら。
https://twitter.com/jugemjugemjugem/status/1353245962127331329?s=20Appleのサンプルの説明
最初にAppleのサンプルの仕組みについて説明します。
このサンプルは、LiDARのDepth情報をもとに画面にPoint Cloud(点群)を表示する、というものです。
実行イメージは次のとおりです。
では、DepthデータをPoint Cloudに変換する仕組みをステップごとに説明します。
1. スクリーン上に敷き詰めた点を作る
最初にスクリーンに敷き詰めた点を作っています。
コード上では、Renderer.swiftのmakeGridPoints
メソッドがこれをしています。この時点の処理結果を画面に表示すると、こんな感じに見えます。
【スクリーンに敷き詰めた点】
(見やすいように赤色をつけています)2. 敷き詰めた点の位置から色を得る
敷き詰めた点の位置にある色をカメラの画像から取得します。
この処理は、シェーダーの
unprojectVertex
関数がしています。サンプラーを使って色を取得しますが、ARKitから取得した画像(ARFrameのcapturedImage)はYCbCr形式なので正確な色を取得するためにテクスチャーを2つ使う必要があります。詳しくはAppple公式/ARFrame/capturedImageをご覧下さい。
Shaders.metal// Sample Y and CbCr textures to get the YCbCr color at the given texture coordinate const auto ycbcr = float4(capturedImageTextureY.sample(colorSampler, texCoord).r, capturedImageTextureCbCr.sample(colorSampler, texCoord.xy).rg, 1); const auto sampledColor = (yCbCrToRGB * ycbcr).rgb;ちょっと分かりづらいですが、私が写した方向に見える色を反映しています。
3. 点の3次元空間上の位置を取得する
点の位置をDepth情報を使って3Dにします。
この処理も
unprojectVertex
関数がしています。
サンプラーでDepthの情報を取得して、worldPoint
関数でワールド座標に変換しています。Shaders.metal// Sample the depth map to get the depth value const auto depth = depthTexture.sample(colorSampler, texCoord).r; // With a 2D point plus depth, we can now get its 3D position const auto position = worldPoint(gridPoint, depth, uniforms.cameraIntrinsicsInversed, uniforms.localToWorld);
worldPoint
関数は、2Dの点をローカル座標 > ワールド座標という順で変換する関数です。カメラ内部パラメータの逆行列を使って、スクリーンの座標系をカメラ座標系のベクトルに変換します。これにdepthをかけることで実際のカメラ座標を得ています。
カメラ座標をワールド座標系への変換はビュー変換の逆行列を使います。通常の3Dレンダリングではビュー変換によってワールド座標からカメラ座標にしますが、今回の処理ではもともとがカメラ座標なので、通常とは逆の手順を踏んでいます。
Shaders.metal/// Retrieves the world position of a specified camera point with depth static simd_float4 worldPoint(simd_float2 cameraPoint, float depth, matrix_float3x3 cameraIntrinsicsInversed, matrix_float4x4 localToWorld) { // ローカル座標への変換 const auto localPoint = cameraIntrinsicsInversed * simd_float3(cameraPoint, 1) * depth; // ワールド座標への変換 const auto worldPoint = localToWorld * simd_float4(localPoint, 1); // ?(後述) return worldPoint / worldPoint.w; }最後に、worldPointを/worldPoint.wで割っていますが、この時点でwの値は1になっており、なぜこの処理をしているのか謎です。
なお、カメラ座標系やスクリーン座標系といった言葉についてはこちらの記事が詳しいです。
画像の座標を空間の座標に変換する
画像座標系からカメラ座標系への変換この処理の結果を横から見ると、このように点に奥行きの情報が加味されていることがわかります。
4. 画面上にレンダリングする
最後にレンダリング、つまり実際の画面を作ります。
レンダリングは、
particleVertex
関数(頂点シェーダー)とparticleFragment
関数(フラグメントシェーダー)で行います。
particleVertex
関数は、ワールド座標系にある点をカメラ座標系に変換し、プロジェクション変換しています。Shaders.metalfloat4 projectedPosition = uniforms.viewProjectionMatrix * float4(position, 1.0);この処理は
unprojectVertex
関数でついでにやれそうですが、particleVertex
関数に分けている理由は、それぞれが次のような役割分担で、更新のタイミングが異なるためです。
unprojectVertex
関数は、カメラに見えている画像を、点群の3Dモデルに変換する
=>カメラが動いた時に、必要な分だけ変換するparticleVertex
関数は、カメラの現在の向きに合わせて、3Dモデルを表示する
=>毎フレームごとにすべての点を表示するなお、点群の3Dモデルは一定の大きさの配列内に収められており、一度に変換するのではなく、ローテーションしながら少しずつ更新していきます。こうすることで、ユーザーが新しい場所に移動すると古い点群を捨てつつ新しい点群を作成しています。
ここまでがAppleのサンプルの説明です。
これ以外にも、カメラ画像をそのまま表示する処理もありますが、この説明では割愛します。サンプルを変更して、ボックスを表示するようにする
サンプルを変更してボックスを変更するようにします。
点を描画するところをボックスに変更するだけです。1. ボックスを作る
ボックスを作るメソッドを用意し、インスタンス変数として保持しておきます。
Renderer.swiftprivate lazy var baseMesh = createMesh() func createMesh() -> MTKMesh { let allocator = MTKMeshBufferAllocator(device: device) let mdlMesh = MDLMesh.newBox(withDimensions: vector_float3(repeating: 1), segments: vector_uint3(repeating: 2), geometryType: .triangles, inwardNormals: false, allocator: allocator) return try! MTKMesh(mesh: mdlMesh, device: device) }2. シェーダーにボックス情報を渡してレンダリングさせる
点群をレンダリングさせるシェーダーに、点群の情報に加えてボックスの情報も渡します。
Appleのサンプルでは、点の数(currentPointCount)をシェーダーに頂点数として渡していましたが、今回は点の数=ボックスの数になるのでインスタンス数として点の数を渡します。
Renderer.swiftrenderEncoder.setDepthStencilState(depthStencilState) renderEncoder.setRenderPipelineState(particlePipelineState) renderEncoder.setVertexBuffer(pointCloudUniformsBuffers[currentBufferIndex]) renderEncoder.setVertexBuffer(particlesBuffer) renderEncoder.setVertexBuffer(baseMesh.vertexBuffers[0].buffer, offset: 0, index: Int(kMesh.rawValue)) renderEncoder.drawIndexedPrimitives(type: baseMesh.submeshes[0].primitiveType, indexCount: baseMesh.submeshes[0].indexCount, indexType: baseMesh.submeshes[0].indexType, indexBuffer: baseMesh.submeshes[0].indexBuffer.buffer, indexBufferOffset: baseMesh.submeshes[0].indexBuffer.offset, instanceCount: currentPointCount )3. シェーダーを変更し、ボックスを描画するようにする
Appleのサンプルではシェーダーは頂点を受け取ってそれをそのままレンダリングしていました。
これを、点とボックスの頂点情報を受け取って、点の位置にボックスを表示するようにします。といっても簡単で、点の配列にアクセスする方法をそれまでは頂点番号(vertex_id)だったものを、インスタンス番号(instance_id)にするだけです。
また、ボックスの頂点は原点の位置にあるため、ボックスの頂点座標に点の座標を足せば、点の位置にボックスを描画することができます。
Shaders.metalvertex MeshVertexOut particleVertex(uint vertexID [[vertex_id]], uint iid [[ instance_id ]], constant PointCloudUniforms &uniforms [[buffer(kPointCloudUniforms)]], constant ParticleUniforms *particleUniforms [[buffer(kParticleUniforms)]], VertexInput mesh [[ stage_in ]]) { const auto particleData = particleUniforms[iid]; auto position = particleData.position + mesh.position/50;50で割っているのは大きさの調整です。この数を小さくするとボックスは大きくなります。
最後に
NoteではiOS開発、AR、機械学習などについて定期的に発信しています。
https://note.com/tokyoyoshidaTwitterでも発信しています。
https://twitter.com/jugemjugemjugem
- 投稿日:2021-01-29T00:59:19+09:00
Xcodeショートカット
Mac キー
Command (または cmd) ⌘
Shift ⇧
option (または alt) ⌥
Control (または ctrl) ⌃
caps lock ⇪
fnウィンドウ・エリア
ショートカットキー キー 効果 Command+Option+Control+Enter ⌘
⌥
⌃
↵
Assistant Editorへ切り替え Command+Option+Shift+Enter ⌘
⌥
⇧
↵
Version Editorへ 切り替え
(バージョン管理との差分が確認可能)Command+0 ⌘
0
ナビゲートエリア表示切替(左側のウィンドウ) Command+Option+0 ⌘
⌥
0
ユーティリティーエリア表示切替(右側のウィンドウ) Command+Shift+Y ⌘
⇧
Y
デバッグエリア表示切替 Command+Option+T ⌘
⌥
T
ツールバー表示切替 Command+T ⌘
T
タブの追加 Command+W ⌘
W
タブを閉じる Command+Shift+Option+T ⌘
⇧
⌥
T
タブをリネーム Command+Shift+T ⌘
⇧
T
現在のプロジェクトを新しいウインドウで開きます Command+1 ⌘
1
Project Navigator 表示切替 Command+2 ⌘
2
Source Control Navigator 表示切替 Command+3 ⌘
3
Symbol Navigator 表示切替
(クラス一覧をツリー表示)Command+4 ⌘
4
Find Navigator 表示切替
(Command+Shift+F の方が便利)Command+5 ⌘
5
Issue Navigator 表示切替
(警告やビルドエラーなどが表示される)Command+6 ⌘
6
Test Navigator 表示切替
(テスト結果が表示される)Command+7 ⌘
7
Debug Navigator 表示切替
(CPU,Memory,消費電力,Disk,通信量、呼び出し階層など)Command+8 ⌘
8
BreakPoint Navigator 表示切替
(ブレークポイントを一覧表示
例外発生時に該当箇所で止める設定なども可能)Command+9 ⌘
9
Report Navigator 表示切替
(ビルドの履歴などが参照できる)option+フォルダ三角形+クリック ⌥
>
Click
全てのグッルプを開く Command+Option+1~9 ⌘
⌥
1 ~ 9
ユーティリティエリア(上部)の左からn番目を表示 検索
ショートカットキー キー 効果 Command+E ⌘
E
検索用の選択でハイライトする Command+F ⌘
F
検索 Command+G ⌘
G
検索において該当した次の検索結果に移動する Command+Shift+G ⌘
⇧
G
検索において該当した一つ前の検索結果に移動する Command+Shift+F ⌘
⇧
F
ワークスペース内のすべてのファイルからgrep 検索 Command+Option+J ⌘
⌥
J
フィルター(ナビゲータエリア(左側のウィンドウ)
下部のテキストフィールドへフォーカスが移る変更があったファイルのみ表示なども可能)Command+Shift+O ⌘
⇧
O
プロジェクト内検索
(メソッド、クラス、ファイル名など該当箇所へ移動することが可能)ファイル操作
ショートカットキー キー 効果 Command+Shift+N ⌘
⇧
N
新しいプロジェクト Command+Shift+O ⌘
⇧
O
指定ファイルを開く Command+Control+N ⌘
⌃
N
新しいワークスペース Control+5 ⌃
5
選択中フォルダ内のファイル一覧を表示 Control+6 ⌃
6
メソッド・MARK単位で移動可能部分一致検索が可能 Command+Option+, ⌘
⌥
,
Assistant Editorで開く Option+クリック ⌥
Click
2画面表示(Assistant Editor)になり、選択したファイルを右側に表示する コード編集
ショートカットキー キー 効果 Command+Z ⌘
Z
戻す Command+Shift+Z ⌘
⇧
Z
進む Command+. ⌘
.
コードの補完 Command+/ ⌘
/
選択行のコメントの切り替え
選択していない場合はカーソル行のみCommand+L ⌘
L
指定行に移動 Command+Control+E ⌘
⌃
E
選択中のテキストと同じ部分をまとめて変更する Comannd+Option+/ ⌘
⌥
/
ドキュメントコメント追加 Command+Option+左矢印 ⌘
⌥
←
ソースを折りたたみする Command+Option+右矢印 ⌘
⌥
→
カーソルを折りたたみから展開する Control + D ⌃
D
右の1文字削除 Control + K ⌃
K
カーソル位置から行端までを切り取り Command+Shift+左矢印 ⌘
⇧
←
現在のカーソルの位置からその行の最初までを選択 Command+Shift+右矢印 ⌘
⇧
→
現在のカーソルの位置からその行の最後までを選択 Control+I ⌃
I
選択範囲のコードフォーマット
選択していない場合はカーソル行のみCommand+[ ⌘
[
カーソル行または選択されている行インデントを下げる Command+] ⌘
]
カーソル行または選択されている行インデントを上げる Command+Option+[ ⌘
⌥
[
選択行(選択していない場合1行)を上に移動 Command+Option+] ⌘
⌥
]
選択行(選択していない場合1行)を下に移動 Command+上矢印 ⌘
↑
カーソルをファイルの先頭へ移動 Command+下矢印 ⌘
↓
カーソルをファイルの最後へ移動 Command+左矢印 ⌘
←
カーソルを行頭へ移動 Command+右矢印 ⌘
→
カーソルを行末へ移動 Control + A ⌃
A
行の最初に移動 Control + E ⌃
E
行の最後に移動 Control + B ⌃
B
前方に移動 Control + F ⌃
F
後方に移動 Option+左矢印 ⌥
←
カーソルを左の1単語分移動 Option+右矢印 ⌥
→
カーソルを右の1単語分移動 Command+Control+左矢印 ⌘
⌃
←
ジャンプ前のコードに戻る Command+Control+右矢印 ⌘
⌃
→
進む(上記の逆) Command+Shift+J ⌘
⇧
J
Project Navigatorへフォーカスの移動 Command+Shift+L ⌘
⇧
L
ライブラリのポップアップを開く Option+クリック ⌥
クリック
ドキュメントポップアップ表示 Command+Control+Shift+? ⌘
⌃
⇧
?
ドキュメントポップアップ表示 Command+クリック
「Jump to Definition」⌘
Click
定義元に移動
※これが面倒な方は、
上部メニュー Xcode > Preferences > Navigation >
Command-clock on Coder を Jump to Definition
に変更すれば、Cmd + Click するだけで、定義元に移動できます。Command+Control+クリック ⌘
⌃
クリック
定義元に移動 Command+Control+J ⌘
⌃
J
定義元に移動 Command+Control+Option+J ⌘
⌃
⌥
J
Assistant Editorで定義元に移動 Command+Control+Space ⌘
⌃
Space
絵文字挿入 Control+Shift+クリック ⌃
⇧
Click
複数列を編集できる Control+Shift+上矢印 ⌃
⇧
↑
複数列を編集できる Control+Shift+下矢印 ⌃
⇧
↓
複数列を編集できる option + ドラッグ ⌥
ドラッグ
マウスカーソルが+の状態で矩形選択、複数列を編集できる Control+¥ ⌃
¥
次の差分に移動 Comannd+Option+C ⌘
⌥
C
コミット Comannd+Option+X ⌘
⌥
X
プル 実行・ビルド・デバッグ
ショートカットキー キー 効果 Command+B ⌘
B
ビルド Command+R ⌘
R
実行 Command+. ⌘
.
停止 Command+\ ⌘
\
ブレイクポイント設定/解除 Command+Option+¥ ⌘
⌥
¥
ブレイクポイント設定/解除 Command+Shift+C ⌘
⇧
C
コンソールview表示 Command+K ⌘
K
コンソールを全部強制終了