



















StreamingScene1 씬으로 이동하게 된다.

현재 씬 이름을 currentSceneName에 저장합니다.
IsLoaded(sceneName)로 씬이 이미 로드된 상태인지 확인합니다.
씬이 로드되지 않은 경우, Load(sceneName) 메서드를 호출하여 씬을 로드합니다.
yield return null;로 코루틴을 종료합니다. (현재는 추가 로직 없이 단순 종료)

씬이 로드되었는지 확인합니다.
현재 로드된 씬 목록(loadedScenes)에 sceneName이 포함되어 있는지 확인합니다.
true면 이미 로드된 씬이라는 의미이고, false면 아직 로드되지 않은 씬입니다.

로딩 중인 씬 목록(loadingScenes)에 씬 이름을 추가합니다.
StartCoroutine(LoadAdditiveAsync(sceneName))을 호출하여 씬 비동기 로드를 시작합니다.

비동기 씬 로드: SceneManager.LoadSceneAsync를 사용하여 씬을 Additive 모드로 로드합니다.
Additive 모드: 기존 씬을 유지한 채 새로운 씬을 추가합니다.
yield return op: 비동기 로드가 완료될 때까지 대기합니다.
씬 로드가 완료되면 로딩 중 목록(loadingScenes)에서 씬을 제거하고, 로드 완료 목록(loadedScenes)에 씬을 추가합니다.
using NUnit.Framework;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class SceneStreamer : MonoBehaviour
{
private static SceneStreamer instance;
public static SceneStreamer Instance
{
get
{
return instance;
}
private set
{
instance = value;
}
}
private void Awake()
{
if(instance == null)
{
instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
string currentSceneName;
List<string> loadedScenes = new List<string>(); //로드를 완료한 씬
List<string> loadingScenes = new List<string>();//현재 로딩중인 씬
List<string> nearScenes = new List<string>();//현재 이웃하는 씬
public void SetCurrentScene(string sceneName)
{
//게임을 하고 있는 동안에 몰래 씬을 꺼야 한다.
StartCoroutine(LoadCurrentScene(sceneName));
}
IEnumerator LoadCurrentScene(string sceneName)
{
currentSceneName = sceneName;
if(IsLoaded(sceneName) == false)
{
Load(sceneName);
}
while (loadingScenes.Count > 0)
{
yield return null;
}
nearScenes.Clear(); //이웃하는 씬들을 정리
LoadNears(sceneName, 0);
while(loadingScenes.Count > 0)
{
yield return null;
}
yield return null;
}
bool IsLoaded(string sceneName) //로드하고 있는지 아닌지 여부
{
return loadedScenes.Contains(sceneName);
}
void Load(string sceneName)
{
loadingScenes.Add(sceneName);
StartCoroutine(LoadAdditiveAsync(sceneName));
}
[SerializeField]
int maxDistance = 1;
void LoadNears(string sceneName,int distance)
{
if(nearScenes.Contains(sceneName))
{
return;
}
nearScenes.Add(sceneName);
if(distance >= maxDistance)
{
return;
}
GameObject scene = GameObject.Find(sceneName);
NearScenes nearSceneList = scene.GetComponent<NearScenes>();
for(int i = 0; i< nearSceneList.sceneNames.Length; i++)
{
Load(nearSceneList.sceneNames[i]);
}
}
IEnumerator LoadAdditiveAsync(string sceneName)
{
AsyncOperation op = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive); //여기서 씬 로드함.
yield return op; //op가 끝날 때까지 코루틴이 여기서 멈춘다. <- 여기로 넘어오면 실제로 로드가 끝난 것.
loadingScenes.Remove(sceneName); //로딩 중인 씬은 삭제
loadedScenes.Add(sceneName); //로드 완료한 씬은 추가
}
}




FadeMode는 CrossFade로 설정



실행 결과


추가로, NavMeshAgent 컴포넌트를 추가한다.


Bake 까지 완료한다.





using UnityEngine;
using UnityEngine.AI;
public class MouseController : MonoBehaviour
{
public GameObject player;
void Update()
{
if(Input.GetMouseButtonDown(0))
{
Camera camera = Camera.main;
Ray ray = camera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if(Physics.Raycast(ray,out hit))
{
player.GetComponent<NavMeshAgent>().SetDestination(hit.point);
}
}
}
}





using Unity.AI.Navigation;
using UnityEngine;
public class NavMeshUpdater : MonoBehaviour
{
public NavMeshSurface meshSurface;
private void Start()
{
meshSurface.BuildNavMesh(); //Bake한다.
}
private void Update()
{
//플레이어와 서페이스에서 벗어나려하면
if(Vector3.Distance(meshSurface.transform.position,transform.position) > 10) //거리가 10m를 넘어가게 되면
{
//1번
meshSurface.transform.position = transform.position;
//미세하게 2번이 빠르다.
meshSurface.BuildNavMesh(); //BAKE한다.
}
}
}
이 스크립트는 플레이어와 NavMeshSurface 사이의 거리가 10m 이상이라면 NavMeshSurface위치를 플레이어의 위치로 초기화 한다. 그러고 나서 Bake를 하는 코드이다.
실행 결과

Volume 기준으로 NavMeshSurface 기준으로 10m이상 플레이어가 떨어진다면 Bake를 하는 것이다. 이렇게 하면 전체 맵에 대한 Bake를 하지 않게 되어 효율적이다.