SDL2.0 on Android 導入

すごくいいカンジのチュートリアル(HowTo: SDL on Android part 1 – Setup)があったので、翻訳してみました!(許可いただけました)。SDL2.0のリリースおめでとうございます!!

モバイル開発を始めたいと思ってたんだ。けど、どうやったらいいんだ?

まずとりあえずそこにある選択肢は、iOS(iPod,iPhone,iPad),Windows Phone(7,8),BlackBerry(BlackBerry, PlayBook)、そしてAndroid(市場のほとんど他のデバイス)、このどのプラットフォームを選ぶかだ。
僕の気持ちは、Androidの方に傾いていたんだ。Androidはほかのプラットフォームより少しはオープンだからね。それに、Kindle Fire HDが気になっていたんだ。そういうわけで、心を決めてKindle Fire HD 7"を購入した。これはかっちりとしたタブレットだけど、僕が作りたいのはゲームだ!

僕の経験と積みあがったコードはSDLのものだった。しかも都合のいいことにSDLの開発チームはモバイル環境でいい感じに動くように新しいSDLを開発してるっていうじゃないか。

もうそこに飛び込んでみるしかないよね。

取り組み型チュートリアルスタイルで僕が取り掛かったことや書きだしたコードを吐き出していこうと思う。

Android開発環境を作ろう

まず、最初のステップはAndroidの開発環境を整えることだ。

Androidでの開発は通常Javaで行われる。だけど僕達はこれからCとC++で書くためにJava Native Interface(JNI)を使う。開発環境を作る手順はそれほどヒドくないけど忍耐強さが必要かもしれないよ。

ただ単に公式ドキュメント(SDL/README-android.txt)に従っていけばいいんだ。
developer.android.comには導入方法と、ツール群のダウンロードホスト、設定方法などがある。

ADTには、SDKEclipseが含まれていて、これだけで何でもできるようになっている。
僕は普段はCode::Blocksを使っているんだけど、Eclipseを試してみるのもそんなにたいしたことではない。デバイスによっては更にすこし特別な設定が必要かも知れない、例えばKindle Fire HDとか。Amazonに詳細な情報がある。

どっちにしても、JDK(Java Development Kit)をセットアップする必要がある。公式の導入ではJDK 6を推奨していたと思う。ここで言えるのは OSが何bitで、JDKEclipseのバージョンがいくつかをはっきりさせておく必要があるってことかな。
Windows上で64-bit(x64)のものを(苛つく前に)動かすことができなかったので、32-bit(x86)のJDKと32-bitの ADT bundleを使うことにするよ。Linux上では問題は起きなかった。

セットアップできたら、Eclipseを開いてみよう。
そして、Window > Android SDK Manager を開こう。ここで、ターゲットにしたいAndroidバージョンのために必要な全ての構成部品をダウンロード・インストールできる。

まずとりあえず、Android SDK ToolsとAndroid SDK Platform-toolsは選ぶ必要がある。
Kindle Fire HDAndroid 4.0.3 (API level 15)なので、そのカテゴリを展開してSDK Platform, ARM EABI v7a System Image (エミュレータのため)を選ぶ。Kindle Fireは他にもいくつかのパッケージを必要とする(USB driver, device definitions, Android Support Libr/ary, etc. – 詳しくはthe Amazon page)。

これで、完全な Java開発環境 for Androidが君の手に入った!
すべてのJavaを使う人たちにバンザーイ! But I want more…

Setting up SDL2

SDL2.0をはじめる一番いい方法はMercurial(hg)を使うことだと思う。MercurialSDL開発に使われているバージョン管理ソフトだ(もちろんそれ以外のプロジェクトにも使われてる)。
しばらくごとに更新されるスナップショットもあるけど、hgを使うほうがいいと思う。(追記:SDL 2.0.0が公式リリースされました!!ここからダウンロードしてこれるよ。mercurialリポジトリも最新のbug fixのために使われてるのでどっちでも好きな方で。)
Mercurialを使えば周辺ライブラリも最新状態に保てる(SDL_image,SDL_ttf,SDL_mixer,etc.)。

まずMercurialを使えるようにして、SDLのtreeをcloneする:

hg clone http://hg.libsdl.org/SDL

もしもTortoiseHGを使ってるならCloneコマンドを使ってURL(http://hg.libsdl.org/SDL)を入れればいい。
SDL/README.androidAndroid portのための記述がある。

SDLはあなたの書いたネイティブコードを読み込むためにJavaのあれこれに気をかけるくれる、それにプロジェクトの雛形でまで備えている(とても使いやすい!)。 けど、それ以外にもまだ全てをリンクするために少しはしないといけないこともある。(それは、また後で)

Projectを作る

まずProjectのディレクトリを作る。これは
SDLフォルダ内のandroid-projectディレクトリをProjectを置きたいところにコピーして、好きな名前(プロジェクト名)にリネームして完成。
つまり、コピーしたディレクトリがProjectのディレクトリになる。それから、SDLディレクトリ全体のコピー(もしくはシンボリックリンク)をProject内のjniディレクトリの中に入れる。

コマンドラインでやるならこんな感じ:

cp -r SDL/android-project workspace/
mv workspace/android-project workspace/myProject
cd workspace/myProject/jni
ln -s ../../../../SDL ./
※jni下に置くSDLは、configureしていないクリーンな状態である必要がある。
  • 'jni'ディレクトリは、コンパイルとリンクするために必要な全てのライブラリを入れるところ。
  • 'src'ディレクトリは、書いたソースコード自体をいれるところ。

このjniディレクトリと、その下にあるsrcディレクトリがプロジェクトの中で一番大切な部分てことだ。

それらのディレクトリ以外にも、下の3つのファイルも重要

  • AndroidManifest.xmlAndroidシステムに向けてあなたのappの情報を記述してる。
  • jni/Application.mk は特殊な環境設定を
  • jni/src/Android.mk はNDKにコードをどうbuildするかを伝える。

Your Very Own Source

いよいよネーティブコードを書き始める時だ。

"Hello World"のためのソースファイルを作ってみよう。
jni/src/main.c(main.cpp もcoolだね)
そして次に
jni/src/Android.mkを開こう
LOCAL_SRC_FILES下のYourSourceHere.cをmain.c(とか名付けた名前に)変える。ここまでくれば、ndk-build(Windowsならndk-build.cmd)を走らせてうまくやれているかどうか確かめよう。

Projectのベースディレクトリ(頂点にあるディレクトリ)で

ndk-build

ndk-buildが見当たらない場合

NDK付属してきてるのでandroid-ndk-r8eなりにPATHが通ってるか確認してみて。
もう、NDKはダウンロードしてきたよね?
NDKディレクトリ(android-ndk-r8eとか)をPATH環境変数に追加しとけば楽だよ。さもなくば、ndk-buildへのfull pathを記述しないといけない。Windowsでは、コントロールパネル > System > Advanced System Settings > Environment Variables ..(セミコロンを使って要素を分けて…、ぶっ壊さないように慎重にね)

ndk-buildコマンドはSDLをコンパイルしてトラブルを起こさない(perfect world)!

ggkuronさんが試したときには、SDLがiconvを使うので、自前で準備する必要がありました。jni/SDL/includeにiconv.hを入れられればOK。

今のコードの結果を今書いているシステム(訳:エディタ?PC?) ではそのまま走らせることはできない、…のでプロジェクトの骨組みとかコードが大丈夫そうかどうかしっかり確認して、Eclipseで読み込もう。
File > New > Project...
Android > Android Project from Existing Code
を選んで、あなたのProjectディレクトリの頂点がどこにあるかを教え、それからFinishをクリック。
”Copy projects into workspace"オプションがあるだろうけど、僕にとってはややこしいといえる結果をもたらしたからたぶんすべきじゃないと思うんだ。

それが終わったら、Eclipse上にいい感じでProjectができてるはずさ。
緑色の"Run"ボタンを押してエミュレータや接続されたデバイス上でのテストしよう。

An Example Program

SDL 2.0の完全なチュートリアルをここに書こうとは思っていない。だから、ここにはデモを置いとこう。コードはここ

これは、まったく全て正しく動いたとしたらスクリーン上に何か画像を表示するだけのプログラムだ。

そのために画像を読み込む必要があるんだけど…。
Androidでは全てのデバイス上に読み込まれるデータを.apkにパックしないといけないので少しめんどくさい。それでも、SDL2にはいい機能がある。

どんなデータでも、Project内のassetsディレクトリに置くとSDLSDL_RWopsを使うとそこを見に行く。そうやってどういう類のデータ読み込める、だけどSDLにはもっといい機能がある。

SDL_LoadBMP()もassetsディレクトリを見にいくんだ。そのおかげでなんの苦痛なしに画像をロードできる。これはまさに今回のmain.c内で使える、動くはずだ。bmpファイルをassetsディレクトリに入れるのを忘れないでね。必要ならこれ使ってよ:

f:id:ggkuron:20130816093430j:plain

Important Settings in AndroidManifest.xml

動くようになったところで、もしかしたらProjectに名前を付けたいと思ったかも知れない。うん、もし違うProjectを作りたくなったらどっちみちユニークな名前が必要なんだ。やってみよう。

まず、ベースディレクトリにあるAndroidManifest.xmlから設定を始めよう。
AndroidManifest.xmlはappがAndroidシステムのためにProjectについて記述したものだ。appの名前と、特定バージョン向けの設定、パーミッション設定がここにある。Androidは特定のデバイスのための設定を判別して動かすこともできるのだ。

まず、個々のプロジェクトパッケージを判別させる。

AndroidManifest.xmlの上の方に、"org.libsdl.app"というpackage nameがあると思う。それをあなたのチームとプロジェクトを表すものに変える。(ドメインを持ってるなら、それを逆順にしたものを使うといいよ)

package名はappごとにユニークである必要がある。デバイスには同じpackage名のappは1つしか入れらない。GooglePlay上にも同じpackageのappは1つだけしか存在しないようになっている。

AndroidManifest.xmlの下の方に動いて、次に進もう

<activity android:name="SDLActivity"

"SDLActivity"をあなたの好きなように変えて(class名になる)。これは本当に好きなように(漢字とかはだめだよ)。 僕は、"MyTest1"にしてみるよ。
そうすると、同名のJava activity を作る必要がある。
sampleが見たいなら、SDL/README-android.txtを(ちょっと僕のバージョンと違う):

package com.dinomage.test1;
import org.libsdl.app.SDLActivity;
/*
 * A sample wrapper class that just calls SDLActivity
 */
  public class MyTest1 extends SDLActivity { }

このように、
packageと書いてる行をあなたのpackage名に
class name をあなたのclass名に変える。

これを、src/com/dinomage/test1/MyTest1.javaというファイルにして保存。

src/org/libsdl/app/SDLActivity.javaはそのまま。

AndroidManifest.xmlで、もうひとつの大切なのはactivity settingsの記述だ。ここに、

android:configChanges="orientation"

このattributeを追加すると、orientation change eventがクラッシュすることなく動くようになる。(すぐに直される類のbugかもしれない)

そして最後に、リソースの参照先が@string/app_nameのように記述されてる。
これは、resディレクトリ(res/values/strings.xml for @string)から引き出される。弄ることもできる。

Android.mkって何だ?

Android.mkはあなたのappのbuild processをコントロールするために使われる。手短にいうと、Makefileだ。
ここに、コンパイルフラグとかライブラリディレクトリとかincludeディレクトリが必要なら書き込む。

ひとつtipを。このようにすると、全てのソースファイルを引き込める。
jni/src/Android.mk

LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.cpp \
$(patsubst $(LOCAL_PATH)/%, %, $(wildcard $(LOCAL_PATH)/src/*.cpp)) \
$(patsubst $(LOCAL_PATH)/%, %, $(wildcard $(LOCAL_PATH)/src/*.c))

Application.mk

Application.mk はbuild環境のための設定が書かれている。僕のはこうなってる:

 APP_STL := gnustl_static APP_ABI := armeabi x86 

これで C++STLを使えるようになって、appをひとつはARM向けに、もうひとつはx86向けに2つ同時に構築できるようになる。
たぶん、デフォルトではarmeabi-v7a(NEONとThumb2サポート)も記述さているだろうが、僕が走らせてみたところAndroid4.0でbugを踏みぬいた。―armeabiとarmeabi-v7aを両方入れたところ、動的に上手くリンクされなかった。結果、荒れた。

Running ndk-build

ndk-buildはAndroidの"make"コマンドだ。
Projectのベースディレクトリで走らせると、ネイティブコードをすべてコンパイルして必要なバイナリを吐き出してくれる。
コマンドラインからndk-buildを走らせるか、Eclipseの外部ツールとして設定するといい。
adbを使って(コマンドラインを通してか、Eclipseの"Run"もしくは"Run As... Android application")でappをデバイスやエミュレータに に届けられる。バイナリはapkにラップされる。

もし、全部やり遂げたら、教えてくれ!

勝手追記

C++11を使う

TOOLCHAINでGCC4.7とかを使えるようにしておく

Application.mkに

NDK_TOOLCHAIN_VERSION=4.7

Android.mkに

LOCAL_CFLAGS += -std=c++11

std::to_stringが使えなかった思ひde