2013年8月8日木曜日

UbuntuにGitをセットアップしてWindowsから接続する

自前のGitサーバが欲しくなり設定したのでその方法をまとめる。今回の環境は以下。

・Gitサーバ
Ubuntu 12.04.2 LTS

・Windowsクライアント
Windows 7 64bit


Ubuntu認証用のSSHキーを用意する
WindowsでSSHキーを用意するにはPuTTYgenを使ったりする方法もあるけれど、git bashからもさくりと作れるので今回はそちらで。git bashをまだインストールしていない場合はGit for Windowsをインストールしよう。

インストールできたら「Git Bash」があるのでそちらを叩いて起動する。初期作業位置はc:\Users\your nameかc:\Users\your name\Documentsなどになっているはずである。さっそくSSHキーを下記のコマンドから生成する。
※your nameは自分のユーザ名で置き換えてもらいたい。

$ ssh-keygen

そうすると以下の内容が出力されるので順々に必要な項目をタイプする。
Generating public/private rsa key pair.
Enter file in which to save the key (/c/users/your name/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Vera vera vera vera ....

/c/users/your name/.ssh/id_rsaの場所はgit sshから参照されるので名前と場所を変更しないように。エンターキーでpassphraseの入力に移ろう。passphraseは入力しなくてもかまわないがセキュリティの観点から入力するのが望ましい。

さて、c:\Users\your name\.sshを参照するとid_rsaとid_rsa.pubができているはずだ。とりあえずWindowsのセットアップはここまでにしてGitサーバをたてる。


Gitサーバをたてる
サーバセットアップまでは以下をそのまま参考してもらいたい。
4.4 Git on the Server - Setting Up the Server

一応手順を訳すとこうなる。

1、サーバにGitをインストールする
$ apt-get install git

2、'git'ユーザを作り、そのユーザ用の.sshディレクトリを作る
$ sudo adduser git
$ su git
$ cd
$ mkdir .ssh

3、Windowsで作ったid_rsa.pubを/home/git/に持ってくる
コピーするなりFTPで渡すなりしてもらいたい。

4、(サーバに複数ユーザいる場合は他のユーザのホームディレクトリに作らないように注意!)gitユーザのhomeの.sshにauthorized_keysファイルを作る
$ cat id_rsa.pub >> /home/git/.ssh/authorized_keys

.sshとauthorized_keysの権限は以下のようにしておく必要があるので注意してほしい。
$ chmod 700 .ssh
$ chmod 600 .ssh/authorized_keys

authorized_keysを作った後はid_rsa.pubは不要なので削除する
$ rm id_rsa.pub

ちなみに複数ユーザを追加する場合はユーザ分のid_rsa.pubをどんどんauthorized_keysに追加していくだけでよい。

この時点でWindowsからgitサーバにSSHでログインできるはずなので、Windowsに戻ってGit Bashから以下のコマンドを試してもらいたい。
$ ssh git@your domain.com
※your domain.comは自分のドメイン名に変更してもらいたい。

ここでPermission deniedされる場合はSSHのキーがへぼっている可能性があるのでキー作成からやり直して再度authorized_keysにpublic key(id_rsa.pub)を追加してもらいたい。

5、Git用の作業ディレクトリを作る
それぞれのリポジトリ用に空のディレクトリを用意する必要がある。git initを--bareオプションで行う。
$ cd /opt/git
$ mkdir project.git
$ cd project.git
$ git --bare init
必要に応じてどんどんディレクトリを増やしていこう。

project.gitのオーナーがgitになっていることを確認しよう。もしもなっていない場合は以下コマンドで変更しておこう。
$ chown -R git:git project.git

いざcommit
さてこれで準備がととのったので再度WindowsにもどりGit Bashから下記のコマンドを実行してみよう。
$ cd myproject
$ git init
$ git add .
$ git commit -m 'initial commit'
$ git remote add origin git@your domain.com:/opt/git/project.git
$ git push origin master

これで他の端末にcloneすることもできるようになったので、他の端末から下記のコマンドを実行してみよう。
$ git clone git@your domain.com:/opt/git/project.git
$ cd project
$ vim README
$ git commit -am 'fix for the README file'
$ git push origin master


まとめ
SSH周りにはまるとアホみたいに時間がかかるけれど、それさえなければ一時間もかからずにGitサーバを用意してリポジトリをどんどん追加できるはずだ。SSH周りの認証がうまくいかない場合は下手に色々といじくるよりも手順を最初からやり直したほうが早いことが往々にしてあるのでそれを試してみるとよいだろう。

2013年7月20日土曜日

ホワイトIT企業で働くとこんな感じ

ブラック企業の代名詞としてよくあがるIT業界。IT土方(土方の方々に失礼なのではと思わないでもない)なんていわれる職場もあるようでひどいところは本当にひどいようだけれども、今の現場に入って数ヶ月、超絶ホワイトっぷりがすごいので書いてみる。


概要
今の案件にはお客さん(A社)の自社開発の企業向けソリューション(α)にGUI開発者として参画している。で、このプロジェクト、営業、開発、保守、運用なんかも含めるとざっと80人規模になる。もしかしたら知らない場所にも人がいるかもしれないけれど、同じフロアでαに確実に専任で携わっていると知れている人だけで80人ぐらいいる。α自体はVer1がすでにリリースされていて複数の大企業に納入されて元気に(?かどうか知らないけど)稼動している状態。


体制説明
色々とトライアンドエラーを繰り返しながら柔軟に体制を変えているようなので以前はまた違ったようだけれども、今は大体こんな感じで回しているようだ。内実はよく分からないので数字は適当。

・現行開発/サーバサイド(3~5人ぐらい)
Ver2の開発でサーバとかデータ周りをいじくり回してる人達。

・現行開発/フロントエンド(3~5人ぐらい)
Ver2の開発でGUIとかクライアントアプリ周りをいじくり回してる人達。

・プロト開発/サーバサイド(5人ぐらい)
Ver3に向けてのプロト開発でサーバとかデータ周りをいじくり回してる人達。

・プロト開発/フロントエンド(10人ぐらい)
Ver3に向けてのプロト開発でGUIとかクライアントアプリ周りをいじくり回してる人達。GUIの凝ったアプリなのでここに開発リソースがたくさん割かれてる。私がいるのもここ。

・プロト設計(5人ぐらい)
Ver3に向けての設計を進める人達。

・営業/コンサル(10人ぐらい)
αを実際のお客さん達に売り込みに行く人達。「αを使ったらこんなに効率よくなりますよー」とか甘言を弄する人々。日本全国飛び回ってるっぽい。

・保守運用(5~7人ぐらい)
良く分からんけど多分保守とかやってる人達だと思う。

・テスト(5~7人ぐらい)
テストケースとか作ってるっぽい人達。これも良く分からん。

ここまではパートナー企業から来てる人達の内訳。それプラス以下。

・A社社員(20人ぐらい)
パートナーの取りまとめを行う人達。設計の最終決定や、スケジュール管理、アーキテクチャ選定、価格交渉などなど最終的な責任を持つ人達。

なんか人数合わないけどざっとこんぐらい居る。


なぜホワイトなのか
で、ここからはいかにホワイトなのかを語る。

・優秀
基本的にみんな優秀。A社社員は言わずもがな、パートナー企業から来てる人達も優秀。とくにコアメンバーはかなりスキルが高いと思われる。で、まれにあまり仕事のできない人が紛れ込んでくることがあるけれども、そういう人達はさくっと契約が打ち切られるのでいつの間にかいなくなっている。

・体制とか運用がしっかりしている
BTS(Bug Tracking System)の活用なんて今じゃどこでもやっていることだけど、A社ほど有効活用できているところは少ない。チケットの意味付け(課題、TODO、実装、レビュー、etc)はしっかりしているし、チケットとバージョンコントロールもちゃんと紐付けがされている(とくにレビューとの紐付けがかなり良くできている)。何よりWikiの充実っぷりがすごく、そのWikiも日々更新され、Wiki上でのコメントのやりとりも活発。意識の高い人が多いのだと思う。

・レビューがすごい
コードレビューが頻繁に行われている。コミットされたコードに対してはA社社員に限らず、パートナーの開発者もガシガシ行って指摘チケットが飛び交っているし、そこで見つけた事例が新しければすぐにWiki化されるというなんとも良くできた仕組み。ここでやべーこいつ、と思われた人は契約カットの憂き目に会うと思われる。またコードだけではなく設計やらテストやらもガンガンレビューされている模様。

・体調不良に優しい
A社社員は言わずもがな、パートナーでも体調不良で長期で休んでも契約がきられない(とりあえず家庭の事情で長期で休んでいる人がいるけれど契約はきられていない模様)。A社社員に療養が必要となれば積極的に休暇を薦めてフォローしあう体制をすぐさま作ってる感じ。よく知らんけど。

・スケジュールがきつくない
9-5時勤務で余裕で回る感じ。なのでいつも定時あがり。ただ、実はA社社員はかなり大変なようで終電間際まで作業している人もちらほらいるらしい。それでも休暇なんかはとりやすいらしく、みなさんガシガシ取得している。パートナーも設計の人達とかは大変そうだ。

・ピリピリしてない
納期のある請負案件だったりすると期日が迫ってくるにつれピリピリしはじめて、嫌な職場になると怒鳴るような人員がいるところもあるけれど、今の現場ではみんなある種のんびりとしている。もちろん自社開発のプロトとはいえスケジュールはあるのでそれに向かって進めているのだけれども、よりよいものを作ろう的なスタンスなのでスケジュールは柔軟である。多分そのせいもあってか余裕があるように見受けられる。これだけでもこの現場のホワイトさが分かろうというもの。

・嫌らしい人がいない
性格の悪い人がいない。A社の社員はみんなシュッとしてて紳士的である。A社自体も大企業でありA社の社風的なものもあると思うけど、とりあえず一緒に仕事する人達(A社社員に限らず)は感じの良い人達が多い。

・技術的に結構チャレンジング
新しい技術を上手いこと使ってよいもの作っていこうぜ!的なスタンスだし、技術的にかなり高度なことをやっていたりもするので知識欲を満たせる。


とまぁこれぞザ・ホワイト企業だな、と数ヶ月働いてみての実感。飽き性なので小金がたまったら働くのをやめて、しばらく自分のやりたい開発とか調べ物をしてからまた請負仕事をする、みたいな感じで色々な職場を見てきたけれど、A社ほどのホワイトっぷりはいまだかつて見たことが無いレベル。ブラックと叫ばれて久しいIT業界だけど、いるところにはホワイトな企業も生息しているというお話。

2013年3月22日金曜日

磯野ー!2Dゲーム開発しようぜー! Android編 その4

長々と続いてきたカツオと中島のゲーム開発も今回で終了だ。今回はTextとEntityModifier周りを解説する。前回までは以下。

磯野ー!2Dゲーム開発しようぜー! HTML5編 その1
磯野ー!2Dゲーム開発しようぜー! HTML5編 その2
磯野ー!2Dゲーム開発しようぜー! HTML5編 その3
磯野ー!2Dゲーム開発しようぜー! Android編 その1
磯野ー!2Dゲーム開発しようぜー! Android編 その2
磯野ー!2Dゲーム開発しようぜー! Android編 その3


Text
private Font _font;
@Override
    protected void onCreateResources() {
        _font = FontFactory.create(this.getFontManager(), this.getTextureManager(), 256, 256, Typeface.create(Typeface.DEFAULT, Typeface.BOLD), 24, Color.WHITE_ARGB_PACKED_INT);
        _font.load();
    }
    @Override
    protected Scene onCreateScene() {
        _scene = new Scene();
        Text text = new Text(100, 10, _font, "Click on the characters!", this.getVertexBufferObjectManager());
        text.setColor(new Color(0, 0, 0));  // 色ごとにFontを用意するのは難儀なので、WhiteでFontを読み込み、後から色を設定する
        _scene.attachChild(text);
    }
文字を表示するのは一見簡単そうなのだけど柔軟さに欠けるので中々に面倒くさい。第一にフォント読み込み時に色を指定するので文字色を後から任意に変更できない。また色毎にフォントを読み込むのは難儀だ。それなので白色フォントで読み込んだ後にEntityの色変更メソッドを通して任意の色へと設定している。第二にフォント読み込み時の領域確保に指定する値の最適値が判然としない。文字の大きさと文字数から判断するしかないのだけど直感的でないので良く分からない。多量の文字を表示するなら大きくなければならないし、かといって無駄に領域を確保するとメモリを無駄に圧迫するのでやっかいな部分だ。最後にこれはバグなのだけど、コンストラクタで指定した文字列よりも長い文字列をsetTextで後から設定すると例外が発生する。そういう状況が起こりえる場合の回避策はコンストラクタで長い文字列を渡しておくしかないだろう。

フォントを指定して読み込む方法は下記を参照してほしい。
src/org/andengine/examples/CustomFontExample.java


EntityModifier
final ButtonSprite ship = new ButtonSprite(50, 50, _shipRegion.getTextureRegion(0), this.getVertexBufferObjectManager());
        final AnimatedSprite shipAnimation = new AnimatedSprite(10, 10, _shipRegion, this.getVertexBufferObjectManager());
        shipAnimation.animate(50);
        shipAnimation.setPosition(50, 50);  // コンストラクタで指定しているけれど、このコードがないとRotationの挙動がおかしい
        ship.setOnClickListener( new ButtonSprite.OnClickListener(){
            @Override
            public void onClick(ButtonSprite pButtonSprite, float pTouchAreaLocalX, float pTouchAreaLocalY) {
                _scene.detachChild(pButtonSprite);
                shipAnimation.setRotation(0);
                SequenceEntityModifier shipModifier =
                    new SequenceEntityModifier(
                        new IEntityModifier.IEntityModifierListener() {
                            @Override
                            public void onModifierStarted(IModifier<ientity> pModifier, IEntity pItem) {
                            }
                            @Override
                            public void onModifierFinished(final IModifier<ientity> pEntityModifier, final IEntity pEntity) {
                                MainActivity.this.mEngine.runOnUpdateThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        _scene.attachChild(ship);
                                        _scene.detachChild(shipAnimation);
                                        pEntity.unregisterEntityModifier((IEntityModifier)pEntityModifier);
                                    }
                                });
                            }
                        },
                        new RotationByModifier(0.5f, 90)
                        ,new MoveModifier(1.0f, 50, CAMERA_WIDTH+50, 50, 50, EaseExponentialOut.getInstance())
                        ,new RotationByModifier(0.1f, 180)
                        ,new MoveModifier(1.0f, CAMERA_WIDTH+50, 50, 50, 50, EaseExponentialOut.getInstance())
                        ,new RotationByModifier(0.5f, 90)
                    );
                shipAnimation.registerEntityModifier(shipModifier);
                _scene.attachChild(shipAnimation);
            }
        });
        _scene.registerTouchArea(ship);
        _scene.attachChild(ship);
EntityModifier周りだけを抜き取ると余計にわけが分からなくなりそうなのでそこらへんをごっそりと持ってきているけれど、注目して欲しいのは10行目から。SequenceEntityModifierを指定すると順番にEntityの値を変更していくことができる。12行目から27行目までは開始と終了のイベントハンドラーでそれ以降が値の変更を記述している。Tweenjsと同様の働きをする。同時に2つ以上の値を変更したい場合は下記のようにParallelEntityModifierを使用する。
// 省略
new RotationByModifier(0.5f, 90),
new ParallelEntityModifier(
 new ScaleModifier(3, 0.5f, 5),
 new RotationByModifier(3, 90)
),
new RotationByModifier(0.5f, 90),
// 省略
またTweenjsと同様にどのように値を変更させるかEase関数を使って指定ができる。Ease関数の種類は下記を参照してほしい。
AndEngine / src / org / andengine / util / modifier / ease /
実際に挙動を見るのが一番早いのでAndEngineExamplesをローカルに設定してEase関数周りのサンプルを動作させてみるのが一番良いだろう。その際にはExtensionsも必要なので忘れずに取得しよう。詳しくはAndEngineのReadMeを参照してほしい。

EntityModifierの開始、終了のイベントハンドラーでUI要素をいじくる場合は注意してほしい。左記2つの関数はワーカスレッドから呼び出されるのでUI要素をいじくるとアプリがクラッシュする。それなので17行目のようにmEngine.runOnUpdateThreadでUIスレッドを呼び出して処理しよう。

EntityModifierのざっくりとした説明は以上になるけれど、今回EntityModifierをいじくりまわしていていくつか不明な点があった。1つ目は、EnityModifierを使いまわせないかとEnityModifier.reset()をいじくりまわしてみたけれど上手くいかなかったこと。2つ目は、unregisterEntityModifierの使いどころ。Entityが不要なEntityModifierをいつまでも参照しているのが嫌だったので一応呼んでいるけれど、AndEngineのサンプルコードを参考にしてもunregisterEntityModifierを呼んでいるものがなかった。AndEngineのコードを追っかけていけば分かることなのだろうけれど現状はどうするのが一番良いのかは不明。


これでAndEngineの解説を終わりとする。AndEngineの基本的な使い方がどういったものか理解できたかと思う。今回は実装しなかったけれどもちろんゲームのメインループにあたる機能や、あたり判定の便利機能などなどゲーム開発に必要な機能は大体そろっている。そういった今回解説しなかった機能はAndEngineExamplesで詳説されているのでそちらを参照してほしい。

2013年3月21日木曜日

磯野ー!2Dゲーム開発しようぜー! Android編 その3

今回は画像周りを解説する。前回までは以下。

磯野ー!2Dゲーム開発しようぜー! HTML5編 その1
磯野ー!2Dゲーム開発しようぜー! HTML5編 その2
磯野ー!2Dゲーム開発しようぜー! HTML5編 その3
磯野ー!2Dゲーム開発しようぜー! Android編 その1
磯野ー!2Dゲーム開発しようぜー! Android編 その2


BitmapTextureAtlas
AndEngineで画像を扱う場合はまずBitmapTextureAtlasでサイズを指定し画像のための領域を作り、そこに実際の画像をどしどし読み込む形になる。コードにすると以下のような形。
    private BitmapTextureAtlas _charactersAtlas;
    private TiledTextureRegion _shipRegion;
    private TiledTextureRegion _squareRegion;

    @Override
    protected void onCreateResources() {
        BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");

        _charactersAtlas = new BitmapTextureAtlas(this.getTextureManager(), 1024, 256); // 指定のサイズで領域を作る
        _shipRegion = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(_charactersAtlas, this, "BlueShip.png", 0, 0, 6, 1);
        _squareRegion = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(_charactersAtlas, this, "WalkingSquare.png", 0, 100, 5, 1);
        _charactersAtlas.load();
    }
BitmapTextureAtlasのコンストラクタで指定する数値は2のべき乗である必要がある。つまり2,4,8,16,32,64,128,256...というわけだ。上記のコードが実行されると下図のような様子になる。



BlueShip.pngは600*100なので幅512では収まらないので幅1024になっているし、WalkingSquare.pngとあわせての必要な高さが230になっているので高さは256となっている。そのため画像のように無為な領域が多量に発生してしまっている。無駄なメモリ使用を減らすためにも画像はすき間無くつめられるようなサイズを意識して用意するとよいだろう。ちなみにBitmapTextureAtlasにあまりにも大きい領域を指定するとアプリがクラッシュすることもあるので注意しよう。4096などを指定すると危うそうだ。


TiledTextureRegion
BitmapTextureAtlasのほかにもここではTiledTextureRegionを使ってスプライトシートアニメーションを読み込むことを明示している。下記のコードで船のアニメーションが簡単に実現できる。
final AnimatedSprite shipAnimation = new AnimatedSprite(10, 10, _shipRegion, this.getVertexBufferObjectManager());
shipAnimation.animate(50); //アニメーション速度
_scene.attachChild(shipAnimation);
また下記のようにTiledTextureRegionから指定のフレームを使ってスプライトオブジェクトを作ることも可能。
final Sprite ship = new Sprite(50, 50, _shipRegion.getTextureRegion(0), this.getVertexBufferObjectManager());


TextureRegion
単純に画像をただ表示するだけならば下記のようにすればよい。
    private BitmapTextureAtlas _backgroundAtlas;
    private TextureRegion _backgroundRegion;
   @Override
    protected void onCreateResources() {
        BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");

        _backgroundAtlas = new BitmapTextureAtlas(this.getTextureManager(), 512, 512);
        _backgroundRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(_backgroundAtlas, this, "Background02.png", 0, 0);
        _backgroundAtlas.load();
    }

    @Override
    protected Scene onCreateScene() {
        _scene = new Scene();
        Sprite bg = new Sprite(0, 0, _backgroundRegion, this.getVertexBufferObjectManager());
        bg.setScaleCenter(0, 0);
        bg.setScale(CAMERA_WIDTH/400f, CAMERA_HEIGHT/300f);
        _scene.attachChild(bg);
    }

BitmapTextureAtlas周りが理解できればAndEngineの画像周りはしごくシンプルだ。次回はEntityModifier周りを解説してこのシリーズも終了となる。

2013年3月19日火曜日

磯野ー!2Dゲーム開発しようぜー! Android編 その2

今回からはAndEngineを使ってのゲーム開発を解説する。前回までは以下。

磯野ー!2Dゲーム開発しようぜー! HTML5編 その1
磯野ー!2Dゲーム開発しようぜー! HTML5編 その2
磯野ー!2Dゲーム開発しようぜー! HTML5編 その3
磯野ー!2Dゲーム開発しようぜー! Android編 その1


サンプルコード
今回使用するコードはhttps://github.com/yooontheearth/andengine-sampleから取得できる。


概要
AndEngineはOpenGLベースのゲームライブラリで現在はバージョンGLES2となっている。AndEngine周りのチュートリアルを探すと大体前バージョンであるGLES1が見つかると思うけれど、GLES2になって仕様が若干変わっているので注意してもらいたい。AndEngine GLES2のコードは以下。
https://github.com/nicolasgramlich/AndEngine


セットアップ
Eclipseのセットアップ方法はよくあるので今回はIntellij IDEAでのセットアップ方法を解説する。今回使用するIDEAのバージョンは12.04のCEだ。


アプリケーションの新規作成の図。Project SDKを4.2としているけれど、AndEngine GLES2は2.2以降で動作する(らしい)。ちなみにちぬあたりMEGAはAndroid 2.3の実機にインストールしたところ問題なく動作した。


AndEngineを追加する準備の図。File→Project Structure→Modulesを選択→上部の緑の十字をクリック→New Moduleを選択する。


AndEngineを追加するの図。Library Moduleを選択→Content rootにダウンロードしたAndEngineのフォルダを選択して(※Package nameは「org.andengine」と入力するように)Finishをクリックする。


AndEngineを確認の図。Module SDKは4.2のこと。AndEngineは3.0以降のConstantをいくつか参照しているので最新のSDKを参照しないとコンパイルエラーになる。


AndEngineを参照するの図。andengine sampleを選択→Dependenciesタブ→右の緑の十字をクリック→Module Dependencyを選択→AndEngineを選択する。AndEngineの参照ができたらExportにチェックしておこう。


では早速コードを見ていこう。

src/com.matsuosoftware.game.andengine_sample/MainActivity.java
public class MainActivity extends SimpleBaseGameActivity {
    public static final int CAMERA_WIDTH = 480;
    public static final int CAMERA_HEIGHT = 320;
    private Camera _camera;

    @Override
    public EngineOptions onCreateEngineOptions() {
        _camera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
        return new EngineOptions(true, ScreenOrientation.LANDSCAPE_SENSOR, new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT), _camera);
    }

    @Override
    protected void onCreateResources() {
        BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");

        // 画像などのResourceを読み込む
    }

    @Override
    protected Scene onCreateScene() {
        _scene = new Scene();
        _scene.setTouchAreaBindingOnActionDownEnabled(true);

        // this.mEngine.registerUpdateHandler()に登録するオブジェクトがいわゆるゲームのメインループになる
        // _sceneに画像をぺたぺた貼り付けていく

        return _scene;
    }
}
上記はもっともシンプルな構成のActivityだ。ちなみに上記を実行しても何も表示されない。AndEngineはSimpleBaseGameActivityから派生したActivityひとつだけで動作する。開発者はSceneオブジェクトにぺたぺたと画像を貼り付けつつ、this.mEngine.registerUpdateHandler(Obj)で登録したObjのonUpdateが定期的に呼ばれるのでそこで当たり判定などを行いつつ開発をすすめていく。ただ基本は上記の3つのメソッドで、エンジンを作り、リソースを読み込み、シーンを使って描画するという流れだ。


次回以降は上記のコードにドシドシ肉付けを行っていく。

2013年3月17日日曜日

磯野ー!2Dゲーム開発しようぜー! Android編 その1

前回まではHTML5での2dゲーム開発を解説したが今回からはAndroidでのゲーム開発を解説する。「次回はAndEngineを」と述べたけれど気が変わったのでAndroid編初回の今回は前回までに作ったHTML5ベースのゲームをWebViewでラッピングし、さもAndroidネイティブで作られたゲームかのように動作させる方法を解説する。前回までのシリーズは以下。

磯野ー!2Dゲーム開発しようぜー! HTML5編 その1
磯野ー!2Dゲーム開発しようぜー! HTML5編 その2
磯野ー!2Dゲーム開発しようぜー! HTML5編 その3


ゲーム概要

Androidローカル(assetsフォルダ)に配置したindex.html、画像、JavaScriptファイルなどをWebViewに読み込んでいる。ゲームの内容は前回までとまったく同じ。
※index.html等々はローカルから読み込んでいるけれど、jQuery、EaselJSなどのライブラリは前回までのコードそのままにCDN(Content Delivery Network)を利用しているのでインターネット経由で取得している。オフラインでも動作可能にするにはこれらの外部ライブラリもローカルに配置しそちらを参照するようにすればよい。


サンプルコード
今回のWebViewのサンプルコードはhttps://github.com/yooontheearth/html5-game-webview-sampleから取得できる。ではさっくりと見ていこう。


src/com.matsuosoftware.game.webview_sample/MainActivity
public class MainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.main);

        WebView webview = (WebView)findViewById(R.id.webview);
        webview.getSettings().setJavaScriptEnabled(true);
        webview.loadUrl("file:///android_asset/index.html");
    }
}
いたってシンプルである。が、順に見ていこう。
・6、7行目、フルスクリーンで表示するためのフラグを設定する
・11行目、JavaScriptを有効にする
・12行目、assetsフォルダからファイルを読み込む。file:///android_assetはassetsフォルダへの特別なファイルパス

ここまででゲームはさもAndroidネイティブアプリかのように起動するのだけれども、見てくれをよくするためにもindex.htmlのbodyタグに以下のスタイル設定が必要となる。

<body style="margin: 0; padding: 0">

とまぁ、これでブラウザで動作させたときと同様のHTML5のゲームがAndroidでラッピングできたわけだけれども、WebViewにはこれだけではなくAndroidからJavaScriptを操作したり値を渡したりその逆もまた可能になっているのでJavaScript上の値をAndroid上で永続化したり復元したりもできるようになっている。詳しくは以下を参照してもらいたい。

・AndroidとJavaScriptとのやりとりは以下のリンクからaddJavascriptInterface()周りを参照してもらいたい。
Building Web Apps in WebView

・JavaScriptのエラーハンドルなどは以下のリンクからsetWebChromeClient、setWebViewClient周りを参照してもらいたい。
WebView


結論
HTML5のコードに手を加えずともWebViewでラッピングしてそのままAndroidアプリとしてリリースすることもできるし、Android用の特別な処理を実装したい場合はAndroidと双方向でやりとりできることも理解できたかと思う。

ただ、HTML5をそのまま使えるというわけで各プラットフォームごとに開発を行わなくてもよいかも!?と明るい展望を一瞬みせてくれたWebViewではあったけれど、その実、実行速度はひっじょうに遅い!!!通常だと50または60FPS平均のものが10FPS以下ほどへと速度が落ちてしまうので大体のゲームはゲームとして成り立たなくなると思われる。通常のアプリ類であればPhoneGapやTitaniumがプラットフォームフリーへの一応の答えになると思うけれど、ゲームはどうなんだろう。以前調べたときはあまりよさげなのはなかった。


というわけでAndroidの能力を搾り出すためにも次回からはAndEngineの使い方を解説していく。

2013年3月16日土曜日

磯野ー!2Dゲーム開発しようぜー! HTML5編 その3

磯野と中島のゲーム開発も今回で3回目。HTML5編は今回で終了だ。ちなみに今回もめがねと坊主頭の出番はない。前回までの分は以下。

磯野ー!2Dゲーム開発しようぜー! HTML5編 その1
磯野ー!2Dゲーム開発しようぜー! HTML5編 その2

coffeescripts/platform.coffee
class GamePlatform
 constructor: (@stage, width, height, contentManager)->
  # 背景の用意
  background = new createjs.Bitmap contentManager.background
  background.setTransform 0, 0, width/400.0, height/300.0   # 背景画像は400*300でCanvasサイズと合わないので拡大する
  @stage.addChild background

  # 文字列表示
  text = new createjs.Text "Click on the characters!", "bold 24px Meiryo", "#000"
  text.x = (width/2)-(text.getMeasuredWidth()/2)
  text.y = 10
  # text.align = 'right'  # align指定はこの方法で
  @stage.addChild text

  # スプライトシートの設定
  shipSpriteSheet = new createjs.SpriteSheet
   images: [contentManager.ship]
   frames:
    width:100   # 1フレームの幅を指定
    height:100  # 1フレームの高さを指定
    regX:50     # 中心点(拡大縮小、回転、移動用の)を指定
    regY:50     #
   animations:
    move:
     frames: [0, 1, 2, 3, 4, 5] # アニメーションフレームの指定
     frequency: 10   # フレームごとの速度
  # スプライトアニメーションの設定
  shipAnimation = new createjs.BitmapAnimation shipSpriteSheet
  shipAnimation.x = shipAnimation.y = 100
  # スプライトシートから指定のフレームの画像を抜き取ってBitmapオブジェクトを作成する
  ship = new createjs.Bitmap createjs.SpriteSheetUtils.extractFrame shipSpriteSheet, 0
  ship.regX = ship.regY = 50 # 中心点をスプライトアニメーションに合わせる
  ship.x = ship.y = 100
  ship.onClick = =>
   shipAnimation.rotation = 90
   @stage.addChild shipAnimation
   shipAnimation.gotoAndPlay "move"
   @stage.removeChild ship
   # オブジェクトアニメーションを設定する
   createjs.Tween.get(shipAnimation)
     .to({x:width+100, scaleX:2, scaleY:2}, 1000, createjs.Ease.cubicIn)
     .to({rotation:270})
     .to({x:100, scaleX:1, scaleY:1}, 1000, createjs.Ease.cubicOut)
     .to({rotation:0}, 500)
     .wait(500)
     .call =>
      @stage.addChild ship
      @stage.removeChild shipAnimation
      shipAnimation.stop()
  @stage.addChild ship

  sqSpriteSheet = new createjs.SpriteSheet
   images: [contentManager.square]
   frames:
    width:100
    height:130
    regX:50
    regY:65
   animations:
    walk:
     frames: [0, 1, 2, 3, 4]
     frequency: 20
  sqAnimation = new createjs.BitmapAnimation sqSpriteSheet
  sqAnimation.x = 100
  sqAnimation.y = 250
  sq = new createjs.Bitmap createjs.SpriteSheetUtils.extractFrame sqSpriteSheet, 0
  sq.regX = 50
  sq.regY = 65
  sq.x = 100
  sq.y = 250
  sq.onClick = =>
   @stage.addChild sqAnimation
   sqAnimation.gotoAndPlay "walk"
   @stage.removeChild sq
   createjs.Tween.get(sqAnimation)
     .to({x:width+100}, 1000, createjs.Ease.quadInOut)
     .to({scaleX:-1})    # Y軸を中心にフリップする
     .to({x:100}, 1000, createjs.Ease.bounceOut)
     .to({scaleX:1})
     .wait(500)
     .call =>
      @stage.addChild sq
      @stage.removeChild sqAnimation
      sqAnimation.stop()
  @stage.addChild sq

  # Containerを使用してオブジェクトをまとめて管理したりもできる
#  container = new createjs.Container()
#       player = new createjs.Bitmap somethingImage
#  container.addChild player
#       player2 = new createjs.Bitmap somethingImage2
#  container.addChild player2
#  @stage.addChild container

  @fps = new createjs.Text "0 fps", "bold 14px Arial", "#000"
  @fps.x = @fps.y = 10
  @stage.addChild @fps
 startGame: ->
  createjs.Ticker.userRAF = true
  createjs.Ticker.setFPS 60
  createjs.Ticker.addListener this
 tick: =>
  #
  # キー入力にあわせたキャラクタの動作やあたり判定などをここで行う
  #
  @fps.text = (Math.round createjs.Ticker.getMeasuredFPS())+" fps"

  @stage.update()
window.GamePlatform = GamePlatform
・4行目、画像からBitmapオブジェクトを生成する
・5行目、setTransformは移動、スケール、回転、傾き、中心点をまとめて指定できる便利メソッド。ここではスケールまでしか指定していない
・6行目、Zオーダーという概念はなく後からstageに追加されたものほど上にくる。Zオーダーの指定がしたい場合は自分で実装しよう
・9行目、テキスト生成はこのとおり
・12行目、alignの指定などもおこなえる
・16行目、スプライトシートの設定を行う。画像の指定は複数可能。そちらの詳細はSpriteSheet Classを参照してほしい
・21行目、アクションの中心点を設定する。意味が不明な場合はXY両方の値を0に変更して実行してみよう。宇宙船の回転のアニメーションの部分で意味が理解できるはずだ
・23行目、BitmapAnimationで使用するアニメーションの設定を行う。ここの設定方法は複数あるのでこちらもSpriteSheet Classを参照してほしい
・28行目、BitmapAnimationオブジェクトを生成する。16行目で行ったスプライトシートのアニメーション設定をこのBitmapAnimationオブジェクトを通して使用する
・31行目、スプライトシートから指定のフレームを抜き取ってBitmapオブジェクトを生成する
・34行目、クリックイベントをハンドルする。これもEaselJSの大きな功績のひとつだ
・37行目、指定のアニメーションを実行する
・40行目、TweenJSを使用してオブジェクトアニメーションを設定する。指定のオブジェクトのプロパティ値を指定の時間と動かし方で変更できる
・46行目、TweenJSのアニメーションの終わりにコールバックを設定する。そのコールバックで宇宙船のアニメーションをstageからはずしたり宇宙船のアニメーションを止めたりする
・77行目、画像をフリップさせる場合はscaleXまたはscaleYに-1を設定する
・87行目、Containerを活用するとオブジェクトのグルーピングも楽に行える
・98行目、Tickerの設定あれこれ。詳細はTicker Classを参照してほしい
・102行目、今回のサンプルではあたり判定やらキー操作でのプレイヤーキャラクターの動作などは実装しなかったけれどそういった実装はtick()で行うようになる
・106行目、FPS(Frame Per Second)を表示する


ざーっと説明したけれども大体EaselJSを使ってのゲーム開発がどういうものか理解できただろうか。前回にも述べたけれどTicker(に登録したオブジェクトのtick())とStageという概念が基礎にあるだけでその上で行うゲーム開発は通常のゲーム開発とさほど変わらない。というわけでこれまでの解説を通してEaselJS、TweenJS、PreloadJSの便利さとか開発のしやすさが伝わったら幸いだ。


次回からはAndEngineの解説を行っていく。

2013年3月15日金曜日

磯野ー!2Dゲーム開発しようぜー! HTML5編 その2

今回はHTML5でのゲーム開発を解説するシリーズの第二回である。前回は以下。

磯野ー!2Dゲーム開発しようぜー! HTML5編 その1

今回からコードを使って解説していく。内容はCoffeeScriptをもとに説明するけれど、javascriptsフォルダ内に対応するJavaScriptファイルがあるのでCoffeeScriptになじみがない方はそちらを参照してほしい。

index.html
<body>
<canvas id="c" width="480" height="320"></canvas>
</body>
なぜかCSSからのサイズ指定が行えないのでCanvasタグに直接widthとheightを指定している。


coffeescripts/contentManager.coffee
class ContentManager
 manifest:[
  {src:'images/Background02.png', id:'background'},
  {src:'images/BlueShip.png', id:'ship'},
  {src:'images/WalkingSquare.png', id:'square'}
 ]
 constructor: (@stage, width, height, @downloadCompleteCallback)->
  @preload = new createjs.PreloadJS false # useXHR=false ローカルからファイルを読み込むのでimageタグを強要する
            # xhrはCross Origin Resource Sharingを許可しない
  @preload.onError = @handleElementError
  @preload.onFileLoad = @handleElementLoad
  @downloadProgress = new createjs.Text "-- %", "bold 18px Arial", "#fff"
  @downloadProgress.x = (width / 2)
  @downloadProgress.y = (height / 2)
  @elementLoadedCount = 0
 startDownload: ->
  @preload.loadManifest @manifest
  @stage.addChild @downloadProgress
  createjs.Ticker.addListener this
  createjs.Ticker.setInterval 50
 handleElementError: (e)=>
  alert "画像読み込み失敗 : #{e.src}"
 handleElementLoad: (e)=>
  @[e.id] = e.result
  @elementLoadedCount += 1
  if @elementLoadedCount is @manifest.length
   @stage.removeChild @downloadProgress
   createjs.Ticker.removeListener this
   @downloadCompleteCallback()
 tick: =>
  @downloadProgress.text = Math.round((@elementLoadedCount/@manifest.length) * 100) + " %"
  @stage.update()
window.ContentManager = ContentManager
何はともあれ画像がないとはじまらないのでPreloadJSを使用して画像を読み込む。
・2行目、読み込む対象画像へのパスを指定する。ここで指定しているidが11行目のonFileLoadイベントの引数e.idとして渡ってくる
・8行目、PreloadJSのコンストラクタにuseXHR=falseを指定している。この引数はPreloadJSにXHRではなくimageタグでの画像取得を行わせるための設定。今回のサンプルはローカルファイルからの読み込みなのでXHRでの取得ができない
・10行目、画像取得時にエラーが発生した場合のコールバックを設定する
・11行目、各画像取得完了時のコールバックを設定する
・17行目、PreloadJSに画像の読み込みを指示する
・19行目、createjs.Tickerのイベントリスナーを登録する。ここで登録したオブジェクトのtickメソッドが定期的に呼び出される。このtickメソッドがいわゆるゲーム開発におけるメインループとなる
・24行目、取得の完了した画像のidをプロパティ名として画像内容を設定する。このidは2行目で設定していたものと同一
・28行目、すべての画像の取得が完了したのでTickerのイベントリスナーを忘れずにはずしておく
・32行目、stage.update()で画面のリフレッシュを行う。stageについては後述する

とざっくりとだがPreloadJSの機能説明をした。簡単に言うと画像の取得が楽、という一言に尽きる。ちなみにPreloadJSを使わずに画像取得をする場合は以下のようになる。
var image = new Image();
image.onload = function() {
 context.drawImage(image, 10, 10);
};
image.src = "http://hoge/test.jpg";
上記のコードを汎用的に使いやすい形にするとPreloadJSになるのが理解できるかと思う。


coffeescripts/index.coffee
$(document).ready ()->
 isCanvasSupported = ->
  elem = document.createElement('canvas')
  return !!(elem.getContext && elem.getContext('2d'))
 unless isCanvasSupported()
  return alert "あなたのブラウザはCanvasが使えないです"

 canvas = $('#c')[0]
 stage = new createjs.Stage canvas

 contentManager = new ContentManager stage, canvas.width, canvas.height, ->
  platform = new GamePlatform(stage, canvas.width, canvas.height, this)
  platform.startGame()
 contentManager.startDownload()
使用しているブラウザがCanvasをサポートしているかチェックしてから先ほどのContentManagerで画像の取得を行っている。全画像取得完了時のコールバックでGamePlatform(後述)をインスタンス化しゲーム本編へと移行する。
・9行目、Stageをインスタンス化している。BitmapオブジェクトやBitmapAnimationオブジェクトなどの描画したいオブジェクトをstage.addChild(Obj)で追加する。stage.update()で追加されたオブジェクトのリストをコンストラクタで指定されたCanvasへと描画する。

EaselJSではTickerとStageが開発の基礎となる。

長くなったので続きは次回

2013年3月14日木曜日

磯野ー!2Dゲーム開発しようぜー! HTML5編 その1

ちぬあたりMEGAAndroid版もリリースし、HTML5とAndroidの2Dゲーム開発が何となく分かったのでこれから数回に分けて解説していく。最初の数回はHTML5(というよりもeaseljs)で残りはAndroidの解説になる。AndroidはAndEngineの使い方がメインとなる。ちなみに中島も磯野も出てはこないのであしからず。

今回のシリーズで使用する画像は下記から拝借している。商用利用する場合は連絡が必要ということなのでその意向がある方はリンク先の指示に従っていただきたい。
XNA Sprites


ゲーム内容

いたってシンプルな2dゲームである。キャラクターをクリックすると右端に消えた後に戻ってくるという内容だ。ちなみにゲームの対象年齢は2~3歳児ぐらいとなっている。


サンプルコード
今回のHTML5のソースコードはhttps://github.com/yooontheearth/html5-game-sampleから取得できる。後述する設定を行うことによってindex.htmlから動作確認できるはずだ。javascriptsフォルダ内のJavaScriptファイルはすべてcoffeescriptsフォルダ内のCoffeeScriptファイルから生成されている。


設定
今回はサーバをたてずにローカルからindex.htmlを起動するのでChromeにいろいろと設定が必要になる(IEとFireFoxは使用していないので不明)。Chromeの初期設定ではローカルからファイルを読み込んだ場合にほかの読み込んだローカルファイルをいじくるとセキュリティエラーが発生するようになっている。なのでそれを回避するために下記のフラグを設定する必要がある。
--allow-file-access-from-files
※セキュリティ上たいへんよろしくないのでテスト中などの場合にのみ使用するように

上記のフラグとともにChromeを起動する方法は下記。これはUbuntuの場合。
$ chromium-browser --allow-file-access-from-files
ちなみに上記のコマンドでChromeを立ち上げる前にすでにChromeのProcessが起動していたりするとフラグの設定が有効にならないのでUbuntuを再起動することをおすすめする。そのほかのWindowsやOS X、Androidなんかの起動方法は下記を参照してほしい。
Run Chromium with flags

どうやってもうまくいかん、という人はApacheでもなんでもよいので適当にWebサーバを立ち上げてhttp://localhost/index.htmlとかしてもらっても今回のサンプルは動作するはずなのでそちらからアプローチしてもらってもかまわない。


ライブラリ
今回はHTML5編の第一回ということで今回のシリーズで使用するライブラリの紹介をする。
EaselJS
ゲームのメインループや画像オブジェクトクラス、スプライトシート・アニメーションクラス、テキストクラスなどなど便利な機能盛り沢山のライブラリでHTML5の2dゲーム開発の核となる。各クラスの詳細な情報はEaselJS Documentを参照してほしい。

TweenJS
オブジェクトのアニメーションや動作を直感的に記述できるライブラリ。メソッドチェイン形式で振る舞いを記述できるので大変便利。デモでどのようなビルトインの動きがあるか参照してほしい。

PreloadJS
画像などを読み込むのに使用する。

上記のライブラリ群にSoundJSというライブラリを追加するとCreateJSというライブラリスイートになる。が、今回はサウンド周りは実装しないのでCreateJSではなく個別に参照している。


次回は前述のサンプルコードを使って色々と解説していく。

2013年1月29日火曜日

Android開発環境を64bit Ubuntu 12.04.1LTSに構築する(挫折)

Androidの開発環境を64bit Ubuntu 12.04.1LTSに構築しようと色々調べまくったのでそれのまとめ。ちなみに私はどうやってもうまくいかないので途中で諦めて64bit Windows 7に環境を構築した。なので以下は不完全な情報だけれどもWebにあがっていた報告では下記情報で正常に動作したらしいので後続の方のためにも記しておく。

今回はOracle VirtualBoxをWindows 7 64bit上で動かし、そのVirtualBox上で動作しているUbuntu 12.04.1 LTS 64bitに環境を構築しようとした。


1、SDKの入手
なにはともあれSDKを入手する。
Get the Android SDK
上記のリンクから「DOWNLOAD FOR OTHER PLATFORMS」を選びLinux 64-bitを取得する。

※私はeclipseではなくintellij ideaをIDEにしているのでADT BundleではなくSDK Tools Onlyを選択した


2、インストール
※ADT Bundleを選択している場合はSetting Up the ADT Bundleを参考にしよう
Setting Up an Existing IDEを参考にしよう。忘れずに「Information for other platforms」をクリックしてLinuxの情報を開示し、ページ最下部にある「Troubleshooting Ubuntu」もクリックしよう。下記コマンドを実行するよう指示があるので忘れずに実行しよう。

sudo apt-get install ia32-libs

これを実行しておかないとAVD ManagerでVirtual Device自体の作成ができない。


3、プラットフォームとパッケージの選択
Adding Platforms and Packagesを参考にAndroid SDK Managerで必要なプラットフォームをインストールしよう。


4、確認
ここまでで基本的な設定は完了した。私は下記の場所へAndroid SDKを配置した。
/home/yoo/devs/android-sdk-linux

下記コマンドでAVD Managerを実行しVirtual Deviceを作成しよう。toolsに作業場所を移している。
yoo@yoo-UbuntuBox:~/devs/android-sdk-linux/tools$ ./android avd

※私は名前を「test」にしTarget Platformは2.1で作成した

適当に作ったVDを選択しStartボタンクリック→LaunchボタンクリックでAndroidフォンのスキンが起動し「A N D R O I D」の文字まで表示されるようだったらOKなのでこれ以上読み進める必要はない。失敗している場合はAndroidフォンのスキンに文字が表示されずに画面がブラックのままで固まる。


5、原因究明
なにはともあれ問題の箇所を把握しよう。ここでは「test」という名前のVDを作成してあるとする。
yoo@yoo-UbuntuBox:~/devs/android-sdk-linux/tools$ ./emulator -avd test -verbose

上記コマンドを実行すると下記のような内容が出力される。
emulator: found SDK root at /home/yoo/devs/android-sdk-linux
emulator: Android virtual device file at: /home/yoo/.android/avd/test.ini
emulator: virtual device content at /home/yoo/.android/avd/test.avd
emulator: virtual device config file: /home/yoo/.android/avd/test.avd/config.ini
emulator: using core hw config path: /home/yoo/.android/avd/test.avd/hardware-qemu.ini
emulator: Found AVD target API level: 7
emulator: 'magic' skin format detected: 320x480
emulator: autoconfig: -skin 320x480
emulator: autoconfig: -skindir (null)
emulator: keyset loaded from: /home/yoo/.android/default.keyset
emulator: found magic skin width=320 height=480 bpp=16

emulator: autoconfig: -kernel /home/yoo/devs/android-sdk-linux/platforms/android-7/images//kernel-qemu
emulator: autoconfig: -ramdisk /home/yoo/devs/android-sdk-linux/platforms/android-7/images//ramdisk.img
emulator: Using initial system image: /home/yoo/devs/android-sdk-linux/platforms/android-7/images//system.img
emulator: autoconfig: -data /home/yoo/.android/avd/test.avd/userdata-qemu.img
emulator: autoconfig: -initdata /home/yoo/.android/avd/test.avd/userdata.img
emulator: autoconfig: -cache /home/yoo/.android/avd/test.avd/cache.img
emulator: Physical RAM size: 512MB

Content of hardware configuration file:
hw.cpu.arch = arm
hw.ramSize = 512
hw.screen = touch
hw.mainKeys = yes
hw.trackBall = yes
hw.keyboard = no
hw.keyboard.lid = yes
hw.keyboard.charmap = qwerty2
hw.dPad = no
hw.gsmModem = yes
hw.gps = yes
hw.battery = yes
hw.accelerometer = yes
hw.audioInput = yes
hw.audioOutput = yes
hw.sdCard = no
disk.cachePartition = yes
disk.cachePartition.path = /home/yoo/.android/avd/test.avd/cache.img
disk.cachePartition.size = 66m
hw.lcd.width = 320
hw.lcd.height = 480
hw.lcd.depth = 16
hw.lcd.density = 160
hw.lcd.backlight = yes
hw.gpu.enabled = no
hw.camera.back = none
hw.camera.front = none
vm.heapSize = 16
hw.sensors.proximity = yes
hw.sensors.magnetic_field = yes
hw.sensors.orientation = yes
hw.sensors.temperature = yes
kernel.path = /home/yoo/devs/android-sdk-linux/platforms/android-7/images//kernel-qemu
kernel.parameters = android.checkjni=1
disk.ramdisk.path = /home/yoo/devs/android-sdk-linux/platforms/android-7/images//ramdisk.img
disk.systemPartition.initPath = /home/yoo/devs/android-sdk-linux/platforms/android-7/images//system.img
disk.systemPartition.size = 200m
disk.dataPartition.path = /home/yoo/.android/avd/test.avd/userdata-qemu.img
disk.dataPartition.size = 200m
avd.name = test
.
QEMU options list:
emulator: argv[00] = "./emulator-arm"
emulator: argv[01] = "-android-hw"
emulator: argv[02] = "/home/yoo/.android/avd/test.avd/hardware-qemu.ini"
Concatenated QEMU options:
./emulator-arm -android-hw /home/yoo/.android/avd/test.avd/hardware-qemu.ini
emulator: registered 'boot-properties' qemud service
emulator: nand_add_dev: system,size=0xc800000,initfile=/home/yoo/devs/android-sdk-linux/platforms/android-7/images//system.img
emulator: mapping 'system' NAND image to /tmp/android-yoo/emulator-5wqpJC
emulator: rounding devsize up to a full eraseunit, now c810000

emulator: nand_add_dev: userdata,size=0xc800000,file=/home/yoo/.android/avd/test.avd/userdata-qemu.img
emulator: rounding devsize up to a full eraseunit, now c810000

emulator: registered 'boot-properties' qemud service
emulator: Adding boot property: 'dalvik.vm.heapsize' = '16m'
emulator: Adding boot property: 'qemu.sf.lcd_density' = '160'
emulator: Adding boot property: 'qemu.hw.mainkeys' = '1'
emulator: Adding boot property: 'qemu.sf.fake_camera' = 'none'
emulator: nand_add_dev: cache,size=0x4200000,file=/home/yoo/.android/avd/test.avd/cache.img
emulator: Initializing hardware OpenGLES emulation support
Failed to load libGL.so
error libGL.so: cannot open shared object file: No such file or directory
Failed to load libGL.so
error libGL.so: cannot open shared object file: No such file or directory
emulator: Kernel parameters: qemu.gles=0 qemu=1 console=ttyS0 android.qemud=ttyS1 android.checkjni=1 ndns=1
emulator: Trace file name is not set

emulator: autoconfig: -scale 1
emulator: Could not open file: (null)/system/build.prop: No such file or directory
emulator: control console listening on port 5554, ADB on port 5555
emulator: sent '0012host:emulator:5555' to ADB server
emulator: ping program: /home/yoo/devs/android-sdk-linux/tools/ddms
emulator: ping command: /home/yoo/devs/android-sdk-linux/tools/ddms ping emulator 20.0 "VMware, Inc." "Gallium 0.4 on llvmpipe (LLVM 0x300)" "2.1 Mesa 8.0.4"

いくつかErrorが散見されるけれど下のほうに下記が見つかる。
Failed to load libGL.so
error libGL.so: cannot open shared object file: No such file or directory

どうやらOpenGL周りがインストールされていないようだ。それなので下記コマンドでインストールする。
sudo apt-get install libgl1-mesa-dev

インストールが完了したら再度実行してみよう。
yoo@yoo-UbuntuBox:~/devs/android-sdk-linux/tools$ ./emulator -avd test -verbose

ここでまた同じエラーが発生した場合はlibGL.soが想定とは違う場所にインストールされているはずなのでインストールされた場所を下記コマンドで見つけよう。

/usrに作業場所を移している。下記コマンドで下記の場所にlibGL.soが複数見つかった。
yoo@yoo-UbuntuBox:/usr$ sudo find -name libGL.so*
./lib/x86_64-linux-gnu/libGL.so
./lib/x86_64-linux-gnu/mesa/libGL.so.1
./lib/x86_64-linux-gnu/mesa/libGL.so
./lib/x86_64-linux-gnu/mesa/libGL.so.1.2
./lib/i386-linux-gnu/mesa/libGL.so.1
./lib/i386-linux-gnu/mesa/libGL.so.1.2

とりあえず適当にライブラリを選択しSimlinkする。
sudo ln -s /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2 ~/devs/android-sdk-linux/tools/lib/libGL.so

これで再度Emulatorを実行しよう。すると下記のようなエラーが発生する場合があるので、その場合は違うlibGL.soライブラリをSymlinkしよう。
Failed to load libGL.so
error libGL.so: wrong ELF class: ELFCLASS64


6、万策尽きた
ここまで体系的にやってきて動かなかったのと、/system/build.propのあたりもエラーっぽくなってるのに加え、まれにADB serverへ接続しようとして接続エラーになったりと、どれが直接敵な問題なのかも不明な状況で問題が噴出することに心が折れて諦めた。

以下は散発的にやったものでどれもうまくいかなかったけれどあわせて載せておく。

・3d accelerationをオフにする
VBox上でVirtual Deviceを動作させる場合は3d accelerationはオフにしないといけないらしく、オフにしたら動作したとの報告あり。

・yoo@yoo-UbuntuBox:~/devs/android-sdk-linux/tools$ ./emulator-arm -avd test -verboseで実行する
./emulatorではなく./emulator-armにしたら動作したとの報告あり。


ほかにもいくつか試した覚えがあるのだけれどメモしておかなかったので正確なところは覚えていない。revision 20以降のSDKで同様の症状に悩まされている人は結構いる模様。少しでも誰かの助けになれば幸いです。