This page has been translated automatically.
UNIGINE 基础课程
1. 简介
2. 虚拟世界管理
3. 3D模型准备
4. 材质
5. 摄像机和光照系统
7. 制作过场动画与动画序列
8. 准备发布项目
9. 物理系统
10. 优化基础
11. 项目2:第一人称射击游戏
12. PROJECT3: Third-Person Cross-Country Arcade Racing Game
13. PROJECT4: VR Application With Simple Interaction

播放声音

In addition to the visuals, sound is an important component of real-time solutions. It is sound that creates the feeling of immersion in the virtual world. A rumbling echo that makes you think the action is taking place in a spacious building, the soft tapping of footsteps on a stone floor or a car speeding past — all this can be simulated. UNIGINE provides a multi-channel sound system with stereo sound support based on HRTF (sound perception modeling function), various 3D effects, obstruction, and multiple sound reverberation. You can play sound in MP3, WAV, or OGA format when objects are in contact to simulate their physical properties at the sound level. For moving objects, the Doppler effect is simulated.除了视觉效果外,声音也是实时解决方案的重要组成部分。正是声音创造了虚拟世界中的沉浸感。让你以为动作发生在宽敞建筑中的隆隆回声、石地板上轻柔的脚步声或疾驰而过的汽车声,所有这些都可以模拟。UNIGINE提供了一个基于HRTF(声音感知建模函数)的多声道声音系统,支持立体声、各种3D效果、声音阻隔以及多重混响。你可以在物体接触时播放MP3、WAV或OGA格式的声音,从声音层面模拟其物理特性。对于移动的物体,还会模拟多普勒效应。

UNIGINE has two types of sound sources: UNIGINE有两种声音源类型:

  • Sound Source — used to create directional sound sources.Sound Source(声音源):用于创建定向声音源
  • Ambient Source — used for playing background music and sounds that can be heard throughout the scene.Ambient Source(环境音源):用于播放整个场景都能听到的背景音乐和声音

A directional sound source can be added to a scene either in the Editor or via code. To add it via code, all you need to do is create an instance of the SoundSource class and specify all the necessary settings. Sound playback can be turned on and off using the play() and stop() methods.定向声音源可以通过编辑器或代码添加到场景中。要通过代码添加,你只需创建SoundSource类的实例并指定所有必要设置即可。可以使用play()stop()方法开启和关闭声音播放。

源代码 (C++)
// 使用给定的声音采样文件创建新声音源
SoundSourcePtr sound = SoundSource::create("sound.mp3");

// 禁用被遮挡时的声音模糊效果
sound->setOcclusion(0);
// 设置声音变得清晰的距离
sound->setMinDistance(10.0f);
// 设置声音超出可听范围的距离
sound->setMaxDistance(100.0f);
// 设置声音放大系数
sound->setGain(0.5f);
// 循环播放声音
sound->setLoop(1);
// 开始播放声音采样 
sound->play();

The sound played depends on the relative position of the sound sources and the listener. The sound is linearly attenuated within the specified range (MinDistance and MaxDistance). If inner and outer sound cones are set, they will also contribute to the attenuation factor (ConeInnerAngle and ConeOuterAngle). In addition, various objects in the scene can also block the propagation of sound from SoundSource (you can even set different sound absorption coefficients for different surfaces). The number of such sound sources is unlimited, as only those within hearing range are played.声音的播放效果取决于声源与听者的相对位置。声音会在指定范围内(MinDistance和MaxDistance)进行线性衰减。如果设置了内外声音锥角(ConeInnerAngle和ConeOuterAngle),它们也会影响衰减系数。此外,场景中的各种物体也会阻挡SoundSource的声音传播(你甚至可以为不同表面设置不同的吸音系数)。这类声源的数量没有限制,因为系统只会播放可听范围内的声源。



To play background music and sounds that should be heard everywhere, you need to create an instance of the AmbientSource class (such sources can only be created via API). When creating it, the necessary parameters are also specified.要播放随处可闻的背景音乐和环境音效,你需要创建AmbientSource类的实例(此类声源只能通过API创建)。创建时同样需要指定必要参数。

注意
For an ambient source to be played, a Player is always required. In case an ambient source needs to be played when neither a world, nor the Editor are loaded, a Player, as well as the sound source should be created in the SystemLogic::init() method; otherwise, no sound will be heard.要使环境音源正常播放,必须存在Player实例。若需要在未加载世界场景或编辑器时播放环境音源,则必须在SystemLogic::init()方法中创建Player和声源,否则将无法听到声音。
源代码 (C++)
// 创建Player以保证环境音源能正常播放
PlayerSpectatorPtr player = PlayerSpectator::create();
player->setPosition(Vec3(0.0f, -3.401f, 1.5f));
player->setViewDirection(vec3(0.0f, 1.0f, -0.4f));
Game::setPlayer(player);

// 创建AmbientSource环境音源
AmbientSourcePtr sound = AmbientSource::create("sound.mp3");

// 设置必要的声音参数
sound->setGain(0.5f);
sound->setPitch(1.0f);
sound->setLoop(1);

// 开始播放声音采样
sound->play();

Sound is processed in a separate thread at 30 frames per second, so changes are not applied instantly. In some cases, such as after stopping playback before changing track, it is necessary to force update the audio stream to avoid application errors.声音处理在独立线程中以每秒30帧的频率进行,因此参数更改不会立即生效。在某些情况下(例如在切换音轨前停止播放时),需要强制更新音频流以避免应用错误。

Practice
实践#

To create an atmosphere in our application, let's add the StereoSystem component to play music tracks using AmbientSource.为在我们的应用中营造氛围,让我们添加StereoSystem组件来使用AmbientSource播放音乐曲目。

The component controls the stereo system in the room (switches it on and off, changes tracks).该组件控制房间内的音响系统(开关机、切换曲目)。

StereoSystem.h
#pragma once
#include <UnigineComponentSystem.h>
#include <UnigineSounds.h>
#include <UnigineVisualizer.h>
#include "Interactable.h"
class StereoSystem :
    public Interactable
{
	// 为我们的类声明构造函数和析构函数,并定义与组件关联的属性名称
	//  首次运行应用程序后,包含以下所有参数的StereoSystem.prop文件将在项目的'data'文件夹中生成
	COMPONENT_DEFINE(StereoSystem, Interactable);
	// 要播放的曲目列表
	PROP_ARRAY(File, sound_tracks);
	// 注册将在世界逻辑相应阶段调用的方法(方法在protected部分声明)
	COMPONENT_INIT(init);
	// 子组件重写的Action方法
	void action(int num = 0);
protected:
	// 声明将在世界逻辑相应阶段调用的方法
	void init();

private:
	// 当前曲目编号
	int current_track = 0;
	// 环境音源(AmbientSource)
	Unigine::AmbientSourcePtr track_player = nullptr;
};
StereoSystem.cpp
#include "StereoSystem.h"
// 组件 StereoSystem 的注册
REGISTER_COMPONENT(StereoSystem);
using namespace Unigine;
using namespace Math;

void StereoSystem::init()
{
	// 设置当光标悬停在对象上时显示的提示文本
	tooltip = "右键点击循环切换声音曲目";

	// 如果列表不为空则初始化第一首曲目
	if (sound_tracks.size() > 0)
		track_player = AmbientSource::create(Unigine::FileSystem::guidToPath(FileSystem::getGUID(sound_tracks[current_track].getRaw())));
	track_player->stop();
}

// 子组件重写的Action方法
void StereoSystem::action(int num)
{
	// 非零操作索引对此组件无效,将被忽略
	if (num != 0)
		return;
	// 如果播放列表为空则不执行任何操作
	if (sound_tracks.size() < 1)
		return;

	// 必要时在切换到下一曲目前停止播放当前曲目
	if (!track_player->isStopped()) {
		track_player->stop();
	}

	// 在切换曲目前停止播放后,需要强制刷新声音流
	Sound::renderWorld(1);

	// 将曲目切换为索引音频列表中的元素
	track_player->setSampleName(Unigine::FileSystem::guidToPath(FileSystem::getGUID(sound_tracks[current_track].getRaw())));

	// 显示当前曲目信息
	Visualizer::renderMessage2D(vec3(0.0f, (float)(WindowManager::getMainWindow()->getClientSize().y - 40) / WindowManager::getMainWindow()->getClientSize().y, 0.0f),
		vec3(1.0f, 0.1f, 0.0f),
		String::format(" > 正在播放曲目 №%d: %s", current_track + 1, sound_tracks[current_track].get()),
		Math::vec4_red, 1, 18, 1);
	track_player->setTime(0.0f);
	track_player->play();

	// 增加当前曲目索引(如果超过曲目数量则重置为0)
	current_track++;
	if (current_track >= sound_tracks.size())
		current_track = 0;
}

Let's save our files and then build and run our application by hitting Ctrl + F5 to make the Component System generate a property to be used to assign our component to nodes. Close the application after running it and switch to UnigineEditor.保存文件后按下Ctrl + F5构建并运行应用程序,使组件系统生成用于将组件分配给节点的属性文件。运行完成后关闭应用程序并切换至UnigineEditor。

Assign the StereoSystem component to the tv1 node in the scene (interior -> tv1) and add tracks from the archviz/sounds folder to the Sound Tracks list:StereoSystem组件分配给场景中的tv1节点(interior -> tv1),并从archviz/sounds文件夹添加音轨到Sound Tracks列表:

Let's also add a few lines to the Fan component to play the fan sound. The important point here is that the Fan component is assigned to the fan_rotator node controlled by Toggle, so if the component disables the fan_rotator node, the logic of the Fan component assigned to it will not be executed and the sound will not be turned off. We need to make that part of the Fan component code (namely the updateSound() method) work even if the node is disabled and be executed every frame.同时为Fan组件添加几行代码来播放风扇音效。这里的关键在于Fan组件被分配给了由Toggle控制的fan_rotator节点,因此如果该组件禁用了fan_rotator节点,分配给它的Fan组件逻辑将不会执行,音效也无法关闭。我们需要确保Fan组件代码中的这部分功能(即updateSound() 方法)在节点被禁用时仍能正常工作,并每帧执行。

So, that's how we declare the updateSound method (passing execution order and invoke_disabled flag as second and third arguments):这样,我们按如下方式声明updateSound方法(将执行顺序和invoke_disabled标志作为第二和第三个参数传递):

源代码 (C++)
COMPONENT_UPDATE(updateSound, 0 /*execution order*/, 1 /*invoke_disabled*/)

As a result, we have the following code and everything should work out well:最终,我们得到以下完整代码,所有功能应该都能正常运行:

Fan.h
#pragma once
#include <UnigineComponentSystem.h>
class Fan :
	public Unigine::ComponentBase
{
public:
	// 为我们的类声明构造函数和析构函数,并定义与组件关联的属性名称
	// 首次运行应用程序后,包含以下所有参数的Fan.prop文件将在项目的数据文件夹中生成
	COMPONENT_DEFINE(Fan, ComponentBase);

	// 组件参数的声明和初始化
	PROP_PARAM(Node, fan_node, nullptr);			// 需要旋转的风扇叶片节点
	PROP_PARAM(Float, speed, 720.0f);			// 旋转速度

	// 注册将在世界逻辑相应阶段调用的方法(方法在protected部分声明)
	COMPONENT_INIT(init);
	COMPONENT_UPDATE(update);
${#HL}$
	// 确保即使组件随节点被禁用,此方法仍会执行
	COMPONENT_UPDATE(updateSound, 0 /*order*/, 1 /*invoke_disabled*/); ${HL#}$

protected:
	// 声明将在世界逻辑相应阶段调用的方法
	void init();
	void update();
${#HL}$
	void updateSound();
private:
	// 音源
	Unigine::SoundSourcePtr sound = nullptr;
	// 要播放的风扇音效
	Unigine::String soundFile ="archviz/sounds/fan_sound.mp3"; ${HL#}$
};
Fan.cpp
#include "Fan.h"
#include <UnigineGame.h>
// 注册Fan组件
REGISTER_COMPONENT(Fan);

// 引入必要的命名空间
using namespace Unigine;
using namespace Math;

// 每帧调用的组件更新方法
void Fan::update()
{
	// 如果未指定风扇节点,则不执行任何操作
	if (!fan_node)
		return;
	// 以指定速度旋转节点
	fan_node->rotate(0, speed * Game::getIFps() , 0);
}
${#HL}$
// 初始化阶段调用的init方法
void Fan::init()
{
	// 使用给定的声音采样文件创建新声音源
	sound = SoundSource::create(Unigine::FileSystem::guidToPath(FileSystem::getGUID(soundFile.get())));

	// 禁用被遮挡时的声音模糊效果
	sound->setOcclusion(0);
	// 设置声音变得清晰的距离
	sound->setMinDistance(1.0f);
	// 设置声音超出可听范围的距离
	sound->setMaxDistance(4.0f);
	// 设置声音放大系数
	sound->setGain(0.5f);
	// 循环播放声音
	sound->setLoop(1);
	// 将音源定位在风扇位置
	sound->setWorldTransform(fan_node->getWorldTransform());
}

// 每帧调用的updateSound方法
void Fan::updateSound()
{
	//  如果组件被禁用且声音正在播放,则停止播放
	if (!isEnabled() && sound->isPlaying()) {
		sound->stop();
		return;
	}
	// 如果组件启用且声音未播放,则开始播放
	if (isEnabled() && !sound->isPlaying())
		sound->play();
}
 ${HL#}$

Let's save our files and then build and run our application by hitting Ctrl + F5 to make the Component System regenerate the Fan property. Close the application after running it, switch to SDK Browser and launch our application by clicking the Run button on our project's card.让我们保存文件,然后按Ctrl + F5构建并运行应用程序,使组件系统重新生成Fan属性文件。运行后关闭应用程序,切换到SDK Browser,点击我们项目卡片上的Run按钮启动应用。

As a result, we have created an interactive room while getting acquainted with the UNIGINE component system and the essential aspects of working with various scene components from the code. This room allows us to arrange and remove objects, interact with them, modify their appearance, and play sounds.最终,我们创建了一个交互式房间场景,同时熟悉了UNIGINE组件系统以及通过代码操作各种场景组件的核心要点。该场景支持物件布置与移除、交互操作、外观修改以及音效播放等功能。

本页面上的信息适用于 UNIGINE 2.20 SDK.

最新更新: 2025-06-09
Build: ()