This document is about: FUSION 2
SWITCH TO

Metaverse Music


Available in the Industries Circle
Circle

概述

音樂 場景允許玩家來測試他的DJ技能,其附有盤來允許他觸發聲音及音樂,以及控制燈光秀。
這展示了透過網路來同步一個音軌或燈光的方法。
場景包含了多個DJ盤。有些控制音樂而另一個控制燈光。

請注意,在桌面模式中,各個盤底部的縮放圖標可以全螢幕顯示盤,以透過滑鼠來更好地控制UI。

Fusion Metaverse Music

音樂盤

各個音樂盤由一個或多個按鈕組成。各個按鈕對應一個音源或一個聲音。聲音可以被設定為一個迴圈。
音樂盤含有一個滑塊來更改音量。

Fusion Metaverse Music

DJ盤行為由3個類別來管理:

  • DJPadVolumeSlider:管理盤的音量
  • DJPadTouch:管理聲音按鈕
  • DJPadManager:管理盤的總體功能

DJ盤音量滑塊

當玩家觸碰滑塊來更改音量時,DJPadVolumeSlider調用DJPadManager ChangeVolume方法。

C#

void RequestVolumeChange(float volume)
{
    padManager.ChangeVolume(volume);
}

public async void ChangeVolume(float volume)
{
    // We use an attribute, so if another volume is requested while taking the authority, the last volume request is the one executed
    lastVolumeRequest = volume;
    if (!Object.HasStateAuthority)
    {
        await Object.WaitForStateAuthority();
    }
    MasterVolume = lastVolumeRequest;
}

DJPadManager中的已連網變數MasterVolume隨後透過網路被同步。
ChangeDetector用於偵測在Render()迴圈中的MasterVolume變數調整。

C#

[Networked]
public float MasterVolume { get; set; } = 1;

ChangeDetector changeDetector;

public override void Render()
{
    base.Render();
    foreach (var changedVar in changeDetector.DetectChanges(this))
    {
        if (changedVar == nameof(MasterVolume))
        {
            OnMasterVolumeChanged();
        }
    }
}

void OnMasterVolumeChanged()
{
    if(volumeManager != null) volumeManager.OnVolumeChanged(this, MasterVolume);
}

這樣任何人可以更新本機音量。

C#

public void OnVolumeChanged(DJPadManager bPMClipsPlayer, float volume)
{
    ChangeSliderValue(volume);
}

DJ盤觸碰

各個按鈕參照一個索引,這樣DJPadManager知道哪個按鈕控制各個音源,並且知道在一個音源狀態改變時應該通知哪個按鈕。
因此當使用者觸碰一個按鈕,它調用DJPadTouchUpdatePadStatus()方法。
然後,UpdatePadStatus()通知新的狀態的DJPadManager

C#

public void UpdatePadStatus()
{
    if (audioSource.clip)
    {
        isPlaying = !isPlaying;
        padManager.ChangeAudioSourceState(this, isPlaying);
    }
}

同時,當已經觸碰一個按鈕,DJPadManager調用DJPadTouchOnAudioSourceStatusChanged方法,以要求它根據新的狀態來更新按鈕顏色。

DJ盤管理器

利用一個名為PadsStatus的網路字典來同步各個按鈕的狀態。

C#

[Networked]
Capacity(50)]
public NetworkDictionary<int, NetworkBool> PadsStatus { get; }

在開始時,DJPadManager在一個audioSourceManagers字典中儲存所有按鈕(所有DJPadTouch執行IAudioSourceManager介面)。

C#

foreach (var manager in GetComponentsInChildren<IAudioSourceManager>(true))
{
    audioSourceManagers[manager.AudioSourceIndex] = manager;
}

DJPadManager接收到一個新的附有ChangeAudioSourceState()的按鈕狀態,它請求狀態授權(如果它還沒有的話),然後更新已連網字典,這樣所有遠端玩家將接收更新。

C#

public async void ChangeAudioSourceState(IAudioSourceManager audioSourceManager, bool isPlaying)
{
    if (!Object.HasStateAuthority)
    {
        await Object.WaitForStateAuthority();
    }
    PadsStatus.Set(audioSourceManager.AudioSourceIndex, isPlaying);
}

RefreshPads(), SyncAudioSource()OnAudioStatusChanged()方法負責:

  • 據音量滑塊值來更新各個按鈕(DJPadTouch)的音源
  • 如果一個按鈕狀態已經改變(舉例而言,一個音訊片段已經完成),更新盤已連網字典
  • 根據已連網字典來同步各個按鈕的音源(播放/停止)
  • 告知一個按鈕,其已更改狀態,以更新按鈕顏色

並非在已連網字典更改後立即進行這些更新,而是基於預先定義的BPM參數來定期執行這些更新
(方法AudioLoop())。

燈光盤

燈光盤控制4個燈光。
針對各個燈光,盤允許使用者來:

  • 切換燈光開/關
  • 切換燈光的移動的開/關
  • 改變燈光強度

燈光盤架構非常類似於音樂盤。
燈光由以下類別來管理:

  • LightPadManager:管理盤的總體功能
  • LightPadTouch:管理燈光按鈕
  • LightSystem:管理燈光狀態
  • LightIntensitySlider:管理燈光強度
  • LightDirectionControler:管理燈光旋轉器

燈光強度滑塊

LightIntensitySlider非常類似於DJPadVolumeSlider
當玩家透過觸碰滑塊來更改強度時,RequestIntensityChange調用LightPadManager ChangeIntensity()方法。

在另一方面,如果遠端玩家更改強度,LightPadManager調用OnLightStatusChanged方法,以更新滑塊位置。

燈光盤觸碰

LightPadTouch管理燈光按鈕。
如同在LightPadManager中定義的,有3種燈光按鈕:

C#

public enum LightManagerType
    {
        OnOff,              // switch on/off the light
        Movement,           // switch on/off the movement
        Intensity           // slider to change the intensity
    }

各個按鈕參照一個燈光索引,這樣LightPadManager知道哪個按鈕控制各個燈光,並且知道在燈光狀態更改時,應該通知哪個按鈕。

當玩家觸碰一個按鈕時,調用UpdatePadStatus()。它告知PadManager,必須更改狀態。
當狀態更改時,由PadManager調用OnLightStatusChanged(),因此必須更新按鈕UI。

燈光系統

LightSystem類別繼承自抽象類別EffectSystem
LightSystem負責根據在ChangeState方法中接收的參數來更改燈光狀態:

燈光方向控制器

LightDirectionControler類別控制旋轉器指令碼,其旋轉燈光遊戲物件。
LightDirectionControler由燈光系統啟用/停用。

燈光盤管理器

LightPadManager參照所有燈光物件(LightInfo)。

各個LightInfo有一個索引及一個效果系統,其可以調整燈光參數。

C#

public struct LightInfo
{
    public int lightIndex;
    public EffectSystem effectSystem;
}

在開始時,LightPadManager註冊所有燈光按鈕到一個字典之中。

同時,有3個已連網字典,來儲存燈光的各種參數(燈光開/關、移動開/關、燈光強度)

C#

[Networked]
[Capacity(MAX_LIGHTS)]
public NetworkDictionary<int, NetworkBool> LightStatus { get; }

[Networked]
[Capacity(MAX_LIGHTS)]
public NetworkDictionary<int, NetworkBool> LightMovementStatus { get; }

[Networked]
[Capacity(MAX_LIGHTS)]
public NetworkDictionary<int, float> LightIntensities { get; }

ChangeDetector changeDetector;

當本機玩家按下一個按鈕,由關聯的方法(ChangeLightState()/ChangeMovementState()/ChangeIntensity())告知LightPadManager
然後更新關聯的已連網字典。

舉例而言,當一個燈光被切換開/關,LightPadManager請求StateAuthority(如果它還沒有它),並且隨後更新已連網字典,這樣所有遠端玩家將接收更新。

C#

public async void ChangeLightState(LightPadTouch lightPadTouch, bool isLightOn)
{
    if (!Object.HasStateAuthority)
    {
        await Object.WaitForStateAuthority();
    }

    // update network status
    LightStatus.Set(lightPadTouch.LightIndex, isLightOn);

    // movement must be stopped if the light is off
    if (!isLightOn)
        LightMovementStatus.Set(lightPadTouch.LightIndex, false);
}

當更新字典後,利用ChangeDetector在本機及遠端玩家上調用Refresh()方法。

C#

public override void Render()
{
    base.Render();
    foreach (var changedVar in changeDetector.DetectChanges(this))
    {
        if (changedVar == nameof(LightStatus))
        {
            Refresh();
        }

        if (changedVar == nameof(LightMovementStatus))
        {
            Refresh();
        }

        if (changedVar == nameof(LightIntensities))
        {
            Refresh();
        }
    }
}

Refresh()負責使用UpdateLightsAndButtons()方法來更新所有燈光狀態及關聯的按鈕

當玩家加入時,它的燈光透過已連網字典被更新。

Back to top