1. 실내 샘플 맵
간단하게 실내용 씬을 만들었습니다.
이걸 뜬금없이 만든 이유는 씬 전환 기능을 테스트할 겸, Collider Trigger로 워프 기능을 구현해보고 만들어봤습니다.
2. Collider Trigger로 워프 기능 구현
[RequireComponent(typeof(Collider))]
public class SceneTriggerTemplate<T> : MonoBehaviour where T : Component
{
[SerializeField] string[] m_CheckTags;
[SerializeField] bool m_CheckStay = true;
List<T> m_ContainsTargetList = new List<T>();
protected virtual void OnTriggerEnter(Collider other)
{
if (m_CheckTags != null && m_CheckTags.Length > 0)
{
if (System.Array.Exists(m_CheckTags, tag => other.CompareTag(tag)) == false)
return;
}
T component = other.GetComponent<T>();
if (component == null) return;
if (m_ContainsTargetList.Contains(component) == false)
{
m_ContainsTargetList.Add(component);
OnEnterTarget(component);
}
}
protected virtual void OnTriggerStay(Collider other)
{
if (m_CheckStay == false)
return;
OnTriggerEnter(other);
}
protected virtual void OnTriggerExit(Collider other)
{
if (m_CheckTags != null && m_CheckTags.Length > 0)
{
if (System.Array.Exists(m_CheckTags, tag => other.CompareTag(tag)) == false)
return;
}
T component = other.GetComponent<T>();
if (component == null) return;
if (m_ContainsTargetList.Remove(component) == true)
{
OnExitTarget(component);
}
}
protected virtual void OnEnterTarget(T entity)
{
}
protected virtual void OnExitTarget(T entity)
{
}
}
우선은 Trigger 관련해서 계속해서 기능 구현이 있을 것 같아, 미리 템플릿으로 짜두었습니다.
OnTriggerEnter, OnTriggerStay, OnTriggerExit을 이용해서 해당 Collider내에 있는지 검사합니다.
public class SceneTrigger_PlayerEntityWarp : SceneTriggerTemplate<PlayerEntityController>
{
[SerializeField] string WarpSceneName;
protected override void OnEnterTarget(PlayerEntityController entity)
{
base.OnEnterTarget(entity);
SceneChanger.Instance.ChangeScene(WarpSceneName);
}
}
템플릿을 상속받아 워프 트리거를 구현해주었습니다.
간단하게 해당 트리거로 플레이어가 들어오면 SceneChanger를 통해 ChangeScene함수가 호출되도록 구현했습니다.
캐릭터의 태그를 Player로 맞춰주었습니다.
사실 PlayerEntityController로 찾기 때문에 안 해주어도 큰 문제없지만, Component를 찾는 것보다 Tag로 검사하는 게 나중에 Component가 늘었을 때 빠를 것 같아서입니다.
Rigidbody와 Capsule Collider를 추가했습니다.
Unity에서는 Rigidbody가 있어야 Trigger에 인식되어서 추가했습니다.
현재는 물리 관련 동작을 사용하지 않을 것이기 때문에 useGravity를 false로 해주고 position, ration을 모두 freeze 해주었습니다.
적당한 위치에 Collider를 만들어서 만들어두었던 Component를 추가했습니다.
앞에 만들어두었던 SampleDungeon씬으로 가도록 설정했습니다.
다만 이전 씬에서의 좌표를 그대로 사용하고 있어서, 들어가고 나오는 느낌이 들지 않는 게 문제인 것 같네요.
이 부분도 나중에 같이 수정해볼 예정입니다.
3. Collider Trigger로 환경 관련 변경 기능 구현
심리스 맵을 만들기 위해 서브 씬으로 나누었는데, 유니티에는 보통 씬에 종속되어 세팅하는 값들이 있습니다.
Directional Light, Lightmap, Navmesh, Global Volume 등등..
그중에 이번에는 Directional Light, Global Volume 그리고 씬마다 캐릭터 카메라 세팅도 조절할 수 있도록 Virtual Camera도 추가하려 합니다.
public class GlobalEnvManager : MonoBehaviourSingletonTemplate<GlobalEnvManager>
{
}
우선은 해당 환경 설정값들을 관리하기 위한 싱글턴을 만들어주었습니다.
Light m_GlobalDirectionalLight;
private void InitGlobalDirectionalLight()
{
var obj = new GameObject("GlobalDirectionalLight");
obj.transform.SetParent(transform);
m_GlobalDirectionalLight = obj.AddComponent<Light>();
m_GlobalDirectionalLight.type = LightType.Directional;
m_GlobalDirectionalLight.renderMode = LightRenderMode.Auto;
m_GlobalDirectionalLight.lightmapBakeType = LightmapBakeType.Mixed;
}
public void ChangeGlobalDirectionalLight(Light light)
{
if (m_GlobalDirectionalLight == null) InitGlobalDirectionalLight();
m_GlobalDirectionalLight.transform.position = light.transform.position;
m_GlobalDirectionalLight.transform.rotation = light.transform.rotation;
// ... //
}
Directional Light를 설정해주는 코드입니다.
문제는 실제 값과 다르게 나오는데 이 부분은 나중에 라이트맵과 같이 볼까 합니다.
Light의 설정값들이 많아 중간에 생략했는데, 자세한 부분은 아래 링크를 봐주시면 됩니다.
GitHub - PieceOfPaper/Unity_LinksAwakening: 젤다의 전설 꿈꾸는 섬 (The Legend of Zelda: Link's Awakening) 모작
젤다의 전설 꿈꾸는 섬 (The Legend of Zelda: Link's Awakening) 모작 - GitHub - PieceOfPaper/Unity_LinksAwakening: 젤다의 전설 꿈꾸는 섬 (The Legend of Zelda: Link's Awakening) 모작
github.com
UnityEngine.Rendering.Volume m_GlobalVolume;
private void InitGlobalVolume()
{
var obj = new GameObject("GlobalVolume");
obj.transform.SetParent(transform);
m_GlobalVolume = obj.AddComponent<UnityEngine.Rendering.Volume>();
}
public void ChangeGlobalVolume(UnityEngine.Rendering.VolumeProfile profile)
{
if (m_GlobalVolume == null) InitGlobalVolume();
m_GlobalVolume.sharedProfile = profile;
}
Volume은 Volume에서 가지고 있는 profile로 갈아 끼도록 수정했습니다.
Volumes | Universal RP | 11.0.0
Volumes The Universal Render Pipeline (URP) uses the Volume framework. Volumes can override or extend Scene properties depending on the Camera position relative to each Volume. URP uses the Volume framework for post-processing effects. URP implements dedic
docs.unity.cn
사실 URP에서는 Local Volume을 제공하고 있지만, 해당 기능은 Collider내에 카메라가 들어왔을 경우 변경시켜주는 역할이라 따로 구현하게 되었습니다.
Cinemachine.CinemachineVirtualCameraBase m_CurrentVirtualCamera;
public void ChangeVirtualCamera(Cinemachine.CinemachineVirtualCameraBase vcam)
{
if (m_CurrentVirtualCamera != null) m_CurrentVirtualCamera.Priority = 10;
m_CurrentVirtualCamera = vcam;
if (m_CurrentVirtualCamera != null) m_CurrentVirtualCamera.Priority = 11;
}
Virtual Camera는 Priority만으로도 교체가 가능하여 Priority만으로 바꿔주도록 했습니다.
Priority 초기값이 10이라 10과 11로 설정했습니다.
public class SceneTrigger_PlayerEntityChangeEnv : SceneTriggerTemplate<PlayerEntityController>
{
[SerializeField] Light m_DirectionalLight;
[SerializeField] UnityEngine.Rendering.Volume m_Volume;
[SerializeField] Cinemachine.CinemachineVirtualCamera m_VirtualCamera;
private void Awake()
{
if (m_DirectionalLight != null) m_DirectionalLight.enabled = false;
if (m_Volume != null) m_Volume.enabled = false;
// if (m_VirtualCamera != null) m_VirtualCamera.enabled = false;
}
protected override void OnEnterTarget(PlayerEntityController entity)
{
base.OnEnterTarget(entity);
if (m_DirectionalLight != null) GlobalEnvManager.Instance.ChangeGlobalDirectionalLight(m_DirectionalLight);
if (m_Volume != null) GlobalEnvManager.Instance.ChangeGlobalVolume(m_Volume.profile);
if (m_VirtualCamera != null) GlobalEnvManager.Instance.ChangeVirtualCamera(m_VirtualCamera);
}
}
이전에 구현한 트리거 템플릿으로 간단하게 구현했습니다.
Awake에서 직접 꺼주는 이유는 다른 Light, Volume과 겹치게 하지 않기 위해 꺼두도록 했습니다.
각각 서브 씬들마다 Directional Light, Volume, Virtual Camera 설정해주고, Trigger들도 설정해주었습니다.
테스트용으로 블룸 값을 강하게 주었는데 눈이 너무 아프네요...
아무튼 잘 동작하는 걸 확인했습니다.
4. 다음에 할 일
이번에 심리스 맵을 구현 해보면서 문제점들이 많이 보였는데, 이 부분들 정리해서 다음에 작업해 볼 예정입니다.
- 서브 씬 로드 판단 코드 개선
- GlobalEnvManager에서 Directional Light 개선
- 씬으로 전환 시 시작 지점 설정
'개인프로젝트 일지' 카테고리의 다른 글
유니티로 "젤다의 전설: 꿈꾸는 섬" 모작 11일차 (0) | 2022.10.10 |
---|---|
유니티로 "젤다의 전설: 꿈꾸는 섬" 모작 10일차 (0) | 2022.10.02 |
유니티로 "젤다의 전설: 꿈꾸는 섬" 모작 8일차 (0) | 2022.09.25 |
유니티로 "젤다의 전설: 꿈꾸는 섬" 모작 7일차 (1) | 2022.09.19 |
유니티로 "젤다의 전설: 꿈꾸는 섬" 모작 6일차 (0) | 2022.09.12 |