Deff_Dev
[Unity/C#] 자동으로 Box Collider 2D 설정해주는 툴 본문
📹 개발 결과
[레포지토리]
❓ 개발 배경
그림이 그려져 있는 영역에만 박스 콜라이더 2D를 설정하고 싶었다.
수동으로 그림에 맞춰 콜라이더를 설정하는건 비효율적이고 리소스가 추가됐을 때, 생산성이 많이 떨어진다고 판단했다.
그래서 스프라이트의 실제 그림 영역을 자동으로 감지하여 정확한 박스 콜라이더 2D를 생성하는 기능을 개발하기로 결정했다.
💻 개발 진행
1. 그림 영역 찾기
spriteRenderer = GetComponent<SpriteRenderer>();
Sprite sprite = spriteRenderer.sprite;
Rect spriteRect = sprite.rect;
Vector2 pivot = sprite.pivot;
int startX = int.MaxValue;
int startY = int.MaxValue;
int endX = int.MinValue;
int endY = int.MinValue;
for (int x = 0; x < (int)spriteRect.width; x++)
{
for (int y = 0; y < (int)spriteRect.height; y++)
{
Color pixel = sprite.texture.GetPixel((int)spriteRect.x + x, (int)spriteRect.y + y);
if (pixel.a > 0)
{
startX = Mathf.Min(startX, x);
startY = Mathf.Min(startY, y);
endX = Mathf.Max(endX, x);
endY = Mathf.Max(endY, y);
}
}
}
먼저 스프라이트의 크기 만큼 반복문을 돌려 픽셀의 alpha 값이 0이 아닌 픽셀을 찾고, 시작 좌표와 끝 좌표를 구한다.
Pixel 값을 (spriteRect.x + x, spriteRext.y + y) 로 구하는 이유는 아래서 설명하겠다.
※ 해당 작업이 이루어질려면 Texture2D의 Read/Write가 체크되어 있어야 한다.
(Tool은 자동으로 체크 및 해제하니 따로 설정할 필요는 없다.)
2. BoxCollider 2D 추가 및 PixelsPerUnit 저장
BoxCollider2D boxCollider = gameObject.AddComponent<BoxCollider2D>();
float pixelsPerUnit = spriteRenderer.sprite.pixelsPerUnit;
PixelsPerUnit은 스프라이트의 실제 크기와 위치를 Unity 씬에서의 적절한 크기와 위치로 매핑하기 위해 사용한다.
3. BoxCollider 2D 크기 설정
float sizeX = (endX - startX + 1) / pixelsPerUnit;
float sizeY = (endY - startY + 1) / pixelsPerUnit;
boxCollider.size = new Vector2(sizeX, sizeY);
endX - startX + 1 하는 이유
⇒ 픽셀을 세는 경우, 시작 픽셀과 끝 픽셀을 모두 포함해야 한다.
ex) startX == 1, endX == 5 라면 픽셀의 너비는 5픽셀이다.
3. BoxCollider 2D 위치 설정
float offsetX = (startX + (endX - startX) / 2f - pivot.x) / pixelsPerUnit;
float offsetY = (startY + (endY - startY) / 2f - pivot.y) / pixelsPerUnit;
boxCollider.offset = new Vector2(offsetX, offsetY);
- (endX - startX) / 2f 및 (endY - startY) / 2f ⇒ 그림 영역의 중간점 계산
- startX + 중심 및 startY + 중심 ⇒ 그림 영역의 실제 중심 좌표
- - pivot.x 및 - pivot.y ⇒ pivot 좌표를 고려하여 위치를 재 조정
- / pixelsPerUnit ⇒ 픽셀 단위 계산 결과를 Unity 월드 단위로 변환
- BoxCollider offset 적용
👿 트러블 슈팅
Pixel 값을 (spriteRect.x + x, spriteRext.y + y) 로 구하는 이유에 대해 설명하겠다.
for (int x = (int)spriteRect.x; x < spriteRect.width; x++)
{
for (int y = (int)spriteRect.y; y < spriteRect.height; y++)
{
Color pixel = texture.GetPixel(x, y);
if (pixel.a > 0)
{
startX = Mathf.Min(startX, x);
startY = Mathf.Min(startY, y);
endX = Mathf.Max(endX, x);
endY = Mathf.Max(endY, y);
}
}
}
기존에 작성한 코드는 단일 Texture2D만 생각하고 개발했다보니 스프라이트 아틀라스에 적용된 스프라이트에서 해당 기능을 실행했을 때, Collider가 제대로 설정이 되지않는 문제가 발생했다.
즉, 스프라이트 아틀라스 내의 개별 스프라이트 위치를 고려하지 않아 생긴 문제였다.
for (int x = 0; x < (int)spriteRect.width; x++)
{
for (int y = 0; y < (int)spriteRect.height; y++)
{
Color pixel = sprite.texture.GetPixel((int)spriteRect.x + x, (int)spriteRect.y + y);
if (pixel.a > 0)
{
startX = Mathf.Min(startX, x);
startY = Mathf.Min(startY, y);
endX = Mathf.Max(endX, x);
endY = Mathf.Max(endY, y);
}
}
}
스프라이트의 아틀라스 내 상대적 위치를 고려하여 정확한 픽셀 값을 구하는 방식으로 수정했다.
스프라이트 아틀라스에 포함된 스프라이트에서도 잘 작동하는 것을 볼 수 있다.
⚒️ 개발 이후
스프라이트 전체 넓이를 탐색하는 작업이므로, 런타임 중 실행시키기엔 비용이 상당했고, 이후 프로젝트에서도 사용할 수 도 있다고 판단하여, 커스텀 에디터 툴로 해당 기능을 사용할 수 있도록 개발했다.
'Unity(유니티) > 유니티 공부' 카테고리의 다른 글
[Unity/C#] Collider 중심 좌표 반환 (0) | 2024.11.12 |
---|---|
[Unity/C#] TextMeshPro Material 스크립트 변경 (3) | 2024.11.06 |
[Unity/C#] Addressable로 Sprite Atlas 로드 및 저장 (0) | 2024.08.13 |
[Unity/C#] Sprite Atlas Packing 시 Rotation 될 때 (0) | 2024.08.13 |
[Unity/C#] Addressable 빌드 오류 (Cannot read BuildLayout header ... ) (0) | 2024.08.11 |