[Unity]IK basic-1

Connected Brain·2025년 1월 24일

IK_practice

목록 보기
1/3

Inverse Kinematic animation 만들기

  • 지난 학기 수업에서 개념만 간단하게 배웠던 Inverse Kinematic 시스템을 Unity에서 직접 만들어 보고 어떤 요소를 고려해야할 지 고민한 부분을 기록하고자 한다.

참고 영상
https://youtu.be/hbgDqyy8bIw?si=Td_2GO6HdAuv7Swe

  • 해당 영상을 따라해보며 영상에 나온 내용을 Unity에 적용하고자 하였다. 본질적인 부분은 크게 다르지 않기때문에 실제 화면상에 표현하는 부분에서만 Unity에 적합하도록 바꾸었다.

개발일지 #1

1. Segment Class 만들기

  • 전체 Inverse Kinematic 시스템을 구성하는 최소단위가 될 Segment에 관한 정보를 저장하는 클래스이다.
public class Segment : MonoBehaviour
{
    public Vector2 a = new Vector2();
    public Vector2 b = new Vector2();
    public float angle;
    public float len;

    public Segment segment(float x, float y, float _angle, float _len)
    {
        a = new Vector2(x, y);

        angle = _angle;
        len = _len;
        CalculateB(a, _len, _angle);

        return this;
    }

    public void CalculateB(Vector2 dot_a, float _len, float _angle)
    {
        //Mathf.Cos()는 안에 라디안 값을 넣어주어야함
        float dx = _len * Mathf.Cos(_angle * (Mathf.PI / 180));
        float dy = _len * Mathf.Sin(_angle * (Mathf.PI / 180));

        b.Set(dot_a.x + dx, dot_a.y + dy);
    }
}
  • 먼저 필요한 변수를 선언하였다.

    public Vector2 a = new Vector2();
    public Vector2 b = new Vector2();
    public float angle;
    public float len;

  • 각각의 Segment는 시작지점과 끝지점(a,b) 그리고 각도와 크기를 저장하고 있다.

  • 그 다음 순서는 Segment 생성자를 만들어 주었다, 앞서 선언한 변수들을 입력받아 저장하도록 했고 자기 자신을 반환하는 형태이다.

  • 이 과정에서 b의 위치를 계산하는 과정이 필요하여 이를 Calculate() 함수에서 실시하도록 하였다.

  • angle의 각을 가지는 직각삼각형을 그리고 삼각비를 이용한다고 생각하면 간단하다.

    [부가 설명]

    _len * Mathf.Cos(_angle * (Mathf.PI / 180));

    여기서 _len은 삼각형의 빗변, Mathf.Cos(_angle (Mathf.PI / 180)을 간단히 하면 Cos(angle)이다.
    따라서 _len을 빗변으로 가지고 밑변이 α인 삼각형이 있다고 가정했을 때
    위의 코드는 _len
    α/_len = α임을 알 수 있다.
    결론적으로 우리가 필요한 좌표의 x값인 α값을 각과 빗변의 길이를 통해서 구할 수 있다.

  • 우리가 필요한 y좌표 값 또한 Sin값을 통해서 알 수 있다. 이렇게 b의 위치까지 계산하여 Segment에 필요한 모든 정보를 담을 수 있다.

2. Unity 화면에 표시하기

  • 앞선 정보를 Unity 화면에 표시하기 위해 Iksystem이라는 스크립트를 만들어 표시에 사용할 게임 오브젝트를 할당해주었다.
public GameObject dot_a;
public GameObject dot_b;
public GameObject arm;

Vector3 arm_Pos = new Vector3();
Vector3 arm_Scale = new Vector3();
Quaternion arm_Rot = Quaternion.identity;
  • dot_a와 dot_b는 a와 b를 표시하는 2D Sprite circle 오브젝트이다. arm는 둘 사이를 잇는 2D Sprite Sqare 오브젝트를 할당하였다.
    -arm의 위치와 회전을 위한 변수 그리고 두 점 사이의 거리를 채우도록 스케일 값을 조절하기 위해 스케일도 선언하였다.
public Vector2 point;
public float angle;
public float len;

그리고 Inspector 창에서 Segment를 변형하기 위한 값을 입력할 수 있도록 변수를 선언한다.

Segment vertex;

private void Awake()
{
    dotSystem = new Segment();
    vertex = dotSystem.segment(point.x, point.y, angle, len);
}
void GenerateSegment()
{
    arm_Pos.Set((vertex.a.x + vertex.b.x) / 2, (vertex.a.y + vertex.b.y) / 2, 0);
    arm_Scale.Set(vertex.len, 1, 1);
    arm_Rot = Quaternion.Euler(0, 0, angle);

    dot_a.transform.position = vertex.a;
    dot_b.transform.position = vertex.b;
    arm.transform.position = arm_Pos;
    arm.transform.localScale = arm_Scale;
    arm.transform.rotation = arm_Rot;
}
  • vertex라는 Segment를 저장하는 변수를 새로 만들고 Inspector 창에서 입력받은 값을 저장해주었다.
  • 아래는 게임 오브젝트를 움직이고 변형하여 정보에 맞게 움직이도록 하였다.
  • 양 끝점은 회전하지 않고 arm이 회전하도록 하였으며, arm은 두 점 중앙에 위치하여 서로를 이어줄 수 있도록 하였다.
  • 다만 좀 걸리는 부분은 vertex에 저장된 a와 b를 직접 읽어올 수 있는 점이 괜찮을까? 하는 고민이 생겨서 향후에 적절한 구조를 고민해봐야 할 것 같다.

결과물


-각도를 바꾸었을 때 회전하는 것을 볼 수 있다.

0개의 댓글