万年素人からHackerへの道

万年素人がHackerになれるまで殴り書きするぜ。

OpenGLで作るiPhone SDKゲームプログラミングを参考に
iPhoneではなくAndroid用のパーティクルを移植してみたが、
うまく動かない・・・

particle.pngを「drawable-hdpi」、「drawable-ldpi」
「drawable-mdpi」へそれぞれ配置した。

変なコメントや、趣味で片言英語に翻訳された部分があるので悪しからず

ParticleSystem.java

package sample.game.utils;

import javax.microedition.khronos.opengles.GL10;
//import java.nio.FloatBuffer;
import sample.game.utils.GraphicUtils;
public class ParticleSystem {
    
//    private GL10  gl;
    private Particle particle[];
    private int capacity;

    /**
     * Constructor
     * @param capacity
     * @param particleLifeSpan
     */
    public ParticleSystem(int capacity, int particleLifeSpan) {
        this.capacity = capacity;
        
        // create dynamically particle array
        particle = new Particle[capacity];
        
        // create array for particle management
        for(int i=0;i<capacity;i++) {
            particle[i] = new Particle();
            particle[i].setLifeSpan(particleLifeSpan);
        }
    }

    public void add(float x, float y, float size, float moveX, float moveY) {
        
        for(int i=0;i<capacity;i++) {
            // search non-active(not using) status particle
            if(particle[i].isActiveFlag() == false) {
                particle[i].setActiveFlag(true); // set active(using)
                particle[i].setX(x);
                particle[i].setY(y);
                particle[i].setSize(size);
                particle[i].setMoveX(moveX);
                particle[i].setMoveY(moveY);
                particle[i].setFrameNumber(0);
                break;
            }
        }
    }
    
    // draw particles TODO
    public void draw(GL10 gl, int textureId) {
    
        // array for vertices
        // every a particle  6頂点 x 2要素(x,y) x max particle number
        float vertices[] = new float[6 * 2 * capacity]; 
         
        // array for colors
        // every a particle 6頂点 x 4要素(r,g,b,a) x max particle number
        float colors[] = new float[6 * 4 * capacity]; 
 
        // array for texture mapping
        // every a particle 6頂点 x 2要素(x,y) x max particle number
        float texCoords[] = new float[6 * 2 * capacity]; 
         
        // active particle count
        int vertexIndex = 0;
        int colorIndex = 0;
        int texCoordIndex = 0;
        
        int activeParticleCount = 0;
        // watch individually every particle
        for (int i=0;i<capacity;i++) {
            // render only active status particle
            if (particle[i].isActiveFlag() == true) {
                
                // add vertices 頂点座標を追加します
                float centerX = particle[i].getX();
                float centerY = particle[i].getY();
                float size = particle[i].getSize();
                float vLeft = -0.5f*size + centerX;
                float vRight = 0.5f*size + centerX;
                float vTop = 0.5f*size + centerY;
                float vBottom = -0.5f*size + centerY;
                
                // polygon1
                vertices[vertexIndex++] = vLeft; vertices[vertexIndex++] = vTop; // left-up
                vertices[vertexIndex++] = vRight; vertices[vertexIndex++] = vTop; // right-up
                vertices[vertexIndex++] = vLeft; vertices[vertexIndex++] = vBottom; // left-down
                
                // polygon2
                vertices[vertexIndex++] = vRight; vertices[vertexIndex++] = vTop; // right-up
                vertices[vertexIndex++] = vLeft; vertices[vertexIndex++] = vBottom; // left-down
                vertices[vertexIndex++] = vRight; vertices[vertexIndex++] = vBottom; // right-down
                
                // color
                float lifePercentage = (float)(particle[i].getFrameNumber()) / (float)(particle[i].getLifeSpan());
                int alpha;
                if (lifePercentage <= 0.5f) { // まだ寿命が半分以上のこっている場合
                    alpha = (int)Math.round(lifePercentage * 2.0f * 255.0f);
                } else {
                    alpha = 255 - (int)Math.round(lifePercentage * 2.0f * 255.0f);
                }
                
                colors[colorIndex++] = 255; colors[colorIndex++] = 255; colors[colorIndex++] = 255; colors[colorIndex++] = alpha;    
                colors[colorIndex++] = 255; colors[colorIndex++] = 255; colors[colorIndex++] = 255; colors[colorIndex++] = alpha;    
                colors[colorIndex++] = 255; colors[colorIndex++] = 255; colors[colorIndex++] = 255; colors[colorIndex++] = alpha;    
                colors[colorIndex++] = 255; colors[colorIndex++] = 255; colors[colorIndex++] = 255; colors[colorIndex++] = alpha;    
                colors[colorIndex++] = 255; colors[colorIndex++] = 255; colors[colorIndex++] = 255; colors[colorIndex++] = alpha;    
                colors[colorIndex++] = 255; colors[colorIndex++] = 255; colors[colorIndex++] = 255; colors[colorIndex++] = alpha;    
                
                // mapping coords
                // polygon1
                texCoords[texCoordIndex++] = 0.0f; texCoords[texCoordIndex++] = 0.0f; // left-up
                texCoords[texCoordIndex++] = 1.0f; texCoords[texCoordIndex++] = 0.0f; // right-up
                texCoords[texCoordIndex++] = 0.0f; texCoords[texCoordIndex++] = 1.0f; // left-down
                // polygon2
                texCoords[texCoordIndex++] = 1.0f; texCoords[texCoordIndex++] = 0.0f; // right-up
                texCoords[texCoordIndex++] = 0.0f; texCoords[texCoordIndex++] = 1.0f; // left-down
                texCoords[texCoordIndex++] = 1.0f; texCoords[texCoordIndex++] = 1.0f; // right-down
                
                // count active particle
                activeParticleCount++;
            }
        }
        
        gl.glEnable(GL10.GL_TEXTURE_2D);
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); // TODO 要チェック
        gl.glVertexPointer(2, GL10.GL_FLOAT, 0, GraphicUtils.getFloatBuffer(vertices)); // 描画する頂点配列を設定
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
//        gl.glColorPointer(4, GL10.GL_UNSIGNED_BYTE, 0, FloatBuffer.wrap(colors));
        gl.glColorPointer(4, GL10.GL_UNSIGNED_BYTE, 0, GraphicUtils.getFloatBuffer(colors));
        gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
//        gl.glRotatef((float) (90/180.0f*Math.PI), 1.0f, 0.0f, 0.0f);// TODO
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, GraphicUtils.getFloatBuffer(texCoords));
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        
        gl.glDrawArrays(GL10.GL_TRIANGLES, 0, activeParticleCount * 6);
        
        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        gl.glDisable(GL10.GL_TEXTURE_2D);
    }

    public void update() {
        for (int i=0;i<capacity;i++) {
            // work only active(using) particle
            if (particle[i].isActiveFlag() == true) {
                particle[i].update();
            }
        }
    }
}
package sample.game.utils;
//import org.primitive.GraphicUtil;
//import javax.microedition.khronos.opengles.GL10;

/**
 * Particle System
 */

public class Particle {
    // gradient value
    private float x;
    private float y;
    private float size;
    private boolean activeFlag;
    private float moveX;
    private float moveY;
    private int frameNumber;
    private int lifeSpan;

    /**
     * Constructor
     *
     */
    public Particle() {
        x = 0.0f;
        y = 0.0f;
        size = 1.0f;
        activeFlag = false;
        moveX = 0.0f;
        moveY = 0.0f;
        frameNumber = 0;
        lifeSpan = 0;
    }
/* 使ってない??
    public void draw(GL10 gl, int tex_id) {
        float lifePercentage = (float)frameNumber / (float)getLifeSpan();
        int alpha;
        
        if (lifePercentage <= 0.5f){ // if life is more than half
            // fade in
            alpha = (int)Math.round(lifePercentage * 2.0f * 255.0f);
        } else {
            // fade out
            alpha = 255 - (int)Math.round(lifePercentage * 2.0f * 255.0f);
        }
        GraphicUtil.drawTexture(gl, tex_id, x, y, size, size, 255, 255, 255, alpha);
    }
    */
    public void update() {
        frameNumber++;
        if (frameNumber >= getLifeSpan()) setActiveFlag(false);
        
        x += moveX;
        y += moveY;
    }

    public void setX(float x) {
        this.x = x;
    }

    public float getX() {
        return x;
    }

    public void setY(float y) {
        this.y = y;
    }

    public float getY() {
        return y;
    }

    public void setLifeSpan(int lifeSpan) {
        this.lifeSpan = lifeSpan;
    }

    public void setActiveFlag(boolean activeFlag) {
        this.activeFlag = activeFlag;
    }
    
    public int getLifeSpan() {
        return lifeSpan;
    }

    public boolean isActiveFlag() {
        return activeFlag;
    }
    
    public boolean setActiveFlag() {
        return activeFlag;
    }
    
    public void setFrameNumber(int frameNumber) {
        this.frameNumber = frameNumber;
    }

    public float getFrameNumber() {
        return size;
    }
    
    public void setMoveX(float moveX) {
        this.moveX = moveX;
    }

    public float getMoveX() {
        return moveX;
    }

    public void setMoveY(float moveY) {
        this.moveY = moveY;
    }

    public float getMoveY() {
        return moveY;
    }
    
    public void setSize(float size) {
        this.size = size;
    }

    public float getSize() {
        return size;
    }
}

■以下、ParticleSystemの読み込み元での処理

・パーティクルシステムの変数初期化

    private static ParticleSystem particleSystem;
    private static int particleTexture;

・パーティクルシステムのオブジェクトの初期化

particleSystem = new ParticleSystem(100, 30);

テクスチャの読み込み(このTextureLoader.javaのソースは初歩からわかるAndroid最新プログラミングを使ってるので、載せられません・・・)TextureLoader

        particleTexture = TextureLoader.loadTexture(gl, this, R.drawable.particle);

毎回呼ばれる処理(update()みたいなメソッドから呼ぶ)で以下を挿入

    particleSystem.update();

パーティクルの追加時

    particleSystem.add(0.0f, 0.0f, 0.5f, moveX, moveY);