广告说的好“没有声音再好的戏也出不来”,下面就让我们为程序加入声音效果。
一般情况下,游戏中的声音分为音乐和音效两个部分。直观上的区别,音乐播放的时间较长,资源文件较大。音效播放时间较短,资源文件比较小。在Android中,我们使用两种不同的方法播放音乐和音效。
播放音乐需要用到MediaPlayer类,MediaPlayer本身比较复杂,这里我们只做一个简单的介绍,满足播放音乐的基本要求即可,读者可以参考帮助文档中的内容深入研究。
为了调用方便,我们创建一个Util类,将播放背景音乐的方法封装到Util中
public class Util {
static MediaPlayer mp;
static void playBGM(String path, boolean looping) {
if(mp == null) {
mp = new MediaPlayer();
}
mp.reset();
try {
mp.setDataSource(path);
mp.prepare();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mp.setLooping(looping);
mp.start();
}
}
让我们分析一下这段代码:首先,我们需要一个MediaPlayer实例,然后重置MediaPlayer到Idle状态。注意,这个操作是必需的,如果当前正在播放,不重置状态直接调用setDataSource会抛出异常。然后指定我们要播放的文件,并调用prepare方法进行准备。如果你想指定开始时间(seekTo)必须在prepare之后进行。还可以设定是否循环,最后就是调用start播放了。
封装好后,我们可以方便的使用这个函数,我们在SceneStartMenu中重载start方法
public void start(SurfaceHolder surfaceHolder){
Util.playBGM("/sdcard/sample.mid", false);
}
这样,程序运行后就开始播放音乐了。还要注意的是,我们应该在适当的时候释放MediaPlayer占用的资源。
运行一下编辑好的代码看看效果吧。sample.mid在res/raw目录下。
下面让我来学习如何播放音效,它将使用完全不同的方法SoundPool。SoundPool虽然是基于MediaPlayer的,但是他被优化用来同时播放多个文件,而且不适合播放较大的文件,可以说,他就是为播放音效定制的。下面就让我们看一下播放音效的代码:
private static int MAX_CHANNEL = 6;
private static SoundPool soundPool;
private static HashMapsoundPoolMap;
private static AudioManager audioManager;
首先设定声道数,如果同时播放的音频超过这个数量,最先播放的音频就会被关闭。然后定义SoundPool变量。大家注意soundPoolMap这个变量,下面会重点讲解。
public static void playSE(String path, float volume, int loop, float rate) {
if(soundPool == null) {
soundPool = new SoundPool(MAX_CHANNEL, AudioManager.STREAM_MUSIC, 10);
}
if(soundPoolMap == null) {
soundPoolMap = new HashMap();
}
if(!soundPoolMap.containsKey(path)) {
soundPoolMap.put(path, soundPool.load(path, 1));
}
这里我们要讲解一下soundPoolMap。在音频被播放前需要先将它载入到内存,载入后,会得到一个streamID,SoundPool中的很多函数都使用这个ID来控制特定的音频流。所以,我们将这个ID保存到map中,并与音频文件的路径相对应。后面就可以通过路径来找到这段音频流了。
if(volume < 0) {
if(audioManager == null)
audioManager = (AudioManager)Main.getInstance()
.getSystemService(Context.AUDIO_SERVICE);
volume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
}
while(soundPool.play(soundPoolMap.get(path),
volume, volume, 0, loop, rate) == 0);
}
这个方法并不是很科学,最好还是在初始化的时候载入音频,大家可以另行撰写一个load方法。
public void playSE(String path) {
playSE(path, -1f, 0, 1f);
}
public void stopSE(String path) {
if(soundPool != null && soundPoolMap != null
&& soundPoolMap.containsKey(path)) {
soundPool.stop(soundPoolMap.get(path));
}
}
修改SceneStartMenu中的start函数测试一下吧
public void start(SurfaceHolder surfaceHolder){
// Util.playBGM("/sdcard/sample.mid", false);
Util.playSE("/sdcard/system11.ogg");
Util.playSE("/sdcard/system12.ogg");
}
现在,这部教程终于可以告一段落了,虽然有点虎头蛇尾,但我大概已经讲解了制作一个简单游戏所用到的各方面的知识。如果还有不足,请读者留言补充,大家互相学习,一起提高。最终我在教程中没有完成坦克大战,不过我会基于坦克大战设计一个新的游戏,不再使用Java,而是一个全新的跨平台框架,到时候再与大家分享我的心得。
本章示例程序 http://u.115.com/file/f1435ee7e8