학회 프로젝트 중 손을 모아서 물방울을 만드는 스킬을 구현하게 됐다. (사실 쓰일지 안쓰일지 모르지만 재밌을 것 같아서 만들어봄)
A. 스킬 정보
손을 모으면 물방울을 키울 수 있음.
물방울이 다 만들어지기 전에 손을 떼면 그냥 물방울은 떨어짐.
물방울을 다 만들고 손을 떼면 그 물방울은 공중에 떠있음.
공중에 떠 있는 물방울을 grab해서 사용할 수 있음.
생각보다 쉽고 간단해보였는데, 개발하면서 꽤 막히는 부분이 있었음..!
B. 두 손 사이의 거리 측정
두 손의 위치는 OVRCameraRig의 Right/LeftHandAnchor로 받아온다.
아주 간단한 거리 공식을 적용해서 두 손 사이의 거리인 handDistance를 구함.
handDistance를 코솔창에 출력해서 테스트해본 결과, handDistance가 0.3일 때 물방울이 생기는 것이 적절했음.
이 값은 언제든지 바꿀 수 있도록 maxHandDistance 변수로 설정.
C. 생성된 물방울 위치 조정
물방울은 항상 두 손의 중간에 가도록 할 것임.
이번에도 아주 간단한 두 점의 중간 위치를 구한 후 waterPos로 설정.
D. 물방울이 커지는 속도 조정
물방울은 두 손이 가까우면 가까울 수록 더 빠르게 커지도록 설정함.
여러가지 시도를 해봤는데, 거리에 반비례해서 커지게하면 극적인 효과가 부족함을 느낌.
따라서 Exp 함수를 사용해서 가까우면 더 극적으로 커지도록 했음.
Exp 함수의 x값은 0~3 정도로 쓰기로 함.
Exp(0) = 1, Exp(3) = 20정도기 때문에 내가 원하는 효과를 얻을 수 있다고 생각.
만약 더 극적으로 커지게 하려면 3보다 더 큰 수를 사용하면 된다.
다음 할 일은 handDistance를 0~3의 값으로 전환해야한다!
물방울을 커지게 할 수 있는 handDistance의 값은 0~0.3 사이의 값이다.
그리고 handDistance의 값이 작을수록 Exp에 들어갈 값은 커야하니까
x = 1-handDistance를 통해 반비례의 관계를 비례 관계로 전환.
0~3 사이의 간격은 3, 0.7~1 사이의 간격은 0.3. 이 간격 차이만 조정해주면 끝.
예를 들어 두 손 사이의 거리가 0.7 <= x_1 <= 1일 때 이 값을 0 <= x_2 <= 3 으로 바꿔주면 됨.
x_2 = (x_1 - 0.7) * 3 / 0.3 을 해주면된다.
위 내용을 코딩화한게 아래 사진.
영상으로 결과를 확인하자..!
<영상 첨부>
E. 물방울의 미완성/완성의 결과
물방울은 크게 두가지 결과가 있음. 미완성과 완성.
처음 생성된 물방울의 크기는 0.05f(scale의 x값)이고 완성된 물방울의 크기는 0.2f이다.
즉, 물방울의 크기가 0.2f가 될 때까지 키우면 완성된 물방울이 되는 것이다.
[미완성 물방울]
물방울 크기가 0.2f가 되기 전에 handDistance가 0.3f보다 멀어지는 경우.
그 때는 그 물방울을 사용할 수 없으니 바로 떨어지게 만든다.
어떻게 떨어지게 만드냐?
초기 물방울은 UseGravity = false이다.
이 UseGravity를 true로 만들어주는 순간 중력을 받고 떨어질 것이다.
또한, 떨어지는 물방울 도중에 잡을 수 없게 초기 물방울의 HandGrabType은 None이다.
즉, 어떤 버튼을 눌러도 물방울을 잡을 수 없게 되어 있다. 따라서, 이렇게 치팅을 방지했다.
[완성 물방울]
물방울 크기가 0.2f가 될 때까지 handDistance를 0.3f 이하로 유지한 경우.
완성된 물방울은 잡기 전까지는 생성된 곳에 떠 있다.
1. 완성된 순간 물방울은 더 이상 두 손의 중간을 따라가지 않는다.
2. 완성된 순간 물방울은 크기가 더 이상 커지지 않는다.
3. 떠있는 물방울을 한번 잡으면 그 때 중력이 작용해서 던질 수 있다.
1, 2번 같은 경우에는 if문을 적절히 사용하여 구현함.
3번의 경우 (a) 물방울이 잡히도록 설정하고 (b) 잡는 순간 UseGravity = true로 설정해야 한다.
(a) 초기의 물방울의 HandGrabType이 None인 것을 바꾸어 Pinch로 Grab이 되도록 설정해야한다.
이 때,
네임스페이스를 사용해줘야 함.
물방울의 HandGrabInteractable 컴포넌트의 SupportedGrabTypes에 Pinch를 추가해줌으로써, pinch를 통해 물방울이 잡히도록 설정했다.
(b) 물방울을 처음 잡는 순간을 알아야 한다. 즉, 처음 잡혔는지 안잡혔는지 check해야함.
firstSelected = false인 경우(아직 한번도 잡힌적 없는 경우)에 if문에 들어가서 UseGravity = true로 설정.
이렇게 하면, 그 다음부터는 물방울에 중력이 적용될 것이다.
F. 추가적인 제약 설정
이렇게 기본적으로 구현하려는 것은 구현이 되었는데, 플레이를 해보면서 유저 경험적으로 수정해야할 부분을 발견했다.
1. 물방울을 잡고있는 상황에서 손이 가까워지면 또 만들어짐.
=> 뭘 잡고있는 상황에서는 물방울이 만들어지지 않게 해야한다.
=> 또한, 물방울을 만들고 있는 상황에서도 뭘 잡으면 물방울 생성을 cancel한다.
지금까지 구현한 것은 어떤 상호에서든 두 손만 가까워지면 물방울이 생성됐다.
그러나 물방울을 모으는 목적이 아니더라도 두 손은 다른 이유로 언제든지 가까워질 수 있다.
즉, 물방울을 모으고 싶지 않은데 나도 모르게 모으게될 수도 있다는 것이다.
따라서, 컨트롤러로 무언가를 잡고있는 경우에는 물방울이 생성되지 않게 했다. 또한 생성되는 도중 무언가를 잡아도 물방울을 캔슬시킨다. 이것을 구현하려면 먼저 컨트롤러가 무언가를 잡고있는지 아닌지를 알려주는 bool이 필요하다.
먼저, 두 손의 HandGrabInteractor 컴포넌트를 레퍼런스 받아온다.
해당 HandGrabInteractor는 IsGrabbing이라는 bool이 있다. 무언가를 그랩 중이면 true, 아니면 false.
이렇게 if문에서 해당 bool을 사용할 수 있다. (full code는 마지막에)
이렇게 수정하고나니 또 다른 문제가 발생함.
2. 두 손이 가까운 상태에서 잡고있는 물방울을 놔버리면 그 순간 또 다른 물방울이 만들어진다. 굉장히 부자연스러움.
=> 두 손이 가까운 상태에서 grab 상태가 변했다고 물방울이 생겨버리면 안된다.
=> 즉, 두 손이 멀었다가 가까워지는 순간을 포착해서 그 순간에만 물방울이 만들어지도록 해야한다.
위에 if문 코드는 Update 함수 안에 있다. 계속 저 조건들을 체크한다는 뜻인데, 갑자기 그랩 중이던 손이 놔버리면 양 손의 IsGrabbing을 true가 되기 때문에 놓자마자 물방울이 또 만들어지게 되는 것이다.
여기서 WasOut boolean 변수를 사용해서,
직전 frame에서 손이 멀었다가 혀재 frame에서 손이 가까워진 순간을 포착했다.
CheckWasOut을 통해 직전 frame에 wasOut이었고(=두손이 멀었었고), 현재 frame에서는 두 손이 가까운지 체크.
CalWasOut은 CheckWasOut한 후에 현재 frame의 상태를 wasOut에 업데이트.
이렇게 프레임마다 체크를 해주면 두손이 멀어졌다가 가까워진 순간이 포착된다.
따라서, 이미 손이 가까워진 상태에서 그랩을 풀어도 멀었다가 가까워진 것이 아니니 바로 물방울이 만들어지지 않는다.
위에 첨부한 if문 코드에서 _WasOut을 통해 체크하고 있는 것을 볼 수 있다.
G. 최종 영상
짜잔~
두 손이 가까워지면 물방울이 생김. But 무언가를 잡고 있을 때는 생기지 않음.
물방울이 다 커지기 전에 손이 멀어지면 떨어짐.
물방울이 다 커지고 나서 손이 멀어지면 그 물방울을 잡을 수 있음.
잡은 물방울을 던져서 식물을 키울 수 있음.
이렇게 만들어보고 싶었던 상호작용 구현 완료!
사실 이 글을 쓰는 상이에 다른 상호작용으로 최종 결정이 되어서.. ㅎㅎ
최종 결정 된 상호작용을 구현하는 게 다음 글이 될 것 같군요.
그렇다면 즐개~ (즐거운 개발이란 뜻,,,,,)
'Unity > Dev' 카테고리의 다른 글
[Unity][VR] 리듬 게임 모듈화 (1) | 2023.06.15 |
---|---|
[Unity] 물방울을 뿌리면 식물이 자라는 interaction 구현하기 (3) (0) | 2023.01.21 |
[Unity] 물 속에서 grab하면 물 구체가 잡히는 interaction 구현하기 (2) (0) | 2023.01.20 |
[Unity] Light Probe로 자연스러운 빛과 실시간 그림자 연출하기 (1) | 2023.01.18 |
[Unity] UI Scripting Automation (2) (0) | 2023.01.17 |