日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

用JavaScript玩转游戏物理(一)运动学模拟与粒子系统

發布時間:2025/3/21 javascript 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 用JavaScript玩转游戏物理(一)运动学模拟与粒子系统 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

系列簡介

也許,三百年前的艾薩克·牛頓爵士(Sir Issac Newton, 1643-1727)并沒幻想過,物理學廣泛地應用在今天許多游戲、動畫中。為什么在這些應用中要使用物理學?筆者認為,自我們出生以來,一直感受著物理世界的規律,意識到物體在這世界是如何"正常移動",例如射球時球為拋物線(自旋的球可能會做成弧線球) 、石子系在一根線的末端會以固定頻率擺動等等。要讓游戲或動畫中的物體有真實感,其移動方式就要符合我們對"正常移動"的預期。

今天的游戲動畫應用了多種物理模擬技術,例如運動學模擬(kinematics simulation)、剛體動力學模擬(rigid body dynamics simulation)、繩子/布料模擬(string/cloth simulation)、柔體動力學模擬(soft body dynamics simulation)、流體動力學模擬(fluid dynamics simulation)等等。另外碰撞偵測(collision detection)是許多模擬系統里所需的。

本系列希望能介紹一些這方面最基礎的知識,繼續使用JavaScript做例子,以即時互動方式體驗。

本文簡介

作為系列第一篇,本文介紹最簡單的運動學模擬,只有兩條非常簡單的公式。運動學模擬可以用來模擬很多物體運動(例如馬里奧的跳躍、炮彈等),本文將會配合粒子系統做出一些視覺特效(粒子系統其實也可以用來做游戲的玩法,而不單是視覺特效)。

運動學模擬

運動學(kinematics)研究物體的移動,和動力學(dynamics)不同之處,在于運動學不考慮物體的質量(mass)/轉動慣量(moment of inertia),以及不考慮加之于物體的力(force )和力矩(torque)。

我們先回憶牛頓第一運動定律:

當物體不受外力作用,或所受合力為零時,原先靜止者恒靜止,原先運動者恒沿著直線作等速度運動。該定律又稱為「慣性定律」。

此定律指出,每個物體除了其位置(position)外,還有一個線性速度(linear velocity)的狀態。然而,只模擬不受力影響的物體并不有趣。撇開力的概念,我們可以用線性加速度(linear acceleration)去影響物體的運動。例如,要計算一個自由落體在任意時間t的y軸座標,可以使用以下的分析解(analytical solution):

當中,分別是t=0時的y軸起始座標和速度,而g則是重力加速度(gravitational acceleration)。

這分析解雖然簡單,但是有一些缺點,例如g是常數,在模擬過程中不能改變;另外,當物體遇到障礙物,產生碰撞時,這公式也很難處理這種不連續性(discontinuity) 。

在計算機模擬中,通常需要計算連續的物體狀態。用游戲的用語,就是計算第一幀的狀態、第二幀的狀態等等。設物體在任意時間t的狀態:位置矢量為、速度矢量為、加速度矢量為。我們希望從時間的狀態,計算下一個模擬時間的狀態。最簡單的方法,是采用歐拉方法(Euler method)作數值積分(numerical integration):

歐拉方法非常簡單,但有準確度和穩定性問題,本文會先忽略這些問題。本文的例子采用二維空間,我們先實現一個JavaScript二維矢量類:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // Vector2.js Vector2 = function(x, y) { this.x = x; this.y = y; }; Vector2.prototype = { ????copy : function() { return?new?Vector2(this.x, this.y); }, ????length : function() { return?Math.sqrt(this.x * this.x + this.y * this.y); }, ????sqrLength : function() { return?this.x * this.x + this.y * this.y; }, ????normalize : function() { var?inv = 1/this.length(); return?new?Vector2(this.x * inv, this.y * inv); }, ????negate : function() { return?new?Vector2(-this.x, -this.y); }, ????add : function(v) { return?new?Vector2(this.x + v.x, this.y + v.y); }, ????subtract : function(v) { return?new?Vector2(this.x - v.x, this.y - v.y); }, ????multiply : function(f) { return?new?Vector2(this.x * f, this.y * f); }, ????divide : function(f) { var?invf = 1/f; return?new?Vector2(this.x * invf, this.y * invf); }, ????dot : function(v) { return?this.x * v.x + this.y * v.y; } }; Vector2.zero = new?Vector2(0, 0);

然后,就可以用HTML5 Canvas去描繪模擬的過程:


Run ? Stop ? Clear ?
?

修改代碼試試看

  • 改變起始位置
  • 改變起始速度(包括方向)
  • 改變加速度
  • 這程序的核心就是step()函數頭兩行代碼。很簡單吧?

    粒子系統

    粒子系統(particle system)是圖形里常用的特效。粒子系統可應用運動學模擬來做到很多不同的效果。粒子系統在游戲和動畫中,常常會用來做雨點、火花、煙、爆炸等等不同的視覺效果。有時候,也會做出一些游戲性相關的功能,例如敵人被打敗后會發出一些閃光,主角可以把它們吸收。

    粒子的定義

    粒子系統模擬大量的粒子,并通常用某些方法把粒子渲染。粒子通常有以下特性:

  • 粒子是獨立的,粒子之間互不影響(不碰撞、沒有力)
  • 粒子有生命周期,生命結束后會消失
  • 粒子可以理解為空間的一個點,有時候也可以設定半徑作為球體和環境碰撞
  • 粒子帶有運動狀態,也有其他外觀狀態(例如顏色、影像等)
  • 粒子可以只有線性運動,而不考慮旋轉運動(也有例外)
  • 以下是本文例子里實現的粒子類:

    12345678910// Particle.jsParticle = function(position, velocity, life, color, size) {????this.position = position;????this.velocity = velocity;????this.acceleration = Vector2.zero;????this.age = 0;????this.life = life;????this.color = color;????this.size = size;};

    游戲循環

    粒子系統通常可分為三個周期:

  • 發射粒子
  • 模擬粒子(粒子老化、碰撞、運動學模擬等等)
  • 渲染粒子
  • 在游戲循環(game loop)中,需要對每個粒子系統執行以上的三個步驟。

    生與死

    在本文的例子里,用一個JavaScript數組particles儲存所有活的粒子。產生一個粒子只是把它加到數組末端。代碼片段如下:

    123456789101112131415161718//ParticleSystem.jsfunction?ParticleSystem() {????// Private fields????var?that = this;????var?particles = new?Array();????// Public fields????this.gravity = new?Vector2(0, 100);????this.effectors = new?Array();????// Public methods?????????????this.emit = function(particle) {????????particles.push(particle);????};????// ...}

    粒子在初始化時,年齡(age)設為零,生命(life)則是固定的。年齡和生命的單位都是秒。每個模擬步,都會把粒子老化,即是把年齡增加,年齡超過生命,就會死亡。代碼片段如下:

    12345678910111213141516171819202122232425262728293031function?ParticleSystem() {????// ... ????this.simulate = function(dt) {????????aging(dt);????????applyGravity();????????applyEffectors();????????kinematics(dt);????};?????????// ...????// Private methods?????????function?aging(dt) {????????for?(var?i = 0; i < particles.length; ) {????????????var?p = particles[i];????????????p.age += dt;????????????if?(p.age >= p.life)????????????????kill(i);????????????else????????????????i++;????????}????}????function?kill(index) {????????if?(particles.length > 1)????????????particles[index] = particles[particles.length - 1];????????particles.pop();????}????// ...}

    在函數kill()里,用了一個技巧。因為粒子在數組里的次序并不重要,要刪除中間一個粒子,只需要復制最末的粒子到那個元素,并用pop()移除最末的粒子就可以。這通常比直接刪除數組中間的元素快(在C++中使用數組或std::vector亦是)。

    運動學模擬

    把本文最重要的兩句運動學模擬代碼套用至所有粒子就可以。另外,每次模擬會先把引力加速度寫入粒子的加速度。這樣做是為了將來可以每次改變加速度(續篇會談這方面)。

    12345678910111213141516function?ParticleSystem() {????// ...????function?applyGravity() {????????for?(var?i in?particles)????????????particles[i].acceleration = that.gravity;????}????function?kinematics(dt) {????????for?(var?i in?particles) {????????????var?p = particles[i];????????????p.position = p.position.add(p.velocity.multiply(dt));????????????p.velocity = p.velocity.add(p.acceleration.multiply(dt));????????}????}????// ...}

    渲染

    粒子可以用很多不同方式渲染,例如用圓形、線段(當前位置和之前位置)、影像、精靈等等。本文采用圓形,并按年齡生命比來控制圓形的透明度,代碼片段如下:

    12345678910111213141516171819function?ParticleSystem() {????// ...????this.render = function(ctx) {????????for?(var?i in?particles) {????????????var?p = particles[i];????????????var?alpha = 1 - p.age / p.life;????????????ctx.fillStyle = "rgba("????????????????+ Math.floor(p.color.r * 255) + ","????????????????+ Math.floor(p.color.g * 255) + ","????????????????+ Math.floor(p.color.b * 255) + ","????????????????+ alpha.toFixed(2) + ")";????????????ctx.beginPath();????????????ctx.arc(p.position.x, p.position.y, p.size, 0, Math.PI * 2, true);????????????ctx.closePath();????????????ctx.fill();????????}????}????// ...}

    基本粒子系統完成

    以下的例子里,每幀會發射一個粒子,其位置在畫布中間(200,200),發射方向是360度,速率為100,生命為1秒,紅色、半徑為5象素。


    Run ? Stop ?
    ?

    修改代碼試試看

  • 改變發射位置
  • 向上發射,發射范圍在90度內
  • 改變生命
  • 改變半徑
  • 每幀發射5個粒子
  • 簡單碰撞

    為了說明用數值積分相對于分析解的優點,本文在粒子系統上加簡單的碰撞。我們想加入一個需求,當粒子碰到長方形室(可設為整個Canvas大小)的內壁,就會碰撞反彈,碰撞是完全彈性的(perfectly elastic collision)。

    在程序設計上,我把這功能用回調方式進行。 ParticleSystem類有一個effectors數組,在進行運動學模擬之前,先執行每個effectors對象的apply()函數:

    而長方形室就這樣實現:

    1 2 3 4 5 6 7 8 9 10 // ChamberBox.js function?ChamberBox(x1, y1, x2, y2) { ????this.apply = function(particle) { ????????if?(particle.position.x - particle.size < x1 || particle.position.x + particle.size > x2) ????????????particle.velocity.x = -particle.velocity.x; ????????if?(particle.position.y - particle.size < y1 || particle.position.y + particle.size > y2) ????????????particle.velocity.y = -particle.velocity.y; ????}; }

    這其實就是當偵測到粒子超出內壁的范圍,就反轉該方向的速度分量。

    此外,這例子的主循環不再每次把整個Canvas清空,而是每幀畫一個半透明的黑色長方形,就可以模擬動態模糊(motion blur)的效果。粒子的顏色也是隨機從兩個顏色中取樣。


    Run ? Stop ?

    互動發射

    最后一個例子加入互動功能,在鼠標位置發射粒子,粒子方向是按鼠標移動速度再加上一點噪音(noise)。粒子的大小和生命都加入了隨機性。


    Run ? Stop ?

    總結

    本文介紹了最簡單的運動學模擬,使用歐拉方法作數值積分,并以此法去實現一個有簡單碰撞的粒子系統。本文的精華其實只有兩條簡單公式(只有兩個加數和兩個乘數),希望讓讀者明白,其實物理模擬可以很簡單。雖然本文的例子是在二維空間,但這例子能擴展至三維空間,只須把Vector2換成Vector3。本文完整源代碼可下載。

    續篇會談及在此基礎上加入其他物理現象,有機會再加入其他物理模擬課題。希望各位支持,并給本人更多意見。


    from:?http://www.cnblogs.com/miloyip/archive/2010/06/14/Kinematics_ParticleSystem.html

    總結

    以上是生活随笔為你收集整理的用JavaScript玩转游戏物理(一)运动学模拟与粒子系统的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。