HTML

Az élet kódjai

Csináld maga. Senki nem csinálja meg helyetted.

Friss topikok

Android demo keretrendszer

2015.05.03. 22:57 Travis.CG

Androidra viszonylag egyszerű demót kódolni. A multimédiás képességekkel felvértezett telefon API-ja rengeteg kényelmi funkciót biztosít, amit egy demó készítésénél felhasználhatunk. Persze nem szabad elfeledkezni, hogy bár néha félelmetes teljesítményt zsúfolnak ezekbe a készülékekbe, egy PC-hez képest sok limitáció van és nagyon sokféle készülék. De néhány egyszerű szabály betartásával ezek a hátrányok leküzdhetőek.

A Google is sokat tesz, hogy a fejlesztés könnyű legyen (hiszen bevételt termelnek). Rengeteg jó eszköz van és még Linux alól is működnek. Ha demót akarunk kódolni, az első szabály, amit adhatok:

Felejtsd el az emulátort.

Komolyan. Az egyik demónk fejlesztését két napra visszavetette, hogy kerestem egy bugot, ami emulátor alatt nem jelentkezett. A második fontos szabály:

Ne feledkezz meg az XML-ről.

Mint minden rendes Java fejlesztésnél, az Androidnál is sok XML turkálás kell. Ha ezt észben tartjuk, nem lesz semmi gond. Most pedig lássuk, hogyan készíthetünk demókat Androidra!

Az itt bemutatásra kerülő keretrendszer OpenGL-t használ a grafika megjelenítésére, ezért célszerű az AndroidManifest.xml-be felvenni a következőt:

<uses-feature android:glEsVersion="0x00020000" android:required="true" />

Ezzel elérjük, hogy minimum OpenGL ES 2.0 kell a demó futtatásához. Ha különböző engedélyekre is szükségünk van, azokat is ide vehetjük fel. Most lássuk a kódot!

package com.cybernetic.genetics.android.demoframework;

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Window;
import android.view.WindowManager;

public class OpenGLActivity extends Activity {

    private GLSurfaceView demoview;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(
            WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN);
        demoview = new cgSurfaceView(this);
        setContentView(demoview);
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_open_gl, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onBackPressed(){
        android.os.Process.killProcess(android.os.Process.myPid());
    }
}

A kód nem csinál túl sok mindent. Előkészíti a képernyőt a rendereléshez és kitölti az egész képernyőt. Általában előírás, hogy bizonyos gombokra a demó lépjen ki, ez itt a vissza gomb lenyomásakor történik. A következő lépés, hogy implementáljuk a cgSurfaceView osztályt. Itt állítjuk be, hogy miként jelenjen meg az OpenGL tartalom. Ez is egyszerű, alig különbözik a Google tutorialtól.

package com.cybernetic.genetics.android.demoframework;

import android.content.Context;
import android.opengl.GLSurfaceView;

import static android.opengl.GLSurfaceView.RENDERMODE_CONTINUOUSLY;

public class cgSurfaceView extends GLSurfaceView {

    private final demoRenderer demo;

    public cgSurfaceView(Context context) {
        super(context);
        setEGLContextClientVersion(2);
        demo = new demoRenderer(context);
        setRenderer(demo);
        setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
    }
}

 Mivel folyamatosan fogjuk változtatni a képernyő tartalmát, ezért a RENDERMODE_CONTINUOUSLY-t kell használnunk. Ugyan csak itt állítjuk be a használni kívánt OpenGL verziószámát is. Ha az alapértelmezettől eltérő pixel formátummal kívánunk dolgozni, azt is itt tehetjük meg. Végezetül jön maga a demó lelke, a rajzoló rutinok, amit a demoRenderer osztályban fogunk össze.

package com.cybernetic.genetics.android.demoframework;

import android.content.Context;
import android.media.MediaPlayer;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.util.Log;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class demoRenderer implements GLSurfaceView.Renderer {
    private FloatBuffer vertex;
    private static float coords[] = {-1f,-1f, 1f,-1f,1f,1f,-1f,1f};

    private final String vertexsource =
            "attribute vec4 pos;" +
            "void main(){" +
            "gl_Position = pos;" +
            "}";
    private final String fragmentsource =
            "precision mediump float;" +
            "void main(){" +
            "gl_FragColor = vec4(1.0, 1.0, 1.0, 0.0);" +
            "}";
    private int defaultprg;
    private MediaPlayer music;
    private Context context;

    public demoRenderer(Context context) {
        this.context = context;
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {

        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

        ByteBuffer bb = ByteBuffer.allocateDirect(8 * 4);
        bb.order(ByteOrder.nativeOrder());
        vertex = bb.asFloatBuffer();
        vertex.put(coords);
        vertex.position(0);

        defaultprg = createGLProgram(fragmentsource);

        music = MediaPlayer.create(context, R.raw.music);
        music.start();
    }

    private int createGLProgram(String fragmentsrc){
        int program;
        int error[] = new int[1];

        int vshader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
        int fshader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
        GLES20.glShaderSource(vshader, vertexsource);
        GLES20.glShaderSource(fshader, fragmentsrc);
        GLES20.glCompileShader(vshader);
        GLES20.glGetShaderiv(vshader, GLES20.GL_COMPILE_STATUS, error, 0);
        if(error[0] == GLES20.GL_FALSE){
            Log.d("Demo", "Vertex Shader error" + GLES20.glGetShaderInfoLog(vshader));
        }
        GLES20.glCompileShader(fshader);
        GLES20.glGetShaderiv(fshader, GLES20.GL_COMPILE_STATUS, error, 0);
        if(error[0] == GLES20.GL_FALSE){
            Log.d("Demo", "Fragment Shader error" + GLES20.glGetShaderInfoLog(fshader));
        }

        program = GLES20.glCreateProgram();
        GLES20.glAttachShader(program, vshader);
        GLES20.glAttachShader(program, fshader);
        GLES20.glLinkProgram(program);
        GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, error, 0);
        if(error[0] == GLES20.GL_FALSE){
            Log.d("Demo", "Compile error" + GLES20.glGetProgramInfoLog(program));
        }

        return program;
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
        int pos = music.getCurrentPosition();
        GLES20.glUseProgram(defaultprg);

        int loc = GLES20.glGetAttribLocation(defaultprg, "pos");
        GLES20.glEnableVertexAttribArray(loc);
        GLES20.glVertexAttribPointer(loc, 2, GLES20.GL_FLOAT, false, 8, vertex);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 4);
        GLES20.glDisableVertexAttribArray(loc);
    }

A munka dandárját ez az osztály végzi. A zene pozícióját használhatjuk az időzítéshez. Ha további modelleket akarunk megjeleníteni, a ByteBufferes átalakításra szükségünk lesz. Vegyük figyelembe, hogy a rendszer nem tökéletes. Gyakorlatilag a fő szálon végezzük az összes inicializálást, ami a nagy mennyiségű modell, textúra, shader betöltésénél problémát okozhat. Mivel a mi demónkat minimalista stílusra terveztük, ez nem okozott gondot. A dokumentáció már a zene inicializálását sem javasolja a fő szálon, de a gyakorlatban ez sehol nem okozott problémát. Apropó, zene. A zene fájl elhelyezéséhez hozzunk létre egy raw könyvtárat a res könyvtáron belül és helyezzük el a zenét music.mp3 néven.

Szólj hozzá!

Címkék: programozás demoscene android

A bejegyzés trackback címe:

https://cybernetic.blog.hu/api/trackback/id/tr817315432

Kommentek:

A hozzászólások a vonatkozó jogszabályok  értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai  üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a  Felhasználási feltételekben és az adatvédelmi tájékoztatóban.

Nincsenek hozzászólások.
süti beállítások módosítása