[Web Sculpting] Rope Algorithm

JINBOK LEE·2023년 8월 7일
1

Web Sculpting

목록 보기
4/4
post-thumbnail

0. 들어가기 앞서

오늘은 물리적인 상호작용을 하는 로프 애니메이션을 구현하기 위해 공부를 진행중이다.

이를 위해 물리 라이브러리를 사용하여 작업할 수 있지만, 그보다 물리 애니메이션의 원리를 파악해보기 위해 아주 간단한 물리 엔진 클래스를 만들고 애니메이션에 활용해보고자 한다.

강의를 보고 차근차근 따라하며 이해하고 있는 과정에 있으며, 수학적으로 로프 애니메이션을 구현하는 알고리즘에 대해 작성해보고자 한다.


1. 알고리즘

먼저, 해당 알고리즘에 활용하고자 하는 클래스는 아래와 같다.

export default class Vector {
  constructor(x, y) {
    this.x = x || 0;
    this.y = y || 0;
  }

  // Vector.add()
  static add(v1, v2) {
    return new Vector(v1.x + v2.x, v1.y + v2.y);
  }

  static sub(v1, v2) {
    return new Vector(v1.x - v2.x, v1.y - v2.y);
  }

  // let vector = new Vector()
  // vector.add()
  add(x, y) {
    if (arguments.length === 1) {
      // arguments > x
      this.x += x.x;
      this.y += x.y;
    } else if (arguments.length === 2) {
      // arguments > x and y
      this.x += x;
      this.y += y;
    }

    // return modified Vector instance
    return this;
  }

  sub(x, y) {
    if (arguments.length === 1) {
      // arguments > x
      this.x -= x.x;
      this.y -= x.y;
    } else if (arguments.length === 2) {
      // arguments > x and y
      this.x -= x;
      this.y -= y;
    }

    // return modified Vector instance
    return this;
  }

  multi(v) {
    if (typeof v === "number") {
      this.x *= v;
      this.y *= v;
    } else {
      this.x += v.x;
      this.y *= v.y;
    }
    return this;
  }

  setXY(x, y) {
    this.x = x;
    this.y = y;
    return this;
  }

  // Pythagoras' theorem
  dist(v) {
    const dx = this.x - v.x;
    const dy = this.y - v.y;
    return Math.sqrt(dx * dx + dy * dy);
  }
}

1-1. 기본 상태

아래와 같은 로프가 있다. 왼쪽 상단의 점을 기준으로(Pinned) 오른쪽 점이 줄에 매달려 있는 상태이다.
1

1-2. 기본 상태 > 애니메이션

아무런 물리력을 설정하지 않고 애니메이션을 동작시키면 아래와 같이 오른쪽의 점이 수직으로 무한정 떨어지게 될 것이다.

우리가 원하는 것은 실제 로프처럼, 회색 선분의 길이는 유지가 되고 중력의 영향을 받아 원형을 그리며 떨어지는 것이다.
2

1-3. 실제와의 차이

따라서, 원래 로프의 길이를 "L" 이라고 하고, 위 이미지처럼 변화한 뒤의 로프의 길이를 "dist" 라고 했을 때, 실제와는 다르게 dist-L 만큼의 길이가 더 늘어나버린 것이다.

우리는 앞으로 여기서 이 차이를 "a" 라고 가정할 것이다.
3

1-4. 알고리즘 컨셉

실제와 같은 로프 애니메이션을 구현하기 위한 컨셉은, 위에서 가정한 "a" 라는 값을 이용하는 것이다.

이러한 차이의 1/2만큼의 길이를 애니메이션이 재생되는 매 프레임마다 로프의 시작점과 끝점에 각각 더해주는 것이다.

이렇게 된다면 매 프레임마다 a/2 만큼, 그다음은 a/2/2 만큼, 그 다음은 a/2/2/2 만큼... 결국 시간이 지남에 따라 a의 값은 0으로 수렴 할 것이다.
4

1-5. 삼각형으로 표현하기

그렇다면 중요한 "a" 의 길이는 어떻게 구할 수 있을까? 이제부터는 수학시간이라고 생각하면 된다. 각 선분은 아래 그림과 같이 정리할 수 있다.

여기서 우리는 매 프레임마다 오른쪽 하단의 점의 x,y 좌표를 알 수 있으므로 dx와 dy의 길이를 알고 있는 상태이며, 또한 피타고라스의 정리를 이용하여 "dist"에 해당하는 값 또한 구할 수 있다.
5

1-6. 삼각비를 이용한 빗변길이 구하기

이제 더 나아가 어릴적 배운 삼각함수의 삼각비를 이용하여 (cos, sin) ax와 ay에 해당하는 값 또한 구할 수 있게 된다.

이후에는 이를 이용하여 또다시 삼각함수를 이용하여 "dist-L"의 값을 알 수 있게 되는 것이다.

이제 원래 컨셉에 맞추어 매 프레임마다 이 값을 시작점과 끝점에 각각 연산해주면 된다.
6


2. 무게 (MASS) 표현하기

위와 같은 알고리즘으로 로프를 그려내면 원하는대로 애니메이션이 작동한다.
그러나 이것은 실제와는 조금 다른 현상을 그려내는데, 그 이유가 바로 "무게"가 적용이 안됐기 때문이다.

자 그렇다면 여기서 한발 더 나아가, 로프를 구성하는 각 점들에 "무게" 를 적용하면 어떻게 될까?

2-1. 무게가 다른 두 점

기존의 두 점을 이은 방식에서, 각 점들의 무게가 다르다고 가정해보자.
아래 그림과 같이 작은 점은 1의 무게를, 큰 점은 3의 무게를 가진다.

초록색 선분으로 표시한 것 처럼,
3의 무게를 가진 End Point 에서는 1 만큼의 늘어남을 가지고
1의 무게를 가진 Start Point(고정점) 에서는 반대로 3 만큼의 늘어남을 가질 것이다.

이 원리는 물리학의 보존 법칙을 기반으로 한다.
두 점의 총 운동량(질량 x 속도)은 일정하게 유지되어야 하는데,
한 점의 질량이 크면 그 점의 위치 조정의 정도는 작아지고
반대로 질량이 작으면 위치 조정의 정도는 커지는 원리이다.

2-2. 무게에 따른 가중치 연산

이를 다시 정리해보면 아래와 같이 정리할 수 있다.
즉, 이전에 구한 "dist-L" 값인 Offset 값에 무게에 따른 가중치를 줄 수 있는 것이다.
이 가중치를 구하고, 우리의 컨셉에 맞춰 각 점들에 대해 늘어나는 길이를 연산해주면 된다.

간단히 말하면, 두 점 사이의 선분이 늘어나거나 줄어들 때, 더 무거운 점은 더 적게 움직이고, 더 가벼운 점은 더 많이 움직이는 것이다.
이 원리를 통해 두 점 사이의 거리를 일정하게 유지하면서도 각 점의 질량에 따라 움직임의 정도를 조절할 수 있다.


3. 구현 결과

  • 좌 : 무게 적용 전
  • 우 : 무게 적용 후


4. 마치며

나의 절친한 백엔드 개발자 지인이 "내 지인 중 게임엔진 개발자가 있는데, 수학 공식이 적힌 노트를 들고 다니며 증명하는게 취미라고 하더라" 라고 이야기 한 적이 있다.

이 이야기를 듣고 처음에는 "개발자가 수학 공식을 증명한다고?" 라는 약간의 충격을 받았고, 왜 그렇게까지 수학에 몰두하는지 잘 이해가 되지 않았었다.

그러나 이번 로프 애니메이션을 구현해보면서, 간단한 물리력을 적용하는데에도 이런 수학적인 접근과 알고리즘을 구현해야 한다는 것을 느꼈고, 지인이 이야기 해준 게임 개발자분께서도 게임 속 물리엔진이나 빛과 관련된 여러 연산들을 위한 것이었겠구나 라는 생각이 들었다.

앞으로 정말 '실제 같은' 상호작용을 구현하면서 더 높은 차원의 UX를 제공하는 개발자로서 성장하고 싶은 마음이 가득한데, 이를 위해서 수학적인 접근에 더 친숙해져야겠다는 생각이 많이 든 시간이었다.


참고

Fast campus 온라인 강의

profile
깔끔한 비즈니스 로직 설계를 위해 공부하는 FE 개발자

1개의 댓글

comment-user-thumbnail
2023년 8월 7일

이렇게 유용한 정보를 공유해주셔서 감사합니다.

답글 달기