왜 만들었나

고등학교 지구과학 시간 발표 자료로 쓰려고 만들었다. 기상 현상(태풍, 뇌우, 우박)이 어떻게 생기는지를 슬라이드로 설명하는 대신, 실제로 돌아가는 3D 화면을 띄워 놓고 발표하고 싶었다.

항목내용
엔진Unity 2021.3.45f2
기여도100% (1인 개발)
용도고등학교 지구과학 시간 발표
다루는 현상태풍 · 뇌우 · 우박
리소스파티클 효과와 텍스처 에셋 전부 직접 제작

PPT를 넘기는 것보다 직관적인 3D 자료가 있으면 듣는 쪽이 이해하기 쉬울 거라고 봤다. 발표의 질을 높이는 게 목적이었지 게임을 만들 생각은 아니었다.

뇌우와 우박 — 파티클

먹구름 아래로 번개와 비가 내리고 집 두 채가 놓인 뇌우 시뮬레이션 화면. 우측 상단에 우박/뇌우 버튼이 있다
뇌우 화면. 오른쪽 위 버튼으로 우박과 뇌우를 전환한다.

번개, 비, 우박 입자는 파티클 시스템으로 만들었다. 이쪽은 기획한 대로 나왔다. 구름 아래로 떨어지는 것들의 밀도와 속도만 조절하면 우박과 비의 차이가 화면에서 바로 구분된다.

설명하기 적합한 형태가 우선사실적으로 만드는 것과 설명하기 좋게 만드는 것은 다르다. 실제 뇌우는 훨씬 어수선하지만, 발표에서는 상승 기류 → 구름 형성 → 강수라는 흐름이 눈에 보여야 한다. 그래서 입자 수를 줄이고 경로를 단순하게 잡는 쪽을 택했다.

태풍 — 셰이더

태풍은 파티클로 풀 수 없었다. 소용돌이 형태 자체가 모양을 가져야 했기 때문이다. 나선형 메시를 만들고 거기에 셰이더를 입혀 회전하는 구름 덩어리로 보이게 했다.

Unity 에디터 씬 뷰에서 위에서 내려다본 나선형 소용돌이 메시와, 아래 게임 뷰에 보이는 태풍 형태
에디터에서 본 태풍 오브젝트. 위에서 보면 나선 형태의 메시다.
바다 위에 떠 있는 흰색 소용돌이 구름 덩어리
바다 위에 띄운 결과 화면.
여기는 의도대로 안 됐다처음 기획했던 태풍의 형태가 완벽하게 구현되지 않아 허리케인에 더 가까운 형태가 됐다. 발표를 진행하고 원리를 설명하는 데에는 문제가 없었지만, 만들려던 모양은 아니었다. 반대로 뇌우와 우박 파티클은 기획 의도대로 잘 묘사됐다.

발표 흐름에 맞춰 단계를 넘기기

기상 현상은 순서가 있다. 수증기가 증발하고, 상승 기류가 생기고, 구름이 만들어지고, 비나 우박이 내린다. 이 순서를 발표 타이밍에 맞춰 넘길 수 있어야 했다.

count 변수 하나를 올리면서 각 단계에 해당하는 파티클 오브젝트를 켜고 끄는 구조로 만들었다. 상태 머신처럼 동작한다.

count += 1;

if (count == 5)
{
    // 모든 이펙트 비활성화 및 초기화
    count = -1;
    rainPar.SetActive(false);
    rainPar2.SetActive(false);
    tT1.SetActive(false);
    // ...
}
else if (count == 1)
{
    // 상승 기류 및 초기 구름 형성
    tT1.SetActive(true);
    upAir1.SetActive(true);
    upAir2.SetActive(true);
}
else if (count == 2)
{
    // 본격적인 뇌우/우박 파티클 재생
    tT2.SetActive(true);
    tT3.SetActive(true);
    rainPar.SetActive(true);
    upAir1.SetActive(false);
    tTen1.SetActive(true);
}
// ... (이후 단계 생략)

지금 이 코드를 읽으면

발표 중에 끊기지 않게

씬을 넘길 때 화면이 멈추면 발표 흐름이 끊긴다. 비동기 로딩(LoadSceneAsync)에 진행률 바를 붙였다.

IEnumerator LoadScene(string name)
{
    lodingImg.SetActive(true);
    yield return null;
    AsyncOperation op = SceneManager.LoadSceneAsync(name);
    op.allowSceneActivation = false;
    float timer = 0.0f;

    while (!op.isDone)
    {
        yield return null;
        timer += Time.deltaTime;
        if (op.progress < 0.9f)
        {
            progressBar.fillAmount = Mathf.Lerp(progressBar.fillAmount, op.progress, timer);
            if (progressBar.fillAmount >= op.progress) timer = 0f;
        }
        else
        {
            progressBar.fillAmount = Mathf.Lerp(progressBar.fillAmount, 1f, timer);
            if (progressBar.fillAmount == 1.0f)
            {
                op.allowSceneActivation = true;
                yield break;
            }
        }
    }
}

이 코드는 자동차 오픈월드 의 메인 메뉴에 쓴 것과 같다. 오타(lodingImg)까지 그대로다. 한 번 만들어 둔 것을 다음 프로젝트에 그대로 옮겨 쓴 흔적이다.

정리

게임이 아니라 교육용 시뮬레이션을 만들면서, 기상 현상을 유니티의 입자 제어와 셰이더로 풀어내는 경험을 했다. 파티클 시스템을 이때 처음 제대로 다뤘다.

형태를 가진 것(태풍)과 흩뿌려지는 것(비·우박·번개)은 접근이 다르다는 것도 여기서 배웠다. 전자는 메시와 셰이더, 후자는 파티클이다. 둘을 바꿔서 시도했다면 훨씬 오래 걸렸을 것이다.

당시 기록만들면서 남긴 글이 개인 블로그에 있다. 기록 1 · 기록 2