FSBTool ERROR: Internal error from FMOD sub-system.と出て、音声ファイルを読み込めない時の対策です。
今回起こったのはMP3ファイルだったので、ファイルの形式を変えてインポートします。
ファイル名を変えるだけではなぜか読み込めないので、OGGファイルにしました。
MP3 → OGGファイルはぐぐれば対応できるサイトが見つかります。
以上、れいでした。
Webサービス制作のための技術情報を。データ分析(Python、機械学習コンペ他)や自作野球ゲームMeisyoのこと中心。
FSBTool ERROR: Internal error from FMOD sub-system.と出て、音声ファイルを読み込めない時の対策です。
今回起こったのはMP3ファイルだったので、ファイルの形式を変えてインポートします。
ファイル名を変えるだけではなぜか読み込めないので、OGGファイルにしました。
MP3 → OGGファイルはぐぐれば対応できるサイトが見つかります。
以上、れいでした。
知りたい方がいらっしゃったので書いてみました。
インスペクタから設定できると、データベース不要で簡単に複数マップを作成・管理できるところが良いところだと思います。
MapSetting.csとして、以下のファイルを作成します。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// -----------------------------------------//
// マップ本体
// -----------------------------------------//
[Serializable]
public class MapData
{
// -------------------------------------------//
// 基礎
// -------------------------------------------//
[SerializeField] string id = ""; // マップID:同一のものは作らないこと
public string Id{ get{ return id; } }
[SerializeField] string name = "マップ名";
public string Name{ get{ return name; } }
[SerializeField] Sprite imageBackground; // 背景画像
public Sprite ImageBackground{ get{ return imageBackground; } }
[SerializeField] int pxWidth = 100; // マップの大きさ(横)
public int PxWidth{ get{ return pxWidth; } }
[SerializeField] int pxHeight = 200; // マップの大きさ(縦)
public int PxHeight{ get{ return pxHeight; } }
// -------------------------------------------//
// スタート・ゴール・障害物関係
// -------------------------------------------//
// スタート位置
[SerializeField] MapPositionData posStart;
public MapPositionData PosStart{ get{ return posStart; } }
// ゴール位置
[SerializeField] MapPositionData posGoal;
public MapPositionData PosGoal{ get{ return posGoal; } }
// 障害物位置(障害物が1種類なら)
[SerializeField] List<MapPositionData> listPosHurdle;
public List<MapPositionData> ListPosHurdle{ get{ return listPosHurdle; } }
// -------------------------------------------//
// メソッド
// -------------------------------------------//
}
// 位置データ(2D)
[Serializable]
public class MapPositionData
{
[SerializeField] int posX = 0; // X座標
public int PosX{ get{ return posX; } }
[SerializeField] int posY = 0; // Y座標
public int PosY{ get{ return posY; } }
}
// -----------------------------------------//
// 設定用ファイル
// -----------------------------------------//
[CreateAssetMenu()]
[Serializable]
public class MapSetting : ScriptableObject
{
[SerializeField] List<MapData> listData;
// -----------------------------------------//
// メソッド
// -----------------------------------------//
public List<MapData> GetData()
{
return listData;
}
}
// -----------------------------------------//
プロジェクト画面から、右クリック > 作成 > MapSettingで作成します。
Scriptable Objectを選択すると、以下のように設定ができるようになります。
マップを増やすのは+/-で調整可能です。

マップデータを呼び出す例となるファイルを作成します。
実際に使う場合はインスペクタから、mapSettingにMapSettingファイル(上記で設定したもの)や各種GameObjectをアタッチしてください。
唐突にオブジェクトプールやPrefabという概念が出てきます。そちらは自分で調べるかChatGPTに聞いてみるとよいでしょう。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MapRead : MonoBehaviour
{
// マップデータ
[SerializeField] MapSetting mapSetting;
List<MapData> listMapData;
// オブジェクト(各々アタッチが必要。これらのObjectは開始時・マップ変更時は非アクティブに設定。)
[SerializeField] GameObject objPlayer; // キャラクター
[SerializeField] GameObject objGoal; // ゴール
[SerializeField] List<GameObject> listObjHurdle; // 障害物(オブジェクトプールのように使用)
[SerializeField] GameObject prfHurdle; // 障害物のPrefabをアタッチ(新規にGameObjectを生成するために使用)
// ロード
void Awake()
{
listMapData = mapSetting.GetData();
}
// 初期設定
void Start()
{
ReadMapData();
}
// マップデータ読み込み
void ReadMapData()
{
MapData mapData = listMapData.Find(x => x.Id == "マップID");
// マップの大きさ設定
// SetMapSize(mapData.PxWidth, mapData.PxHeight); // 同様に設定してみてください
// スタート・ゴール位置の設定
SetMapStart(mapData.PosStart);
SetMapGoal(mapData.PosGoal);
// 障害物を設置
// SetMapHurdle(mapData.ListPosHurdle); // スタート・ゴールを設定できたら、コメントアウトを外して試してみてください
}
// 開始位置設定
void SetMapStart(MapPositionData mapPos)
{
objPlayer.transform.position = new Vector3(mapPos.PosX, mapPos.PosY, 0f);
// 表示ON
objPlayer.SetActive(true);
}
// ゴール位置設定
void SetMapGoal(MapPositionData mapPos)
{
objGoal.transform.position = new Vector3(mapPos.PosX, mapPos.PosY, 0f);
// 表示ON
objGoal.SetActive(true);
}
// 障害物設定
void SetMapHurdle(List<MapPositionData> listMapPos)
{
// 現状のObjを全て非表示にする
foreach(GameObject objHurdle in listObjHurdle) objHurdle.SetActive(false);
// 表示するオブジェクト・位置を設定
for(int i=0; i<listMapPos.Count; i++)
{
// 一時Obj
GameObject objTemp = null;
// listObjHurdleの個数を把握
if(listObjHurdle.Count <= i) // 不足する場合
{
objTemp = Instantiate(prfHurdle); // prfHurdleを元にオブジェクトを新規生成
objTemp.SetActive(false); // とりあえず非表示にしておく
listObjHurdle.Add(objTemp); // オブジェクトプールリストに追加
}
else // 充足している場合
{
objTemp = listObjHurdle[i];
}
// 配置・表示
MapPositionData mapPos = listMapPos[i];
objTemp.transform.position = new Vector3(mapPos.PosX, mapPos.PosY, 0f);
objTemp.SetActive(true);
}
}
}
マップを複数に増やすには、データ作成→管理用UI作成→マップ作成→…と、非常に面倒だったので、管理用UIを作成せずに楽に作れるよう設計しました。
もし「こうした方がいいよ」というのがあれば教えてください。
ゲーム 監督たちの甲子園(仮)で、オーダー設定を作っています。
ただ、動作がクソ重いので最適化でどれくらい重いのか、最適化するとどうなるのか比較したいと思います。
動作イメージは下記の通りです。

動画の通りPCでも少しフリーズします。少し古いスマホだとちょっとイラっとするくらい待機時間が入ります。
実験はオーダー設定の3種類の時間をカウントします。
DisplayOrderFirst:オーダー(スタメン、控え、ベンチ外)の表示
ChangeOrder:メンバー変更
ChangeBench:ベンチメンバー設定
読み込み速度測定利用するデバイスは、よくあるノートPCです。
全く最適化をしていないので重いのかな;つД`)
最適化方法はUnity 最適化メモに準じます。
DisplayOrderFirst:53%減
ChangeOrder:75%減
ChangeBench:69%減
大きく削減効果が見られました。
とてもスムーズに動作するように感じます!
最も効果があったのは、Instantiate()、Destroy()回数の最適化でした。
選手表示用のGameObjectの生成を何度も行うことをやめ、必要なタイミングだけInstantiateを行うようにしました。
オブジェクトプールは元々同じようなものが導入されており、より読み込み回数が少なくなるように変更しました。
その他はLinq削除、Debug.Log削除、Start, Awake, OnEnable で負荷の重い処理をしないを行いました。
上記のInstantiate()に関して、Start, Awake, OnEnable で負荷の重い処理をしないと組み合わせて・・・ChangeOrder用とChangeBench用のGameObject生成にはプリロード関数を作っておき、負荷がかかりにくいタイミングでupdate(フラグ管理)で読み込みをします。
負荷が気にならない部分に読み込みを分散するのは、体感に大きな影響を与えそうですね!
DisplayOrderFirst:256ms
ChangeOrder:121ms
ChangeBench:129ms

クリック時の引っかかり感がどうしても気になる…。PCでこれはキツい。
DisplayOrderFirst:120ms(ロード80ms、表示40ms)
ChangeOrder:30ms
ChangeBench:40ms
クリック時の待機時間がかなり気にならなくなりました。
いずれかの選手を交代すると複数のデータを再設定する必要があるのですが、今回は生成部分を大きくカットした仕様となります。
より最適化するのであれば、交代に関与していない選手の再ロードは必要ないので、削減が考えられます。
とはいえ再ロードを含めた表示速度が30~40msのため、それほど改善の余地はないでしょう。
Meisyo3(前作)では、野球の試合中になぜか重い・・・続けていると落ちることが多くありました。orz
今回はこちらを利用しつつ、体感良く作れるようにします!
確かにInstantiate()の調整やオブジェクトプールの積極的な利用、プリロードの最適化もしてなかったな。。。
なぜかinfo.plistが見えない問題があるので、備忘として書き残しておく。
(1)プロジェクトのxcodeprojectファイルをXcodeで開く
(2)該当Project選択
(3)TARGET選択
(4)Infoタブ選択
この手順でInfo.plistが自動的に生成される。
続いて、
(5)xcodeをいったん閉じる
(6)プロジェクトのworkspaceファイルをXcodeで開く
(7)Project > Target > Infoタブで開くと見えるようになる。
原因としては、
「プロジェクトのworkspaceファイルをXcodeで開く」で始めることだった。
なぜか「info.plistが生成されてないよ!(意訳)」となる。再現しねえ・・・と思ったら、上記のxcodeprojectファイルから開くようにするとうまく行った。
Androidばっかりやっていると、iOSの仕様わからんよなあ・・・
特に、UnityやFlutterみたいなクロスプラットフォームができるモノを使っていて、かつ初めてiOSのリリースとなると( ^ω^)・・・ツライ
Androidが簡単すぎんのよな
それではまた。。
English Ver.
The problem that info.plist is not visible or cannot be created, which occurs in Xcode 13 or later…
I have a problem that I can’t see the info.plist for some reason, so I’m leaving this as a reminder.
(1) Open the xcodeproject file of the project with Xcode.
(2) Select the appropriate Project
(3)Select TARGET
(4)Select the Info tab
Info.plist is automatically generated by this procedure.
Continue,
(5)Close xcode once
(6) Open the project workspace file in Xcode.
(7) Open the file in Project > Target > Info tab. (Is This File Generated? Yes…Complete!)
The cause,
It was to start with “Open the project’s workspace file in Xcode”.
For some reason, “info.plist is not generated! (translation)”. I thought “I can’t reproduce this…”, but when I tried to open it from the above xcodeproject file, it worked.
I am glad if I could help!
皆さま、お疲れ様です( ^o^)
だいぶ更新間隔が空きましたが、いかがお過ごしでしょうか。
今回はMeisyoとは異なる別の制作物を作っていました。
珍しくトランプカードゲームです。
1人で遊べる最もシンプルな脳活ゲーム NEXT CARD
ルールは簡単、5枚の手札から次のカードを選ぶだけ。
オフラインで遊べて、スキマ時間にサクッとプレイ。次のカード、あなたはどれを選ぶ?
下記で遊べます!
1プレイ数秒で終わることもある死にゲーに仕上がっています。
新しく取り組んだこととしては・・・以下の6点です。
・2Dゲー
・デザインの事前設計
・UIの手触り感の向上
・画像素材作成
・カード演出
・ローカライズ
これまでのゲームとは大きく違うのは、はじめて演出に注力したことでしょうか。
気持ちよいカードの動きや音となるように、ちょっと工夫を凝らしました。
1プレイでもどうぞ。
・Web
・Google Play
・Apple App Store
(1)Google PlayとApp Storeのチーム名(パブリッシャー名)は同じにしよう。審査がめっちゃややこしい。
Google PlayをRei-Farmsでリリース後、App Storeを個人名でリリースしようとしたら、、「このアプリと見た目同じやからCopycatに該当するんやけど、利用許可あるなら見せて?」と。結局リリースまで3週間かかりました。
上記の文面を引き出すのに2週間+App Storeの方との電話。orz
以下流れ
審査「Copycatやで~」
ワイ「わかった、変更した。このアプリのどこがどう悪い?」
審査「Copycatやで~」
ワイ「怪しそうなところ変更した。それで、このアプリのどこがどう悪い?具体的に教えて」
審査「Copycatやで~」
ワイ(あっ・・・電話対応してもらおかな・・・)
電話「CopycatはGoogle Playで出てるこのアプリと同じやろ?あなたのアプリというなら利用許可もらって」
ワイ「どう証明すればよい?」
電話「詳しくはわからん」
ワイ「」
ワイ「・・・このスクショ」
審査「ん-これじゃだめですねー」
ワイ「おう・・・じゃあこれでどうですか。」
審査「ん-これじゃだめですねー」
ワイ「おう・・・じゃあこれでどうですか。どういう情報が欲しいか教えてくれや。」
審査「ん-これじゃだめですねー。対象アプリのパブリッシャーから、利用者名が許可もらってることを証明できる書類ください。」
ワイ「Web(Rei-Farms)ブログで出せばええかな。ブログを編集できるのはRei-Farmsだけやし。。(どういう形式か指定してくれんのか…)」
審査「これでOK~あ、iPadでダメだからもう1回審査ね」
ワイ「やったぜ」
(2)野球ゲームMeisyoシリーズ、演出全くねえやん
・・・;つД`)3Dにするにはちょっと早すぎた気がシマシタ。Meisyo3とか。
何をどうすれば売れそうなのか、何となく見えてきたようです。orz
このゲームのメインユーザーとしては、高年齢層を想定しています。
「簡単で取っつきやすいゲームならしてもいいかな」
「でもあまりにも簡単すぎてもつまらない」
果たして・・・どれくらいプレイされるのでしょうか?
ただ、ユーザーを獲得したいのであればソリティア等の売れているゲームを題材とすべきでした。反省。
検索ボリュームもそうですし、既存ゲーの「新作」は売れますしね。
かつ、ユーザーは「ソリティアがしたい」のであって、新しいゲームをプレイしたいわけじゃないはずです。
特に高年齢層は、ソリティア+αなら対応したいと思うと想定しますが、完全に新しいものはどうでしょうか・・・。
モバイルゲーム市場の統計データとしては、パズルゲームやカードゲームがよりプレイされているようっです。
ただし、40代以降の壮年~高齢層、特に男性のプレイ率が著しく下がるんですよね。
Meisyoにおいても、ユーザー動向を気にして制作していきたいですね。
また、ゲーム開発のお作法についてもちょっと気を付けねばならないかなと。
ソリティアを題材としたゲームなら、ソリティアらしさを考えてね、と。
なお、新作としてMeisyo4(仮)を構想していますが、タイトルからはMeisyoの名前がなくなるかと思います。「有名になってからシリーズ名乗ろうね」という当たり前のツッコミを受け…。ゲームのメインが高校野球+甲子園なので、、売れるために、タイトルをもっとわかりやすくしたいんですよね。
上記で話題に出したお作法についても、よく考えて出したいです。
「高校野球」「甲子園」と来れば、野球のルールを守るのは当然として、地域(都道府県)や球場(甲子園)に根差した重要な「らしさ」があるので、開発時はそこに注意したいところ。
ユニフォーム、敵チーム名、ルール(開催時期含む)・・・「高校野球らしさとは何か?」ですね。
上記のように新しいゲームを作ることで視点が増えました。やったぜ。
Twitterでは、ほぼゲーム制作のことしか書いていませんが、、、ウラではいろいろなことに手を出しています。
では、また何かあれば書きます~( ^o^)/~
メインカメラの挙動は以下参考にしました。ありがとうございました!
Unityの画面のアスペクト比と解像度を自動変換 全スマホ・複数解像度に対応させる
メインカメラにAspectKeeper.csとAspectKeeperSub.csをアタッチ。
AspectKeeperSub.csのSubCameraObjにサブカメラのゲームオブジェクトをアタッチ。
SetRectを好きな値に設定。

AspectKeeper.cs内のaspectVecを好きな大きさに変更・保存。
※AspectKeeper.csは任意のカメラにアタッチすることで、自動でaspectVecのアスペクト比に調整してくれます。
※各カメラにアスペクト比の値を一つ一つ入れるのが面倒だったので、一括で変更するようにしました。
AspectKeeperSub.csは変更点なし。
画面の大きさが変わった時にのみ再読み込みするようにして、参照サイトより読み込み負荷を減らしています。
何か不備があれば教えてください。
AspectKeeper.cs
[C#]
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteAlways]
public class AspectKeeper : MonoBehaviour
{
private Camera targetCamera;
private Vector2 aspectVec = new Vector2(800f, 440f);
private Vector2 saveScreen = Vector2.zero;
void Start()
{
targetCamera = this.GetComponent
}
void Update()
{
SetRect();
}
void SaveScreen()
{
saveScreen = new Vector2(Screen.width, Screen.height);
}
void SetRect()
{
if(saveScreen == new Vector2(Screen.width, Screen.height))
{
return;
}
// 保存
SaveScreen();
// データ取得
var screenAspect = Screen.width / (float)Screen.height; //画面のアスペクト比
var targetAspect = aspectVec.x / aspectVec.y; //目的のアスペクト比
var magRate = targetAspect / screenAspect; //目的アスペクト比にするための倍率
var viewportRect = new Rect(0, 0, 1, 1); //Viewport初期値でRectを作成
// 変換
if (magRate < 1)
{
viewportRect.width = magRate; //使用する横幅を変更
viewportRect.x = 0.5f - viewportRect.width * 0.5f;//中央寄せ
}
else
{
viewportRect.height = 1 / magRate; //使用する縦幅を変更
viewportRect.y = 0.5f - viewportRect.height * 0.5f;//中央寄せ
}
// set
targetCamera.rect = viewportRect; //カメラのViewportに適用
Debug.Log("SetRect");
}
}
[/C#]
AspectKeeperSub.cs
[C#]
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteAlways]
public class AspectKeeperSub : MonoBehaviour
{
public GameObject subCameraObj;
public Rect setRect = new Rect(0, 0, 1, 1);
private Camera targetCamera;
private Camera subCamera;
private Rect saveRect = Rect.zero;
void Start()
{
targetCamera = this.GetComponent
subCamera = subCameraObj.GetComponent
}
void Update()
{
SetRect();
}
void SaveScreen()
{
saveRect = targetCamera.rect;
}
void SetRect()
{
if(saveRect == targetCamera.rect)
{
return;
}
// 保存
SaveScreen();
// データ取得
Rect targetRect = targetCamera.rect;
Rect subRect = subCamera.rect;
// X
float baseX = setRect.x – 0.5f;
float corX = targetRect.width / 1f;
subRect.x = baseX * corX + 0.5f;
subRect.width = setRect.width * corX;
// Y
float baseY = setRect.y – 0.5f;
float corY = targetRect.height / 1f;
subRect.y = baseY * corY + 0.5f;
subRect.height = setRect.height * corY;
// set
subCamera.rect = subRect;
Debug.Log(“SetRectSub”);
}
}
[/C#]
アプリ開発者によくある悩み・・・登録者がゲームを続けてくれません!!。
続けてくれないをより細かく言うと、(1)コンテンツを一通りプレイしてもらいたいのか、(2)毎日プレイしてほしいのかによって分析方法が変わります。
どちらかというと(2)の方が難しいので、そちらを新規登録を増やすという観点から解説していきます。
この記事はゲームアプリ運営の分析ノウハウ vol.1 概要編の続きです。
分析するゲームのコンテンツを「一本道のストーリーとする」と、分析をイメージしやすいと思いますのでそちらで解説します。
前提として、ストーリー1~5まで一本道でゲームが進むとします。
分析すべきは、以下の3です。
1・ストーリー1のプレイ人数
2・ストーリー2のプレイ人数
3・ストーリー1→2の移行割合(=分析2の人数/分析1の人数)
ストーリー1~5まで、移行割合が低くなるポイントはどこか?を特定することが重要です。
ちなみに、例えばストーリー3の移行割合が低かったとしても、ストーリー3自体が悪いとは限らないことに注意してください。
想像してみてください。
「つまんねーなコレ」ってなってすぐに離脱します(ゲームやめます)か?
買い切りゲーだと特に、ちょっと違和感があっても買った手前勿体ないので続けるでしょうし、それをやめるような区切りを感じさせたポイントがストーリー3の可能性があります。原因ではないかもしれません。
そのように、離脱ポイントがわかってもすぐに結論に至れず、対策を打ちづらいことが分析の難しさを顕著に表しているでしょう。
そもそも買い切りゲーを分析して意味があるのか議論はあると思います。
アップデートができるならいいですし、次回作が決まっているなら参考になりますね。参考になるって言っても、買い切りゲーは前作とシステムやUIが全く同じでは出しづらい(売れない、目新しさがない)のでなかなか難しかったりする…。某連番ゲームの分析結果を見ましたが、結果は出たものの中身にはなかなか反映されずとかも…。
「毎日プレイしてくれません」・・・確かに言いたいことはわかります。ただ、ユーザーがプレイを続けるようになるまでのステップを考えてみましょう。はじめはそうですね・・・アプリストアで見つけるところから始めてみましょうか。
アプリストアで見つける
→インストール(するか迷う。見た目で楽しそうか、競合アプリはどうか、容量との戦いは・・・。)
→新規登録(登録画面がないアプリも多いですね)
→チュートリアル開始(ないアプリも多いですね)
→チュートリアル終了
(高い壁)
→続けたい理由を見つける
(高い壁)
→プレイを続ける
vol.1にソーシャルゲームなら新規登録からの1日後継続率が25%を超えると書きましたが、この(めっちゃ多い)ステップを通して25%・・・?嘘やろって思います。分析するのは、このそれぞれのステップから離脱せず、かつ毎日続けたいようなコンテンツとなっているかどうかです。なんか文字で書くとめっちゃ厳しそう・・・
この目標値を超えてるアプリはすごいと思います。特に、広告経由のユーザーの継続率は酷いものです。
なぜなら、広告で移るようなユーザー≒広告媒体でアプリを探す(が、定着せず移行し続けていることが多い)ユーザーなので、目新しい高品質のゲームを探しています。ゲームを求めているのに、流動している・・・つまり定着しづらい。飽きっぽい人とかが多いイメージです。
そのため、継続率のカウントを行うユーザーは精査した方が良いと思います。検索経由で来た人とユーザー層が本当に大きく違いますので。
分析するユーザー層を定義する・・・とても重要なことです。
ちなみに、運営しているMeisyo+では検索経由ユーザーの1日後継続率は20~40%程度です。広告ユーザーになると10%を切ったりします。アドフラウド(偽広告、広告クリックBot)的なものもあるだろうけどつらい。
では、具体的な分析指標を羅列していきます。
・N日後継続率
・各コンテンツプレイ率(1日目にどのコンテンツをプレイしてなければならないか?)
・プレイ時間別UU数(1日目にどのくらいプレイしていないとならないか?)
・
・
・
ここで情報を整理します。まとめると、ここで考えるべきは4W2Hです。誰が(WHO)何を(WHAT)いつ(WHEN)どのように(HOW)どれくらい(HOW MUCH)・・・と。
どこで(WHERE)はアプリの想定利用用途によっては重要になる場合がありますが、スマートフォンゲームでは・・・そこまでかなと。データを取りにくいので。
そのように、どのようなプレイをされているかを把握する準備をしましょう。
なぜ準備するかって・・・?準備だけでいいです。もっと重要なことがあるので。
プレイヤーを増やすために重要なのは、アプリストアで見つけられる、認知されることです。ゲーム内容をブラッシュアップするよりも大事です。
結局、インストール人数=認知人数×インストール確率です
インストール確率を上げる努力をするよりも、認知する人数を増やす方が効果的なことが多いです。アプリ制作者が普通に出したアプリは、本当に認知されていないので・・・。
ここも分析の考え方(A/Bテスト)を使います。
アプリストアに訪問するユーザーに見つけてもらうために、キーワードや各種画像をいろいろなパターンで試します。期間を分けて出し分けて、どのパターンが良いのか記録します。
ちなみに、(1)アプリ検索で見つけてもらいやすくするには文字情報が重要で、その検索の中で(2)(競合の中から)選んでもらうには画像や動画が重要です。
(1)は検索はいまだに文字情報が重要です。
最新AIモデルであっても画像を読み取って正しく解釈することにしのぎを削っています。検索内容に反映はまだ難しいのではないでしょうか。
※2025年になりましたが、直近の生成AI(画像等対応)でもゲームバナーのような整理されていない情報では意味のある読み取りはほとんどできません。請求書のような整理された情報ならかなり精度が高くなりましたが、整理されてないとまだまだ厳しいですね…。
(2)は私のこれまでの経験からです。
画像を変えるだけでコンバージョン率(見た人のうち、インストールする人の割合)が変わる≒(インストールを)選ぶかどうかは画像に影響を受ける≒競合アプリとの比較もする人はするだろうから、競合アプリの中からインストールされるには画像は意味があるはずである、という話の流れです。
ただ注意したいことは、説明画像は置けばいいってものじゃないですし、読まれるのはごく一部だと思ってください。説明画像をGoogle Playで最高8枚並べられるのですが、最初しか見てもらえないと思った方が良いと思います。検索が1ページ目の上位3つくらいしか見られない事実のように、ユーザーはサクッと意思決定しちゃいます。そのため、売り文句は1枚目に入れましょう。
そのように、インストールまでの各ステップで落とし穴になりそうなところをくまなく分析・改善していきます。
ざっくり書いてもインストールまででこの文量・・・;つД`)
結局何が重要かというと、ステップのはじめの方を対策しましょうってことです。ゲーム内よりインストール時点、その前・・・。
ここもはじめの方を対策しましょう
特に新規登録は離脱ポイントになります。
・新規登録しないで良いゲームは新規登録させない
・新規登録ページを作らない(ワンクリック登録で済ませる)
・多くのデバイスでプレイすべきであるゲームはガンバレ…(メールアドレス登録、外部サイト連携)
ユーザー候補は面倒なことがあれば離脱します。
新規登録をした後はデータが取得可能になるため、細かいステップで分析できるようになります。
(ゲーム制作をしている中では数は少ないですが)事前ダウンロードが必要なゲームは、事前ダウンロードが離脱ポイントになりやすいので以下の対応を検討します。
・離脱率は許容できる値か
・許容できない場合は、下記対応ができるか
・チュートリアルを進めることができるようにする(のウラでダウンロード)
・PVを流す
・説明動画を流す
チュートリアルは上記の「おまけ:(1)コンテンツを一通りプレイしてもらいたいの分析方法」と同じように分析可能です。
チュートリアルは基本一本道ですし、移行割合(離脱率)も出せます。
ちなみによく聞かれるのはチュートリアル離脱ユーザーへの対策です。基本無理です。初日にそこでプレイ離脱するようなユーザーは何日待っても戻ってきませんし、毎日プレイするユーザーにはなりえませんでした…。
上記の通り、分析すべき所と打ち手は無数にあります。
そのため、分析しようと思えば何年でも分析し続けることが可能なので、ある程度やることを絞って重要なポイントだけ分析していきたいですね。
以降はよく使う考え方を紹介します。
「おまけ:(1)コンテンツを一通りプレイしてもらいたいの分析方法」の通り、ユーザーはステップを追うごとにどんどん離脱していきます。
この考え方をファネル分析・・・上流が大きくて下流が小さくなるファネルを参考にした分析方法です。イメージはググってみてください。
この考え方で、アプリ分析でやりたいことはだいたいが可能です。この考え方で対応が不可能なものは、できることが複数パターンあるなかで、どれが一番効果があるかです。それは次の決定木分析で分析できます。
決定木分析は、できることが複数パターンあるなかで、どれが一番効果があるかを分析する手法です。
継続率が高くなるユーザーはどのような行動をしているのかなんてことを分析してくれます。
・・・コンセプトはすごいんですよ?数理的にもしっかりできている。
否定的に書いている理由としては、そういうことを専門にしているコンサル(ワイも含む)がですね・・・継続率が高くなるユーザーはログインを良くしているみたいな当たり前の結果を示唆と言ったり、継続率が高くなるユーザーは課金額が多いなんてことも言われたことがあります。ワイはそ奴らに「課金をガッツリするような人間がすぐ離脱するわけないやろーが。無課金と比べんな」というツッコミを送ります。
そのように、決定木分析は当たり前の結果や異常値なユーザーを取ってきやすい分析です。それは要因でも何でもありません。
モデルや計算ロジックは悪くないんです。使い方がおかしいだけです。
使い方を正しくするには、仮説を持つ必要があります。
育成カードゲームであれば、「カード獲得」→「カード強化」や「装備獲得」→「装備強化」のようにどちらもプレイしておいてほしいコンテンツの流れがあると思います。それらが要因に出てくる必要がありますし、装備強化をノーマル武器からレアまでするなら5回必要みたいなレベルデザインが機能していることも決定木で確認できます。細かい手法はググってね。
そういえば分析記事(決定木分析)を書いてた→決定木分析(Python CHAID)を解釈する
そのように、少し高度な分析になると使い方がめっちゃ難しいので、基礎的な分析で対応した方が良いです…ホンマ(‘A`)
ちなみに、上記で出した例は決定木分析を使わずとも、「新規登録者のうちカード獲得枚数別の継続率」のような形で分析することが可能です。
なお、私の決定木分析の結果の読み方は、「最低限(要因A)を〇回プレイしていないと、ここまでしか継続率が上がらないのか…」です。決定木分析(継続要因分析)の要因は継続要因というより、離脱要因としてとらえています。最低限しきい値とでも言いましょうか。そうしないと希望的観測で語る人がいるので。
今回は、毎日プレイするユーザーが増えないよというお悩みに、新規登録の面から解説を行いました。
疑問点や質問、リクエストがあればコメント等いただけるとありがたいです。
以前から、Twitterなどでこんな悩みを見聞きしていました。
「ゲームアプリを運営する際に、どのような視点で、どのような指標を分析すべきかわからない」というものです。今回はそちらのお悩みに対して、仕事で使っている分析ノウハウをお伝えできればと思います。
確かに、指標だけでもたくさんあるし、専門用語もよくわからない。
私も初めて見た時思いました。「わけわかんねぇ・・・」と。
KGI、KPI、新規継続率、既存継続率・・・
初めて見る方ははじめまして。
総合コンサルティング会社でデータ分析を担当しているれいです。新卒から製薬企業品質管理、データコンサル、総合コンサル(分析メイン)など、データサイエンティスト、プロジェクトマネージャーなど職種の違いはありますが、データ分析メインでやってきました。
ゲームはソーシャルゲーム中心に受け持ちましたが、コンシューマーやプラットフォーム系、筐体系の分析も経験があります。それらの経験のエッセンスを抽出してお伝えできればと思います。
今回は、ゲームアプリ(特にソーシャルゲーム)でよく使っていた分析とその考え方をお伝えします。そのため、買い切りアプリなどには適用できない考え方も多くあると思います。
まずは、大きな視点からロジックツリーを使い、問題になっていそうな指標を発見します。コンサルティングの現場ではよくありますが、全体観がない状況で分析して目標数値を最適化も、結局全体でみると何も変わらなかったみたいなことになりがちです。
そのため、大きい視点からまず分析していきます。
以下のように、売上から見ることが多いです。
売上 → プレイ人数 × 一人あたり売上
出てきた指標をさらに分解します。
プレイ人数 → 新規ユーザー + 既存ユーザー
一人あたり売上 → 広告売上 + 課金売上
そして、時間軸を設定します。
(月別)売上 → (月別)プレイ人数 × (月別)一人あたり売上
(日別)売上 → (日別)プレイ人数 × (日別)一人あたり売上
あなたのアプリはどこに問題がありそうでしょうか?
売上がそこまで多くないアプリでは、プレイ人数、一人当たりの売上どちらも低いです。
そもそも売上がゼロ(0)!という方は登録ユーザー、プレイユーザーを増やしましょう。
これまで私が経験したソーシャルゲームを指標とすると、プレイ人数は数万~数百万の単位です。少なくとも1万人はいないとサービス終了ラインに入ってくると思います。
課金額は、新規ユーザーに限定すると、開始30日以内の一人当たり課金売上が1,000円あると「まあいけるかな」という感じです。
既存ユーザーの課金額はゲームやイベントによってかなり大きく変化します。課金者一人当たり1か月の課金売上で1万5,000円程度はあるアプリが多かったですね。
・・・自分のアプリとスケールが違いすぎる・・・!と思われた方も多いかもしれません。
そのため、次のステップでは、いかに1円でも収益を増やすかを考えるための視点を提示します。
色々な指標はありますが、まずはユーザー(プレイ人数)を増やすことが重要です。
プレイ人数は下記のように分解できます。
プレイ人数 → 新規ユーザー + 既存ユーザー
かつ、新規ユーザーが既存ユーザーになるので、まずは新規をきちんと取れるか(既存ユーザーに移行させられるか)ということを考えましょう。
ここで定義の確認です。
新規:登録初日のユーザー
既存:登録初日ではないユーザー
ちなみに初心者は定義をおろそかにしがちですが、定義を固めないと正しい事後比較ができません。思ってたんと違う・・・みたいな指標を分析していたりします。
登録初日から次の日以降に、いかにプレイしてもらうかが重要になってきます。
そのため、指標は新規ユーザーの新規登録1日後継続率を見ます。
新規登録1日後継続率とは読んで字の通りですが、1日と継続の定義を加味すると、
「登録の次の日(1日後)にログイン(継続)しているかどうか」を判定します。
計算式は、新規ユーザー1日後継続ユーザー数 ÷ 新規ユーザー総数です。
1日後・・・新規登録の24-48時間の中にログインしていた人とします。
ソーシャルゲームでは最低25%、良いゲームだと50%を超えます。
それ以下になるとユーザーが定着しづらいゲームとなるので、一人当たり課金額がかなり高くないとサービス終了秒読みになります。
そのため、目標値は最低30%としましょう。
指標が決まったら、カスタマージャーニーマップや、ユーザーの遷移図を使って問題点を発見→対処していきます。
そういったものがなければ、自分で新しくゲーム内アカウントを作って、新規登録1日以内に触れられそうな部分をリストアップし、イケてなさそうな部分を改修していきます。
ここで重要なのは、最初に触れるものが一番大事というファネル思考です。
ユーザーはゲーム内のコンテンツに触れるたびに、「もういいかな」となって離脱していきます。
そのため、初期のコンテンツであればあるほどユーザーの離脱に影響します。
そこ辺りを注意して問題点に対処していってください。
新規1日後継続率が改善したとしましょう。
次は3日後継続率、7日後、14日後・・・と少しずつ確認のポイントを後ろにずらして、離脱していないことを確認します。
前提としてソーシャルゲームは毎日プレイしてもらうことを想定しているため、そのような日数で判定しています。
もう少しタイムスパンが長いようなゲームだと、月間プレイ人数で分析することが多かったです。
継続ユーザーについては、月間ユーザーの次の月の継続率が8割を下回らなければ問題ないです。
下回っているようであれば、下記のような観点で分析します。
・継続しなかったユーザーはそれ以降復帰していないか?
・ログインしてもコンテンツをプレイしない人はどれくらいいるか?(ログボ勢)
・いつ、どれくらいの人数がログインしているか?減っているタイミングはないか?
・毎日のプレイ回数は想定より少なくないか?少なくなっていないか?
単純に人数が減っているように見えても、理由によっては対応可能です。
・飽きてプレイしない → 離脱
・その他
飽きてプレイしないのが一番対応しづらいので、プレイ回数がどんどん減っているなどがあれば対応が必要でしょう。
続いて重要な視点は、一人当たりの売上を増やすことです。
一人あたり売上 → 広告売上 + 課金売上
広告売上については、下記のように分解しましょう。
広告売上 → 広告単価 × エンゲージメント回数
エンゲージメント回数(表示、クリック等を達成した回数)を増やすか、広告単価を増やすかが考えられます。
エンゲージメント回数を増やすには、以下のように分解するとよいでしょう。
エンゲージメント回数 → 広告表示回数 × エンゲージメント確率
確率は増やせませんが、表示回数は増減することができます。
広告の表示回数を増やしたらユーザーが離脱しそう・・・?大丈夫です。
とある実験では、表示回数を現実的な範囲内で増減する場合は、継続率は変わらなかったそうです。
実体験でも、広告を減らしたところで継続率変わらず、収益だけ減ったという・・・orz
人の行動は意外と直感的ではないのです。
課金売上では、分析するポイントは複数あります。
・商品軸:売上 → 商品単価 × 購入数
・人軸:売上 → 課金額 × 課金者数
施策で注意してほしい点は少ないです。
・商品数を増やしても、売上が上がるとは限らない
(ユーザーは欲しいものしか買わない…別視点では選択肢が増えすぎると、逆に売上が下がるようなことも起こり得ます)
・安売りをすると、後々売り上げが下がる
(利益の先食いになってしまうようです…)
・限定商品はマジで売れる
(きちんと他商品と差別化していれば、不思議と売れます)
今回は「ゲームアプリを運営する際に、どのような視点で、どのような指標を分析すべきかわからない」について、ざっと確認しました。
ざっと確認というテイでブログを書いてみましたが、凄く・・・長いですね。
まずは、一つでも分析してみると、温度感や難易度が分かってくるかと思います。
ぜひお試しください(^^)/~
○○をどう分析すればいいかわからない、もっと深い分析(要因分析、需要予測など)がしたいなどがあれば、Twitter Rei_FarmsにDMいただければと思います。
お仕事の依頼も受け付けていますので、お気軽にお問い合わせください。
とあるきっかけから動画撮影・個別配信アプリを作りたいなということでいろいろなサイトで調べていました。
が・・・配信アプリ作成がうまく行かない。
例)
サーバー構築はubuntu上にlibnginx-mod-rtmpを使えば簡単にできますし、サーバーがあれば配信自体はOBS Studioでできます。
配信アプリだけエラーが出て、なぜかうまく行かない(理由がわからない)を解決していきましょう!
上記の例にある記事を参考に、サーバー側はローカルネットワークでライブ配信する(2020年11月11日)、アプリ側を【完全無料】Flutterを使って簡易ライブ配信アプリを構築する(2021.03.14に公開)で実装してみてください。
すべての作業を完了させ、アプリを実行させるとエラーが出てしまうと思います。そこからスタートです。
以下のパッケージをpub getします。それぞれのpackage page(例)で必要な設定を行うことを忘れないでください。
dependencies:
flutter:
sdk: flutter
# camera
camera: ^0.9.4+19 # カメラ
camera_with_rtmp: ^0.3.2 # 配信(RTMP)用
path_provider: ^2.0.9 # パスを通す
# video
video_player: ^2.3.0 # 動画確認用
wakelock: ^0.6.1+2 # 動画撮影中に画面をOFFにしない
camera_with_rtmpにエラーが出る場合はhttps://githubhot.com/repo/pinkfish/flutter_rtmppublisher/issues/36をご覧ください。
flutter_rtmppublisher/example/main.dartのコードをコピペし、lib/main.dartの中身をすべて書き換えます。
ベースコードは下記の通り記載されていて、内部の変数がNull Safetyに対応していません。
CameraController controller; String imagePath; String videoPath; String url;
そのため、下記のように変更を行います。
CameraController? controller; String? imagePath; String? videoPath; String? url;
その他のコードにも対応していない部分が複数存在するので、対応します。
まずは、lib/rtmp_publisherディレクトリを作成します。
続いて、そのディレクトリにflutter_rtmppublisher内のファイルをコピーします。

※こんな感じでディレクトリとファイルが存在しているようにします。
lib/main.dartの下記importを、
import 'package:camera_with_rtmp/camera.dart';
下記のように変更します。自分のパッケージ名については、適宜変更ください。
import 'package:自分のパッケージ名/rtmp_publisher/camera.dart';
上手くできたと思います。
アプリ上での再生はfijkplayer、PC上での再生はVLCなどをお使いください
再生はvideo_player(や派生したパッケージ)で可能とされていますが、試してみてうまくいったのはfijkplayerパッケージのみでした。
今回は動画配信アプリを作ってみました。それを通じて、パッケージの利用やパッケージの改善方法を知ることができたと思います。
うまく行かなかった原因は、Flutterのコーディングルールに2021年後半に大きな内容変更があったようで、後方互換性がなくなっていました。(ツライ…)
疑問点などがあればコメントをお願いいたします。それでは!
FlutterのGoogleアカウントでの認証(Authentication)のためにSHA-1キーが必要です。ただし、簡単に取得ができません。そのため、下記を参考にして進めます。
Google Auth公式
上記のページに、下記のコマンドをコマンドプロンプトに入力するように書かれています。
keytool -list -v -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore
ただし、以下のような表示が出て実行できません。
'keytool' は、内部コマンドまたは外部コマンド、 操作可能なプログラムまたはバッチ ファイルとして認識されていません。
このエラーは、「パソコンにkeytoolが入っていないので実行できないよ!」(意訳)ということですね。
keytoolはJavaJDKに入っているので、JavaJDKのインストールをすれば対応が可能です。
Windows:https://www.oracle.com/java/technologies/downloads/#java8-windows
ダウンロードにはOracleの登録が必要です。なぜ会社情報が必要なんだろう・・・。
その後、パスを通します。
デフォルトでは下記のディレクトリにkeytoolが存在するので、まずは周辺のディレクトリを探してみてください。存在を確認してからパスを指定してください。
C:\Program Files\Java\jdk1.8.0_321\bin
では実行します。パスワードのデフォルト値はandroidです。
keytool -list -v -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore
SHA-1の値をコピーして終了です!