Androidの描画比較(AIR for Androidと、OpenGL)
Posted by momomo | Filed under Web, ゲーム, パソコン
きっかけ
以前AIR for Androidで作りかけたアプリがあって、その時は描画がもっさりしていたのと、
常駐できなかったので断念してました。
その後ゲーム作成を通じてFlashでStage3Dを扱う技術を獲得したので、
再度作ってみようかなという気になり、数日前からいじりはじめました。
AIR for Androidはネイティブより遅いというイメージがあったんだけども、
Stage3D使ったらどうなるだろう?という疑問が沸いてきたので、
FPS比較くらいならすぐにできるだろうと思い、試してみました。
やった事
AndroidアプリをJavaで書くのは少しやった程度なので、OpenGLの書き方なんぞ分かりません。
ネットを探し回って意味が良く分からない関数もあるままで、なんとか動かしましたっていう状態です。
一方AIRのほうは慣れたもので、すらすらとなんの障害もなく書けました。
計測方法としては、単純にpng画像を500個置いて左右に動かしてFPS計測するだけです。
Javaの方で変な文字が出てるのは気にしないでね
結果
デバッグビルドではありますが、両方とも同じくらいの速度になりました。
Galaxy SⅢα で 17 FPS 程度。
ただ、コード量としては3倍くらいの差になり、AIRの圧勝でした。
iOS版への対応も楽でコードも短いとなったら、ネイティブコード書く理由はないですね!
あと、EclipseのほうのAndroidエミュレータだと糞重たくて1FPSとかになってたけど、
AIRのほうのエミュレータだと割とさくさく動いてました。
ますますAIRいいね!
あとは常駐できればアプリ完成が見えてくる・・・
ということでAIR Native Extensionを試してきます。
コード(AIR)
FPS.as
package { import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageQuality; import flash.display.StageScaleMode; import flash.events.Event; import starling.core.Starling; [SWF(width="600", height="1000", backgroundColor="0x000000", frameRate="60")] public class FPS extends Sprite{ private var _starling:Starling; public function FPS() { addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler); } private function addedToStageHandler(e:Event):void { removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler); stage.align = StageAlign.TOP_LEFT; stage.quality = StageQuality.HIGH; stage.scaleMode = StageScaleMode.NO_SCALE; Starling.handleLostContext = true; _starling = new Starling(Main, stage); _starling.showStats = true; _starling.start(); _starling.showStatsAt("right", "bottom", 5); } } }
Main.as
package { import starling.ImageEx; import starling.core.Starling; import starling.display.DisplayObjectContainer; import starling.display.Image; import starling.display.Sprite; import starling.events.Event; import starling.textures.Texture; public class Main extends Sprite { private var spriteNum:int = 500; private var sprites:Array = new Array; [Embed(source = "sample.png")] private static var tenshi:Class; public function Main(){ addEventListener(starling.events.Event.ADDED_TO_STAGE, addedToStageHandler); } private function addedToStageHandler(e:starling.events.Event):void{ removeEventListener(starling.events.Event.ADDED_TO_STAGE, addedToStageHandler); for(var i:int=0; i<spriteNum; i++){ sprites[i] = makeImage(this, tenshi, true, Math.random()*500, Math.random()*500); move(sprites[i]); } } public function move(img:Image):void{ Starling.juggler.tween(img, 1, {x:img.x+Math.random()*50-25, onComplete:function(){ move(img) } }); } public static function makeImage(parent:DisplayObjectContainer, bmpClass:Class, visible:Boolean, x:Number=0, y:Number=0):Image{ var image:Image = new Image(Texture.fromBitmap(new bmpClass())); image.x = x; image.y = y; parent.addChild(image); image.visible = visible; return image; } } }
コード(Java)
MainActivity.java
package com.example.fps; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.view.Menu; import android.widget.TextView; public class MainActivity extends Activity { SampleGlView glView; FpsTextView fps; TextView text; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // ハンドラセット DrawStatic.setHandler(new Handler()); setContentView(R.layout.activity_main); glView = (SampleGlView) findViewById(R.id.sampleGlView1); text = (TextView)findViewById(R.id.textView1); glView.setTextView(text); } }
SampleGlView.java
package com.example.fps; import android.content.Context; import android.opengl.GLSurfaceView; import android.util.AttributeSet; import android.widget.TextView; public class SampleGlView extends GLSurfaceView { SampleRenderer renderer; public SampleGlView(Context context) { super(context); renderer = new SampleRenderer(context); setRenderer( renderer ); } public SampleGlView(Context context, AttributeSet attrs) { super(context, attrs); renderer = new SampleRenderer(context); setRenderer( renderer ); } public void setTextView(TextView v){ renderer.textView = v; } }
SampleRenderer.java
package com.example.fps; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.content.Context; import android.opengl.GLSurfaceView.Renderer; import android.opengl.GLU; import android.widget.TextView; public class SampleRenderer implements Renderer{ private Context context; public TextView textView; private int spriteNum = 500; SampleSprite[] tenshi_img = new SampleSprite[spriteNum]; // フレームレート表示用 private long mFpsCountStartTime = System.currentTimeMillis(); private int mFramesInSecond = 0; private int mFps = 0; private Runnable fpsRunnable; { // フレームレート更新用の Runnable fpsRunnable = new Runnable(){ @Override public void run() { textView.setText(mFps + "Fps"); } }; } public SampleRenderer(Context _context){ context = _context; for(int i=0; i<spriteNum; i++){ tenshi_img[i] = new SampleSprite(); } } @Override public void onDrawFrame(GL10 gl) { // 描画用バッファをクリア gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); for(int i=0; i<spriteNum; i++){ tenshi_img[i].draw(gl); tenshi_img[i].pos_x += Math.random()*5 - 2.5; if(tenshi_img[i].pos_x > 400) tenshi_img[i].pos_x = 0; } upFps(); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { gl.glViewport(0, 0, width, height); gl.glMatrixMode(GL10.GL_PROJECTION);//プロジェクションモードに設定 GLU.gluOrtho2D(gl, 0.0f, width, 0.0f, height);//平行投影用のパラメータをセット } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { //背景色をクリア gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); //ディザを無効化 gl.glDisable(GL10.GL_DITHER); //深度テストを有効化 gl.glEnable(GL10.GL_DEPTH_TEST); //テクスチャ機能ON gl.glEnable(GL10.GL_TEXTURE_2D); //透明可能に gl.glEnable(GL10.GL_ALPHA_TEST); //ブレンド可能に gl.glEnable(GL10.GL_BLEND); gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); for(int i=0; i<spriteNum; i++){ tenshi_img[i].setTexture(gl,context.getResources(), R.drawable.sample_img); tenshi_img[i].pos_x = (float)Math.random() * 500; tenshi_img[i].pos_y = (float)Math.random() * 500; } } private void upFps(){ long nowTime = System.currentTimeMillis();// 現在時間を取得 long difference = nowTime - mFpsCountStartTime;// 現在時間との差分を計算 if (difference >= 1000) {// 1秒経過していた場合は、フレーム数のカウント終了 mFps = mFramesInSecond; mFramesInSecond = 0; mFpsCountStartTime = nowTime; DrawStatic.addRunnable(fpsRunnable); } mFramesInSecond++;// フレーム数をカウント } }
SampleSprite.java
package com.example.fps; import java.io.IOException; import java.io.InputStream; import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.opengles.GL11; import javax.microedition.khronos.opengles.GL11Ext; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.opengl.GLUtils; public class SampleSprite { //テクスチャNo public int textureNo; //表示位置 float pos_x; float pos_y; float pos_z; //テクスチャ(画像)の位置とサイズ int texX; int texY; int texWidth; int texHeight; //配置する時の幅と高さ float width; float height; public void setTexture( GL10 gl, Resources res, int id ){ InputStream is = res.openRawResource(id); Bitmap bitmap; try{ bitmap = BitmapFactory.decodeStream(is); } finally{ try{ is.close(); } catch(IOException e){ } } gl.glEnable(GL10.GL_ALPHA_TEST); gl.glEnable(GL10.GL_BLEND); gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE); //テクスチャIDを割り当てる int[] textureID = new int[1]; gl.glGenTextures(1, textureID, 0); textureNo = textureID[0]; //テクスチャIDのバインド gl.glBindTexture(GL10.GL_TEXTURE_2D, textureNo); //OpenGL ES用のメモリ領域に画像データを渡す。上でバインドされたテクスチャIDと結び付けられる。 GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); //テクスチャ座標が1.0fを超えたときの、テクスチャを繰り返す設定 gl.glTexParameterx(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT ); gl.glTexParameterx(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT ); //テクスチャを元のサイズから拡大、縮小して使用したときの色の使い方を設定 gl.glTexParameterx(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR ); gl.glTexParameterx(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR ); texX = 0; texY = bitmap.getHeight(); texWidth = bitmap.getWidth(); texHeight = -bitmap.getHeight(); pos_x = 0; pos_y = 0; pos_z = 0; width = bitmap.getWidth(); height = bitmap.getHeight(); } public void draw( GL10 gl){ gl.glDisable(GL10.GL_DEPTH_TEST); //背景色を白色で塗りつぶし gl.glColor4x(0x10000, 0x10000, 0x10000, 0x10000); //テクスチャ0番をアクティブにする gl.glActiveTexture(GL10.GL_TEXTURE0); //テクスチャIDに対応するテクスチャをバインドする gl.glBindTexture(GL10.GL_TEXTURE_2D, textureNo); //テクスチャの座標と幅と高さを指定 int rect[] = { texX, texY, texWidth, texHeight}; //テクスチャ画像のどの部分を使うかを指定 ((GL11) gl).glTexParameteriv(GL10.GL_TEXTURE_2D,GL11Ext.GL_TEXTURE_CROP_RECT_OES, rect, 0); //描画 ((GL11Ext) gl).glDrawTexfOES( pos_x, pos_y, pos_z, width, height); gl.glEnable(GL10.GL_DEPTH_TEST); } }