왜 만들었나Why I Built It
고등학교 지구과학 시간 발표 자료로 쓰려고 만들었다. 기상 현상(태풍, 뇌우, 우박)이 어떻게 생기는지를 슬라이드로 설명하는 대신, 실제로 돌아가는 3D 화면을 띄워 놓고 발표하고 싶었다.
I built this as visual material for a high school Earth Science presentation. Instead of explaining how typhoons, thunderstorms, and hail form using static slides, I wanted to run a live 3D scene on screen while speaking.
| 항목 | 내용 |
|---|---|
| 엔진 | Unity 2021.3.45f2 |
| 기여도 | 100% (1인 개발) |
| 용도 | 고등학교 지구과학 시간 발표 |
| 다루는 현상 | 태풍 · 뇌우 · 우박 |
| 리소스 | 파티클 효과와 텍스처 에셋 전부 직접 제작 |
| Item | Details |
|---|---|
| Engine | Unity 2021.3.45f2 |
| Role | 100% (Solo development) |
| Purpose | High school Earth Science class presentation |
| Phenomena Covered | Typhoons · Thunderstorms · Hail |
| Assets | All particle effects and texture assets made from scratch |
PPT를 넘기는 것보다 직관적인 3D 자료가 있으면 듣는 쪽이 이해하기 쉬울 거라고 봤다. 발표의 질을 높이는 게 목적이었지 게임을 만들 생각은 아니었다.
I figured an interactive 3D visual would be much easier for classmates to follow than flipping through PowerPoint slides. The goal was a clearer presentation, not making a game.
뇌우와 우박 — 파티클Thunderstorms and Hail — Particle System

번개, 비, 우박 입자는 파티클 시스템으로 만들었다. 이쪽은 기획한 대로 나왔다. 구름 아래로 떨어지는 것들의 밀도와 속도만 조절하면 우박과 비의 차이가 화면에서 바로 구분된다.
Lightning, rain, and hailstones were built with Unity's Particle System. This part came out just as planned. Simply tuning the emission density and fall speed beneath the cloud layer made hail and rain immediately distinguishable on screen.
태풍 — 셰이더Typhoon — Custom Mesh and Shader
태풍은 파티클로 풀 수 없었다. 소용돌이 형태 자체가 모양을 가져야 했기 때문이다. 나선형 메시를 만들고 거기에 셰이더를 입혀 회전하는 구름 덩어리로 보이게 했다.
A typhoon could not be solved with particles alone because the vortex needed a defined silhouette. I modeled a spiral mesh and applied a scrolling shader so it looked like a rotating cloud mass.


발표 흐름에 맞춰 단계를 넘기기Stepping Through Stages During the Talk
기상 현상은 순서가 있다. 수증기가 증발하고, 상승 기류가 생기고, 구름이 만들어지고, 비나 우박이 내린다. 이 순서를 발표 타이밍에 맞춰 넘길 수 있어야 했다.
Weather phenomena happen in stages: water vapor evaporates, updrafts form, clouds build up, and rain or hail falls. I needed to advance through those stages on cue while speaking.
count 변수 하나를 올리면서 각 단계에 해당하는 파티클 오브젝트를 켜고 끄는 구조로 만들었다. 상태 머신처럼 동작한다.
I wired a single count variable so each click incremented the stage and toggled the corresponding particle GameObjects on or off, acting like a simple state machine.
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);
}
// ... (이후 단계 생략)
count += 1;
if (count == 5)
{
// Disable all effects and reset
count = -1;
rainPar.SetActive(false);
rainPar2.SetActive(false);
tT1.SetActive(false);
// ...
}
else if (count == 1)
{
// Updrafts and initial cloud formation
tT1.SetActive(true);
upAir1.SetActive(true);
upAir2.SetActive(true);
}
else if (count == 2)
{
// Main thunderstorm/hail particle playback
tT2.SetActive(true);
tT3.SetActive(true);
rainPar.SetActive(true);
upAir1.SetActive(false);
tTen1.SetActive(true);
}
// ... (subsequent stages omitted)
지금 이 코드를 읽으면Looking Back at This Code Now
- 초기화에서
count를 0이 아니라 −1로 되돌린다. 함수 맨 위에서 이미count += 1을 했기 때문에, −1로 돌려놔야 다음 호출에서 0이 된다. 증가를 먼저 하고 판정을 나중에 하는 순서 때문에 생긴 보정이다. - 단계 판정과 연출이 한 덩어리다.
else if마다SetActive가 줄줄이 붙어 있어서 단계를 하나 추가하려면 분기 전체를 다시 읽어야 한다. 단계별로 켤 오브젝트 배열을 데이터로 두고 순회하면 분기가 사라진다. tT1,tTen1,upAir1같은 이름은 지금 보면 뭘 가리키는지 알 수 없다. 발표 하루 전에 붙인 이름의 대가다.5라는 종료 단계 수가 코드에 그대로 박혀 있다. 단계를 늘리면 이 숫자도 같이 고쳐야 한다.
- Resetting
countto -1 instead of 0. Becausecount += 1runs at the very top of the function, resetting to -1 is a workaround so the next call lands on 0. - Stage logic and object toggling are tangled together. Every
else ifbranch has a long list ofSetActivecalls, meaning you have to read the entire chain just to insert a step. Storing an array of GameObjects per stage would eliminate the branching. - Names like
tT1,tTen1, andupAir1are unreadable today. That is the price of naming variables the night before a presentation. - The final stage count (
5) is hardcoded directly in the condition.
발표 중에 끊기지 않게Keeping Scene Transitions Smooth During the Talk
씬을 넘길 때 화면이 멈추면 발표 흐름이 끊긴다. 비동기 로딩(LoadSceneAsync)에 진행률 바를 붙였다.
A frozen screen between scenes would break the flow of the presentation, so I attached a progress bar to async scene loading (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)까지 그대로다. 한 번 만들어 둔 것을 다음 프로젝트에 그대로 옮겨 쓴 흔적이다.
This is the exact same coroutine I used in the main menu of Car Open World—right down to the lodingImg typo. I copied my working script straight into the next project.
정리Wrap-Up
게임이 아니라 교육용 시뮬레이션을 만들면서, 기상 현상을 유니티의 입자 제어와 셰이더로 풀어내는 경험을 했다. 파티클 시스템을 이때 처음 제대로 다뤘다.
Building an educational simulation rather than a game was my first proper hands-on practice with Unity's particle system and shaders.
형태를 가진 것(태풍)과 흩뿌려지는 것(비·우박·번개)은 접근이 다르다는 것도 여기서 배웠다. 전자는 메시와 셰이더, 후자는 파티클이다. 둘을 바꿔서 시도했다면 훨씬 오래 걸렸을 것이다.
It also taught me that phenomena with a coherent volume (typhoons) and phenomena made of scattered elements (rain, hail, lightning) require different tools: meshes and shaders for the former, particles for the latter. Trying to force them the other way around would have taken much longer.