第 8 课

目录

设置音乐区域的 State

在将互动音乐集成到游戏中时,我们通常会将乐曲关联到不同的游戏事件或动作,从而让玩家可以在音乐段落和所述事件或动作之间建立某种联系。其实,音乐系统的创建方式有很多种。我们可以根据玩家的游戏进度或所在位置来切换音乐,也可让玩家自由地选择要听的内容。当然,两种方式并用也行;不过,同一时间一般只能有一首活跃的音乐或者多个节奏或曲调相同的分轨,以免造成声景嘈杂不堪。对于这些场景,我们可以利用 State(状态)来很好地控制当前音乐的 State,而不必担心会受到某个特定游戏对象的影响(详见“第 5 课”中有关游戏对象作用域和全局作用域的说明)。

接下来,我们要修改 Player 进入 Village(村庄)时音乐的 State。为此,需要在 Village 附近添加 Trigger 音量,然后通过脚本来根据 Player 所在的位置设置 State。

  1. 在 Unity 菜单栏中,依次转到 Audiokinetic > Certification > 301 > Lesson 8,然后选择 Setting a Music Region State

    首先,我们要选择一个与 Village 区域相称的 Trigger 形状。

    通过俯瞰整个 Village,发现它有点像圆形。所以,Sphere Trigger 应当比较适合。

    [技巧]

    Sphere Trigger 是效能最高一种 Trigger 类型,它比其他一些形状占用的 CPU 处理资源要少。

  2. 调节 Scene 视图,以便俯瞰 Village 区域。

    [备注]

    确保未在 Hierarchy 中选中任何游戏对象。假如不确定,请单击游戏对象下方的空白区域。

  3. 在 Hierarchy 中,右键单击空白区域,然后转到 3D Object,并选择 Sphere

  4. 使用 Move Tool、Rotate ToolScale Tool 来调节 Sphere,使其覆盖整个 Village 区域。

    接下来,我们为 Trigger 形状设置一个不同的 Mesh 以便可以看到其内部,并将 Sphere Collider 设为 Is Trigger 以便能够进入该 Trigger。

  5. 在 Inspector 的 Sphere Collider 中,选中 Is Trigger 复选框。

    假如您想将 Sphere 形状设为不可见,可以一并禁用 Mesh Renderer。不过,为了便于测试,我们还是将 Sphere 形状设为透明材质。这样的话就可以看出何时进入 Trigger。为此,我们将使用“第 3 课”中所用的附带 Trigger_Red Material 来绘制 Sphere。

  6. 在 Project 视图中,搜索 Trigger_Red,然后将 Trigger_Red Material 拖到 Sphere 游戏对象上。

    接下来,我们要添加脚本以便为 Sphere Trigger 设置 State。

  7. 创建一个新的脚本并命名为 SetMusicState,然后添加到 Sphere,最后将其打开。

  8. 我们会获得一个已经定义了基本的 Start() 和 Update() 函数的脚本(如“第 4 课”所述)。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class SetMusicState : MonoBehaviour {
    
        // 使用此函数进行初始化。
        void Start () {
            
        }
        
        // 每帧调用 Update 函数一次。
        void Update () {
            
        }
    }

    因为这里并不需要使用这些函数,所以我们来清除 SetMusicState 类之内的所有内容。

  9. 选中 SetMusicState 的大括号之内的所有代码行,然后按下 Backspace。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class SetMusicState : MonoBehaviour {
    
    }

    我们可以使用 OnTriggerEnter 函数来检测 Player 何时进入 Trigger。假如脚本中提供有该函数,Trigger 将会进行调用。此函数的命名和使用方式与 AkEvent 的 Trigger On 选项 AkTriggerEnter 相似(之前在“第 1 课”中讲过相关内容)。

  10. 在空白行中,键入 void OnTriggerEnter

    我们注意到,Visual Studio 显示了相应的下拉菜单。在此,它提示了 OnTriggerEnter 函数。这正是我们要插入的函数。在双击 OnTriggerEnter 元素之后,Visual Studio 会自动完成该函数。在 Visual Studio 自动完成函数时,还会通过访问修饰符 private 加以声明。也就是说,该函数只能用在 SetMusicState 类之内。

    [备注]

    假如代码编辑器中没有显示下拉菜单,您可以按照以下步骤直接手动键入。

  11. 双击下拉菜单中的 OnTriggerEnter 元素或者手动键入代码行,以此插入 private void OnTriggerEnter(Collider other){}

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class SetMusicState : MonoBehaviour {
        private void OnTriggerEnter(Collider other){
        
        }
    }
    

    在 OnTriggerEnter 的圆括号之内,我们会看到名为 other 的 Collider 变量。在 Sphere 游戏对象 Collider 检测到另一 Collider(包括 Trigger)时,将会调用 OnTriggerEnter 函数以及检测到的 Collider。换句话说,将会通过 other 变量来引用刚刚进入此 Trigger 的 Collider。比如,在 Player 游戏对象进入刚才创建的 Sphere Trigger 时,将会调用 OnTriggerEnter 以及 other 变量,并以此引用与 Player 绑定的 Collider 组件。

    因为只想在 Player 游戏对象进入 Trigger 时改变 State,所以必须识别进入 Trigger 的 Collider 是否带有 Player 标记。为此,可结合使用 if 语句和 CompareTag() 函数来检测 Collider 对象是否进入 Trigger。接下来,我们仔细看下 if 语句。

    If 语句会接收输入并根据给定条件来进行判定,这样程序会在条件判定为 true 时执行相应的代码段。反之,若 If 语句返回 false,则会跳过代码段。在编写 if 语句时,可直接键入 if 并后跟一对圆括号 (),然后在其内插入所要判定的布尔表达式,最后再在圆括号之后添加一对大括号 {},并在其内包含要运行的代码。我们来看个非常简单的例子。

        if (Number == 42){
            print("Number is 42.");
        }else {
            print("Number is not 42.");
        }
    

    接下来,我们返回练习。在 if 语句的圆括号之内,我们需要使用 CompareTag() 函数来检测 Collider 对象是否进入 Trigger,以此确定 other 游戏对象是否为 Player。为此,请在 CompareTag 函数的圆括号之内以字符串形式写入 Player 标记的名称(同样为 Player)。在此之后,CompareTag 函数会返回 true 比对结果(true 或 false)。

    [备注]
    字符串方便用来存储文本。为了声明字符串,可将文本包括在两个双引号 "" 之内。

  12. 在 OnTriggerEnter 函数的大括号之内,键入 if(other.CompareTag("Player")){ }

    public class SetMusicState : MonoBehaviour {
        private void OnTriggerEnter(Collider other){
            if(other.CompareTag("Player")){
                
            }
        }
    }

    [备注]

    注意,代码编辑器可能会自动添加右大括号。所以,请确保没有添加太多右大括号。

    通过在 if 语句之内设置 State,可确定 Player 进入了 Trigger。接下来,我们需要创建 Wwise 专有 State 属性。

  13. 在类的最上面键入 public AK.Wwise.State OnTriggerEnterState;,以此声明 Wwise 专有 State 属性。

    public class SetMusicState : MonoBehaviour {
        public AK.Wwise.State OnTriggerEnterState;
        private void OnTriggerEnter(Collider other){
            if(other.CompareTag("Player")){

    接下来,我们便可在 OnTriggerEnter() 函数之内设置 MusicState。

  14. If 语句之内,键入 OnTriggerEnterState.SetValue();,然后保存脚本

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class SetMusicState : MonoBehaviour {
        public AK.Wwise.State OnTriggerEnterState;
        private void OnTriggerEnter(Collider other){
            if(other.CompareTag("Player")){
                OnTriggerEnterState.SetValue();
            }
        }
    }

  15. 在 Unity Inspector 的 SetMusicState 脚本中,单击 OnTriggerEnterState 属性,然后依次展开 MusicStates > Music_Regions,并双击 Village

    搞定!下面来试试吧。

  16. 进入 Play 模式,然后跑进 Sphere Trigger。

    在投放到 Training Area(训练场)时,会听到 Ambient Music Playlist Container(音乐播放列表容器)。然而,一旦进入 Sphere Trigger,音乐便会切换到 Village Music Playlist Container。

    这一过程只负责在进入 Trigger 时设置 State。不过,在离开时,State 并不会切换回 Ambient Music Playlist Container。接下来,我们另外添加一个 Wwise 专有 State 类属性,以此在离开 Trigger 时设置 State。现在您可以退出 Play 模式了。

  17. 切换回代码编辑器,并在 OnTriggerEnterState 属性的声明下面添加一个空白行,然后在该行中键入 public AK.Wwise.State OnTriggerExitState;

    public class SetMusicState : MonoBehaviour {
        public AK.Wwise.State OnTriggerEnterState;
        public AK.Wwise.State OnTriggerExitState;
        private void OnTriggerEnter(Collider other){
            if(other.CompareTag("Player")){
                OnTriggerEnterState.SetValue();
            }
        }
    }

    我们可以使用 OnTriggerExit() 函数来检测 Player 何时离开 Trigger。为了节省代码编写时间,我们来直接复制粘贴 OnTriggerEnter() 函数,然后再将其改成 OnTriggerExit() 函数。

  18. 选中整个 OnTriggerEnter() 函数(包括大括号),然后右键单击并选择“复制”。

  19. 在 OnTriggerEnter() 函数的右大括号下面添加一个空白行,然后右键单击该行并选择“粘贴”。

    public class SetMusicState : MonoBehaviour {
        public AK.Wwise.State OnTriggerEnterState;
         public AK.Wwise.State OnTriggerExitState;
        private void OnTriggerEnter(Collider other){
            if(other.CompareTag("Player")){
                OnTriggerEnterState.SetValue();
            }
        }
        
        private void OnTriggerEnter(Collider other){
            if(other.CompareTag("Player")){
                OnTriggerEnterState.SetValue();
            }
        }
    }

  20. 现在我们可以直接将 OnTriggerEnter 重命名为 OnTriggerExit,并将 OnTriggerEnterState 重命名为 OnTriggerExitState,然后保存脚本

        private void OnTriggerExit(Collider other){
            if(other.CompareTag("Player")){
                OnTriggerExitState.SetValue();
            }
        }

    最后,我们还要指派 OnTriggerExitState 属性,并试试整合效果如何。

  21. 在 Unity Inspector 中,单击 On Trigger Exit State 属性,然后依次转到 MusicStates > Music_Regions,并双击 Nowhere

  22. 进入 Play 模式,然后跑进 Sphere Trigger。

    跟之前一样,此时应当会开始播放 Village 音乐。

  23. 跑出 Sphere Trigger。

    在对脚本执行更改之后,现在应当会开始播放 Ambient 音乐。现在您可以退出 Play 模式了。

这种方式便于设置且易于使用。不过,必须确保用来设置 State 的 Trigger 之间不能重叠,否则可能会导致 State 出现冲突。对此,我们会在后面的“为重叠的 State 区域创建系统”章节中进一步说明。