ikoan unity 開発メモ

Unityに関するアレコレを日々つづっていこうと思います

apkのサイズが大きくなる

環境

Unity2018.2.10f1
target SDK 28

状況

開発中のアプリをandroid buildしたら、まだ開発初期(テクスチャ1枚とかレベル)なのに、apk のサイズが60MBになった。
これはあまりよろしくないなぁと思い、少し調査してみました。

影響がありそうなbuild設定

下記に羅列してみます。

  • development build
  • target architecture
  • Unity の library ファイル

上記を1つずつ検証してみます。
まずは
development build ON
target architecture ARMv7,ARM64,x86
でbuildしてみます。
target architectureにARM64が入っているので、もちろんIL2CPP bulidになっています。
ビルド結果は「60.4MB」です

他、いくつかのパターンを試した結果は下記のようになりました。

devBuild ARMv7 ARM64 x86 ScriptingBackend apk Size(MB)
ON IL2CPP 60.4
OFF IL2CPP 41.4
ON × IL2CPP 40.6
ON × IL2CPP 42.5
ON × Mono 40.6

developmentBuild外しただけでも40Mとなると、LTE下でダウンロードしてもらうためには、まだまだ他のやり方も模索する必要がありそうです。

ARMv7、ARM64、x86がそれぞれ同じサイズぐらいなので、思い切ってx86を切ってしまうとか、当分ARM64は放置しておくとかでしょうか。

アーキテクチャごとにapk分割

UnityのPlayer Settingsをみると、アーキテクチャチェックボックスの下に「Split APKs by target architecture」というチェックボックスがあります。
これを試しにONにして実行して見たところ、どうもアーキテクチャごとにapkを勝手に分けてくれるみたいです。ググってみると、Android Studioにもありそう・・・?
ただ、現在Unityでは、まだ experimental になっているので、実験段階ということですね。早く実用化して欲しいです。
アーキテクチャごとにapk分割した後、どうやってストアで配信するかはまた別の記事で。

Unityのライブラリについて

最近TimelineやSpriteAtlasを試してみるために、いくつかライブラリを追加してしまったので、それを外すとどうなるかを試してます。
なお、using namespaceを外すだけでなく、関連するリソースも外さないと、ライブラリは未使用判定にならないと思います。

  • UnityEngine.Playablesを外す、playable assetを削除(Timeline関連)

60.4MB → 59.5MB

  • UnityEngine.U2Dを外す、Sprite Atlasの参照を削除(SpriteAtlas関連)

60.4MB → 59.6MB

あまり変化がないですね・・・
SpriteAtlasのサイズは結構大きいと思っていたのですが、それを含めてもこれだけしか減らないんですね。
Unityのライブラリはもっとごそっと減らす方法を考えないといけないんでしょうか。
それこそUI全体外してみるとか・・・?
uGUIやSprite外して、UI系のアセット使うとか、全部3Dオブジェクトで気合いでなんとかする、とかしたらどれぐらいのサイズになるんでしょうね。

まとめ

apkのサイズが大きくなったことに関して、対応アーキテクチャが増えたのと、Unityでできることが増えた反面、必要なライブラリのサイズが大きくなってしまったことが原因なのでしょう。
せっかくある機能ですが、使用するかどうかはアプリのサイズ、代替機能などの検証が必要になりそうです。
とはいえ、現在 experimental である、apk 分割がうまくいくのであれば、それはそれでいいのではないかなと思っています。
テストをどうするかが悩ましいところですが。。。

補足

Unite2018でGoogleの松田さん(@hak)が確か、いつからか64bit対応が必須になる、とおっしゃっていた気がするんですが、別のサイトを見てみると、ネイティブコードを使用していないなら対応の必要がないとも書いてあったような・・・?
そうなると無理にIL2CPPビルドにしなくても大丈夫なんでしょうかね?

Unity Collaborateを使ってみた - 共有まで

環境

Unity2018.2.10.f1

前提

複数人でプロジェクトを管理する際に、GitHubやBitBucketや自前Gitなど考えはしましたが、今後CloudBuildを使うかもしれないという状況下でCloudBuild調べていたら、普通にUnity Teamでプロジェクト共有すればよくない?という話になったので、とりあえず無料版を試してみることにしてみました。

Unity Teamとは

プロジェクトを共有したり、Cloud Buildを使用するための機能。
BasicライセンスとAdvancedライセンスがあり、Advancedライセンスは月9$で利用可能(2018/9月現在)。また、AdvancedライセンスはUnity Proライセンスでは標準で使用可能らしいです。
主な機能の差はコチラ。
Get Teams - Unity

簡単にまとめると

  • プロジェクトのサイズ

Basicは1GB、Advancedは25GBまで。これはバージョン管理されたサイズは含まれないらしいです。
これは25GB単位で追加可能。

  • チーム人数

どちらも基本は3人まで。Advancedは有料で人数を追加可能みたいです。

  • CloudBuild

Advancedライセンスでしか利用できません。

  • バージョン履歴

Advancedは無期限ですが、Basicは最新版からの過去90日間までしか保存されないみたいです。

利用方法

Unity Collaborate - Unity マニュアル
基本的には上記サイトに則った感じです。
Unityのマニュアルは機能が網羅されていてすごいですね。

まず、必要がないかもしれませんが、とりあえず私はUnityのwebページ上で組織管理画面のメンバー&グループに、プロジェクト共有者を追加しました。
次にUnity Editor上のServicesからCollaborateをONにしました。
次にまたWebページ上でプロジェクトごとの管理画面にうつり、Settings > UsersのMANAGE SEATSで共有したいユーザーを追加しました。
ここら辺、メモを取らずになんとなくやりながらだったので、正直うろ覚えです・・。すいません。

さて、CollaborateをONにしたことで、たとえプロジェクトをある程度進めていたとしても、まずプロジェクト作成時のバージョンのみがコミットされていることになっています。
ですので、エディタ上のcollabボタンを押してコミットしましょう。
Collabボタンを押すと、履歴メモを書くところとPublish Nowというボタンが出てくるので、必要ならメモを記載して、ボタンを押してコミットします。簡単ですね!

プロジェクトを共有される側の方はUnityを起動した際に表示されるプロジェクトを選択する画面の左に、On DiskとIn the Cloudとありますので、In the Cloudから該当するプロジェクトを選択しましょう。
あとは勝手にダウンロードしてきてくれます。

他の機能

まだ使い始めたばかりなので、何ができて、何ができないのか正直わからないことが多いです。
とりあえずファイル単位でコミットすることは可能みたいです。
おいおい、機能が分かり次第、追記していこうと思います。

Timelineで簡単なアニメーションを作ってみる

環境

Unity2018.2.8f1

やること

クリア演出をせっかくなのでTimelineで作ってみようかと思います。
とても簡単なものを作るつもりなので、正直Animatorでも作れそうですが、勉強ということで。

実装

実装するものは至極簡単で、クリアという文字が出て、適当なパーティクルでも飛ばしてみましょう。
適当なサウンドも鳴らしてみましょう。
サウンドはこちらからお借りしました。
ジングル・ショートループ素材|著作権フリーの無料音楽素材ダウンロードサイト「ミュージックノート」

まずTimelineオブジェクトを作成します。
Project view の Create から、Timelineを選択し、適当に名前をつけましょう。
作成したTimelineオブジェクトをHierarchyにドラッグ&ドロップします。
そうするとPlayable DirectorというComponentがアタッチされたオブジェクトが生成されます。

次にWindow > Sequence > Timeline を選択すると、Timelineの編集ウインドウが表示されます。
Addを選択すると、Timelineに追加するtrackの種類が表示されます。
今回はアニメーションとサウンド、パーティクルなので

  • Animation Track
  • Audio Track
  • Control Track

のトラックを追加します。

テキスト

クリアという文字をアニメーションさせるオブジェクトを作成しましょう。
uGUIのTextオブジェクトを作成し、文字をCLEAR、サイズや色などは適当に設定します。
Animation Trackに設定したいので、Animator をアタッチし、適当なアニメーションをつけましょう。
下から上に透過しながら出てくるぐらいで大丈夫でしょう。
このTextオブジェクトをTImelineウィンドウのAnimation Trackのところへドラッグ&ドロップします。
これだけだとまだトラックが追加されていません。
Textオブジェクトをセットした時に赤い丸のUIが表示されました。本来(?)ならこっちを使ってアニメーションを登録するのかもしれません。
今回はあらかじめアニメーションを準備していたので、それを追加します。追加方法は赤丸の隣にあるメニューを開き、「Add From Animation Clip」を選択すると、選択できるAnimation Clipが出てきますので、その中から使用したいものを選択します。

サウンド

サウンドを鳴らすために、まずAudioSourceを用意します。
適当にGameObjectを作成し、AudioSourceのコンポーネントをアタッチします。
そのGameObjectをAudio Trackにドラッグ&ドロップします。
先ほどと同様にメニューを開くと、「Add From Audio Clip」がありますので押すと、選択できるAudio Clip一覧が出てきますので、その中から使用したいものを選択します。
余談ですが、個人的にはAudioSourceはbgm,jg,se2個などの4つを準備して、それをアプリ中に使い回していたのですが、Timeline再生中に他の場所から同一AudioSoureに別のAudioClipを追加するとどうなるんでしょうか。Unity側でそのことを想定していない限り、再生中の音が止まってしまう気がします。専用のAudioSourceを用意した方が無難なのかもしれません。

パーティクル

パーティクルを制御するには、Control Trackを追加します。
追加したトラック上で右クリックをして、「Add From GameObject」を選択するか、対象とするParticle SystemのGameObjectをドラッグ&ドロップして追加します。おそらく設定したLifetimeに応じてトラックの長さが自動生成されるので、適宜いい感じに調整します。

完成!

f:id:ikoans:20180928204427p:plain
f:id:ikoans:20180928204434g:plain
完成するとこんな感じになります。
以上がTimelineを用いた基本的な使い方になります。

UniteではTimeline中にプレイアブルなシーンを追加したり、カットシーンを追加したり、プログラマブルな拡張など、応用的なことを沢山していたので、それらもそのうち記事化したいと思います!

AdMobでバナー広告を表示してみる

環境

Unity2018.2.8.f1

やりたいこと

AdMobを使用して、バナー広告をアプリ画面に表示させたい。
ターゲットプラットフォームはAndroid,iOSの2つ。
ゆくゆくはメディエーションも使用したいし、動画リワードや動画インステも対応したい。

Admobに登録

Sign up for AdMob - AdMob Help
まずは上記でAdMobアカウントを作成します。
流れに沿って、必要事項を記入していきます。
AdMobのアカウントを作成したら、広告を表示するためのアプリIDとアプリごとに広告ユニットを作成します。
作成したら、AppIDなどを後で使用するので控えておきましょう。

手順

下記を参考に進めていきましょう。
developers.google.com

プラグインのインストール

まずは下記リンクからプラグインをダウンロードしておきます。
f:id:ikoans:20180926175105p:plain

UnityPackageをダウンロードし、広告を表示するプロジェクトにインポートします。
環境がmacだからかわかりませんが、cocoapodsも勝手にインストールしてくれます。
rubyがないよってWarningが出てるけど大丈夫でしょうか・・

ちなみにここまでで、先ほどのAdMobのページのInclude the Mobile Ads SDKも終わったことになります。PlayServiceResolverがうまいことやってくれています。

Android用手順

アプリのAndroidManifestファイルを用意し、下記の記述を追加します。f:id:ikoans:20180926181405p:plain

iOS用手順

XCode上で

  • BuildSettings の Enable Modules(C and Objective-C)を Yes にします
  • BiuldSettings の Other Linker Flags に$(inherited) を追加します
ソースコードの準備

適当にMonoBehaviourを継承したソースを用意します。
その中でMobileAds.Initialize( AppID ) を呼び出します。

次にバナーを表示するプログラムを準備します。
BannerViewのインスタンスを下記のように作成します。
bannerView = new BannerView(adUnitId, AdSize.Banner, AdPosition.Top);
表示位置に関してはカスタムも可能です。
次にAdRequestを生成し、bannerView.LoadAd( adRequest ); を実行すれば、バナー広告を呼び出せます。
f:id:ikoans:20180927142821p:plain

エラー時の注意

XCodeを開く時には、xcworkspaceを選択して開くようにしてください。そうしないとPods のプロジェクトが関連付けされなく、GoogleMobileAds.frameworkが追加されないため、広告が表示されません。

メディエーション設定

Nend

下記を参考。
https://developers.google.com/admob/unity/mediation/nend
iosのバージョンは8.1以降にしないとbuildが通らないので注意。

まとめ

なかなかAdMobで承認されずに広告が表示されずにヒヤヒヤしましたが、最終的には無事表示されてことなきを得ました。
次はメディエーション設定の追加とインタースティシャル広告、動画リワード広告の実装を試そうと思います。

shader練習 ブラーシェーダー

環境

Unity2018.2.8f1

目的

画面をぼかす処理を作成したい。
そのためのブラーシェーダを作成します。

実装

画面をぼかす処理で一番簡単なのは、表示解像度を下げることになります。
要はテクスチャを解像度を下げたテクスチャに書き込み、それを引き伸ばして表示すれば、ボケて表示されることになりますね。
解像度を下げる、つまり数ピクセルの情報を一つのピクセルにまとめる際に、数ピクセル内の色の平均値をとるのか、それとも1つのピクセルの色だけを使用するかによって、ボケ方が変わってくると思います。
今回はshaderで実装するということになりますので、考え方は前者の数ピクセル内の色の平均値をとる方になります。

上記の考え方をshaderで実装するには、あるピクセルの色を決定する際に、周辺のピクセルからも色をとって加えてしまえばいいわけですね。
下記は平均を取る際の一番簡単な実装方法になります。

単純なブラー処理を行うピクセルシェーダー

これは中心のピクセルと周辺の4ピクセルの色を平均して色情報を決定したものとなります。
この他にも、「周辺8ピクセルの色をとる」「平均と取る時に中心ピクセルからの距離に応じて重みをつける」「隣接ピクセルだけでなく、数ピクセル離れたピクセルの色を取る」などができると思います。、
それぞれ、ぼかしが綺麗になると思いますので、ぜひ試していただければと思います。

f:id:ikoans:20180920195837g:plain

画面全体に効果をかける

今までは、マテリアルにshaderをセットし、特定のテクスチャをぼかす事をして来ました。
次に画面全体をぼかす事を試してみます。
Unity2018ですと、CommandBufferやRenderPipelineを実装してやるやり方もあるかもしれませんが、いかんせん私自身がまだ習得していないので、そのやり方はいずれできたら。
今回はCameraにアタッチするComponentを用意し、OnRenderImageでshaderの効果を画面全体にかけるようにします。
※過去のImageEffectのやり方ですね。現在のPostProcessingStackがどうかはわかりませんが・・・


shaderの効果を画面全体にかけるために、MonoBehaviourのOnRenderImageを ...

上記スクリプトをCameraにアタッチすると、いい感じに画面全体がぼやけました。
f:id:ikoans:20180920203839g:plain

Spriteのタッチ判定

環境

Unity2018.5f

最初に

uGUIのタッチ判定はEventSystemが管理してくれます。
ただすべてのオブジェクトをuGUIで表示するのは、まぁそれはそれでありなのでしょうが、用途次第では困ったりすることもあります。あるはずです。
なので、Spriteオブジェクトをタッチすることで操作する方法を今回は勉強しようと思います。
※普通に考えれば、Spriteはあくまで「描画するもの」であり、「操作するもの」ではありませんが。
なので本来なら操作するものはすべてUIで表示されるべきではあります。
今回は描画順序を操作するにあたり、Spriteで操作した方が遥かに楽そうだったので、この手法をとりました。

世の中のスマホアプリのオブジェクトをタッチして操作するゲーム(例えばカードゲームなど)はUIで表示しているのか、スプライトとして表示しているのかきになる点ではありますね。
そもそも描画順序とかだるいんですよねぇ。カードリストとかスクロールしなきゃいけないとして、そのスクロールエリアからドラッグで指定位置まで移動させるとなると、大抵は描画順序がキモになります。開発初期はまだしも、そこに別のボタンやら何やらが入ってくると・・・。まぁそんな小言は置いておきましょう(笑)

タッチ判定を取るために

今回はuGUIと同居することが前提です。
なので、これから実装するタッチ判定もすべてEventSystemに管理してもらいます。
すでにuGUIで何かオブジェクトを配置しているならHierarchyにEventSystemがあるはずですが、ない場合は作成しておきましょう。

まずRayを飛ばす、RayCasterをCameraにアタッチします。
どのCameraかは、タッチ判定をとりたいオブジェクトを描画するCameraにですね。
EventMaskやMax Ray Intersectionsは用途に応じて変更してください。

次にタッチ判定を取得したいオブジェクトに
BoxCollider、EventTriggerをアタッチします。
EventTriggerに取得したいイベントを登録する必要があるわけなのですが、それはエディタ上でもスクリプト上からでもどちらでも構いません。
Script上からやる場合は、IPointerClickHandlerなどのinterfaceを継承したクラスを作成し、タッチ判定を取得したオブジェクトにアタッチしましょう。

タッチ判定の順序

今回はSprite にEventTriggerをアタッチして、タッチ判定を取得しました。
その場合のタッチ判定オブジェクトが同一カメラ上で重なったときのタッチ処理の優先度は描画順と同一みたいですね。
Spriteの場合は、SortingLayer、OrderInLayer、z値(というよりカメラからの距離?)に準ずるみたいです。
おそらく3Dオブジェクトの場合も同じようになるのではないかと思います。

まとめ

意外と簡単にSpriteをタッチ判定をEventSystemと使って実現することができました。
これがuGUIと混同した場合はどうなるかとかありますが、おそらく描画順ではないのかとは思います。

【番外】レンダリング基礎

概要

Unityも大分高機能になり、もともとCommandBufferを使用してレンダーパイプラインに特殊効果を追加する、というものが、もはやレンダリングパイプラインを自分で書いてね!というレベルまで到達してしまった。
ちょっともうここら辺をスルーするわけにはいかなくなってきたので、基礎から学び始めようかと思います。

参考図書

普通に数学の勉強にもなるのでオススメです。
クオータニオンとか難しいです。
今回は「8.3 グラフィックスパイプライン」について、備忘録を兼ねてまとめていきたいと思っています。

ざっとした描画フロー

下記のようになる。

  1. CPUがドローコールを作成
  2. 頂点シェーダーの処理
  3. プリミティブアセンブリ
  4. ラスタライズ
  5. フラグメントシェーダの処理
  6. ROP処理
  7. フレームバッファへの書き出し

1. CPUがドローコールを作成
現在のシーンの状況(ポリゴンやマテリアルの情報)に対応したドローコールをCPUが描画前準備として作成する。
送るものは、頂点情報やUV情報、ある場合は法線情報や頂点カラー情報、接戦情報もそれに当たるでしょう。

2. 頂点シェーダーの処理
頂点シェーダーでは、CPUから渡された頂点の情報をもとに、ビュー空間、プロジェクション空間への変換を行います。ここら辺の説明は、参考資料の別章または他のサイトさんを参考にしていただければと思います。
また、各頂点の情報をここでいじったりもできます。例えばモデルの中心を基準に2倍に拡大したりなど。

3. プリミティブアセンブリ
頂点シェーダーの出力である頂点情報などをもとに、空間上にどのような図形があるかを構築します。
構築された図形群がカメラ内に収まっているかを判定し、収まっていないなら、その図形情報や頂点情報を捨てます。
図形はカメラ内だが一部頂点のみカメラ外の場合、カメラ表示空間に沿って新しく頂点が追加されるようです。
その後、このタイミングでカリングが行われることとなります。

4. ラスタライズ
ラスタライズとは先ほど作成されたプリミティブの情報をセル(四角)単位に落とし込むことです、
各セルには位置情報や色情報、UV情報や深度情報が、関連頂点から補間され、保持されることになります。

5. フラグメントシェーダの処理
先ほどラスタライズした、各セルに対して、プログラマブルな処理を行います。
ここで色情報を変換したり、色を表示しなかったり、などなど、多くのことが行えます。
その反面、各セルごとに処理が行われるため、解像度が高い端末に多くの描画情報があった場合、一気にここの負荷が高くなりますので要注意です。

6. ROP処理
5で処理した各フラグメント(セル)に対して、最終的な描画チェックを行います。
描画矩形内に収まっているのか、ステンシルチェック、デプスチェックなどのことです。
上記チェックを行い、実際に描画されると判断されたら、フレームバッファ内に存在している対応ピクセルの色とブレンディングが行われます。アルファ合成だったり、加算合成だったりですね。
また、OpenGL ES2.0ではこのタイミングでMSAA(マルチサンプリアンチエイリアシング)という、簡単にいうと表示を滑らかにする処理が行われます。ただ、この処理は解像度を数倍の大きさにしたバッファに一度書き出し、最終的にバッファに書き込むときに周囲数ピクセルブレンドする、という処理なので、滑らかにしようとすればするほどメモリを多く必要としますし、描画コストももちろん高くなります。

7.フレームバッファへの書き出し
フレームバッファとは、いわゆる画面に描画されるものそのもののことです。
このフレームバッファ内に保持されている情報がスクリーンに描画されることとなります。

まとめ

以上が実際にスクリーンにレンダリングされるまでの一連の流れとなります。
私自身、まだ全容を把握できていないので、間違いがあるかもしれません。その場合は指摘していただけると助かります。