TAKOYAKING’s blog 一覧

TAKOYAKING’s blog

たこ焼き系

チュートリアルはやっぱり大切だった!(個人開発)

apps.apple.com

UnityAnalitycsで一番最初のステージで離脱してしまっている人の割合を調べると約35% ~ 40%くらいいました。
言い換えると一番最初のステージをクリアした人は60%から65%程度になってしまっていることが発覚しました。

ただ最初のステージをクリアするとその後の離脱率はとても低かったので、もしかして、最初のステージをクリアできるように誘導してあげれば、良い感じになるのではと思い、簡易的なチュートリアルを入れて、効果を測定してみました。

チュートリアルのデザイン

こんな感じにしてみました。
f:id:TAKOYAKING:20191226230333p:plain
チュートリアルと言うよりも簡易的なヒント形式にしています。
開始時にポップアップでメッセージが出てきて、その後はヘルプ画面に格納されるアニメーションがあって、ヘルプ画面からいつでもメッセージを見れるようにしています。

これなら最初にこのメッセージを読むことを強制できますし、ヘルプ画面に格納されるアニメーションがあるので、ヘルプ画面もついでにみてくれるかもしれません。 (・・・と言う想像です。)

効果

UnityAnalitycs管理画面
ビフォー
f:id:TAKOYAKING:20191226222926p:plain
66%

アフター
f:id:TAKOYAKING:20191226222932p:plain
81%


66% -> 81%と露骨に差が出ました!

この効果は一時的なものではなくずっとこんな調子です。

感想

今まではチュートリアルを作ることに少し懐疑的でした。
理由は3つです。

  • 作成が面倒 (実装コスト)
  • UIが変わるとチュートリアルを修正しないといけなくなるかも (実装コスト)
  • 効果あるのかどうかわからず、個人的にはだらだらチュートリアル続くのはユーザー体験を損ねるかもと思ってしまう (ユーザー体験)

特に3つ目はだらだらチュートリアルが続いたり、チュートリアルがうっとしくなったりするのが嫌だったので今まで、あまり説明を入れていませんでした。

ただここまで露骨に効果が出ると、ゲームの開発でチュートリアルへの意識が変わりそうです。

やってよかった!

unityのProjectフォルダタブを複数配置する

apps.apple.com
最近まで知らなかったのですが、projectフォルダを複数表示できるらしいことを知りました。

今まではprojectフォルダが一個しかなかったのでScriptのフォルダとTextureのフォルダを交互に使う場合はいちいち対象のフォルダーまでクリックし直さないといけなかったのでとても面倒でした。

f:id:TAKOYAKING:20191221171908p:plain
こんな感じでProjectタブを複数配置できると一方はScriptを入れているフォルダにして、もう一方はTextureを入れているフォルダを表示すれば、交互に素早くアクセスできます。
結構便利です!

やり方

f:id:TAKOYAKING:20191221172112p:plain
やり方は上の写真のように
Projectタブで右クリック > Add Tab > Project
で追加できます。

unity2018からunity2019にあげるとAnima2d関連でエラーがでた

apps.apple.com
Auto Puppetのプロジェクトをunityを2018から2019.2へバージョンアップしようとしたらanima2dで
AmbiguousMatchException: Ambiguous match found.
というエラーがconsoleに出続ける現象に遭遇しました。
ただ普通に起動はできるみたいですが、気持ち悪いので直しました。

AmbiguousMatchException: Ambiguous match found.
System.RuntimeType.GetMethodImpl (System.String name, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder binder, System.Reflection.CallingConventions callConv, System.Type[] types, System.Reflection.ParameterModifier[] modifiers) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Type.GetMethod (System.String name, System.Reflection.BindingFlags bindingAttr) (at <437ba245d8404784b9fbab9b439ac908>:0)
Anima2D.AnimationWindowImpl_51_52_53.InitializeReflection () (at Assets/Anima2D/Scripts/Editor/AnimationWindowExtra/AnimationWindowImpl_51_52_53.cs:58)
Anima2D.AnimationWindowImpl_54.InitializeReflection () (at Assets/Anima2D/Scripts/Editor/AnimationWindowExtra/AnimationWindowImpl_54.cs:15)
Anima2D.AnimationWindowImpl_55.InitializeReflection () (at Assets/Anima2D/Scripts/Editor/AnimationWindowExtra/AnimationWindowImpl_55.cs:18)
Anima2D.AnimationWindowImpl_56.InitializeReflection () (at Assets/Anima2D/Scripts/Editor/AnimationWindowExtra/AnimationWindowImpl_56.cs:18)
Anima2D.AnimationWindowImpl_2017_1.InitializeReflection () (at Assets/Anima2D/Scripts/Editor/AnimationWindowExtra/AnimationWindowImpl_2017_1.cs:17)
Anima2D.AnimationWindowExtra..cctor () (at Assets/Anima2D/Scripts/Editor/AnimationWindowExtra/AnimationWindowExtra.cs:57)
Rethrow as TypeInitializationException: The type initializer for 'Anima2D.AnimationWindowExtra' threw an exception.
Anima2D.EditorUpdater.AnimationWindowTimeCheck () (at Assets/Anima2D/Scripts/Editor/EditorUpdater.cs:108)
Anima2D.EditorUpdater.Update () (at Assets/Anima2D/Scripts/Editor/EditorUpdater.cs:185)
System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <437ba245d8404784b9fbab9b439ac908>:0)
Rethrow as TargetInvocationException: Exception has been thrown by the target of an invocation.
System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Delegate.DynamicInvokeImpl (System.Object[] args) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.MulticastDelegate.DynamicInvokeImpl (System.Object[] args) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Delegate.DynamicInvoke (System.Object[] args) (at <437ba245d8404784b9fbab9b439ac908>:0)
UnityEditor.EditorApplication.Internal_CallUpdateFunctions () (at /Users/builduser/buildslave/unity/build/Editor/Mono/EditorApplication.cs:310)
環境
  • 2018.2.2f1 -> 2019.2.16f1 のアップデート
  • Mac
対処法
  1. unityをアップデート
  2. エラーが出るのでいったんanima2dフォルダをまるごと削除してAsset Storeからanima2dを再度インポート

fastlaneを使ってiTunes Connectへmetadataのアップロードを自動化する (個人開発用)

apps.apple.com
このアプリを作っているときにiTunes Connectへアプリを申請する情報を毎度作るのが面倒でした。特にプロモーションテキストは前のものからコピーしてくれないので、いちいちコピーし直さないといけなくて、複数の言語に対応していると結構面倒でした。
fastlaneはそれを自動化してくれるので、自動化してみました。

fastlaneでやることの例

  • iOSのビルドでipaを作成する
  • ipaiTunes Connectへアップロード
  • iOSアプリ申請のメタデータ (プロモーションテキストとか、説明文とか、タイトルとか)をアップロード(今回はここのみ対応)
  • iOSのアプリ申請のスクショをアップロード

他にもあるらしいです。
詳しくは
fastlane docs

環境

今回やること

メタデータのアップロードのみを対応したいと思います。ゆくゆくは全部するつもりだけど、長くなるので一番簡単で効果が大きいところをしてみます。

スクショはアップロードしません。(アップロード時間かかるし、個人開発ではそんなに変更することないので)

今回は対応しないけど最終的な個人開発iOS用自動化構成計画

  • fastlaneからunityのxcodeworkspaceの生成
  • fastlaneからipaファイルを生成
  • 日本語リリースノートを入れると勝手に他の言語へも自動翻訳
  • fastlaneからipaとmetadataをiTunes Connectへアップロードし、申請する
  • 作業終わればslackへ通知

多分これで個人開発では十分すぎるかなと思います。

手順

  1. renv使ってrubyをインストール (renv入っていたら不要)
  2. fastlaneインストール
  3. プロジェクトの作業フォルダでfastlane init
  4. iTunesConnectの設定情報を記述
  5. fastlane deliver init
  6. deliver用のlaneを構築

renv使ってrubyをインストール (renv入っていたら不要)

省略
以下renvが入っている前提です。

fastlaneインストール

fastlane docs
ここをみながらインストールします。
以下抜粋

# Using RubyGems
sudo gem install fastlane -NV

# Alternatively using Homebrew
brew cask install fastlane

適宜環境に合わせた方法で実行してください。
(sudo つけるつけないなど)

プロジェクトの作業フォルダでfastlane init

お好きな作業フォルダfastlane initしてください。
fastlaneのフォルダが出現します。
この時iOSプロジェクトの配下に作ると色々自動で作ってくれるみたいでしたが、unityは毎度iOSプロジェクトを生成し直すので、僕の場合はiOSのプロジェクト配下には置かず、違うところに置きました。

Appfile
Fastfile
が生成されていると思います。
Appfileが設定情報を記述するところで
Fastfileはlaneを記述するところになります。

iTunesConnectの設定情報を記述

AppFileに設定情報を記述していくことになるのですが、このまま直接書いていっても問題ないのですが、書いた情報をgitにコミットしてしまうのは気がひけるので、
.env.defaultファイルに記述し、それをAppFileから参照する形にします。
もちろん.envファイルはgitignoreします。これでgitに重要な情報をコミットせずにすみます。

.envについては以下です。
Keys - fastlane docs

最終的なファイルは以下です。
Appfile

app_identifier ENV["APP_IDENTIFIER"] # The bundle identifier of your app
apple_id ENV["APPLE_ID"]  # Your Apple email address

.env.default

SLACK_URL="http://~~"
APP_IDENTIFIER="com.your.project"
APPLE_ID="your.mail@example.com"

ついでにslackのフックするためのURLも入れておきます。
slackのフックURLについては自身のslackアカウントから取得してください。

fastlane deliver init

deliver - fastlane docs
metadataやscreenshot関係はdeliverコマンドとなります。

fastlane deliver init

するとmetadata、screenshotがもろもろダウンロードされます (便利すぎ!)

もう一回ダウンロードし直したい場合は以下のコマンドがあります。

fastlane deliver download_screenshots
fastlane deliver download_metadata

ただ注意点として、現在作成した最新のものを取得するので最新のものが提出準備中なら提出準備中のものを取得するみたいです。中途半端に作成してしまうと、中途半なものを取得してしまうので、何もiTunesConnectで作業準備していない時に実行するほうが最初はいいかもしれません。(オプションあるかは不明?)

deliver用のlaneを構築

Deliverfileに書いてもいいのですが、Fastfileのlaneの中で記述しました。(特に理由はありません。)
Fastfile

platform :ios do
    desc "Description of what the lane does"
    lane :upload_metadata do
        # add actions here: https://docs.fastlane.tools/actions
        deliver(
            submit_for_review: false, # Appleの審査に出すかどうか
            force: true, # If set to true, no HTML report
            skip_screenshots: true, # 時間節約
            skip_binary_upload: true, # ipaアップするかどうか
            copyright: "#{Time.now.year} TAKOYAKING",
            automatic_release: true, # Appleの審査後にこのバージョンを自動的にリリースする
            # metadata_path: "./metadata"
        )
    end
end

after_all do |lane, options|
    slack(
        default_payloads: [],
        message: "#{lane} All OK!",
    )
end

error do |lane, exception, options|
    slack(
        message: exception.message,
        success: false
    )
end

処理流れとしてはmetadataをアップロードする。成功すればslackへ。失敗すればslackへ。
という流れになります。

オプションは詳しくは以下です。
deliver - fastlane docs

skip_screenshots
skip_binary_upload
は共にtrueにしています。metadataだけにして時間節約したいからです。

metadata_pathは指定しなくても自動でとってくれます。最初間違ったパスを指定してしまっていて、小一時間悩むはめになったので、指定しない方向でいきます。

copyrightにはこの形式で設定していますが、年をこの形式にしていないと、注意文が出たのでこれにしました。

lane名はupload_metadataにしています。最終的にはfastlane upload_metadataで実行します。

使い方

bundle exec fastlane upload_metadata
bundle execを省略しても良いのですが、省略するとbundle execつけたほうが速いよとメッセージが出るので、とりあえずつけときました。
どちらでも実行可能です。

これでmetadata > 各言語 > release_notes.txtにアプデ内容書いてコマンド実行という流れに持っていけそうです。

感想

まだmetadataの部分しかブログに書けていないですが、時間があったらビルド、ipaアップロード、翻訳自動化まで書こうかなと思っています。

metadataだけでも結構便利です。いつもiOSの提出は作業はちょいちょい生じるので結構、億劫になっていましたが、かなり効率化できたかなと思います。
個人開発なので全部自動化は対応しなくてもmetadataだけ自動化とかでも結構コスパいいなと思います。

よくわからない点

app preview(動画)が見当たりませんでした。 app previewに対応したみたいなissueはあったのでできるのかなと思ったけどわからなかったです。
でも既存のapp previewは引き継いでくれるみたいなので別にいいかな


続き ビルド編
fastlaneでiOSビルドをしてiTunesConnectへアップロードする (個人開発) - TAKOYAKING’s blog

iMac 2019 bluetoothイヤホンが接続されない時の苦肉の対処法

Yahooポイントで買ったbluetoothイヤホン (6000 ~ 8000円くらい?)がiMac 2019と正しく接続してくれず、ずっと接続が不安定で、プツプツ切れたりします。

androidiPadは普通に接続されるのでbluetoothイヤホンが悪いわけではなさそうです。

iMac 2019は買ったばかりだし、とても利用するので悔しくて、色々方法を探しました。

  • 再起動
  • もう一度ペアリング
  • plistを何とかかんとか

などググってできた物を片っ端から試してみましたが、どれも効果はありませんでした。

原因

iMac 2019はキーボードとtrackpadの2台bluetooth接続していますので、検証の結果、3台目のイヤホンを接続するときは干渉してしまうみたいです。

苦肉の対処法

方法1 キーボードのスイッチを切ってキーボードを使用しない
方法2 trackpadにライトニングケーブルをさして、ずっと充電状態にする

結果

方法1は当たり前ですが、キーボード使えなくなってしまうし、キーボードを再接続しようとした時に、再接続してくれなかったりと色々不便です。
なので方法2が苦肉の策として僕は採用しました (悔しい!)

ですが、方法2のtrackpadにライトニングケーブルをさして、ずっと充電状態にするとbluetooth接続はずっと安定し、切断されることもなくなりました。

ただなんかとても悔しい

Android App BundleでUnityのゲームをアップロードしたらapkの頃よりも容量が半分になった

Google Play Consoleへのアップロード形式がapkだけでなくaab (Android App Bundle)でもアップロードできるようになっていました。

手順

www.petitmonte.com

このサイトの通り、

  1. ARM64をオン
  2. ビルド設定で「App Bundleをビルド(Google Play)」をオン

にしました。
もし作っているアプリが古いアプリだと、Google Play アプリ署名というのをしていないのでこれだけではAABファイルはアップロードできません。

僕の作っているのはデジタルパペットというアプリで4年ほど前だったのでまだしてませんでした。

apps.apple.com

なので以下のサイトの通りアプリ署名を行いました。
blog.lab7.biz

  1. PEPKツールをダウンロードして実行
  2. 秘密鍵Google Play Consoleへアップロード

これで作業は完了です

結果

びっくりしたことに容量が半減しました!

f:id:TAKOYAKING:20191209174230p:plain

47.7 MB → 23.3MBへ大きく減っていました。

これだけの作業でこんなに減らせるのはやらないと損ですね!

実際にGoogle Play からダウンロードしてみるとダウンロード速度はめちゃくちゃ速かったです。 (約23MBですからね!)

(注:全アプリがこれだけ容量減らせるかはわかりません。)

Unity Test Runner を導入したら、めっちゃ良かった

apps.apple.com
デジタルパペットのプロジェクトにUnity Test Runnerを導入してみました。

導入した経緯

新機能を付けようと思ったが、数年ぶりのプロジェクトだったので、どういうテストをいつもしていたか忘れてしまい、どうしようかとおもっていたら、Unityのテストがあることがわかったので、せっかくの機会なので導入してみようとなりました!

メリット
  • 実装中のバグの発見がとても早かった
  • テストボタン押すだけなので、手動よりも楽!
  • なんかうれしいー!
デメリット
  • 効果的なところにテスト入れないとダメかもしれません
  • テストの実装コスト
  • 最初導入するまでちょっとつらいかも?
結果

f:id:TAKOYAKING:20191027234552g:plain
Unity Test RunnerにはEdit Mode(単体テスト)とPlay Mode(結合テスト)があって
Play Modeのテストの方が効果が高かったです (※ gifはPlay Modeです)

デジタルパペットはパズルゲームなので同じコマンドを入力すると同じ結果になります。

なのでコマンドを入力して、同じ結果になるかをテストすれば、コードを変えてもテストコードを変えなくても済む仕様に強いテストコードが出来上がります。

どこをテストすべきか?

コードを変更しても、テストコードを変更しなくてよいテストコードが良い気がします!
必ず同じ結果になるパズルゲームなので、結果に対して、テストコードさえ書けば、仕様の変更に影響を受けにくい、テストコードのできあがりです。
個人開発なので無理をしないテストコードが僕は好きです。

どういう書き方のテストコードが良いか

これは他の言語と同じで、テスト項目ずつ、メソッドをつくる
できれば、フレームワークの機能を使って、テストコードを楽に生成できるテストケースを使うのがベストだと思います。

感想

テストコードは最小の労力で程々の効果を得れるのが僕の理想なので、結果が変わらないようなゲームを作っている方はオススメです!

次回はサンプルコードをのっけたいと思います