一、Impacter插件
Impacter插件是AudioKinetic公司出的Wwise插件,主要用于撞击声,如:场景物件的碰撞,脚步声,攻击的击中声等
1、采样(音)源插件——使用尽可能少的资源产生尽可能多的声音
指数型增加随机数:5个样本分层后可生成25个声音的随机(未计算参数的随机变化)
2、交叉合成方式——Impact与Body的随机组合
导入的音频采样经过分析后拆分成两个分层
Impact:撞击起始点的瞬态/脉冲声
Body:撞击时发声物体的共振声
3、物理化设置——将DSP算法转化成直观的质量、速度、位置和粗糙度的参数
Mass(质量):
DSP——时间拉伸、音高变换或信号压缩
效果——改变大小或重量的感受
参数值——0.01 - 2 :轻(频率高) - 重(频率低)
Velocity(速度):
DSP——振幅、音色(频率)
效果——改变撞击力度的感受
参数值——0.05 - 1 :慢 (振幅小、频段窄)- 快(振幅大、频段宽)
Position(位置):
DSP——音色(频率)
效果——感受表面的不同位置
参数值——0 - 1 :中间(频段宽) - 边缘(频段窄)
Roughness(粗糙度):
DSP——添加不和谐频率波形
效果——感受表面粗糙程度
参数值——0 - 1 :光滑(噪音成分少) - 粗糙(噪音成分多)
二、Impacter在Unity中基于物理碰撞的使用
1、核心脚本——ImpacterComponent继承至MonoBehaviour的组件
组件功能:
ImpactEmitter——发声预制体,挂载了脚本的游戏对象与其他对象碰撞时动态生成可发声的预制体的引用。
AkEvent——发生碰撞时播放的Wwise声音事件
AkBank——声音事件所依赖的Wwise声音包
IsEmitter——本游戏对象是否发声
IsPosition——是否计算碰撞的位置,用于传参数给Impacter改变声音属性
PlaceMode——需要计算碰撞位置的物体在场景中的摆放方式(水平/垂直)
IsOverrideMass——是否重写游戏对象的质量,false时使用游戏对象刚体上的Mass值
RtpcName(绑定Wwise中设置的RTPC参数名,用于运行时实时传递参数值到声音引擎中修改声音属性):
Mass(手动设置)——重写的游戏对象的质量(Mass)值,与Impacter的Mass绑定
Velocity(引擎计算)——游戏对象碰撞时两物体的相对速度,与Impacter的Velocity绑定
Position(引擎计算)——游戏对象碰撞时的位置值,与Impacter的Position绑定
Roughness(手动设置)——游戏对象的粗糙度,与Impacter的Roughness绑定
代码:
using UnityEngine;
using System;
public enum PlaceMode
{
Vertical,
Horizontal
}
public class ImpacterComponent : MonoBehaviour
{
public GameObject impactEmitter;
public AK.Wwise.Event akEvent;
public AK.Wwise.Bank akBank;
public bool isEmitter;
public bool isPosition;
public PlaceMode placeMode;
public bool isOverrideMass=false; //是否自定义质量
//RTPC参数,与Wwise命名一致
public string rtpcNameMass;
[Range(0, 100)]
public float overridMassValue; //自定义质量值
public string rtpcNameVelocity;
private float velocity;
public string rtpcNamePostion;
private float position;
public string rtpcNameRoughness; //自定义粗糙度
[Range(0, 1)]
public float roughness;
private Rigidbody _rb;
void Start()
{
_rb = GetComponent<Rigidbody>();
}
void Update()
{
}
private void OnCollisionEnter(Collision collision)
{
//加载bank、实例化发声体、调用声音事件
if (isEmitter)
{
akBank.Load();
GameObject emitter = Instantiate(impactEmitter, transform);
ImpactEmitter ie = emitter.GetComponent<ImpactEmitter>();
GetImpactVelocity(collision);
if (isPosition)
{
GetImpactPosition(collision);
}
ie.PostAkEvent(akEvent, rtpcNameMass, GetImpactMass(),
rtpcNameVelocity, velocity,
rtpcNamePostion, position,
rtpcNameRoughness, roughness);
}
}
/// <summary>
/// 获取游戏对象质量
/// </summary>
private float GetImpactMass()
{
if (_rb && isOverrideMass==false)
{
_rb.mass=Mathf.Min(_rb.mass, 100);//质量值与100取最小值,限制值范围
return _rb.mass;
}
else
{
return overridMassValue;
}
}
/// <summary>
/// 获取游戏对象的速度
/// </summary>
private void GetImpactVelocity(Collision otherCollision)
{
if (otherCollision != null)
{
velocity = otherCollision.relativeVelocity.magnitude;
velocity = Mathf.Min(velocity, 50);
}
}
/// <summary>
/// 获取碰撞点,转换成float值
/// </summary>
private void GetImpactPosition(Collision otherCollision)
{
Vector3 hitPos = otherCollision.GetContact(0).point;
Vector3 meshCenterPos = GetComponent<MeshFilter>().sharedMesh.bounds.center;
Vector3 extent = GetComponent<MeshFilter>().sharedMesh.bounds.extents;
float scale = new Vector3(extent.x, 0, extent.z).magnitude;
if (placeMode==PlaceMode.Horizontal)
{
hitPos = new Vector3(hitPos.x, 0, hitPos.z);
meshCenterPos = new Vector3(meshCenterPos.x, 0, meshCenterPos.z);
float hitLength = (meshCenterPos - hitPos).magnitude;
position =Math.Min(0.8f, hitLength / scale)/0.8f;
//Debug.Log(position);
}
if (placeMode == PlaceMode.Horizontal)
{
hitPos = new Vector3(hitPos.x, hitPos.y, 0);
meshCenterPos = new Vector3(meshCenterPos.x, meshCenterPos.y, 0);
float hitLength = (meshCenterPos - hitPos).magnitude;
position = Math.Min(0.8f, hitLength / scale)/0.8f;
}
}
}
2、发声预制体
主要功能:
- 发声碰撞时在碰撞点实例化
- 从ImpacterComponent获取参数值并播放声音
- 声音播放结束后回调删除预制体
脚本代码
using UnityEngine;
[RequireComponent(typeof(AkEvent))]
public class ImpactEmitter : MonoBehaviour
{
//AkEvent的回调标记
private AK.Wwise.CallbackFlags _inFlags = new AK.Wwise.CallbackFlags();
void Update()
{
}
/// <summary>
/// 发声体:声音的播放,RTPC的设置、事件的回调
/// </summary>
public void PostAkEvent(AK.Wwise.Event in_event, string in_massRtpcName, float in_mass,
string in_velocityRtpcName, float in_velocity,
string in_positionRtpcName, float in_position,
string in_roughnessRtpcName, float in_roughness)
{
AkSoundEngine.SetRTPCValue(in_massRtpcName, in_mass, gameObject);
AkSoundEngine.SetRTPCValue(in_velocityRtpcName, in_velocity, gameObject);
AkSoundEngine.SetRTPCValue(in_positionRtpcName, in_position, gameObject);
AkSoundEngine.SetRTPCValue(in_roughnessRtpcName, in_roughness, gameObject);
_inFlags.value = 1; //回调标记:事件播放完成
in_event.Post(gameObject, _inFlags, OnEndOfEvent);
}
/// <summary>
/// 回调,事件播放完成后销毁发声体
/// </summary>
private void OnEndOfEvent(object in_cookie, AkCallbackType in_type, AkCallbackInfo in_info)
{
if (in_type == AkCallbackType.AK_EndOfEvent)
{
{
Destroy(this.gameObject);
}
}
}
}