Closed, Open, UpsideDown, Pouring 상태와 물 저장/무게 시스템
이번에는 우산 시스템을 본격적으로 프로토타입으로 옮겼다. 이 게임에서 우산은 단순한 장식이나 방어 도구가 아니라, 환경과 상호작용하는 핵심 장치다. 그래서 처음부터 완성형 애니메이션이나 정교한 모델을 붙이기보다는, 우산이 어떤 상태를 가지고 있고 그 상태에 따라 환경과 어떻게 반응하는지를 먼저 검증하는 쪽으로 접근했다.
현재 우산 시스템의 중심은 PlayerUmbrellaController.cs다.
우산을 처음부터 들고 시작하지 않기
처음에는 플레이어가 우산을 이미 가지고 있는 구조로 생각할 수도 있었다. 하지만 기획상으로는 플레이어가 탑 안에서 우산을 얻게 되는 흐름이 더 자연스럽다. 그래서 우산을 처음부터 손에 들고 시작하는 대신, 바닥에 떨어진 UmbrellaRoot를 플레이어가 줍는 구조로 바꿨다.
현재 구조는 다음과 같다.

Player
└─ UmbrellaAttachPoint
UmbrellaRoot
├─ TempUmbrella
└─ PourOrigin
플레이어가 UmbrellaRoot에 닿으면 UmbrellaPickup.cs가 PlayerUmbrellaController.AcquireUmbrella(...)를 호출한다. 그러면 UmbrellaRoot 전체가 UmbrellaAttachPoint 아래로 붙는다.
처음에는 TempUmbrella만 옮겨도 되지 않을까 생각했지만, 그렇게 하면 문제가 생긴다. PourOrigin 같은 기준 오브젝트가 UmbrellaRoot 아래에 남아 있으면, 플레이어가 우산을 들고 있어도 물을 붓는 기준점은 플레이어를 따라오지 않는다. 그래서 최종적으로는 UmbrellaRoot 전체를 줍는 구조가 더 맞다고 판단했다.
우산 상태 나누기
현재 우산 상태는 네 가지다.
| 상태 | 의미 |
| Closed | 우산을 접은 기본 상태 |
| Open | 우산을 펼쳐 비를 막는 상태 |
| UpsideDown | 우산을 거꾸로 들어 물을 받는 상태 |
| Pouring | 저장한 물을 붓는 상태 |
처음에는 Inverted라는 이름을 사용했지만, 나중에 우산 살이 뒤집히는 의미와 헷갈릴 수 있다고 판단했다. (이건 추후 추가되야하기에..)
그래서 현재 “거꾸로 들기” 상태는 UpsideDown으로 바꿨다.
이 상태들은 단순히 비주얼만 바꾸는 것이 아니라, 실제 상호작용 규칙을 나눈다.
| 상태 | 비를 맞을 때 | 물 저장 | 물 붓기 |
| Closed | 플레이어가 젖음 | 안 함 | 안 함 |
| Open | 플레이어가 젖지 않음 | 안 함 | 안 함 |
| UpsideDown | 플레이어가 젖고, 우산에 물도 저장 | 가능 | 가능 준비 상태 |
| Pouring | 저장된 물을 방출 | 저장량 감소 | 진행 중 |
플레이어가 맞은 비와 우산에 저장된 물을 분리하기
처음에는 우산에 저장된 물만 생각했다. 하지만 실제로는 플레이어가 직접 비를 맞는 양도 따로 보고 싶었다. 그래서 currentStoredWater와 별개로 currentPlayerRainAmount를 추가했다.
| 값 | 의미 |
| currentStoredWater | 우산 안에 저장된 물의 양 |
| currentPlayerRainAmount | 플레이어가 직접 맞은 비의 양 |
| maxStoredWater | 우산이 저장할 수 있는 최대 물 |
| maxPlayerRainAmount | 플레이어 젖음량 최대값 |
이렇게 나누면 상태별 규칙을 더 명확히 만들 수 있다.
- Closed: 플레이어가 비를 맞으므로 currentPlayerRainAmount 증가
- Open: 우산이 비를 막으므로 currentPlayerRainAmount 증가 안 함
- UpsideDown: 플레이어가 비를 맞고, 동시에 우산에도 물이 저장됨
- 비 영역 밖: naturalDryRate에 따라 currentPlayerRainAmount가 서서히 감소
비 영역은 UmbrellaRainArea.cs에서 처리한다. 이 스크립트는 플레이어가 트리거 안에 있을 때 ApplyRainExposure(...)를 호출한다.
즉 비 영역 자체가 복잡한 물리 시뮬레이션을 하는 것이 아니라, “지금 플레이어가 비를 맞고 있다”는 신호를 우산 컨트롤러에 전달하는 구조다.
Open 상태는 비를 막기만 한다
중간에 한 번은 Open 상태일 때 플레이어의 젖음량이 더 빨리 줄어들게 만들었다. 하지만 생각해보니 우산을 펼쳤다고 이미 젖은 몸이 빠르게 마르는 것은 자연스럽지 않았다.
그래서 지금은 Open 상태가 하는 일은 단순하다.
- 비를 막는다
- 플레이어 젖음량이 더 이상 증가하지 않는다
- 대신 젖음량 감소는 비 영역 밖에서 자연 건조로만 처리한다
이렇게 정리하니 상태의 의미가 더 명확해졌다.
| 상황 | Player Rain 변화 |
| 비 영역 안 + Closed | 증가 |
| 비 영역 안 + Open | 변화 없음 |
| 비 영역 안 + UpsideDown | 증가 |
| 비 영역 밖 | 서서히 감소 |

우산에 물이 차면 무게도 늘어나게 하기
우산을 거꾸로 들어 물을 받는다면, 단순히 물 게이지가 차는 것만으로 끝내기보다 플레이어에게도 영향을 주는 편이 더 재미있다고 생각했다. 그래서 저장된 물만큼 플레이어의 무게가 늘어나도록 했다.
현재 계산은 단순하다.
현재 무게 = 기본 Rigidbody 질량 + 저장된 물 * storedWaterWeightMultiplier
기본값은 1 물 = 1kg처럼 생각할 수 있다. 예를 들어 우산에 물이 5만큼 차면, 플레이어 무게도 5kg 증가한다.
| 변수 | 의미 |
| baseWeightKg | 시작 시 Rigidbody 질량 |
| currentWeightKg | 현재 계산된 플레이어 무게 |
| storedWaterWeightMultiplier | 저장된 물을 무게로 환산하는 배율 |
이 값은 RefreshWeight()에서 갱신한다. 물을 받거나, 물을 붓거나, 우산 안의 물을 버릴 때마다 무게가 다시 계산된다.
현재 이동은 ForceMode.Acceleration 기반이라 수평 이동에는 질량 증가가 크게 드러나지 않을 수 있다. 하지만 점프나 다른 물리 상호작용에서는 이후 충분히 활용할 수 있는 값이다. 나중에는 저장된 물이 많을수록 점프가 낮아지거나, 발판이 더 강하게 눌리는 식으로 확장할 수 있다.
상태를 바꾸면 물을 버리기
UpsideDown 상태에서는 우산 안에 물이 담긴다. 그렇다면 그 상태에서 다시 Open이나 Closed로 바꾸면 어떻게 해야 할까?
이 부분은 우산의 방향을 바꾸는 순간 안의 물을 쏟아버리는 것으로 처리했다.
현재 규칙은 다음과 같다.
- UpsideDown -> Open
- UpsideDown -> Closed
- Pouring -> Open
- Pouring -> Closed
이런 전환이 발생하면 currentStoredWater를 0으로 만든다. 그리고 무게도 바로 다시 계산한다.
이 규칙 덕분에 우산에 물을 저장하려면 UpsideDown 상태를 유지해야 한다. 반대로 우산을 펼치거나 닫으면, 저장했던 물을 포기하는 선택이 된다.
이건 나중에 퍼즐에서도 의미가 생길 수 있다. 플레이어가 물을 담은 채 이동할지, 비를 막기 위해 우산을 펼쳐 물을 포기할지 선택하게 만들 수 있기 때문이다.
마우스로 피킹한 위치를 향해 물 붓기
물 붓기는 처음에 PourOrigin.forward 방향으로만 쏘도록 만들었다. 하지만 이 방식은 PourOrigin의 방향을 조금만 잘못 잡아도 물이 엉뚱한 곳으로 나갔다.
그래서 지금은 마우스로 피킹한 지점을 기준으로 물을 붓는다.
흐름은 다음과 같다.
- 마우스 위치에서 카메라 ray를 쏜다.
- 맞은 지점 또는 바닥 평면의 지점을 구한다.
- 플레이어는 그 방향을 바라본다.
- PourOrigin에서 그 지점 방향으로 물 ray를 쏜다.
- 맞은 오브젝트에 UmbrellaWaterTarget이 있으면 물을 전달한다.
즉 조준선과 붓기선이 같은 지점을 향하도록 만든 것이다.
현재 F3 디버그에서는 다음 선들이 보인다.
| 색 | 의미 |
| 초록색 | 플레이어가 바라보는 방향 |
| 주황색 | 마우스로 피킹한 조준 지점 |
| 청록색 | 실제 물 붓기 ray |
이 디버그를 넣으니 PourOrigin 위치나 방향이 잘못됐을 때 바로 확인할 수 있었다.

WaterTarget과의 상호작용
물을 받을 대상은 UmbrellaWaterTarget.cs로 만들었다.
현재 기능은 단순하다.
- requiredWater만큼 물을 받으면 활성화
- 받은 물의 양은 receivedWater에 저장
- 활성화되면 색상을 바꿈
- F3 디버그가 켜져 있으면 오브젝트 위에 요구량, 현재량, 활성화 여부를 표시
| 값 | 의미 |
| requiredWater | 활성화에 필요한 물 양 |
| receivedWater | 현재 받은 물 양 |
| isActivated | 활성화 여부 |
이 구조는 나중에 발판, 씨앗, 기계 장치 같은 퍼즐 오브젝트로 확장하기 좋다. 지금은 색만 바뀌지만, 실제 게임에서는 문을 열거나, 식물을 자라게 하거나, 특정 장치를 작동시키는 식으로 연결할 수 있다.

F3 디버그를 만든 이유
이번 작업 중간중간 가장 많이 필요했던 것은 “지금 시스템이 실제로 어떤 값을 가지고 있는가”를 보는 일이었다.
그래서 F3 디버그를 넣었다.
현재 F3을 누르면 다음 정보를 확인할 수 있다.
디버그 정보 설명
| State | 현재 우산 상태 |
| Has Umbrella | 우산 소유 여부 |
| Stored Water | 우산에 저장된 물 |
| Player Rain | 플레이어가 직접 맞은 비 |
| Weight | 저장된 물까지 반영한 현재 무게 |
| Aim Hit | 마우스 조준이 맞춘 대상 |
| Pour Hit | 물 ray가 맞춘 collider |
| Pour Target | 실제 물을 받은 target |
이 디버그가 없으면, 물이 안 들어가는 이유가 ray 방향 때문인지, target collider 때문인지, state 때문인지 구분하기 어렵다. 특히 우산 상태가 늘어나기 시작하면서 디버그는 거의 필수가 되었다.
현재 우산 시스템 정리
지금까지 구현한 우산 시스템은 아직 완성형은 아니다. 하지만 간단한 핵심 루프는 들어갔다.
- 플레이어가 바닥의 우산을 줍는다.
- 우산 상태를 바꾼다.
- 비 영역에서 플레이어가 젖거나 우산에 물이 찬다.
- 우산에 저장된 물만큼 플레이어 무게가 늘어난다.
- 상태를 바꾸면 저장된 물을 버릴 수 있다.
- 마우스로 피킹한 지점을 향해 물을 붓는다.
- 물을 받은 오브젝트가 반응한다.
- F3 디버그로 이 과정을 확인한다.
이제 이 시스템을 기반으로 이후에는 더 많은 환경 상호작용을 붙일 수 있다. 예를 들어 물을 받아 식물을 키우거나, 특정 무게 이상일 때 발판을 누르거나, 비를 막아 특정 장치를 보호하는 식으로 확장할 수 있다.
마무리
이번 작업은 우산을 단순히 펴고 접는 장비가 아니라, 상태와 자원을 가진 상호작용 도구로 만드는 첫 단계였다.
Closed, Open, UpsideDown, Pouring 상태를 나누고, 플레이어 젖음량, 우산 물 저장량, 무게, WaterTarget 반응까지 연결하면서 기획서에 있던 우산의 핵심 문법이 조금씩 실제 게임 시스템으로 옮겨지기 시작했다.
아직 애니메이션이나 정식 UI, 모바일 조작은 붙지 않았지만, 이제 우산이 환경과 상호작용할 수 있는 최소 루프는 만들어졌다.
'Unity - '한 우산 아래'' 카테고리의 다른 글
| 이단 점프와 우산 활공 구현하기 (0) | 2026.04.14 |
|---|---|
| 우산에 담긴 물의 무게로 문을 여는 퍼즐 만들기 (1) | 2026.04.12 |
| Unity에서 이동 방향을 바라보는 플레이어 회전 만들기 (0) | 2026.04.12 |
| 맵툴 제작기 3 - 2000줄 넘은 Unity Editor Tool 코드 정리하기 (0) | 2026.04.08 |
| 맵툴 제작기 2 - AABB는 왜 안 맞았는가 : Surface Snap에서 Face Anchor까지 (0) | 2026.04.08 |