【自分用メモ】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;
    }
}

 

7月に入って、広告収益が激減

temp

上図は7月に入ってからの日々の広告収入のグラフ。
見ればわかるように、7月に入ってから収益が激減している。
これでは、年末までに収益倍増は望めそうもない。困ったものじゃ。

このような場合は、原因を推測し、適切な対応を行わなくてはならない。
5、6日 はいったん収益が回復したので、収益が減ったのはUSの3連休のせいかと推測したのだが、その後の推移をみると全然関係なかったようだ。

temp

上図は日々の広告クリック数のグラフ。
クリック数は先月とくらべてほとんど変わっていない。

収益=クリック数*クリック単価 なので、7月に入って収益が激減しているのは、クリック単価が激減したからだとわかる。

クリック単価はもろもろの社会情勢で決まってくるので、おいらの方でどうこうできるものではない。
したがって、収益が減っているのはとりあえず気にせず、アプリのDL数・クリック数を増やして、クリック単価が増加することを期待するしかありませんな。

 

「AIテキサス・ホールデム」DL数の(希望的)予測

h1

上図は、おいらのアプリの中でDL数稼ぎ頭である、「AIテキサス・ホールデム」の日々のDL数の推移グラフだ。概ね安定しているが、何度か平均DL数が変化している箇所がある。2015年11月末は原因が明確で、TVでテキサスホールデムが紹介され、それにより日本でのDL数が急増した。2015年7月、2016年4月にも急増しているが、これらの原因は定かではない。

h2

上図は日本からのみのDL数のグラフだ。前述の理由で2015年11月にDL数が急増してからは、徐々にDL数が減少しているように見える。にもかかわらず、全世界でのDL数が減少していないのは、日本以外の国からのDL数が増加しているからだ。

h3

上図はUSからのDL数の推移。2015年7月頃は平均1DLあるかないかだったのが、半年後の2016年1月頃は3DL程度に、さらに半年後の2016年7月には10DL程度になっている。つまり半年で約3倍のペースで増加しているということだ。
実数で見ても、以前は日本からのDL数が50%以上とダントツだったのが、2016年6月頃からはUSのDL数が日本のそれを上回る日が多くなっている。

というわけで、今後の予想だが、日本からのDL数は、(積極的なアップデートを行わなければ)今後もゆるやかに減少していくが、日本以外からのDL、特にUSからは半年に3倍程度のペースで増加していくと(希望的に)予測される。
2016年7月初め現時点での1日のDL数は40~50、日本からのそれは 5~12なのでこれが半減しても 2~6 にしかならない。日本以外は倍増すると(希望的)予測されるので、28~45 が倍増して 56~90DL/day と予測される。

さて、2016年末にはどの程度になることやら。

SDKBOX を使って cocos2d-x プロジェクトに AdMob を追加

SDKBOXを使うと、cocos2d-x プロジェクトに AdMob を簡単に追加できるみたいだぞ。
オリジナルの解説は AdMob Integration Guide を読んでね。

プロジェクトに AdMob を組み込む

SDKBOX がすでにインストールされていれば、プロジェクトに AdMob を組み込むのは簡単だ。
コマンドプロンプトをひらき、プロジェクトのディレクトリに行き、以下のようにタイプするだけだ。

$sdkbox import admon

これだけで、AdMob のためのコードや設定がプロジェクトに追加される。

コードの追加・設定変更

上記だけでは、まだ AdMob 広告が表示されるわけではない。以下の様にコードの追加や設定変更が必要だ。

設定ファイルの修正

project/Resources に  sdkbox_config.json が追加されているので、それをテキストエディタで開く。
“ca-app-pub-XXXXXXXXXX/XXXXXXXX” という広告IDがあるので、それを適切なものに置き換え、
(後でコードから参照するための)広告シンボル名、バナー広告のサイズ位置を設定する。

        "AdMob": {
            "test": true, 
            "ads": {
                "gameover": {
                    "type": "interstitial", 
                    "id": "ca-app-pub-XXXXXXXXXX/XXXXXXXX"
                }, 
                "home": {
                    "width": 300, 
                    "type": "banner", 
                    "id": "ca-app-pub-XXXXXXXXXX/XXXXXXXX", 
                    "alignment": "bottom", 
                    "height": 50
                }
            }
        }

コードの修正

ソースコードに SDKBOX 関連のコードを記述する場合、ソースに以下の分を追加しよう。

#ifdef SDKBOX_ENABLED
#include "PluginAdMob/PluginAdMob.h"
#endif

初期化:

bool AppDelegate::applicationDidFinishLaunching() {
#ifdef SDKBOX_ENABLED
    sdkbox::PluginAdMob::init();
#endif
    // initialize director
    :
    :

キャッシュ:
自動的にはキャッシュされないので、表示する前にかならずキャッシュすること。

sdkbox::PluginAdMob::cache("home");
sdkbox::PluginAdMob::cache("gameover");

表示:

sdkbox::PluginAdMob::show("home");
sdkbox::PluginAdMob::show("gameover");

ここ2年間の広告収益の推移と今年後半の方針・目標

早いもので、もう2016年も前半が終わってしまった。
というわけで、これまでを振り返りつつ、今年後半の目標とかを述べてみたい。

ここ2年間の広告収益の推移

g

上図は一ヶ月ごとの広告収益(合計(黄)=webコンテンツ(緑)+スマホアプリ(赤))のグラフだ。
※ Y軸は金額だが、具体的な金額はナイショ ;^p
おかげさまで、2016Q2の収益はQ1の約1.5倍、前年Q2と比べると約4.4倍にもなった。
まさしく倍々ゲームで増えていて、非常に嬉しい限りだ。

このように指数関数的に収益が増えている原因としては以下が考えられる。

  • 昨年11月末に「テキサスホールデム」がTVで紹介され、「AIホールデム」のDL数が飛躍的に増えた
  • 今年に入ってから、おいらの中で人気があるアプリを iOSに移植し、リリースした
  • 3月にインテ広告を導入した

上記はうまくいっている要因だが、今年に入ってからは3本しかアプリをリリースしておらず、しかもそれらのDL数は全然伸びていない、というネガティブな要因もある。

webコンテンツとスマホアプリの収益を比べると、以前は前者の収益が多かった(というか、スマホアプリの収益が少なすぎたのだが)のだが、去年12月に逆転し、現在では後者が前者の2~3倍にもなっている。

各アプリの6月のアプリ広告収入に占める割合をみると、ホールデム:約50%、数独:約14%、さくさく将棋:約12% となっている。DL数的にはホールデムは数独・さくさく将棋の約10倍もあるのだが、収益的にはそこまでの差はついておいらず、健闘しているとも言える。これば、ホールデムは日本以外でのDL数が多く、日本・欧米以外ではクリック単価が 1/10程度なことが一因だと考えている。

今年後半の方針

  • 数(3~6)本のアプリをとりあえずリリース
    • 関数電卓
    • 詰将棋
    • ハーフギャモン
    • パズル・ボードゲーム・ツール? etc…
  • 上記の中でDL数の多いものをアップデート
  • 数独・ホールデムのアップデート

今年後半の目標

  • 12月の合計収益が6月の約2倍