2010年11月8日月曜日

androidで効果音マネージャ

やっぱりゲームには効果音が必要なので、調べると、SoundPoolってのが良いらしい。
ところで、ゲーム作ったことがある人には常識だけど、同じフレーム内で同じ音声を鳴らしたって意味が無い。むしろ読み込みやらなんやらで負荷が増大してるはず。

なので、効果音を鳴らしたい時に、直で鳴らすのではなく、「鳴らしたい予約」をしておいて、あとで予約されているものを全て鳴らすべき。

さらに言えば、60fpsで動いてるとして、1フレーム目に鳴らした音を2フレーム目に鳴らしてもやっぱり無駄。 なので、前回再生フレームを覚えておいて、ある程度の間隔があった場合だけ再生するようにするのがよさそう(今回はまだここまでやってないけど)

と、いうわけで、こんな感じでSoundManagerを作ってみた。

package dividebyzero.net;

import java.util.HashMap;

import android.content.Context;
import android.media.AudioManager;
import android.media.SoundPool;

public class SoundManager {
    // 個々のサウンド
    public SoundManager(int sndId) {
        this.sndId = sndId;
        this.isPlay = false;
    }

    private int sndId;
    private boolean isPlay;
    private float leftVol, rightVol;

    public void play(float leftVol, float rightVol) {
        this.leftVol = leftVol;
        this.rightVol = rightVol;
        this.isPlay = true;
    }

    public void play(int vol) {
        play(vol, vol);
    }

    public void play() {
        play(100, 100);
    }

    // 管理スタティックゾーン
    private static HashMap<String, SoundManager> soundMap = new HashMap<String, SoundManager>();
    private static SoundPool sp;
    private static Context _context;

    public static SoundManager get(String name) {
        return soundMap.get(name);
    }

    public static void init(Context context) {
        _context = context;
    }

    // 予約されたものを全部再生
    public static void playAll() {
        for (SoundManager obj : soundMap.values()) {
            if (obj.isPlay) {
                sp.play(obj.sndId, obj.leftVol, obj.rightVol, 1,0, 1f);
                obj.isPlay = false;
            }
        }
    }

    public static SoundManager add(int id, String name) {
        SoundManager sm = soundMap.get(name);
        if (sm == null) {
            if (sp == null)
                sp = new SoundPool(10, AudioManager.STREAM_MUSIC, 100);
            sm = new SoundManager(sp.load(_context, id, 1));
            soundMap.put(name, sm);
        }
        return sm;
    }

    public static void release() {
        sp.release();
    }
}

使い方としては、効果音(出来ればogg)をres-rawフォルダに突っ込んで、


public void init(){
         SoundManager.add(R.raw.jump,"適当な名前");
}

public void update(){
        if(ボタンを押したら){

                SoundManager.get("適当な名前").play();
        }
//////////////////////////////////最後に
        SoundManager.playAll();
}

という感じ。
文字列がキーになっているのが、ちょっとオーバーヘッドありそうですが、気になる人はget()の戻り値がSoundManager型なので、確保しておいてplay()すりゃー良いと思います。

ちょっとSoundPoolのインスタンスの作る場所が気に食わないけど、初期化タイミングが難しいですよなぁ。
もうちょっと修正する予定。

これとは別に、BGMでループ再生とかにはMediaPlayerを使う必要がありそうです。

0 件のコメント:

コメントを投稿