집 >
웹 프론트엔드 >
JS 튜토리얼 >
JavaScript를 사용하여 게임 물리로 플레이하기 (1) 운동학 시뮬레이션 및 입자 시스템_javascript 기술
JavaScript를 사용하여 게임 물리로 플레이하기 (1) 운동학 시뮬레이션 및 입자 시스템_javascript 기술
WBOY
풀어 주다: 2016-05-16 18:24:49
원래의
1455명이 탐색했습니다.
시리즈 소개 아마도 300년 전 아이삭 뉴턴 경(1643-1727)은 오늘날 많은 게임, 애니메이션에서 물리학이 널리 사용된다는 것을 상상하지 못했을 것입니다. 이러한 응용 분야에서 물리학을 사용하는 이유는 무엇입니까? 저자는 우리가 태어나면서부터 물리적 세계의 법칙을 느끼고 이 세계에서 물체가 어떻게 "정상적으로 움직이는지" 깨닫고 있다고 믿습니다. 구부러진 공), 끈 끝에 묶인 돌은 고정된 주파수로 진동합니다. 게임이나 애니메이션의 개체가 사실적으로 느껴지려면 "정상적인 움직임"에 대한 우리의 기대와 일치하는 방식으로 움직여야 합니다. 오늘날 게임 애니메이션에는 운동학 시뮬레이션, 강체 동역학 시뮬레이션, 끈/천 시뮬레이션, 연체 동역학 시뮬레이션(연체 역학 시뮬레이션), 유체 역학 시뮬레이션(유체 역학 시뮬레이션) 등 다양한 물리 시뮬레이션 기술이 적용됩니다. , 등. 또한 많은 시뮬레이션 시스템에서는 충돌 감지가 필요합니다. 이 시리즈에서는 이 분야의 가장 기본적인 지식 중 일부를 소개하고, 계속해서 JavaScript를 예시로 사용하며, 실시간 대화형 방식으로 경험해 보고자 합니다. 이 기사 소개 시리즈의 첫 번째 기사인 이 기사에서는 매우 간단한 두 가지 공식만으로 가장 간단한 운동학 시뮬레이션을 소개합니다. 운동학적 시뮬레이션은 많은 물체(마리오의 점프, 대포알 등)의 움직임을 시뮬레이션하는 데 사용할 수 있습니다. 이 기사에서는 입자 시스템을 사용하여 몇 가지 시각적 특수 효과를 만들 것입니다(입자 시스템은 실제로 게임을 플레이하는 데 사용될 수 있습니다). 시각 특수 효과뿐만 아니라). 운동학 시뮬레이션 운동학은 물체의 움직임을 연구하는데, 역학과 다른 점은 물체의 질량/관성 모멘트를 고려하지 않으며, 물체에 가해지는 힘과 토크를 고려하지 않는다는 점입니다. 먼저 뉴턴의 운동 제1법칙을 떠올려 봅시다. 물체에 외부 힘이 작용하지 않거나 알짜 힘이 0일 때, 원래 정지해 있던 것은 항상 정지해 있을 것입니다. 움직이는 물체는 항상 일정한 속도로 직선을 따라 움직일 것입니다. 이 법칙은 "관성의 법칙"이라고도 불립니다. 이 법칙에 따르면 모든 물체는 위치 외에도 선형 속도 상태를 갖습니다. 그러나 힘의 영향을 받지 않는 객체만을 시뮬레이션하는 것은 흥미롭지 않습니다. 힘의 개념을 제쳐두고 선형 가속도를 사용하여 물체의 움직임에 영향을 미칠 수 있습니다. 예를 들어, 언제든지 t에서 자유 낙하하는 물체의 y축 좌표를 계산하려면 다음 분석 솔루션을 사용할 수 있습니다.
여기서 및 는 각각 t에서의 y축 좌표입니다. =0 초기 좌표 및 속도, g는 중력 가속도입니다. 이 분석 솔루션은 간단하지만 몇 가지 단점이 있습니다. 예를 들어 g는 상수이므로 시뮬레이션 과정에서 변경할 수 없습니다. 또한 물체가 장애물을 만나 충돌할 때 이 공식을 적용하기가 어렵습니다. 이 불연속성을 처리하십시오. 컴퓨터 시뮬레이션에서는 연속적인 물체 상태를 계산해야 하는 경우가 많습니다. 게임 용어로 이는 첫 번째 프레임의 상태, 두 번째 프레임의 상태 등을 계산하는 것을 의미합니다. 언제든지 t에서 물체의 상태를 가정합니다. 위치 벡터는 이고, 속도 벡터는 이고, 가속도 벡터는 입니다. 시간의 상태로부터 다음 시뮬레이션 시간의 상태를 계산하려고 합니다. 가장 간단한 방법은 수치 적분을 위해 오일러 방법을 사용하는 것입니다.
오일러 방법은 매우 간단하지만 정확성과 안정성 문제가 있으므로 이 질문에서는 무시하겠습니다. 이 기사의 예에서는 먼저 JavaScript 2차원 벡터 클래스를 구현합니다.
function ParticleSystem() { // ... this.simulate = function(dt) { aging(dt); applyGravity() applyEffectors(); 운동학(dt) }; // ... // 비공개 메서드 function ageing(dt) { for (var i = 0; i < 입자.길이; ) { var p = 입자[i]; p.age = dt if (p.age >= p.life) kill(i); 🎜>else i ; } } function kill(index) { if (particles.length > 1) particles[index] = 입자[particles.length - 1]; particles.pop(); } // ... }
kill() 함수에서는 트릭이 사용됩니다. 배열의 입자 순서는 중요하지 않으므로 중간에 있는 입자를 삭제하려면 마지막 입자를 해당 요소에 복사하고 pop()을 사용하여 마지막 입자를 제거하면 됩니다. 이는 일반적으로 배열 중간에서 요소를 직접 삭제하는 것보다 빠릅니다(C의 배열이나 std::Vectors의 경우에도 마찬가지). 운동학 시뮬레이션 이 글의 운동학 시뮬레이션 코드 중 가장 중요한 두 문장을 모든 입자에 적용하면 됩니다. 또한 각 시뮬레이션에서는 먼저 중력 가속도를 입자 가속도에 기록합니다. 이는 앞으로 가속도가 매번 변경될 수 있도록 수행됩니다(이에 대해서는 후속편에서 설명하겠습니다).
// .. . function applyGravity() { for(입자의 var i) particles[i].acceleration = that.gravity } function kinematics(dt) { for (입자의 var i) { var p = 입자[i]; p.position = p.position.add(p.velocity.multiply(dt)) p.velocity = p. speed.add (p.acceleration.multiply(dt)); } } // ... }
렌더링 입자는 다음과 같습니다. 원, 선분(현재 위치 및 이전 위치), 이미지, 스프라이트 등을 사용하여 다양한 방법으로 렌더링합니다. 이 글에서는 원을 사용하고 연령 대 수명 비율에 따라 원의 투명도를 제어합니다.
var dt = 0.01; function SampleDirection() { var theta = Math.random() * 2 * Math.PI return new Vector2(Math.cos(theta), Math.sin (세타) ) } function step() { ps.emit(new Particle(new Vector2(200, 200), SampleDirection().multiply(100), 1, Color.red, 5)) ; ps.simulate(dt); ps.render(ctx); start("basicParticleSystemCanvas", 단계);
td>
코드를 수정하여 사용해 보세요
실행 위치 변경< /li>
위로 발사, 발사 범위는 90도 이내입니다
반경 변경
프레임당 5개의 입자 방출
;
단순 충돌 해석 솔루션에 비해 수치 적분을 사용하는 것의 이점을 설명하기 위해 이 기사에서는 입자 시스템에 단순 충돌을 추가합니다. 요구 사항을 추가하고 싶습니다. 입자가 직사각형 챔버의 내부 벽(전체 캔버스 크기로 설정 가능)에 부딪히면 충돌하고 반동합니다. 충돌은 완벽하게 탄력적입니다. 프로그래밍 측면에서 저는 콜백을 사용하여 이 기능을 수행합니다. ParticleSystem 클래스에는 운동학 시뮬레이션을 수행하기 전에 각 이펙터 객체의 apply() 함수가 실행됩니다. 직사각형 챔버는 다음과 같이 구현됩니다.
이는 입자가 내부 벽의 범위를 초과하는 것을 감지하면 실제로 해당 방향으로 속도 성분을 반전시키는 것입니다. 또한 이 예제의 메인 루프는 더 이상 매번 전체 캔버스를 지우지 않고 매 프레임마다 반투명 검정색 직사각형을 그려 모션 블러 효과를 시뮬레이션합니다. 입자의 색상도 두 가지 색상에서 무작위로 샘플링됩니다.
var ps = new ParticleSystem(); ps.이펙터.push(new ChamberBox(0, 0, 400, 400)); var dt = 0.01; var oldMousePosition = Vector2.zero, newMousePosition = Vector2.zero; function SampleDirection(angle1, angle2) { var t = Math.random(); var theta = 각도1 * t 각도2 * (1 - t); 새 Vector2(Math.cos(theta), Math.sin(theta))를 반환합니다. } function SampleColor(color1, color2) { var t = Math.random(); color1.multiply(t).add(color2.multiply(1 - t))를 반환합니다. } function SampleNumber(value1, value2) { var t = Math.random(); 반환 값1 * t 값2 * (1 - t); } function step() { var speed = newMousePosition.subtract(oldMousePosition).multiply(10); 속도 = 속도.add(sampleDirection(0, Math.PI * 2).multiply(20)); var color = SampleColor(Color.red, Color.yellow); var life = 샘플번호(1, 2); var size = SampleNumber(2, 4); ps.emit(new Particle(newMousePosition, 속도, 수명, 색상, 크기)); oldMousePosition = newMousePosition; ps.simulate(dt); ctx.fillStyle="rgba(0, 0, 0, 0.1)"; ctx.fillRect(0,0,canvas.width,canvas.height); ps.render(ctx); } start("interactiveEmitCanvas", step); canvas.onmousemove = function(e) { if (e.layerX || e.layerX == 0) { // Firefox e.target.style.position='relative'; newMousePosition = new Vector2(e.layerX, e.layerY); } else newMousePosition = new Vector2(e.offsetX, e.offsetY); };
<버튼 onclick="stop();" type="button">중지
总结 本文介绍了最简单的运动school模拟,使用欧拉方法数值积分,并以此法去实现一个有简单碰撞的粒子系统。本文的精华其实只有两条简单公式(只有两个加数 and 两个乘数),希望让读单,其实물리模拟可以很简单.但这例子能扩至삼국공공间,只须把Vector2换成Vector3。本文完整源代码可下载。 续篇会谈及在此基础上加其他물리现象,有机会再加入其他물리模拟课题。希望各位支持,并给本人更多意见。