3. Matter.js 개요
기본 구성
p5.js 수도코드
- 지금까지 모든 예제를 수도코드(pseudocode)로 일반화 해 봅시다.
setup()- 객체들을 생성합니다.
draw()- 힘을 계산합니다.
- 객체들에 힘을 적용합니다 ()
- 가속도와 속도를 기반으로 객체의 위치를 변경합니다.
- 객체를 그립니다.
Matter.js 수도코드
- Matter.js에서는,
setup()- 객체들을 생성합니다.
draw()- 객체를 그립니다.
Matter.js의 핵심 개념
- 엔진(Engine)
- 물리 시뮬레이션 자체를 관리하는 요소입니다.
- 시뮬레이션 세계와 세계를 업데이트 하는 다양한 속성들을 관리합니다.
- 보디(Bodies)
- 세계의 기본 요소로, 시뮬레이션되는 물리적 객체를 의미합니다.
- 보디는 위치와 속도를 가집니다. 지금까지 우리가 만든 클래스와 유사하죠.
- 또한 형상을 정의하는 기하학적 모양도 가지고 있습니다.
- 보디 는 입자 처럼, 물리 엔진 세계에 있는 물체 를 설명하기 위해 사용하는 일반적인 용어로 신체와 관련이 없습니다.
- 복합체(Composite)
- 여러 보디로 이루어진 복합 엔터티를 생성할 수 있게 해주는 컨테이너입니다.
- 세계 자체가 복합체의 예시이며, 생성된 모든 보디는 세계에 추가되어야 합니다.
- 제약 조건(Constraints)
- 보디 간 연결 역할을 합니다.
Matter.js에서의 벡터
- 또 다른 중요한 요소로 벡터가 있죠.
- x, y 요소를 사용해 위치, 속도, 힘을 정의하는, 크기와 방향이 있는 엔터티입니다.
- 그런데
p5.Vector객체와 문법이 약간 다릅니다.
p5.js
// 벡터 정의
let v = createVector(1, -1);
// 벡터 덧셈
let a = createVector(1, -1);
let b = createVector(3, 4);
a.add(b);
let c = p5.Vecotr.add(a, b);
// 벡터 스칼라 곱
let m = createVector(1, -1);
m.mult(4);
// 벡터 크기, 정규화
let n = createVector(3, 4);
let size = n.mag();
n.normalize();
Matter.js
// 벡터 정의
let v = Matter.Vector.create(1, -1);
// 벡터 덧셈
let a = Matter.Vector.create(1, -1);
let b = Matter.Vector.create(3, 4);
Matter.Vector.add(a, b, a);
let c = Matter.Vector.add(a, b);
// 벡터 스칼라 곱
let m = Matter.Vector.create(1, -1);
m = Matter.Vector.mult(4);
// 벡터 크기, 정규화
let n = Matter.Vector.create(3, 4);
let size = Matter.Vector.magnitude(n);
n = Matter.Vector.normalise(n);
엔진(Engine)
- 물리 엔진들은 대부분
world객체를 포함하고 있습니다. - 이는 보통 좌표 공간와 시뮬레이션하는 보디들을 관리하고, 시간을 제어하는 등의 물리 세계와 시뮬레이션의 컨트롤러 역할을 합니다.
let Engine = Matter.Engine; // Matter.js Engine 클래스의 별칭
let engine; // Matter.js 물리 엔진의 참조
function setup() {
createCanvas(640, 360);
engine = Engine.create(); // Matter.js 엔진 생성
}
- 코드 첫 줄에서는
Engine변수를 생성하고,Matter.Engine를 할당합니다. Engine의create()를 호출하면 Matter.js는 기본 중력이 아래로 향하는 벡터(0, 1)인 물리 엔진과world를 반환합니다.- 다음과 같이 조정할 수도 있죠.
engine.gravity.x = 1;
engine.gravity.y = 0; // 엔진의 중력을 수평으로 설정- 물론 중력은 시뮬레이션 동안 동적으로 조정하거나 (0, 0) 벡터로 설정해 비활성화 할 수 있습니다.
객체 구조 분해 할당(Object Destructuring)
다음과 같이 자바스크립트의 객체 구조 분해 할당 문법을 사용할 수 있습니다.
const { Engine } = Matter;
외부 라이브러리를 사용할 때는 const 를 사용하는 것이 좋습니다. 초기화 후 값이 재할당 되지 않는 변수를 선언할 때 사용하는데요, 나중에 코드에서 Engine 변수를 실수로 덮어쓰는 것을 방지할 수 있습니다.
const { Engine, Vector } = Matter;
코드를 단순화 할 수 있습니다.
보디(Bodies)
- 보디는 이전 장에서 만들었던
Vehicle,Particle,Mover등의 클래스와 동일하며, 공간을 이동하고 힘을 경험하는 객체입니다. - Matter.js의 보디는 **
Matter.Bodies**에 있는 팩토리 메서드를 사용해 생성됩니다.- 다양한 종류의 보디를 생성할 수 있는 메서드들이 있습니다.
- 팩토리 메서드는 객체를 생성하는 함수입니다.
new키워드를 사용하는 대신 **createVector()**가p5.Vector객체를 생성하는 팩토리 메서드인 것처럼 말입니다.- 객체가 생성자에서 생성되느냐 팩토리 메서드에서 생성되느냐는 라이브러리 제작자의 스타일과 디자인 선택에 달려 있습니다.
- 먼저
rectangle()메서드로 보디를 생성해보죠.let box = Bodies.rectangle(x, y, w, h); - 이제 위치와 크기를 가진 보디가 생성되었고, 참조는
box변수에 저장됩니다. - 보디에는 많은 속성이 있으며, 이는 움직임에 영향을 줍니다.
- 예를 들어, 밀도는 결국 그 보디의 질량을 결정합니다.
- 마찰과 복원력(탄성)은 다른 보디와 접촉할 때의 상호작용 방식을 결정합니다.
- 대부분의 경우 기본값이면 되지만, Matter.js는 추가적인 인수를 받아 이러한 속성을 지정할 수 있습니다.
- JavaScript 객체 리터럴 형태로 키-값 쌍을 쉼표로 구분하고 중괄호로 감싸서 전달합니다.
let options = {
friction: 0.5,
restitution: 0.8,
density: 0.002,
};
let box = Bodies.rectangle(x, y, w, h, options);
- 속도나 각속도 등은 정적 메서드로 지정할 수 있습니다.
const v = Vector.create(2, 0);
Body.setVelocity(box, v);
Body.setAngularVelocity(box, 0.1);
복합체(Composite)
- 이렇게 생성된 보디는 명시적으로 세계에 추가되어야 합니다. 물리 세계는 엔진 자체에 저장된
world라는Composite객체입니다.// box 객체를 엔진의 world에 추가
Composite.add(engine.world, box);
연습 문제 6.1
지금까지 배운 내용을 바탕으로, 원형(circular) 보디를 만들기 위해 다음 코드의 빈칸을 채워보세요.
let options = {
friction: 0.5,
restitution: 0.8,
};
let ball = Bodies.circle(x, y, radius, options);
렌더(Render)
- 이제 보디를 그려봅시다.
- 먼저
Render클래스를 호출합니다.- 특정 캔버스에 렌더러가 그리도록 지정하기 위해 캔버스를 변수에 저장합니다.
- Matter.js는 p5.js를 모르기 때문에, 여기에 할당된 캔버스는 p5.js 캔버스 객체의
elt속성에 저장된 네이티브 HTML5 캔버스입니다. - Matter.js의 기본 캔버스 크기는 800x600 이므로 다른 크기를 원한다면
options에 이를 지정해야 합니다.
let canvas = createCanvas(640, 360); // 캔버스를 변수에 저장
let params = {
canvas: canvas.elt,
engine: engine,
options: { width: width, height: height },
}; // 렌더러 설정
let render = Render.create(params); // 렌더러 생성
```제 - 렌더 객체를 실행합니다.
Render.run(render); // 렌더러 실행 - 기본 프레임 속도인 초당 60프레임으로 엔진을 실행하는 내장 러너를 사용해야 합니다. 이는 p5.js의
draw()루프를 사용하는 과정입니다.Runner.run(engine); // 엔진을 실행합니다.
예제 코드 6.1: Matter.js 기본 렌더와 러너
// Mattejs 기본 클래스
const { Engine, Bodies, Composite, Body, Vector, Render } = Matter;
function setup() {
// 캔버스에 대한 참조 저장
let canvas = createCanvas(640, 240);
// 물리 엔진 생성
let engine = Engine.create();
// 렌더러 생성 및 캔버스 할당
let render = Render.create({
canvas: canvas.elt,
engine,
options: { width: width, height, height },
});
Render.run(render);
// 마찰력과 탄성력을 가진 상자 생성
let options = {
friction: 0.01,
restitution: 0.75,
};
let box = Bodies.rectangle(100, 100, 50, 50, options);
// 상자에 각도 및 각속도 할당
let v = Vector.create(5, 0);
Body.setVelocity(box, v);
Body.setAngularVelocity(box, 0.1);
// 시뮬레이션 세계에 박스 추가
Composite.add(engine.world, box);
// 지면에 대한 정적 보디 생성 및 추가
let ground = Bodies.rectangle(width / 2, height - 5, width, 10, {
isStatic: true,
});
Composite.add(engine.world, ground);
// 러너 생성
let runner = Matter.Runner.create();
// 엔진 실행
Matter.Runner.run(runner, engine);
}