2018-02-05 00:38:03
DroidKaigi 2018 向け資料 - gomobile でゲームを作る

DroidKaigi 2018 では以下の枠で発表する予定。

DAY.01 (Feb 8th, 2018) 14:00-14:30 Room 7 - gomobile でゲームを作る
資料はココ → gomobile でゲームは作れる

発表内容についての徒然

DroidKaigi と言うからには Androider たちの集いであることは間違いないと思うんだけど、
さて、であればそもそも Go 言語が登場すること自体が結構レアな事態であり、ややもすれば
聴衆は Go 言語のこと全然知らないってこともありうるんじゃないかと想像したりしている。

ということに鑑みて、「Go とは何か」みたいなところから話題を始めようと思っているが、
だとすると 30 分ではあまり細かい話 (JNI で Toast 出すのが何故困難か、とか) まで踏み込めない構成になってしまい、
なかなか悩ましいところである。ニッチ枠であるならば、もっとニッチにニッチを煮詰めたような内容にしたいのであるが…。

ということで、ニッチにニッチを煮詰めたような内容はブログ記事に書いておき、
興味があったら見てね、みたいな感じにしとこうという結論に達した。
発表内容はライトに。リンク先はもう少し深く、みたいな。

DroidKaigi は、実は見に行くのも初めてなので、他のセッションを聞いて廻るのも楽しみである。

2018-01-29 08:41:12
gomobile build で JNI から Toast を出せなかった話

本日は 2018/01/29 であり、DroidKaigi 本番 (2018/02/08) までもう二週間もないところまできた。
来週の木曜日である。ヤバイ。準備捗ってない…。

とはいえ色々調査は進めている。
gomobile 関連で色々調べて分かったことを書いておく。

JNI だけで Toast を出すのは無理な気がする

Toast.makeText(this, "hogehoge", Toast.LENGTH_SHORT);

「Java でやるなら」、上のような書き方をすれば Toast は出る。実に簡単。

これと同じことを gomobile with JNI でやろうと思うと、以下のようなコードになる。
(下記はエラーチェックを省略している)

先に書いておくと、下記のコードは正しく動作しない。

package toast

/*
#include <jni.h>
#include <stdlib.h>

static int
showToast(uintptr_t java_vm, uintptr_t jni_env, uintptr_t jni_ctx, char* text) {
    JNIEnv* env = (JNIEnv*)jni_env;
    jobject ctx = (jobject)jni_ctx;

    // Toast クラスの取得 ... 1
    jclass toast = (*env)->FindClass(env, "android/widget/Toast");

    // makeText メソッドの ID を取得 ... 2
    jmethodID makeText = (*env)->GetStaticMethodID(env, toast, "makeText",
        "(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;");

    // String を生成 ... 3
    jstring jstr = (*env)->NewStringUTF(env, "hogehoge");

    // makeText メソッドを呼び出し ... 4
    jobject toastobj = (*env)->CallStaticObjectMethod(env, toast, makeText,
        ctx, jstr, 0); // 0 = Toast.LENGTH_SHORT

    // show メソッド の IDの を取得 ... 5
    jmethodID methodShow = (*env)->GetMethodID(env, toastobj, "show", "()V");

    // show メソッドを呼び出し ... 6
    (*env)->CallVoidMethod(env, toastobj, methodShow);

    return 0;
}
*/
import "C"

import (
    "fmt"

    "github.com/pankona/gomo-simra/simra/jni"
)

type t struct{}

func NewToaster() Toaster {
    return &t{}
}

func (t *t) Show(text string) error {
    jni.RunOnJVM(
        func(vm, env, ctx uintptr) error {
            ret := C.showToast(C.uintptr_t(vm), C.uintptr_t(env), C.uintptr_t(ctx), C.CString(text))
            fmt.Printf("ret = %d\n", ret)
            return nil
        })
    return nil
}

上記のコードは、「4」のところが正しく動作しないようである。toastobj には NULL が返却される。
いまのところ原因はよく分かっていないが、makeText の第二引数が CharSequence であることと、
そこに jstring を渡している、ということで、型があってないというのが原因なのではないかと想像する。

jstringjava/lang/String と同じものという理解でおり、であれば CharSequence として扱っても
良いのではないかという気がしないでもない (StringCharSequence のサブクラスなので) のだが、
事実、正しく動作していないところを鑑みるには、やっぱりダメなんじゃないかという気がしている。

Java のレイヤでは書ける以下のような例は、
JNI のレイヤでは表現することができないと思われる。

CharSequence cs = new String("hogehoge");

ちなみに、「AndroidNDKプログラミング第二版」には、JNI から Toast を出す例が掲載されていたが、
肝心の CharSequence に関しては Java のレイヤで生成されたものを JNI に引き渡す構成になっており、
もう少し原因究明に届かなかった。

(2018/01/31 追記)

上記は完全にウソだった。勘違いであった。申し訳ございません!

ちゃんとどのようなエラーが (4) で吐かれているか補足してみたところ、以下の例外が投げられていた。

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

つまり Toast を出そうとはしているようであるが、UI スレッド以外から呼び出すことはできない、ということで
Toast の出力に失敗しているようである。CharSequence は完全に無罪であった。すまん、CharSequence

さて、上記のエラーを解消するためには、Toast を UI スレッド上で実行する必要がある。
定番のやり方は Handler を用いるやり方である。例えば以下のようなやり方。

activity.runOnUiThread(new Runnable() {
    public void run() {
        Toast.makeText(activity, "Hello", Toast.LENGTH_SHORT).show();
    }
});

これを JNI で表現するにはどうすれば良いかを考えれば良い。
無名クラスとか出てきていてやっぱり無理なんじゃないかっていう気がしているが、
とりあえず試みてみようとは思う。

Java のレイヤに CharSequence をとらないメソッドを定義すればいけるが…

上記の原因の仮説を若干補強するものとして、
CharSequence を取らずに Toast を出すメソッドを Java レイヤに準備しておき、
それを JNI から呼び出すようなコードを書いた場合は無事に Toast が出力される。

以下のようなコードをあらかじめ Java のレイヤに書いておき、
それを上記の JNI と同様のノリで呼び出すようにする。

// static Activity goNativeActivity;
// onCreate にて goNativeActivity = this;

public void showToast(final String message) {
    Handler h = new Handler(goNativeActivity.getApplication().getMainLooper());
    h.post(new Runnable() {
        @Override
        public void run() {
            Toast.makeText(goNativeActivity, message, Toast.LENGTH_SHORT).show();
        }
    });
}

今回、DroidKaigi 向けには gomobile build を使って Go だけで Android アプリを書いてみようという試みであるので、
Java に手を入れていく手法は有り難いのであるが、追っての調査はまた別の機会にとっておくことにする。

2018-01-08 23:10:33
DroidKaigi 2018 に向けて (その2)

DroidKaigi 2018 に向けての準備を改めて整理した。
プレゼンで伝えたいことは、gomobile build で Android 向けゲームを作るときのTIPS集 みたいなことなので、
そこまで Kokeshi Scramble はフィーチャーしないほうがいいよね、と思いを改め。

伝えたいこと

  • まず、開発の進め方を紹介。
    • まず PC 版で作っていって → Android 上でテストする、みたいな流れ。
    • ついでに Mac 版、Windows 版、Linux 版も作れてお得、みたいな話もする。
  • gomobile build でいきなり APK 吐けて楽なのは良いものの、いざ作っていくとこんなハマり事があるよ、っていう紹介。
    • ハマり事の回避方法の紹介。
    • ハマり事をいちいち回避しなくても、うちのライブラリを使えばいいかもよ、という宣伝。
      あわせて、よそのライブラリも紹介。
  • おまけで、Kokeshi Scramble の話を入れておく。

みたいな感じの流れが良いかな、ということをうすらぼんやり考え。

本番までの作業

だいたい残り 1 ヶ月であり、作業に使えない日もあるだろうことを考えると、
作業に使える日数は 20 日程度と考えるべきかと思う。
1 日 1 〜 2 時間捻出したら 20 〜 40 時間。だいたい 3 〜 6 人日ってところだろうか。
ここに収まる中で準備を進めないといけないが、以下のような対応をしておきたい。

  • gomo-simra 拡張。以下の機能を加える (各々についてサンプルも追加) 。
    • アイコンを設定する (これは gomo-simra 拡張は不要の予定なので設定方法を記載する)。
    • トーストを出す。
    • SharedPreference を使う。
    • 指定の URL をブラウザで読み込む (広告クリック的な機能を想定) 。
  • 以下の改善も予定。
    • README.md をもう少し詳しく書く。使い方の例が分かるような。
  • その上で、スライドを更新。
    • 各ハマり事への解決策をコードを踏まえて紹介する。
  • あとは発表の練習。

トータルで 5 人日くらいになりそうな作業見積だがどうか。
Kokeshi Scramble の改善まではちょっと手が付けられないだろうという感じかな。

という感じで作業を何となくでも洗い出しておくと、いざ着手したときにスムーズに作業が進められるよね、という期待を込めて。
DroidKaigi、気合い入れていくぞ。

2018-01-06 23:09:05
DroidKaigi 2018 に向けて

DroidKaigi 2018 に向けての準備

DroidKaigi 2018 にはしゃべる側で参加する予定である。
以下、準備しておこうと思う内容のメモ。

  • ネタは gomobile でゲーム作った話。
    golang-tokyo #9 にて発表済みの内容 をリファインする。
  • DroidKaigi なので、主に Android 向けの開発について触れる。
  • gomobile build のハマりどころは、概ねの内容はそのまま使う。
    • インテントを投げる、トーストを出す、アイコンのセット、あたりは新たに実際に動くコードを示すようにする。
    • 特に SharedPreference 使ったデータの保存を盛り込む。
      (これができるようになるだけでだいぶ開発しやすさが変わると思われる。)
  • 引き合いに出す予定の Kokeshi Scramble は、できれば Google Play にあげとく。
    • できればもう少し遊べるようにしとく (ゲームバランス的な意味で) 。

DroidKaigi までに行う Kokeshi Scramble の改修は以下を想定。

  • gomo-simra の最新版に追従する。gomo-simra の API を変えてしまったので頑張って追従する。
  • 召喚可能なユニット数に上限を設ける。
  • もっと体力を多めにしてユニット一体一体を大事に扱うゲームデザインにする。
  • もう少しプレイヤーに介入させるべく、ドラッグアンドドロップによるユニットの移動を導入する。
  • そもそもちゃんと Android 端末上で動くようにしとく…。

その他、可能であれば行いたい細かい点の改善。

  • ユニットの攻撃モーションを作る。剣であれば振る、魔法であれば詠唱から発動、など。
  • 敵の残数を表示する。現状はいつまで続けていいか分からないので。

DroidKaigi までもう残り一ヶ月程度。
コツコツやっていくぞー。今年も頑張るぞー。