数独サイト

「数独」でぐぐった場合

234.0KPV/月、今日の数独20問

154.6KPV/月

224.9KPV/月

509KPV/月

参考:http://vivi.dyndns.org/ は 132.1KPV/月

 

 

2016年11月までの広告収入

■ 現状分析

下図はここ3年間の月ごとの(web+アプリ)広告収入の推移グラフ。

temp3

具体的な金額は公開していないが、今年の月々の収入は大学生のアルバイト程度で、サラリーマンの給料程度には届かず(しかもボーナスも無い)、広告収入だけで暮らしていけるレベルではない。
しかし、見てわかるように、前年比約3.5倍、指数関数的に増加しているので、このペースが続けば、1年後あたりには広告収入だけで暮らしていけるレベルになると期待している。
もちろん、今後もこれまでのようなペースで収入が増加していくという保証はまったくない。今後の開発、マーケティング活動、運次第である。

ちなみに、増加している総収入の推移は成長曲線に従うと考えられる。具体的にはロジスティック曲線だ。最初期は増加していることがわからないほど徐々に増加し、ある時点から指数関数的に急激に増加し、変曲点を経て導関数が減少し、上限に近づいていく。
毎月の収入は総収入を微分したものなので、初期の頃は指数関数だが、変曲点のところがピークになり、そのあとは徐々に減少していく。微分方程式で記述すれば dN/dx = rN(1-N/K) となる。

下図は月々の広告収入をwebコンテンツ(AdSense)とアプリ(AdMob)に分けたグラフ。

temp4

webコンテンツによる収入は多少の上下はあるが、おおむね緩やかに上昇している。
広告配信を行う無料アプリは、2014年8月からリリースを開始した。初期の頃は地をはうような収入であったが、アプリ数が増加し、また後で詳細に述べる「AIホールデム」のダウンロード(以下、DLと略す)数増加に伴い、2015年12月にはアプリ広告収入がwebコンテンツ収入を上回り、11月にはその月の総広告収入の約80%を占めるまでに至っている。

下図はアプリ収入の大部分を占める「AIホールデム」の毎月のDL数の推移だ。

temp

リリース直後はほとんどDLが無かったのが、2015年11月末に日本のTV番組(「1年の半分をカジノで過ごす東大卒の男」)に2012年「ポット・リミット・オマハ・シックス・ハンデッド」世界チャンピオンの木原直哉氏が出演してテキサスホールデムが紹介され、それにより日本からのDL数が一気に増えた。おいらはこれを「木原インパクト」と呼んでいる。

さらに、2016年8月中旬以降、DL数の伸びが急激に増えた。その理由ははっきりしていないが、Google Play にてブーストが効きづらくなったことが主な要因ではないかと考えている。

下図は、月々のDL数をUS・日本・その他の国に分けたもの。

temp

日本からのDL数は、「木原インパクト」の効果が徐々になくなってきていることにより徐々に減少していたが、8月が底で徐々に上向いてきている。
8月中旬から、世界各国からのDL数が急激に増えている。前述した様に、その理由は不明だ。

下図は「AIホールデム」の月々の広告収入をUS・日本・その他の国に分けたもの。

temp2

US, その他の国々からの収入は指数関数的に増加している。基本的に広告収入はDLが数に比例するのだが、日本・US以外の国々はクリック単価が低く、そのためにUSからの収入が最も多くなっている。

下図は8月中旬以降のアプリDAU(日々のアクティブユーザ数)のグラフ。

temp3

広告収入は基本的にDL数に比例すると書いたが、DLされてもすぐにアンインストールされたものは収入には結びつかない。また、アンインストールされなくても、ストレージに眠っているだけでも意味がなく、日々使用される(広告をクリックしていただく)ことが大事だ。そういう意味で、DAUは広告収入にダイレクトに結びつく重要な要素である。

8月中旬以降、DAUはほぼ線形に増加していて、11月に入ってから線形回帰の傾きが倍増している。これは、たんなるゆらぎかもしれないが、最近ヘルプ機能を徐々に実装してきた成果かもしれないと思っている。

下図は「AIホールデム」の1日ごとのDL数の推移グラフ。マッチ棒のようなマークは、アップデートを表す。

temp

一般的に、アップデートを行わないと継続的なDLが行われないと言うが、このグラフを見る限りでは、アップデートとDL数増加の明らかな因果関係は認められない。
むしろ、DL数が増加すると、開発意欲が湧き、アップデートしているような現状になっている。

が、アップデートが製品寿命を延ばす効果があるのは明らかであるので、今後も定期的に行っていくつもりだ。

■ 教訓

  • 中身の時代
    • ユーザ、スポンサー、場の提供者全員に対して有益か?
  • Google を介した口コミ (※ ユーザの積極的行動が不要)
  • プレイ時間の長期化・最大化を狙う
  • 以下の戦略が有効
    • 入り口を優しく
    • やりこみ要素
    • オンラインランキング(他のプレイヤーとの比較・競争)
    • トレーニング要素?
    • 実用性?

■ 今後の方針

  • 「AIホールデム」に約半分の工数をつぎ込み、ヘルプ・機能の充実、品質向上などを継続的に行う。
  • 新規アプリリリース
    • パズル系
    • ギャンブル系
      • バックギャモン
    • ツール系
  • 古いアプリのアップデートも行っていきたい。
    • 特に、古いアプリは見た目が悪く、ヘルプも皆無なので、そのあたりに工数をさきたい。

■ 今後の希望的予測

  • 日々のDL数の増加傾向はあと数ヶ月は続く
  • 「AIホールデム」累計DL数:2016年末は5万弱、2017年末は20万本~30万本
  • 広告収入の増加率は3倍/年が続く
  • 2017年中にウン10年前の大卒初任給を超える
  • 現在の大卒初任給並になるのは2018年以降

【cocos2d-x v3】 RadioButton の使い方

RadioButton クラスは v3.8 で追加された比較的新しいクラスだ。

  • RadioButtonGroup オブジェクトを生成し、子ノードとしてシーンに追加
  • 選択肢の分だけ、RadioButton を生成し、RadioButtonGroup オブジェクトに 追加し、シーンへも子ノードとして追加する
  • RadioButton オブジェクトにイベントリスナーを指定する
// Create a radio button group
auto radioButtonGroup = RadioButtonGroup::create();
this->addChild(radioButtonGroup);

// Create the radio buttons
for(・・・) {
        RadioButton* radioButton = RadioButton::create("cocosui/radio_button_off.png", "cocosui/radio_button_on.png");
        float posX = startPosX + BUTTON_WIDTH * i;
        radioButton->setPosition(Vec2(posX, winSize.height / 2.0f + 70));
        radioButton->setScale(1.2f);
        radioButton->addEventListener(CC_CALLBACK_2(LabelToggleTypeTest::onChangedRadioButtonSelect, this));
        radioButton->setTag(i);
        radioButtonGroup->addRadioButton(radioButton);
        this->addChild(radioButton);
}

【ToDoアプリ】新規項目の追加方法

ToDoアプリの新規項目の追加方法について考えてみる

以下の2方式が考えられる

  • 普通のエディタのように改行すると行頭に「□ 」が表示され、そこに項目テキストを入力する
  •  追加ボタンや新規項目メニューを実行すると、ダイアログがでて、そこで項目テキスト、オプション情報を入力し、【OK】を押す

Google Keep が前者の方式、Trello が後者の方式だ。
前者の方が項目を続けて入力するのは楽だが、オプション情報の設定方法を別途用意しなくてはいけない。

コマンドモードを用意するのであれば、ダイアログを表示するものとそうでないコマンドを用意すればいいかもしれない。

最低限動作するものをまず実装してみるという方針なので、前者をとりあえず採用してみる。

 

【自分用メモ】ToDo アプリを作りたい

現状:

  • テキストエディタで1項目=1行で管理している
  • 状態、オープン日付、クローズ日付、適用バージョン、項目テキスト を記述
  • 最小粒度、より大きな粒度の項目が同じレベルで記述されている
  • 項目はオープン日付でソートされている
  • プロジェクトごとにToDoのテキストファイルを作成し、編集・閲覧している

例(ハーフギャモン):

◎ 09/24 09/24 0.001 AI が3ゾロで全部のサイの目の移動ができないと、クラッシュする
● 09/24 ゴールしたら、バーにある相手の石をヒットしたようになった
● 09/25 AI サイコロがゾロ目の時、サイコロが4つ表示されない
● 09/25 バーに複数石がある場合は、個数を表示するかずらして表示する
◎ 09/25 09/25 0.001 石移動時のZオーダが大きくなっていない?
◎ 09/25 09/25 0.001 バーの左右にハイライト・影を描画

現状の問題点:

  • 状態変更、日付の入力を手編集で行っているので、非効率である
    • ViVI, さくさくエディタでは Alt+E, I, 2 で日付を入力できるが、それでも手数が必要
  • 異なる粒度のものが同列に並んでいて分かりづらい
    • → 階層化したい
  • 優先順位、重要度、カテゴリ、タイプ情報が無い
    • 項目に追加してもいいのだが、編集が面倒だし、項目が多くなりすぎると視認性に劣る
  • Windows 上にテキストで置いているので、携帯端末から参照・修正しづらい
    • Dropbox上に置けば条件は満たすのだが、現状はそのような運用は行っていない

ToDoアプリを作りたい

  • 専用アプリを開発し、効率的にToDo管理を行いたい
  • Qt または cocos2d-x で開発?
    • cocos2d-x で本格的なツールを開発可能か? または妥当か?
    • Andoroid 版を Qt で開発するのは妥当か?
  • 日付の自動入力、わかりやすい表示、状態変更、統計機能など、自分の欲しい機能を実装しほうだい
  • Android 版は無料+広告付き and 有料版?
  • WIndows 版でも同様?
    • Webビューを使いweb広告を表示すればおk?
  • 最低限の機能のものをまず作り、それを開発に使用しながら機能追加・使い勝手向上

実装したい機能:

  • タスク追加
    • 自動的にオープン状態に
    • オープン日付自動設定
    • 優先順位(A-E)、重要度(A-E)、タイプ、タグ、期限などを設定可能
  • 階層化
    • 複数のタスクを「カード?」としてまとめる?
    • 複数のカード?を「プロジェクト?」としてまとめる?
    • 3階層固定でおk?
    • trello 的な表示、全体・部分を一覧表示、マインドマップ的表示?
  • 表示
    • 「□ タスク一覧を表示」のように、チェックボックスとタスク項目は必須
    • 優先順位により背景色を変える?
    • 重要度別にアイコンを表示?
    • ステータスにより表示位置を変える
    • どの項目を表示するかを選択可能
    • デフォルトではオープン項目だけが、優先順位順に表示される
    • ボタンで、ソート方法を変更可能
    • 「履歴」を表示・非表示
    • カード、プロジェクト一覧ではタスクの統計情報を表示
  • 検索・絞込?
  • 編集
    • 項目は普通に編集可能(入力、削除、コピペ、カーソル移動)
    • 先頭のチェックボックスをチェックすると、状態がクローズになる(日付時刻は自動付加)
    • 末尾のゴミ箱アイコンをクリックすると項目削除
    • マウスによる位置移動(優先順位を自動的に変更?)
    • (メモリの許す限り)無限undo
      • 終了すると undo 情報を消去される?

実装方針:

  • まずは最低限のものを数週間?で実装してみる
    • 階層構造無し
    • 状態(オープン、クローズ)、項目テキストのみ?
  • とりあえず Windows版を Qt を使って実装?

アプリの広告表示回数

temp

上図はここ1年のアプリの1日ごとの広告表示回数。
広告収益=広告表示回数*クリック率*クリック単価 なので、アクティブユーザ・プレイ時間を増やし広告表示回数を増やすことが広告収益を増加させることに繋がる。

で、国ごとの収益をみると、最も多いのが日本で67%、ついでUSで22%、3位はオーストラリアで2%となっている。

temp2

上図は日本だけの広告表示回数。

temp3

上図はUSだけの広告表示回数。

Inkscape でギア(設定)アイコンを作ってみる

Inkscape で、★型と3つの同心円を論理演算してギア(設定)アイコンを作れるという画像(下図)をフェイスブック・ツイッターに投稿したところ、思いの外反響があったので、その具体的な方法を解説しておく。
temp
なお、この方法の元ネタは Inkscape Tutorial – Rusty Gear なので、興味がある人は参照してね。

■ 図形の論理演算

ギアの作り方を理解するには、図形どうしの論理演算について理解しておく必要がある。
ちょっと理解しづらいかもしれないので、実際に操作をし、自分の目で確かめてみることを強くおすすめする。

temp

まずは、上図のように矩形と円を用意する。

temp

そしてそれを上図のように一部を重ねる。
これで、2つの図形の論理演算の準備が整ったので、実際にやってみよう。

temp2

重ねた2つのシェイプを選択し、パス>結合(Union) メニューを実行すると下図のようになる。

or

結合(Union)は論理演算の OR(論理和) に対応している。
論理和を理解している人であれば、結合結果の形がこのようになるのは自然に受け入れられるだろう。

結果の図形が赤になったのは、赤い矩形が青い円よりも(Zオーダーが)下にあるからだ。
下図のように、Zオーダを入れ替えると、結果の色が青になる。

or2

同様に、パス>交差(Intersection)を実行すると下図のようになる。
論理演算の AND(論理積)に対応している。

and

次は パス>差分 を実行した場合。上にある図形で下にある図形をくり抜くイメージだ。

diff2

上下関係が逆になっていると下図のように結果も違ってくる。

diff1

以上が、ギアアイコンを作るために必要な図形の論理演算だ。
これらが理解できていないと、具体的な操作方法を聞いてもまったく理解できないので、しっかり理解するようにしよう。
先にも書いたが、実際に自分で試してみて、体で覚えることが肝要だ。

演習問題:

  1.  図形を3つ選択し、論理演算を行うとどうなるかを試してみなさい。

■ ギアアイコンの作り方

まずは、下図のような材料を用意します。

gea0

最初のは頂点数を8にした★型シェイプ、あとは半径が異なる真円3つだ。
あとは、これらを論理演算していけばギアアイコンができあがる。
くどいようだが、前節の説明を理解してないとチンプンかんぷんなので、よくわからなくなったら、前節を読み返し、実際に試しててね。

gea1

最初に、星形の上に一番大きい円を中心を合わせて重ね、パス>交差 を実行する。
これで、星形のとがった部分が切り落とされる。

gear2

次に2番めの大きさの円を中心を合わせて重ね、パス>結合 を実行する。
ギアの根本が埋まる。

gear3

最後に、3番目の円を中心に合わせて重ね パス>差分 を実行する。
ギアの演習部分だけが残る。というわけだ。

演習問題:

  1.  星形、円の半径を変えて、いろんなバランスのギアアイコンを作成してみなさい。

■ まとめ

基本シェイプを論理演算で組み合わせることで、思いもかけない図形を作り出すことができるぞ。

【自分用メモ】MenuItemImage

MenuItemImage ▷ MenuItemSprite ▷ MenuItem ▷ Node

MenuItemSprite は、_normalImage、_selectedImage、_disabledImage : Node* をメンバ変数に持つ。
それぞれが 通常、選択(押下)時、ディセーブル時の画像だ。
通常は _normalImage が setVisible(true) で表示されており、他は setVisible(false) で非表示となっている。

ボタンが押下されると MenuItemSprite::selected() が呼ばれ、_normalImage は非表示にされ、代わりに _selectedImage が表示される。
ボタンが離されると MenuItemSprite::unselected() が呼ばれ、表示が元に戻される。

cocos2d-x Android版にクリップボード コピペ機能実装

cocos2d-x で開発中の関数電卓の Android 版にクリップボードのコピペ(copy&paste)機能をちょいと苦労して実装したので、それを記述しておく。

コピペ機能はフレームワークが用意していてもいいと思うのだが、cocos2d-x はツール開発に使えないことはないもののメインの用途はゲームなので、コピペ機能をサポートしていなくてもいたしかたないところだ。
なので、自分で実装しなくてはいけない。

手順は以下のようになる。

  1. Java で copy, paste を行う関数を実装する
  2. JNI を用いて上記関数をコールするインタフェースを実装する

1. Java で copy, paste を行う関数を実装する

“AppActivity.java”:

public class AppActivity extends Cocos2dxActivity {
    private static ClipboardManager s_cm;
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState); 
        s_cm = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
    }
    public static void setClipboardText(String text) {
        s_cm.setText(text);
    }
    public static String getClipboardText() {
        String text = s_cm.getText().toString();
        return text;
    }
}

クリップボードにテキストを設定する関数は setClipboardText(String text) : void, クリップボードに入っているテキストを取得する関数は getClipboardText() : String とした。
クリップボード処理を行う方法はいろいろあるようだが、上記のコードが一番簡単で、動作確認できたので、採用することにした。
クリップボードマネージャを関数内で取得しようとすると、ビルド時にエラーとなってしまったので、onCreate() でスタティック変数に格納したものを参照するようにしている。

2. JNI を用いて上記関数をコールするインタフェースを実装する

“InterfaceJNI.h”:

class InterfaceJNI
{
public:
    static void setClipboardText(const std::string &text);
    static std::string getClipboardText();
};

上記はインタフェースの定義。InterfaceJNI クラスを定義し、set/get 関数をスタティック関数として定義している。
引数・返り値の型は std::string とした。

“InterfaceJNI.cpp”:

#define JNICLASSNAME "org/cocos2dx/cpp/AppActivity"

void InterfaceJNI::setClipboardText(const std::string& text){
    JniMethodInfo methodInfo;
    if(JniHelper::getStaticMethodInfo(methodInfo , JNICLASSNAME , "setClipboardText", "(Ljava/lang/String;)V")){
        jstring str = methodInfo.env->NewStringUTF(text.c_str());
        methodInfo.env->CallStaticVoidMethod(methodInfo.classID , methodInfo.methodID , str);
        methodInfo.env->DeleteLocalRef(str);
        methodInfo.env->DeleteLocalRef(methodInfo.classID);
    }
}
std::string InterfaceJNI::getClipboardText(){
    std::string text = "N/A";
    JniMethodInfo methodInfo;
    if(JniHelper::getStaticMethodInfo(methodInfo , JNICLASSNAME , "getClipboardText", "()Ljava/lang/String;")){
        jstring jstr = (jstring)methodInfo.env->CallStaticObjectMethod (methodInfo.classID , methodInfo.methodID);
        const char* ptr = methodInfo.env->GetStringUTFChars(jstr, NULL);
        text = std::string(ptr);
        methodInfo.env->ReleaseStringUTFChars(jstr, ptr);
        methodInfo.env->DeleteLocalRef(methodInfo.classID);
    }
    return text;
}

上記がNJIを用いて Java 関数をコールする部分の実装。
JniMethodInfo methodInfo; を定義し、それの getStaticMethodInfo() 関数を用いて、名前、引数・返り値シグニチャで指定されるメソッドを探す。
メソッドが見つかった場合は CallStaticVoidMethod() または CallStaticObjectMethod() を用いて、見つかったメソッドをコールする。
前者は、std::string を Java の string に変換し、第3引数として渡している。
後者は返ってきたオブジェクトを jstring にキャストし、そこから文字列アドレスを取り出し、std::string に変換し返している。
また、DeleteLocalRef() で不要になったメモリを開放している(?)。

cocos2d-x v3 への sdkbox admob 組み込み

  • プロジェクトディレクトリで、「sdkbox import admob」 を実行し、AdMob モジュールを組み込む
  • Resources/sdkbox_config.json をエディタで開き、広告IDを修正し、”test”: true, 行を消す
    • 修正しない場合は、テスト用広告が表示されるようだ
    • “test”: true, 行が残っていると、広告IDを変更してもテスト用広告が表示される
  • 広告表示コードをレイヤーに記述する
    • 広告をキャッシュして、表示すればいいのだが、init() で cache(), show() をコールしてもうまくいかなかった
    • init() では cache() のみをコールし、update() 関数内で、広告が有効になったことを確認してから、show() をコールするといいようだ
bool HelloWorld::init() {
    ....
    m_adShowed = false;  //  表示済みフラグ
    ....
    sdkbox::PluginAdMob::cache(kHomeBanner);
    scheduleUpdate();	//	for update()
    return true;
}
void HelloWorld::update(float delta)
{
    if( !m_adShowed && sdkbox::PluginAdMob::isAvailable(kHomeBanner) ) {
        sdkbox::PluginAdMob::show(kHomeBanner);
        m_adShowed = true;
    }
}