本章音效文件都来自于公共许可: http://files.cnblogs.com/mignet/sounds.zip

在游戏中,播放背景音乐和音效是基本的功能。

Libgdx提供了跨平台的声音播放功能,支持的文件格式有:
•wav (RIFF WAVE)
•mp3 (MPEG-2 Audio Layer III)
•ogg (Ogg Vorbis)

Libgdx有2个接口来管理音乐和音效:Music和Sound.

Music通常要花更多的CPU周期,因为它在播放到声卡之前需要解析。Sound就不用了,因为Sound在加载的时候都已经解析过了。

常规用法:Sound sound = Gdx.audio.newSound(Gdx.files.internal("sound.wav"));同时记得sound.dispose(); // free allocated memory

除了Music和Sound,Libgdx还提供了底层的接口AudioDevice、AudioRecorder用来访问声卡以记录和播放原始的声音样本。

AudioDevice接口允许您直接发送PCM编码音频样本到音频设备。例如:

AudioDevice audioDevice = Gdx.audio.newAudioDevice(44100, false);//44.1 kHz

audioDevice.dispose(); // free allocated memory

发送到声卡的声音数据可以是一组浮点数或者是一组16位带符号短整形数.

void writeSamples(float[] samples, int offset, int numSamples);//offset (start),numSamples (length)
void writeSamples(short[] samples, int offset, int numSamples);

立体声意味着需要采集的样本是双倍的,因为立体声就是左声道和右声道交叉合成的。就是说44.1 kHz的采样率需要44100的单声道样本和88200的立体声样本。

AudioRecorder audioRecordedr = Gdx.audio.newAudioRecorder(44100, false);

audioRecorder.dispose(); // free allocated memory

同样的:void read(short[] samples, int offset, int numSamples);

现在,我们拥有足够的知识来生成自己的声音了,自己合成并保存,播放合成的声音测试效果,都可以实行了。

但是这样做对于很多经验丰富的程序员来讲都太高端了,哈哈。我们这里不讨论。

一个可行的解决方案来获得一些不错的音响效果是使用一个现有的自由的和开放源码的声音生成器。

程序员自己的音效生成工具->声音生成器:

sfxr

这个生成器最初是由托马斯“DrPetter“佩特森在2007年开发的,后来慢慢的又出现了几个sfxr变体版本,像bfxr, cfxr, as3sfxr

首先来看sfxr:

sfxr之所以可以很快流行并被全球的程序员们使用,是因为只需要简单的按下这个软件上面的“RANDOMIZE”按钮,就可以生成声音。

而且还提供了很多基本的音效像PICKUP/COIN, LASER/SHOOT, EXPLOSION, POWERUP, HIT/HURT, JUMP, BLIP/SELECT

生成想要的声音之后,导出.wav文件就可以用了。

官方源码:https://code.google.com/p/sfxr/

cfxr是Cocoa sfxr的缩写,是Mac OS下原生的Cocoa应用程序,专门为Cocoa写的实现版本。

官方源码:https://github.com/nevyn/cfxr/

bfxr:这个功能很丰富,可以创建更复杂的音效,有兴趣可以自己摸索。

官方源码:https://github.com/increpare/bfxr/

现在开始在我们的游戏中使用音乐和音效。先把文件添加到assert(背景音乐没有提供下载因为太大了,你可以随便用什么音乐文件代替):

和前面的资源管理提到的一样,我们使用内部类来统一管理各种资源。在Asserts中添加:

    public AssetSounds sounds;
public AssetMusic music; public class AssetSounds {
public final Sound jump;
public final Sound jumpWithFeather;
public final Sound pickupCoin;
public final Sound pickupFeather;
public final Sound liveLost; public AssetSounds(AssetManager am) {
jump = am.get("sounds/jump.wav", Sound.class);
jumpWithFeather = am.get("sounds/jump_with_feather.wav",
Sound.class);
pickupCoin = am.get("sounds/pickup_coin.wav", Sound.class);
pickupFeather = am.get("sounds/pickup_feather.wav", Sound.class);
liveLost = am.get("sounds/live_lost.wav", Sound.class);
}
} public class AssetMusic {
public final Music song01; public AssetMusic(AssetManager am) {
song01 = am.get("music/keith303_-_brand_new_highscore.mp3",
Music.class);
}
}

然后在init里添加:

public void init(AssetManager assetManager) {
this.assetManager = assetManager;
// set asset manager error handler
assetManager.setErrorListener(this);
// load texture atlas
assetManager.load(Constants.TEXTURE_ATLAS_OBJECTS, TextureAtlas.class);
// load sounds
assetManager.load("sounds/jump.wav", Sound.class);
assetManager.load("sounds/jump_with_feather.wav", Sound.class);
assetManager.load("sounds/pickup_coin.wav", Sound.class);
assetManager.load("sounds/pickup_feather.wav", Sound.class);
assetManager.load("sounds/live_lost.wav", Sound.class);
// load music
assetManager.load("music/keith303_-_brand_new_highscore.mp3",
Music.class);
// start loading assets and wait until finished
assetManager.finishLoading();
Gdx.app.debug(TAG,
"# of assets loaded: " + assetManager.getAssetNames().size);
for (String a : assetManager.getAssetNames())
Gdx.app.debug(TAG, "asset: " + a);
TextureAtlas atlas = assetManager.get(Constants.TEXTURE_ATLAS_OBJECTS);
// enable texture filtering for pixel smoothing
for (Texture t : atlas.getTextures())
t.setFilter(TextureFilter.Linear, TextureFilter.Linear);
// create game resource objects
fonts = new AssetFonts();
bunny = new AssetBunny(atlas);
rock = new AssetRock(atlas);
goldCoin = new AssetGoldCoin(atlas);
feather = new AssetFeather(atlas);
levelDecoration = new AssetLevelDecoration(atlas);
sounds = new AssetSounds(assetManager);
music = new AssetMusic(assetManager);
}

还记得我们在Options菜单中让用户来设置音乐音效吗,现在派上用场了。

创建新类AudioManager来管理播放和停止:

package com.packtpub.libgdx.canyonbunny.util;

import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound; public class AudioManager {
public static final AudioManager instance = new AudioManager();
private Music playingMusic; // singleton: prevent instantiation from other classes
private AudioManager() {
} public void play(Sound sound) {
play(sound, 1);
} public void play(Music music) {
stopMusic();
playingMusic = music;
if (GamePreferences.instance.music) {
music.setLooping(true);
music.setVolume(GamePreferences.instance.volMusic);
music.play();
}
} public void stopMusic() {
if (playingMusic != null)
playingMusic.stop();
} public void onSettingsUpdated() {
if (playingMusic == null)
return;
playingMusic.setVolume(GamePreferences.instance.volMusic);
if (GamePreferences.instance.music) {
if (!playingMusic.isPlaying())
playingMusic.play();
} else {
playingMusic.pause();
}
} public void play(Sound sound, float volume) {
play(sound, volume, 1);
} public void play(Sound sound, float volume, float pitch) {
play(sound, volume, pitch, 0);
} public void play(Sound sound, float volume, float pitch, float pan) {
if (!GamePreferences.instance.sound)
return;
sound.play(GamePreferences.instance.volSound * volume, pitch, pan);
}
}

修改MenuScreen:

    private void onSaveClicked() {
saveSettings();
onCancelClicked();
AudioManager.instance.onSettingsUpdated();
} private void onCancelClicked() {
btnMenuPlay.setVisible(true);
btnMenuOptions.setVisible(true);
winOptions.setVisible(false);
AudioManager.instance.onSettingsUpdated();
}

修改CanyonBunnyMain的create方法:

@Override
public void create() {
// Set Libgdx log level
Gdx.app.setLogLevel(Application.LOG_DEBUG);
// Load assets
Assets.instance.init(new AssetManager());
// Load preferences for audio settings and start playing music
GamePreferences.instance.load();
AudioManager.instance.play(Assets.instance.music.song01);
// Start game at menu screen
ScreenTransition transition = ScreenTransitionSlice.init(2,
ScreenTransitionSlice.UP_DOWN, 10, Interpolation.pow5Out);
setScreen(new MenuScreen(this), transition);
}

Libgdx自动处理在游戏暂停和返回时的音乐播放问题,所以这里不需要额外的代码修改。

继续修改WorldController:

..
public void update(float deltaTime) {
..
if (!isGameOver() && isPlayerInWater()) {
AudioManager.instance.play(Assets.instance.sounds.liveLost);
..
}
private void onCollisionBunnyWithGoldCoin(GoldCoin goldcoin) {
        goldcoin.collected = true;
        AudioManager.instance.play(Assets.instance.sounds.pickupCoin);
        score += goldcoin.getScore();
        Gdx.app.log(TAG, "Gold coin collected");
    }     private void onCollisionBunnyWithFeather(Feather feather) {
        feather.collected = true;
        AudioManager.instance.play(Assets.instance.sounds.pickupFeather);
        score += feather.getScore();
        level.bunnyHead.setFeatherPowerup(true);
        Gdx.app.log(TAG, "Feather collected");
    }

修改BunnyHead:

public void setJumping(boolean jumpKeyPressed) {
switch (jumpState) {
case GROUNDED: // Character is standing on a platform
if (jumpKeyPressed) {
AudioManager.instance.play(Assets.instance.sounds.jump);
// Start counting jump time from the beginning
timeJumping = 0;
jumpState = JUMP_STATE.JUMP_RISING;
}
break;
case JUMP_RISING: // Rising in the air
if (!jumpKeyPressed) {
jumpState = JUMP_STATE.JUMP_FALLING;
}
break;
case FALLING:// Falling down
case JUMP_FALLING: // Falling down after jump
if (jumpKeyPressed && hasFeatherPowerup) {
AudioManager.instance.play(
Assets.instance.sounds.jumpWithFeather, 1,
MathUtils.random(1.0f, 1.1f));
timeJumping = JUMP_TIME_OFFSET_FLYING;
jumpState = JUMP_STATE.JUMP_RISING;
}
break;
}
}

ok,运行起来看看。游戏从无声世界进入了有声世界里。从此,程序员也可以自己玩音乐了。

[libgdx游戏开发教程]使用Libgdx进行游戏开发(10)-音乐和音效的更多相关文章

  1. [libGDX游戏开发教程]使用libGDX进行游戏开发(12)-Action动画

    前文章节列表:  使用libGDX进行游戏开发(11)-高级编程技巧   使用libGDX进行游戏开发(10)-音乐音效不求人,程序员也可以DIY   使用libGDX进行游戏开发(9)-场景过渡   ...

  2. [libGDX游戏开发教程]使用libGDX进行游戏开发(1)-游戏设计

    声明:<使用Libgdx进行游戏开发>是一个系列,文章的原文是<Learning Libgdx Game Development>,大家请周知.后续的文章连接在这里 使用Lib ...

  3. [libgdx游戏开发教程]使用Libgdx进行游戏开发(9)-场景过渡

    本章主要讲解场景过渡效果的使用.这里将用到Render to Texture(RTT)技术. Libgdx提供了一个类,实现了各种常见的插值算法,不仅适合过渡效果,也适合任意特定行为. 在本游戏里面, ...

  4. 使用Html5+C#+微信 开发移动端游戏详细教程: (四)游戏中层的概念与设计

    众所周知,网站的前端页面结构一般是由div组成,父div包涵子div,子div包涵各种标签和项, 同理,游戏中我们也将若干游戏模块拆分成层,在后续的代码维护和游戏程序逻辑中将更加清晰和便于控制. We ...

  5. 微信小程序开发教程 #043 - 在小程序开发中使用 npm

    本文介绍了如何在微信小程序开发中使用 npm 中包的功能,大大提高微信小程序的开发效率,同时也是微信小程序系列教程的视频版更新. 微信小程序在发布之初没有对 npm 的支持功能,这也是目前很多前端开发 ...

  6. PythonWeb开发教程(一),开发之前需要准备什么

    什么是web开发呢,其实就是开发一个网站了.那开发网站需要用到哪些知识呢 1.python基础,因为用python开发的,所以python指定要会,最起码你也得会条件判断,循环,函数,类这些知识: 2 ...

  7. [libgdx游戏开发教程]使用Libgdx进行游戏开发(11)-高级编程技巧 Box2d和Shader

    高级编程技巧只是相对的,其实主要是讲物理模拟和着色器程序的使用. 本章主要讲解利用Box2D并用它来实现萝卜雨,然后是使用单色着色器shader让画面呈现单色状态:http://files.cnblo ...

  8. [libgdx游戏开发教程]使用Libgdx进行游戏开发(2)-游戏框架搭建

    让我们抛开理论开始code吧. 入口类CanyonBunnyMain的代码: package com.packtpub.libgdx.canyonbunny; import com.badlogic. ...

  9. [libgdx游戏开发教程]使用Libgdx进行游戏开发(7)-屏幕布局的最佳实践

    管理多个屏幕 我们的菜单屏有2个按钮,一个play一个option.option里就是一些开关的设置,比如音乐音效等.这些设置将会保存到Preferences中. 多屏幕切换是游戏的基本机制,Libg ...

随机推荐

  1. To Chromium之版本管理

    Git. 1.由于想直接submit到Chromium的官方Branch需要申请权限,目前拿不到,所以打算snapshot一个chromium版本. 本地搭建一个git的server/client,方 ...

  2. 文件系统中 atime,lazytime,relatime 详聊

    atime,ctime,mtime是文件inode的三个时间戳,分别表示文件最近一次的访问时间:inode本身的更改(changed)时间:文件数据的更改(modify)时间:这几个概念还是很好区分. ...

  3. Delphi组件开发教程指南目录

    用Delphi开发的时间也不短了!刚接触Delphi的时候,就觉得组件这个东西非常方便!根本不必知道组件内部的作为,只要使用就好了!然而用的多了,也不免会对各种delphi组件的内部实现方式产生兴趣! ...

  4. Redis使用手册

    简介 Redis 是一个开源的使用 ANSI C 语言编写.支持网络.可基于内存亦可持久化的日志型. Key-Value数据库. Redis面向互联网的方案提供了三种形式: 1.主从 主机进行写操作, ...

  5. JSP九大内置对象及其方法

    内置对象包括 request response pageContext session application out config page exception 1.out (1)clear()清除 ...

  6. 【题解】Bzoj4316小C的独立集

    决定要开始学习圆方树 & 仙人掌相关姿势.加油~~ 其实感觉仙人掌本质上还是一棵树,长得也还挺优美的.很多的想法都可以往树的方面上靠,再针对仙人掌的特性做出改进.这题首先如果是在树上的话那么实 ...

  7. [洛谷P1430]序列取数

    题目大意:给定一个序列$s$,每个人每轮可以从两端(任选一端)取任意个数的整数,不能不取.在两个人都足够聪明的情况下,求先手的最大得分. 题解:设$f_{i,j}$表示剩下$[i,j]$,先手的最大得 ...

  8. WCF分布式开发步步为赢(13):WCF服务离线操作与消息队列MSMQ

    之前曾经写过一个关于MSMQ消息队列的文章:WCF分布式开发必备知识(1):MSMQ消息队列 ,当时的目的也是用它来作为学习WCF 消息队列MSMQ编程的基础文章.在那篇文章里,我们详细介绍了MSMQ ...

  9. Codeforces Round #350 (Div. 2) C

    C. Cinema time limit per test 2 seconds memory limit per test 256 megabytes input standard input out ...

  10. HDU2255:奔小康赚大钱(KM算法)

    奔小康赚大钱 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Subm ...