成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

【騰訊Bugly干貨】canvas粒子引擎手把手教學,教你驚艷領導和用戶

移動開發
好吧,說是“粒子引擎”還是大言不慚而標題黨了,離真正的粒子引擎還有點遠。廢話少說,先看[demo],掃描后點擊屏幕有驚喜哦...

 

[[165460]]

前言

好吧,說是“粒子引擎”還是大言不慚而標題黨了,離真正的粒子引擎還有點遠。廢話少說,先看[demo],掃描后點擊屏幕有驚喜哦... 

本文將教會你做一個簡單的canvas粒子制造器(下稱引擎)。

世界觀

這個簡單的引擎里需要有三種元素:世界(World)、發射器(Launcher)、粒子(Grain)。總得來說就是:發射器存在于世界之中,發射器制造粒子,世界和發射器都會影響粒子的狀態,每個粒子在經過世界和發射器的影響之后,計算出下一刻的位置,把自己畫出來。

世界(World)

所謂“世界”,就是全局影響那些存在于這這個“世界”的粒子的環境。一個粒子如果選擇存在于這個“世界”里,那么這個粒子將會受到這個“世界”的影響。

發射器(Launcher)

用來發射粒子的單位。他們能控制粒子生成的粒子的各種屬性。作為粒子們的爹媽,發射器能夠控制粒子的出生屬性:出生的位置、出生的大小、壽命、是否受到“World”的影響、是否受到"Launcher"本身的影響等等……

除此之外,發射器本身還要把自己生出來的已經死去的粒子清掃掉。

粒子(Grain)

最小基本單位,就是每一個騷動的個體。每一個個體都擁有自己的位置、大小、壽命、是否受到同名度的影響等屬性,這樣才能在canvas上每時每刻準確描繪出他們的形態。

粒子繪制主邏輯

上面就是粒子繪制的主要邏輯。

我們先來看看世界需要什么。

創造一個世界

不知道為什么我理所當然得會想到世界應該有重力加速度。但是光有重力加速度不能表現出很多花樣,于是這里我給他增加了另外兩種影響因素:熱氣和風。重力加速度和熱氣他們的方向是垂直的,風影響方向是水平的,有了這三個東西,我們就能讓粒子動得很風騷了。

一些狀態(比如粒子的存亡)的維護需要有時間標志,那么我們把時間也加入到世界里吧,這樣方便后期做時間暫停、逆流的效果。

  1. define(function(require, exports, module) { 
  2.    var Util = require('./Util'); 
  3.    var Launcher = require('./Launcher'); 
  4.  
  5.    /** 
  6.     * 世界構造函數 
  7.     * @param config 
  8.     *          backgroundImage     背景圖片 
  9.     *          canvas              canvas引用 
  10.     *          context             canvas的context 
  11.     * 
  12.     *          time                世界時間 
  13.     * 
  14.     *          gravity             重力加速度 
  15.     * 
  16.     *          heat                熱力 
  17.     *          heatEnable          熱力開關 
  18.     *          minHeat             隨機最小熱力 
  19.     *          maxHeat             隨機最大熱力 
  20.     * 
  21.     *          wind                風力 
  22.     *          windEnable          風力開關 
  23.     *          minWind             隨機最小風力 
  24.     *          maxWind             隨機最大風力 
  25.     * 
  26.     *          timeProgress        時間進步單位,用于控制時間速度 
  27.     *          launchers           屬于這個世界的發射器隊列 
  28.     * @constructor 
  29.     */ 
  30.     function World(config){ 
  31.     //太長了,略去細節 
  32.     } 
  33.     World.prototype.updateStatus = function(){}; 
  34.     World.prototype.timeTick = function(){}; 
  35.     World.prototype.createLauncher = function(config){}; 
  36.     World.prototype.drawBackground = function(){}; 
  37.     module.exports = World
  38.  }); 

大家都知道,畫動畫就是不斷得重畫,所以我們需要暴露出一個方法,提供給外部循環調用:

  1. /** 
  2.   * 循環觸發函數 
  3.   * 在滿足條件的時候觸發 
  4.   * 比如RequestAnimationFrame回調,或者setTimeout回調之后循環觸發的 
  5.   * 用于維持World的生命 
  6.   */ 
  7.  
  8. World.prototype.timeTick = function(){ 
  9.  
  10.     //更新世界各種狀態 
  11.     this.updateStatus(); 
  12.  
  13.     this.context.clearRect(0,0,this.canvas.width,this.canvas.height); 
  14.     this.drawBackground(); 
  15.  
  16.     //觸發所有發射器的循環調用函數 
  17.     for(var i = 0;i<this.launchers.length;i++){ 
  18.        this.launchers[i].updateLauncherStatus(); 
  19.        this.launchers[i].createGrain(1); 
  20.        this.launchers[i].paintGrain(); 
  21.     } 
  22.  }; 

這個 timeTick 方法在外部循環調用時,每次都做著這幾件事:

  1. 更新世界狀態
  2. 清空畫布重新繪制背景
  3. 輪詢全世界所有發射器,并更新它們的狀態,創建新的粒子,繪制粒子

那么,世界的狀態到底有哪些要更新?

顯然,每一次都要讓時間往前增加一點是容易想到的。其次,為了讓粒子盡可能動得風騷,我們讓風和熱力的狀態都保持不穩定——每一陣風和每一陣熱浪,都是你意識不到的~

  1. World.prototype.updateStatus = function(){ 
  2.     this.time+=this.timeProgress; 
  3.     this.wind = Util.randomFloat(this.minWind,this.maxWind); 
  4.     this.heat = Util.randomFloat(this.minHeat,this.maxHeat); 
  5. }; 

世界造出來了,我們還得讓世界能造粒子發射器呀,要不然怎么造粒子呢~

  1. World.prototype.createLauncher = function(config){ 
  2.     var _launcher = new Launcher(config); 
  3.     this.launchers.push(_launcher); 
  4. }; 

好了,做為上帝,我們已經把世界打造得差不多了,接下來就是捏造各種各樣的生靈了。

捏出第一個生物:發射器

發射器是世界上的第一種生物,依靠發射器才能繁衍出千奇百怪的粒子。那么發射器需要具備什么特征呢?

首先,它是屬于哪個世界的得搞清楚(因為這個世界可能不止一個世界)。

其次,就是發射器本身的狀態:位置、自身體系內的風力、熱力,可以說:發射器就是一個世界里的小世界。

最后就是描述一下他的“基因”了,發射器的基因會影響到他們的后代(粒子)。我們賦予發射器越多的“基因”,那么他們的后代就會有更多的生物特征。具體看下面的良心注釋代碼吧~

  1. define(function (require, exports, module) { 
  2.    var Util = require('./Util'); 
  3.    var Grain = require('./Grain'); 
  4.  
  5.    /** 
  6.     * 發射器構造函數 
  7.     * @param config 
  8.     *          id              身份標識用于后續可視化編輯器的維護 
  9.     *          world           這個launcher的宿主 
  10.     * 
  11.     *          grainImage      粒子圖片 
  12.     *          grainList       粒子隊列 
  13.     *          grainLife       產生的粒子的生命 
  14.     *          grainLifeRange  粒子生命波動范圍 
  15.     *          maxAliveCount   最大存活粒子數量 
  16.     * 
  17.     *          x               發射器位置x 
  18.     *          y               發射器位置y 
  19.     *          rangeX          發射器位置x波動范圍 
  20.     *          rangeY          發射器位置y波動范圍 
  21.     * 
  22.     *          sizeX           粒子橫向大小 
  23.     *          sizeY           粒子縱向大小 
  24.     *          sizeRange       粒子大小波動范圍 
  25.     * 
  26.     *          mass            粒子質量(暫時沒什么用) 
  27.     *          massRange       粒子質量波動范圍 
  28.     * 
  29.     *          heat            發射器自身體系的熱氣 
  30.     *          heatEnable      發射器自身體系的熱氣生效開關 
  31.     *          minHeat         隨機熱氣最小值 
  32.     *          maxHeat         隨機熱氣最小值 
  33.     * 
  34.     *          wind            發射器自身體系的風力 
  35.     *          windEnable      發射器自身體系的風力生效開關 
  36.     *          minWind         隨機風力最小值 
  37.     *          maxWind         隨機風力最小值 
  38.     * 
  39.     *          grainInfluencedByWorldWind      粒子受到世界風力影響開關 
  40.     *          grainInfluencedByWorldHeat      粒子受到世界熱氣影響開關 
  41.     *          grainInfluencedByWorldGravity   粒子受到世界重力影響開關 
  42.     * 
  43.     *          grainInfluencedByLauncherWind   粒子受到發射器風力影響開關 
  44.     *          grainInfluencedByLauncherHeat   粒子受到發射器熱氣影響開關 
  45.     * 
  46.     * @constructor 
  47.     */ 
  48.  
  49.    function Launcher(config) { 
  50.        //太長了,略去細節 
  51.    } 
  52.  
  53.    Launcher.prototype.updateLauncherStatus = function () {}; 
  54.    Launcher.prototype.swipeDeadGrain = function (grain_id) {}; 
  55.    Launcher.prototype.createGrain = function (count) {}; 
  56.    Launcher.prototype.paintGrain = function () {}; 
  57.  
  58.    module.exports = Launcher
  59.  
  60. }); 

發射器要負責生孩子啊,怎么生呢:

  1. Launcher.prototype.createGrain = function (count) { 
  2.        if (count + this.grainList.length <= this.maxAliveCount) { 
  3.            //新建了count個加上舊的還沒達到最大數額限制 
  4.        } else if (this.grainList.length >= this.maxAliveCount && 
  5.            count + this.grainList.length > this.maxAliveCount) { 
  6.            //光是舊的粒子數量還沒能達到最大限制 
  7.            //新建了count個加上舊的超過了最大數額限制 
  8.            count = this.maxAliveCount - this.grainList.length; 
  9.        } else { 
  10.            count = 0
  11.        } 
  12.        for (var i = 0; i < count; i++) { 
  13.            var _rd = Util.randomFloat(0, Math.PI * 2); 
  14.            var _grain = new Grain({/*粒子配置*/}); 
  15.            this.grainList.push(_grain); 
  16.        } 
  17.    }; 

生完孩子,孩子死掉了還得打掃……(好悲傷,怪內存不夠用咯)

  1. Launcher.prototype.swipeDeadGrain = function (grain_id) { 
  2.     for (var i = 0; i < this.grainList.length; i++) { 
  3.         if (grain_id == this.grainList[i].id) { 
  4.             thisthis.grainList = this.grainList.remove(i);//remove是自己定義的一個Array方法 
  5.             this.createGrain(1); 
  6.             break; 
  7.         } 
  8.     } 
  9. }; 

生完孩子,還得把孩子放出來玩:

  1. Launcher.prototype.paintGrain = function () { 
  2.     for (var i = 0; i < this.grainList.length; i++) { 
  3.         this.grainList[i].paint(); 
  4.     } 
  5. }; 

自己的內部小世界也不要忘了維護呀~(跟外面的大世界差不多)

  1. Launcher.prototype.updateLauncherStatus = function () { 
  2.     if (this.grainInfluencedByLauncherWind) { 
  3.         this.wind = Util.randomFloat(this.minWind, this.maxWind); 
  4.     } 
  5.     if(this.grainInfluencedByLauncherHeat){ 
  6.         this.heat = Util.randomFloat(this.minHeat, this.maxHeat); 
  7.     } 
  8. }; 

好了,至此,我們完成了世界上第一種生物的打造,接下來就是他們的后代了(呼呼,上帝好累)

子子孫孫,無窮盡也

出來吧,小的們,你們才是世界的主角!

作為世界的主角,粒子們擁有各種自身的狀態:位置、速度、大小、壽命長度、出生時間當然必不可少

  1. define(function (require, exports, module) { 
  2.     var Util = require('./Util'); 
  3.  
  4.     /** 
  5.      * 粒子構造函數 
  6.      * @param config 
  7.      *          id              唯一標識 
  8.      *          world           世界宿主 
  9.      *          launcher        發射器宿主 
  10.      * 
  11.      *          x               位置x 
  12.      *          y               位置y 
  13.      *          vx              水平速度 
  14.      *          vy              垂直速度 
  15.      * 
  16.      *          sizeX           橫向大小 
  17.      *          sizeY           縱向大小 
  18.      * 
  19.      *          mass            質量 
  20.      *          life            生命長度 
  21.      *          birthTime       出生時間 
  22.      * 
  23.      *          color_r 
  24.      *          color_g 
  25.      *          color_b 
  26.      *          alpha           透明度 
  27.      *          initAlpha       初始化時的透明度 
  28.      * 
  29.      *          influencedByWorldWind 
  30.      *          influencedByWorldHeat 
  31.      *          influencedByWorldGravity 
  32.      *          influencedByLauncherWind 
  33.      *          influencedByLauncherHeat 
  34.      * 
  35.      * @constructor 
  36.      */ 
  37.     function Grain(config) { 
  38.         //太長了,略去細節 
  39.     } 
  40.  
  41.     Grain.prototype.isDead = function () {}; 
  42.     Grain.prototype.calculate = function () {}; 
  43.     Grain.prototype.paint = function () {}; 
  44.     module.exports = Grain
  45. }); 

粒子們需要知道自己的下一刻是怎樣子的,這樣才能把自己在世界展現出來。對于運動狀態,當然都是初中物理的知識了:-)

  1. Grain.prototype.calculate = function () { 
  2.     //計算位置 
  3.     if (this.influencedByWorldGravity) { 
  4.         this.vy += this.world.gravity+Util.randomFloat(0,0.3*this.world.gravity); 
  5.     } 
  6.     if (this.influencedByWorldHeat && this.world.heatEnable) { 
  7.         this.vy -this.world.heat+Util.randomFloat(0,0.3*this.world.heat); 
  8.     } 
  9.     if (this.influencedByLauncherHeat && this.launcher.heatEnable) { 
  10.         this.vy -this.launcher.heat+Util.randomFloat(0,0.3*this.launcher.heat); 
  11.      } 
  12.      if (this.influencedByWorldWind && this.world.windEnable) { 
  13.          this.vx += this.world.wind+Util.randomFloat(0,0.3*this.world.wind); 
  14.      } 
  15.      if (this.influencedByLauncherWind && this.launcher.windEnable) { 
  16.         this.vx += this.launcher.wind+Util.randomFloat(0,0.3*this.launcher.wind); 
  17.     } 
  18.     this.y += this.vy; 
  19.     this.x += this.vx; 
  20.     thisthis.alpha = this.initAlpha * (1 - (this.world.time - this.birthTime) / this.life); 
  21.  
  22.     //TODO 計算顏色 和 其他 
  23.  
  24. }; 

粒子們怎么知道自己死了沒?

  1. Grain.prototype.isDead = function () { 
  2.     return Math.abs(this.world.time - this.birthTime)>this.life; 
  3. }; 

粒子們又該以怎樣的姿態把自己展現出來?

  1. Grain.prototype.paint = function () { 
  2.     if (this.isDead()) { 
  3.         this.launcher.swipeDeadGrain(this.id); 
  4.     } else { 
  5.         this.calculate(); 
  6.         this.world.context.save(); 
  7.         this.world.context.globalCompositeOperation = 'lighter'
  8.         thisthis.world.context.globalAlpha = this.alpha; 
  9.         this.world.context.drawImage(this.launcher.grainImage, this.x-(this.sizeX)/2, this.y-(this.sizeY)/2, this.sizeX, this.sizeY); 
  10.         this.world.context.restore(); 
  11.     } 
  12. }; 

嗟乎。

后續

后續希望能夠通過這個雛形,進行擴展,再造一個可視化編輯器供大家使用。

對了,代碼都在這:https://github.com/jation/CanvasGrain

 

責任編輯:倪明 來源: 騰訊Bugly
相關推薦

2016-04-27 09:49:16

用戶模型產品總結

2017-12-01 05:01:35

WiFi干擾無線網絡

2021-07-14 09:00:00

JavaFX開發應用

2011-01-10 14:41:26

2011-05-03 15:59:00

黑盒打印機

2025-05-07 00:31:30

2021-05-10 06:48:11

Python騰訊招聘

2023-06-05 13:07:38

2023-04-26 12:46:43

DockerSpringKubernetes

2022-01-08 20:04:20

攔截系統調用

2022-03-14 14:47:21

HarmonyOS操作系統鴻蒙

2022-07-27 08:16:22

搜索引擎Lucene

2022-12-07 08:42:35

2011-02-22 13:46:27

微軟SQL.NET

2021-02-26 11:54:38

MyBatis 插件接口

2021-12-28 08:38:26

Linux 中斷喚醒系統Linux 系統

2022-01-29 21:54:58

電商用戶數據

2011-02-22 14:36:40

ASP.NETmsdnC#

2024-03-05 18:27:43

2024-04-02 08:58:13

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 99国产精品99久久久久久 | 日本不卡免费新一二三区 | 日韩欧美一区二区三区在线播放 | 国产一区二区在线播放 | 欧美 中文字幕 | 国产资源在线观看 | 欧美日本免费 | 亚洲+变态+欧美+另类+精品 | 欧美日韩精品久久久免费观看 | 1000部精品久久久久久久久 | 午夜影院操| 成人av电影天堂 | 欧美操操操 | 九九亚洲 | 色就是色欧美 | 国产在线精品一区二区 | 精品国产91亚洲一区二区三区www | 久草a√ | 99精品欧美一区二区三区综合在线 | 91伊人 | 黑人巨大精品欧美一区二区免费 | 黄色一级视频 | 欧洲色 | 欧美啊v在线观看 | av色站| 伦理午夜电影免费观看 | 精品在线免费观看视频 | 国产精品成人国产乱一区 | 玖玖爱365| av中文字幕网 | 怡红院怡春院一级毛片 | 色吧综合网 | 午夜精品 | 欧美成人精品 | 综合久久99 | japan25hdxxxx日本 做a的各种视频 | 精品久久久久一区二区国产 | 天天操操 | 国产精品国产三级国产aⅴ入口 | 国产成人免费视频网站高清观看视频 | 精品国产不卡一区二区三区 |