1) 초기 배치,
원점(부모 오브젝트 좌표)를 기준으로 (x, y) 만큼 떨어진 지점(자식 오브젝트 위치)을 삼각형으로 그려보면 위의 그림과 같이 나옵니다.
그 빗변의 길이인 r (radius, 반지름)이 동일하게끔 하면 부모 오브젝트와 거리가 동일한 원형의 좌표가 나옵니다.
자식과 부모 오브젝트가 이루는 각도 θ
삼각함수의 정의에 따라,
cos θ = x / r => x = r * cos θ
sin θ = y / r => y = r * sin θ
이렇게 r, θ 가 있다면 자식 오브젝트의 로컬 x, y 좌표를 구할 수 있게 됩니다.
여기서 원의 반지름 r은 개발자가 지정하며
각도 θ는자식 오브젝트 수에 따라 초기 각도를 자동으로 부여하게끔 하도록 해보겠습니다.
원 한바퀴 360도를 라디안으로 표현하면 2 π 입니다.
2 π * ( 몇번째 자식인지 인덱스 / 자식 오브젝트의 수)
로 준다면 자식 오브젝트 수에 따라 자동으로 초기 각도를 줄 수 있습니다.
2) 회전 시켜주려고 할 때,
자식 오브젝트들의 트랜스폼으로부터 로컬포지션의 좌표를 받아올 수 있습니다.
현재 x, y 로컬 좌표가 있으므로 이를 통해 현재 각도 θ를 구해봅시다.
아크탄젠트 atan2 는 2개의 인수(각각 y, x 좌표 차이)로 , 부호가 구분된 θ 를 구할 수 있는 함수입니다.
여기서 부모 오브젝트의 상대 좌표는 0, 0 원점이므로 자식 오브젝트의 로컬 x,y 좌표만 있으면 되는 것이지요.
atan는 역탄젠트 함수라고도 불리며 간단히 얘기하면 tan함수의 역입니다.
tan θ = y / x
atan ( tan θ ) = atan ( y / x )
θ = atan ( y / x )
이렇게 자식 오브젝트의 로컬 좌표를 이용해 현재 각도 θ를 구했습니다.
회전 속도에 따라 다음 각도로 θ를 증가/감소시켜주고,
이에 해당하는 새로운 x, y 좌표를 계산하여 이 좌표로 이동하면 원형 이동은 완성입니다.
원의 반지름 r은 주어진 수가 있고 변경한 θ 는 이미 값을 알고 있기에,
제일 위의 식을 다시 가져와서
x = r * cos θ
y = r * sin θ
새로 만들어진 좌표로 이동하면 끝납니다.
using UnityEngine;
// 부착된 오브젝트의 자식 오브젝트들이 주위를 회전하게 만들기
public class RotateAround : MonoBehaviour
{
// 자식 트랜스폼들
Transform[] childTrans;
// 자식의 수
int childNum;
// 회전 속도
public float rotateSpeed = 0.6f;
// 회전 반경
public float radius = 3;
// Start is called before the first frame update
void Start()
{
// 모든 자식의 트랜스폼
childNum = transform.childCount;
childTrans = new Transform[childNum];
for (int i = 0; i < childNum; i++)
{
childTrans[i] = transform.GetChild(i);
}
// 초기 위치 설정: 자식들을 원형으로 배치
for (int i = 0; i < childNum; i++)
{
// 2Pi를 자식 수로 나누어 어느 각도에 위치할지
float angle = i * Mathf.PI * 2 / childNum;
// 삼각함수, 반지름 이용하여 초기 좌표
Vector3 newPosition = new Vector3(Mathf.Cos(angle), Mathf.Sin(angle), 0) * radius;
childTrans[i].localPosition = newPosition;
// RotateAround_API를 쓸 때 주석 해제하면 각도 자동으로 부여
// childTrans[i].localRotation = Quaternion.Euler(Vector3.forward * Mathf.Rad2Deg * angle);
}
}
private void FixedUpdate()
{
RotateAround_OnlyPosChange(true);
}
// 유니티 api인 RotateAround를 사용하여 모든 자식 오브젝트를 회전하게끔
// 자식들이 로컬포지션(sqrt(x^2+y^2))을 반지름 삼아 부모 오브젝트 주위를 공전
// isClockWise = true > 시계 방향, false > 반시계 방향
void RotateAround_API(bool isClockWise)
{
// 회전 방향
Vector3 dir;
if (isClockWise)
dir = Vector3.back;
else
dir = Vector3.forward;
// 모든 자식 오브젝트 공전
for (int i = 0; i < childNum; i++)
{
// rotateSpeed에는 FixedDeltaTime인 0.02를 이미 곱해둔 값으로 설정하여 연산을 줄임
childTrans[i].RotateAround(transform.position, dir, rotateSpeed);
}
}
// 위의 api는 자식 오브젝트도 회전하는 것으로 보인다
// 해당 메서드는 자식 오브젝트들을 부모 오브젝트 주위를 원형으로 그리며 이동만 하여 회전값은 그대로 유지합니다.
// isClockWise = true : 시계 방향, false : 반시계 방향
void RotateAround_OnlyPosChange(bool isClockWise)
{
// 자식 오브젝트들을 부모 오브젝트 중심으로 회전
for (int i = 0; i < childNum; i++)
{
// 현재 좌표
Vector3 currentPos = childTrans[i].localPosition;
// 부모와 이루는 각도 계산
float angle = Mathf.Atan2(currentPos.y, currentPos.x);
// 회전 방향에 따른 각도 증감
// rotateSpeed에는 FixedDeltaTime인 0.02를 이미 곱해둔 값으로 설정하여 연산을 줄임
if (isClockWise)
angle -= Mathf.Deg2Rad * rotateSpeed;
else
angle += Mathf.Deg2Rad * rotateSpeed;
// 다음 좌표로 이동
Vector3 newPosition = new Vector3(Mathf.Cos(angle), Mathf.Sin(angle), 0) * radius;
childTrans[i].localPosition = newPosition;
}
}
}
결과
1) RotateAround_API(bool isClockWise)
부모 오브젝트를 기준으로 자전+공전을 동시에 하게끔 하고 싶다면 RotateAround()를 이용한 함수를 쓰는 것이 좋습니다.
단순한 형태의 오브젝트가 부모를 기준으로 회전하게끔 하고 싶을 때 유용하며 모양도 예쁘게 나옵니다.
2) RotateAround_OnlyPosChange(bool isClockWise)
자식 오브젝트들의 회전값을 변화시키지 않고 부모 주변을 회전시키고자 할 때는 위치만 옮겨주는 해당 메서드를 사용하는 것이 좋습니다.
'기능 구현 방법 정리' 카테고리의 다른 글
유니티 UI 이미지 마스크 (0) | 2025.01.27 |
---|---|
유니티 UI에 스파인 애니메이션 넣기 (0) | 2025.01.26 |
유니티) 레이어마스크(LayerMask) 사용법, 2D 가장 가까운 적 탐색 (0) | 2024.12.02 |
유니티) 머티리얼(Material) 색상 곱하기 (0) | 2024.11.25 |
유니티, 스파인) 머티리얼 교체 없이 색상만 바꾸고 싶을 때 (0) | 2024.11.25 |