디스크 골프 [#13] - 씬 구조 만들기1
저번에 타이틀씬을 만들었고 이번에 로비씬을 만들어서 맵선택을 가능하도록 만들어보자.
아직 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();
});
}
}