VR 콘텐츠 제작

디스크 골프 [#13] - 씬 구조 만들기1

송현호 2023. 12. 13. 15:09

저번에 타이틀씬을 만들었고 이번에 로비씬을 만들어서 맵선택을 가능하도록 만들어보자.

 

 

아직 UI를 꾸미지 않은 로비씬이다 기능을 넣어서 특정 맵을 선택해서 입장 버튼을 누르면 그 씬으로 전환되도록 할 생각이다.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class App : MonoBehaviour
{
    private void Awake()
    {
        DontDestroyOnLoad(this.gameObject);
    }

    private void Start()
    {
        AsyncOperation oper = SceneManager.LoadSceneAsync("TitleScene");
        oper.completed += OnLoadCompleteTitleScene;
    }
    private void OnLoadCompleteTitleScene(AsyncOperation obj)
    {
        TitleMain titleMain = GameObject.FindObjectOfType<TitleMain>();
        titleMain.onLoadLobbyScene = () =>
        {
            LoadLobbyScene();
        };
    }

    private void LoadLobbyScene()
    {
        AsyncOperation oper = SceneManager.LoadSceneAsync("LobbyScene");
        oper.completed += OnLoadCompleteLobbyScene;
    }

    private void OnLoadCompleteLobbyScene(AsyncOperation obj)
    {
        LobbyMain lobbyMain = GameObject.FindObjectOfType<LobbyMain>();
        lobbyMain.onLoadGameScene = (int num) =>
        {
            LoadGameScene(num);
        };
    }

    private void LoadGameScene(int num)
    {
        Debug.Log(num);
        //AsyncOperation oper = SceneManager.LoadSceneAsync("GameScene");
    }
}

 

 using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class LobbyMain : MonoBehaviour
{
    public System.Action<int> onLoadGameScene;
    public Button[] btns;

    private void Start()
    {
        for (int i = 0; i < btns.Length; i++)
        {
            btns[i].onClick.AddListener(() =>
            {
                onLoadGameScene(i);
            });
        }
    }
}

 

일단 버튼을 누르면 숫자가 출력되는 간단한 코드를 짜봤다.

 

 

그런데 어떤 버튼을 눌러도 6이 출력되었다, 아무래도 클로저(Closure) 문제인 것 같다.

 

for (int i = 0; i < btns.Length; i++)
{
    btns[i].onClick.AddListener(() =>
    {
        onLoadGameScene(i);
    });
}

 

위 코드에서 for문에 있는 i를 참조할때 컴파일이 끝난 상태에서 i는 6이기 때문에 어떤 버튼을 눌러도 6이 출력되는 것이다.

따로 변수를 만들어서 저장하는 식으로 문제를 해결해보자.

 

 

for (int i = 0; i < btns.Length; i++)
{
    int j = i;
    btns[j].onClick.AddListener(() =>
    {
        onLoadGameScene(j);
    });
}

 

코드를 이렇게 작성하면 j는 새로 선언되어 값을 할당받기 때문에 코드는 새로 선언된 j값을 참조하게 된다.

 

 

이제 생각한대로 숫자를 출력하게 됐다.

출력한 숫자대로 각 씬으로 이동할 수 있도록 비동기씬전환을 만들어 보자.

빌드세팅에 전환할 씬을 추가한다음.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class App : MonoBehaviour
{
    private void Awake()
    {
        DontDestroyOnLoad(this.gameObject);
    }

    private void Start()
    {
        AsyncOperation oper = SceneManager.LoadSceneAsync("TitleScene");
        oper.completed += OnLoadCompleteTitleScene;
    }
    private void OnLoadCompleteTitleScene(AsyncOperation obj)
    {
        TitleMain titleMain = GameObject.FindObjectOfType<TitleMain>();
        titleMain.onLoadLobbyScene = () =>
        {
            LoadLobbyScene();
        };
    }

    private void LoadLobbyScene()
    {
        AsyncOperation oper = SceneManager.LoadSceneAsync("LobbyScene");
        oper.completed += OnLoadCompleteLobbyScene;
    }

    private void OnLoadCompleteLobbyScene(AsyncOperation obj)
    {
        LobbyMain lobbyMain = GameObject.FindObjectOfType<LobbyMain>();
        lobbyMain.onLoadGameScene = (int num) =>
        {
            LoadGameScene(num);
        };
    }

    private void LoadGameScene(int num)
    {
        switch (num)
        {
            case 0:
                AsyncOperation oper0 = SceneManager.LoadSceneAsync("MountainScene");
                break;
            case 1:
                AsyncOperation oper1 = SceneManager.LoadSceneAsync("IslandScene");
                break;
            default:
                Debug.Log("아직 안 만듬");
                break;
        }
    }
}

 

플레이를 해서 씬 전환이 잘 되는지 한번 실험해보자.

 

 

 

씬 전환은 잘된다, 씬이 전환될때 살짝 시간이 걸리는 문제점이 있지만 이 부분은 나중에 고치도록 하자.

플레이버튼을 만들어서 씬을 선택한다음 플레이버튼을 누르고 시작하도록 바꿔주었다.

 

 

 using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class LobbyMain : MonoBehaviour
{
    public System.Action<int> onSelectGameScene;
    public System.Action onLoadGameScene;
    public Button[] btns;
    public Button playBtn;

    private void Start()
    {
        for (int i = 0; i < btns.Length; i++)
        {
            int j = i;
            btns[j].onClick.AddListener(() =>
            {
                onSelectGameScene(j);
            });
        }

        playBtn.onClick.AddListener(() =>
        {
            onLoadGameScene();
        });
    }
}

 

private void OnLoadCompleteLobbyScene(AsyncOperation obj)
{
    LobbyMain lobbyMain = GameObject.FindObjectOfType<LobbyMain>();
    lobbyMain.onLoadGameScene = () =>
    {
        LoadGameScene();
    };

    lobbyMain.onSelectGameScene = (int num) =>
    {
        SelectGameScene(num);
    };
}

private void SelectGameScene(int num)
{
    this.selectedGameSceneNum = num;
}

private void LoadGameScene()
{
    switch (this.selectedGameSceneNum)
    {
        case 0:
            AsyncOperation oper0 = SceneManager.LoadSceneAsync("MountainScene");
            break;
        case 1:
            AsyncOperation oper1 = SceneManager.LoadSceneAsync("IslandScene");
            break;
        default:
            Debug.Log("아직 안 만듬");
            break;
    }
}

 

이대로 사용하면 일반유니티3d게임에서만 사용할 수 있기 때문에 VR환경에서 사용할 수 있도록 바꿔주어야 한다.
Oculus에서 제공하는 RayExample씬에서 OVRCameraRig와 캔버스를 카피해서 수정한다음 사용하기로 했다.

 

 

캔버스의 스크롤뷰와 텍스트 부분을 삭제해주었다.

 

 

캔버스에 있던 Vertical Layout Group과 Content Size Fitter를 삭제해서 크기를 자유롭게 조정할 수 있도록 변경하였다.

그리고 맵 사진 스크롤뷰 플레이버튼을 만들어보자

 

스크롤뷰에 Content 빈 오브젝트를 만들고 vertical layout group과 content size fitter를 추가해주었다.

 

 

셀은 원래 컨버스의 셀을 재활용했고 스크롤뷰에 scroll rect컴포넌트와 mask를 붙여서 스크롤뷰를 완성시켜주었다.

 

 

그리고나서 토글에 버튼을 추가해서 이벤트를 전달하려고 했는데

 

버튼을 추가할 수 없다는 내용의 안내문이 출력되었다.

버튼을 추가할 수 없으니 Toggle Deselect를 이용해서 스테이지를 선택할 수 있도록 만들어 보자.

 

public void DebugToggle()
{
    Debug.Log("클릭");
}

 

 using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class LobbyMain : MonoBehaviour
{
    public System.Action<int> onSelectGameScene;
    public System.Action onLoadGameScene;
    public Toggle[] toggles;
    public Button playBtn;

    private void Start()
    {
        for (int i = 0; i < toggles.Length; i++)
        {
            int j = i;
            toggles[j].onValueChanged.AddListener((toggle) =>
            {
                Debug.Log(toggle);
            });
        }

        playBtn.onClick.AddListener(() =>
        {
            onLoadGameScene();
        });
    }

    public void DebugToggle()
    {
        Debug.Log("클릭");
    }
}

 

토글에 이벤트를 할당하려면 인수가 필요하다고 해서 인수를 전달해주었다.

인수가 무엇을 의미하는지 디버그로 한번 찍어보았다.

 

 

아무래도 toggle은 누른 다음의 버튼이 isOn이 false인지 true인지 전달해주는 것 같다.
isOn일때 클릭하면 On상태를 유지하도록 하고 false일때 클릭하면 On이 되도록 해보자,

한 버튼을 클릭하면 다른 버튼은 Off상태가 되도록 하겠다.

 

한 버튼을 클릭했을때 다른 버튼을 꺼지게 하는 방법을 찾아보다가 토클 그룹 기능을 발견했다.

 

https://docs.unity3d.com/kr/2018.4/Manual/script-ToggleGroup.html

 

토글 그룹 - Unity 매뉴얼

토글 그룹(Toggle Group) 은 실제로 표시되는 별도의 UI 컨트롤이 아니라 토글 집합의 동작을 조절하기 위한 하나의 방법입니다. 같은 그룹에 속하는 토글 중에서 한 번에 오직 한 개의 토글만 켜질

docs.unity3d.com

content에 토글 그룹을 추가하고 활성화된 버튼을 클릭했을때 꺼지지 않도록 만들자.

 

토글 그룹을 설정하려면 content에 토글 그룹 컴포넌트를 추가하고 

토글의 Group프로퍼티에 content를 추가하면 된다.

 

 using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class LobbyMain : MonoBehaviour
{
    public System.Action<int> onSelectGameScene;
    public System.Action onLoadGameScene;
    public Toggle[] toggles;
    public Button playBtn;

    private void Start()
    {
        for (int i = 0; i < toggles.Length; i++)
        {
            int j = i;
            toggles[j].onValueChanged.AddListener((toggle) =>
            {
                onSelectGameScene(j);
                Debug.LogFormat("현재 스테이지 번호 : {0}", j);
            });
        }

        playBtn.onClick.AddListener(() =>
        {
            onLoadGameScene();
        });
    }
}