ラベル Android の投稿を表示しています。 すべての投稿を表示
ラベル Android の投稿を表示しています。 すべての投稿を表示

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の使い方を解説していく。

2010年6月30日水曜日

ZipファイルをUnzip(解凍)するクラス

Androidアプリを開発していて、Zipファイルを解凍するクラスを作ったので下記に公開しておく。

public class UnzipFiler
{
    private final static int CHUNK_SIZE = 32 * 1024;
    byte[] _fileIOBuffer = new byte[CHUNK_SIZE];
    
    public void unzipFile(File zipFile, String directory)
  throws IOException
    { 
 ZipInputStream in = null; 
 FileOutputStream os = null; 
 try 
 {
  in = new ZipInputStream (new FileInputStream(zipFile)); 
  ZipEntry entry = null; 
  while ((entry = in.getNextEntry ())!= null) 
  { 
   String entryName = entry.getName();     
   if (entry.isDirectory ()) { 
    File file = new File (directory, entryName); 
    file.mkdirs(); 
   } 
   else { 
    File file = new File(directory, entryName);
    if (file.exists()){
     file.delete();
    }
    os = new FileOutputStream (file); 
    
    int unzippedSize = 0;
    int reportedProgress = 100;
    int bytesRead = 0; 
    while ((bytesRead = in.read (_fileIOBuffer))!= -1) {
     os.write(_fileIOBuffer, 0, bytesRead);
     unzippedSize += bytesRead;
    }
    os.close();
   }
  }
 }
 catch (FileNotFoundException e) {    
  Log.v("unzip", e.getMessage());
 } 
 catch (IOException e) {
  Log.v("unzip", e.getMessage());
 } 
 finally{
  if (in != null ){
   in.close();
  }
  if (os != null ){
   os.close();
  }
 }
    }  
}

Stack overflowに投稿したのと同じものだけれど、こうした方が良い、などの意見があったらコメントお願いします。

How should I decompress a large data file in AsyncTask.doInBackground?

2010年6月5日土曜日

AndroidのエミュレーターでASP.NET Dev Serverのlocalhostにアクセスする方法

Androidアプリをエミュレーター上で実行しWebアクセスするときに、テスト用にlocalhostを接続先に指定すると「Connection to http:localhost refused」とかなんとか言われて接続できない。

この理由はエミュレーター上でlocalhostとはエミュレーター自身を指すので、テスターが期待するようにはエミュレーターを実行しているマシンをルックアップしてくれない。なので解決方法は指定するURLを下記のようにプライベートIPアドレス(PvIP)へと変更しなければならない。

HttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet("http://192.168.0.2/foo/bar"); 
HttpResponse response;
response = httpclient.execute(httpget);
// 以下略
(HttpGetのサンプルはこちらを参考にどうぞ:How-to: Android as a RESTful Client

PvIPはコマンドプロンプトなどからipconfigを実行して取得すればよい。

で、このPvIPを指定する場合にASP.NET Development Serverを指定すると動かない。デブサバとはなんぞや、というのは下図のようなASP.NET開発者にはなじみの開発環境用のお手軽自動サーバーのことだ。


仮にデブサバが起動している状態でブラウザからhttp://192.168.0.2:52267/へとアクセスしても、サイトが見つからないと拒絶されてしまう。そのため、デブサバをPvIPで使うのは早々にあきらめて、IISでWebアプリケーションを仮想ディレクトリに登録する必要がある。

「デブサバのlocalhostにアクセスする方法」とこのポストのタイトルにあるが、それは色々と試したけれど無理だったので、もう普通にIISでテストしたいアプリを仮想ディレクトリにしたほうが早い。設定した仮想ディレクトリは下図のようになる。


一番下の項目のstripmeにアクセスするにはhttp://192.168.0.2:56526/となる。これでAndroidのエミュレーターからもHttpGetが正常に期待したサイトへとリクエストされるようになる。