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);
}
}

