This document is about: FUSION 2
SWITCH TO

메타버스 뮤직


Available in the Industries Circle
Circle

개요

Music 씬을 통해 플레이어는 소리와 음악을 트리거 할 수 있는 패드로 DJ 기술을 테스트할 수 있을 뿐만 아니라 조명 쇼를 제어할 수 있습니다. 이것은 네트워크를 통해 오디오 트랙이나 조명을 동기화하는 방법을 보여줍니다. 그 씬은 여러 개의 DJ 패드를 포함합니다. 어떤 것은 음악을 조절하고 다른 것은 조명을 조절합니다.

데스크톱 모드에서는 각 패드 하단의 줌 아이콘을 사용하여 마우스로 UI를 더 잘 제어할 수 있도록 전체 화면으로 표시할 수 있습니다.

fusion 메타버스 뮤직

뮤직 패드

각각의 뮤직 패드는 하나 또는 그 이상의 버튼들로 구성되어 있습니다. 각각의 버튼에는 오디오 소스와 소리가 대응합니다. 소리는 루프로 설정될 수 있습니다. 뮤직 패드에는 볼륨을 변경할 수 있는 슬라이더가 포함되어 있습니다.

fusion 메타버스 뮤직

DJ 패드 동작은 3개의 클래스로 관리됩니다:

  • DJPadVolumeSlider : 패드의 볼륨을 관리합니다
  • DJPadTouch : 사운드 버튼을 관리합니다
  • DJPadManager : 패드 전체적인 기능을 관리합니다

DJPadVolumeSlider

플레이어가 슬라이더를 터치하여 볼륨을 변경하면 DJPadVolumeSliderDJPadManager 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이 네트워크를 통해 동기화됩니다. Render() 루프에서 MasterVolume 변수 수정을 탐지하기 위해 ChangeDetector를 사용합니다.

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);
}

DJPadTouch

각 버튼은 인덱스를 참조하므로 DJPadManager는 각 오디오 소스를 제어하는 버튼을 알고, 오디오 소스 상태가 바뀌었을 때 어떤 버튼을 통지해야 하는지 알 수 있습니다. 그래서 사용자가 버튼을 터치하면 DJPadTouchUpdatePadStatus() 메소드를 호출합니다. 그러면 UpdatePadStatus()DJPadManager에게 새 상태를 알립니다.

C#

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

또한 버튼을 터치하면 DJPadManager는 새로운 상태에 따라 버튼 색상을 업데이트하도록 하기 위해 DJPadTouchOnAudioSourceStatusChanged 메소드를 호출합니다.

DJPadManager

PadsStatus라는 네트워크 사전 덕분에 각 버튼의 상태가 동기화됩니다.

C#

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

처음에 DJPadManager는 모든 버튼을 audioSourceManagers 사전에 저장합니다(모든 DJPadTouchIAudioSourceManager 인터페이스를 구현합니다).

C#

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

DJPadManagerChangeAudioSourceState()가 있는 새 버튼 상태를 받으면 해당 버튼 상태가 없으면 상태 권한에 요청한 다음 네트워크 사전을 업데이트하여 모든 원격 사용자가 업데이트를 받도록 합니다.

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

LightIntensitySliderDJPadVolumeSlider와 매우 유사합니다. 플레이어가 슬라이더를 터치하여 강도를 변경하면 RequestIntensityChangeLightPadManager ChangeIntensity() 메소드를 호출합니다.

반면 원격 플레이어가 강도를 변경하면 슬라이더 위치를 업데이트하기 위해 LightPadManager에서 OnLightStatusChanged 메소드를 호출합니다.

LightPadTouch

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에게 알려줍니다. OnLightStatusChanged()는 상태가 변경되면 PadManager에 의해 호출되므로 버튼 UI를 업데이트해야 합니다.

LightSystem

LightSystem 클래스는 EffectSystem이라는 추상적 클래스에서 상속받습니다. LightSystemChangeState 메소드로 수신되는 파라미터에 따라 조명 상태를 변경하는 역할을 합니다:

LightDirectionControler

LightDirectionControler 클래스는 라이트 게임 객체를 회전시키는 회전 스크립트를 제어합니다. LightDirectionControler 는 LightSystem에 의해 활성화/비활성화됩니다.

LightPadManager

LightPadManager는 모든 조명 객채(LightInfo)를 의미합니다.

LightInfo에는 조명의 매개변수를 수정할 수 있는 인덱스와 효과 시스템이 있습니다.

C#

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

LightPadManager는 처음에 모든 조명 버튼을 사전에 등록합니다.

또한 조명의 다양한 매개 변수(조명 ON/OFF, 이동 ON/OFF, 조명 강도)를 저장할 수 있는 네트워크 사전이 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;

로컬 플레이어가 버튼을 누르면 LightPadManager가 관련된 메소드(ChangeLightState()/ChangeMovementState()/ChangeIntensity())로 알려줍니다. 그런 다음 연결된 네트워크 사전이 업데이트됩니다.

예를 들어 조명이 켜지거나 꺼지면 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