<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          模擬經(jīng)典流體解謎游戲!Cocos Creator 三步實(shí)現(xiàn)動(dòng)態(tài) 2D 液體

          共 3426字,需瀏覽 7分鐘

           ·

          2022-01-15 00:22

          a854900c4768461ceff57f677e4e4a44.webp


          游戲中可交互的液體是一種頗為吸引人的元素,比如經(jīng)典人氣解謎游戲《鱷魚小頑皮愛洗澡》就通過簡(jiǎn)單的「引水入缸」玩法收獲了一大票玩家。


          在 Cocos Creator 3.x 中,若想實(shí)現(xiàn) 2D 液體、同時(shí)兼顧運(yùn)行效率,可以選擇使用 Box2D 的物理粒子效果來進(jìn)行模擬。物理粒子系統(tǒng)除了適合用于模擬液體以外,我們也可以用于模擬任何可變形的物體。這里將解析由引擎技術(shù)支持中心帶來的 Cocos Creator 的動(dòng)態(tài) 2D 液體解決方案。


          點(diǎn)擊文末【閱讀原文】下載 DEMO:

          https://store.cocos.com/app/detail/


          PART 1

          使用方式

          場(chǎng)景搭建


          在 Cocos Creator 中新建一個(gè)空?qǐng)鼍?,并?chuàng)建一個(gè)?UICanvas


          354088c27d73e36c0ce369fc90ac13bd.webp


          創(chuàng)建一個(gè)用于液體渲染的相機(jī) Camera-001


          6b00ea3f2fefa4f52fed6891580214b1.webp


          對(duì)于這個(gè)相機(jī),使其 ClearFlag = SOLID_COLOR


          87eb0d247b080801a211f2cf003a46f1.webp


          這個(gè)相機(jī)的作用是將液體繪制到一張 RT 之后將這個(gè)動(dòng)態(tài)紋理投射到 UI 內(nèi)的某個(gè) sprite 上面。


          之后就在場(chǎng)景內(nèi)布置碰撞體,既然是 Box2D,碰撞體記得選 2D,不然碰撞體就會(huì)使用 Bullet 和 Box2d 無關(guān)了:


          095a71e583b8c5a581a7e459ca1d52f7.webp


          Cocos Creator 里封裝了兩種物理引擎 Bullet 和 Box2D,兩者處于單獨(dú)的世界。


          在 Box2d 里面如果希望碰撞體之間的碰撞有效,那么至少有一方需要持有 Rigidbody2D 組件。因此需要給碰撞體添加一個(gè) RigidBody2D,其類型選擇為 static 。這樣物理引擎不會(huì)去模擬他的速度和受力情況。


          aafcd0d83cd411e07768b88cc816a8b2.webp


          添加液體


          新建一個(gè)空的 UINode,也就是只有 UITransform 組件的一個(gè) Node:


          e0e73c9bc45b6529a471a8ff9006cce7.webp


          為其添加 WaterRender 這個(gè)組件:


          9e3ec25f15d94dc586e42f2de444d56d.webp


          之后指定好他的一些值:


          • 自定義材質(zhì);

          a6b543c14e7bcbc3caeb31f9b4c2db3e.webp


          • FixError :FixError 指向的是一張1個(gè) 2x2 的純色小紋理;
          e940592328651a00012bc38781263e45.webp


          • 水管:水管由多個(gè)碰撞體構(gòu)成,這樣可以約束液體,使其往我們想要的地方流動(dòng);
          82c997c043021a817aea0161ba56f12f.webpcecacd415244b99bf4cd2bb761895c70.webp


          效果預(yù)覽


          播放預(yù)覽一下效果(這里開啟了物理調(diào)試,可以更清晰的觀察到粒子的運(yùn)行狀態(tài)):


          7f0cc985c2028605b3ab1220ff05c981.webp


          PART 2

          前置知識(shí)

          物理引擎


          Box2D 是一款輕量級(jí)的 2D 游戲物理引擎。主流游戲引擎的 2D 物理部分大都使用 Box2D 來完成的。在物理引擎模擬中,通過質(zhì)心的受力,計(jì)算出其速度和加速度等最終得到物體所在的位置,之后渲染引擎會(huì)讀取物理引擎的計(jì)算結(jié)果并將其應(yīng)用到渲染上。


          LiquidFun


          1f280994d5bc1361bd416615b930918b.webp

          LiquidFun 是基于 Box2D 的擴(kuò)展庫,作用就是給 Box2D 添加了模擬液體的粒子系統(tǒng)。該庫由谷歌高級(jí)程序員 Kentaro Suto 開發(fā),源代碼由 C++ 編寫,并翻譯為 JavaScript。


          組裝器


          在游戲引擎中,繪制精靈或者模型時(shí),都需要通過生成特定的頂點(diǎn),并調(diào)用驅(qū)動(dòng)方法(OpenGL,DirectX ...等)繪制到屏幕上。在 Cocos Creator?里面,如果我們要繪制一系列頂點(diǎn)到屏幕上,需要使用到?Assembler 組裝器


          組裝器顧名思義就是將頂點(diǎn)組裝起來,以供渲染組件使用。通過這個(gè) Assembler,可以自定義頂點(diǎn)的位置、顏色、紋理坐標(biāo)、索引。


          Cocos Creator 里有多種 Assembler

          /**
          ?*?simple?組裝器
          ?*?可通過?`UI.simple`?獲取該組裝器。
          ?*/
          +
          export?const?simple:?IAssembler?

          /**
          ?Tiled組裝器
          */

          export?const?tiled:?IAssembler?=?

          ...


          DEMO 中通過讀取物理引擎內(nèi)粒子的位置,計(jì)算出了頂點(diǎn)緩存內(nèi)所有頂點(diǎn)的相關(guān)信息。


          PART 3

          原理解析

          render.ts 里面有兩個(gè)類 WaterRenderWaterAssembler 。


          WaterRender 解析


          WaterRender 是整個(gè) DEMO 的核心類,負(fù)責(zé)粒子的創(chuàng)建和渲染。


          Renderable2D


          WaterRender 繼承自 Renderable2D在 Cocos Creator 中,任何需要渲染的 Node 對(duì)象都會(huì)持有一個(gè) RenderableComponent,其中 Renderable2D 是 Cocos Creator 中渲染 2D 組件的基類。


          通過重寫?_render 方法,自定義自己的渲染方案。這里通過使用自定義的 _assembler 來組裝需要繪制的幾何體。

          /**
          *commitComp會(huì)提交當(dāng)前的渲染數(shù)據(jù)給渲染管線
          */

          protected?_render(render:?any)?{
          ????render.commitComp(this,?this.fixError,?this._assembler!,?null);
          }


          創(chuàng)建粒子系統(tǒng)


          我們可以把液體理解為由很多個(gè)小的水滴組成。這樣對(duì)于物理引擎來說,就可以選擇使用粒子系統(tǒng),以一種高效的方式,來模擬大量水滴運(yùn)動(dòng)的行為。


          創(chuàng)建粒子系統(tǒng):

          ??var?psd_def?=?{
          ????????????strictContactCheck:?false,
          ????????????density:?1.0,
          ????????????gravityScale:?1.0,
          ????????????radius:?0.35,??//這里指定了粒子的半徑
          ?????????
          ????????????...
          ???}

          this._particles?=?this._world.physicsWorld.impl.CreateParticleSystem(psd);


          創(chuàng)建粒子組:

          var?particleGroupDef?=?{
          ???...
          ????shape:?null,
          ????position:?{
          ????????x:?this.particleBox.node.getWorldPosition().x?/?PHYSICS_2D_PTM_RATIO,
          ????????y:?this.particleBox.node.getWorldPosition().y?/?PHYSICS_2D_PTM_RATIO
          ????},
          ????//?@ts-ignore
          ????shape:?this.particleBox._shape._createShapes(1.0,?1.0)[0]
          };

          this._particleGroup?=?this._particles.CreateParticleGroup(particleGroupDef);
          this.SetParticles(this._particles);


          粒子組為粒子發(fā)射器定義了一組粒子,這些粒子擁有自定義的形狀:

          //創(chuàng)建BoxCollider2D的幾何形狀
          shape:?this.particleBox._shape._createShapes(1.0,?1.0)[0]


          通過對(duì)液體的觀察,可以發(fā)現(xiàn)液體有一些常見的特性:

          • 水往低處流,水滴會(huì)沿著碰撞體的表面進(jìn)行移動(dòng) gravityScale: 1.0,定義了粒子受重力影響的系數(shù);

          • 黏連性,可觀察到兩個(gè)水滴靠近時(shí),會(huì)在液體的作用力下相互吸引,通過定義 viscousStrength 來定義粒子的黏連;

          • 壓縮,液體粒子間會(huì)進(jìn)行壓縮,由下面的值來定義粒子允許進(jìn)行的壓縮:

          ??pressureStrength?
          ??staticPressureStrength
          ??staticPressureRelaxation?
          ??staticPressureIterations
          • 表面張力, 我們都知道在水面上放硬幣,硬幣不會(huì)沉底的實(shí)驗(yàn)。這個(gè)就是液體的表面張力。通過下面兩個(gè)屬性,可以調(diào)整液體的表面張力:

          ????surfaceTensionPressureStrength:?0.2,
          ????surfaceTensionNormalStrength:?0.2,


          WaterAssembler 解析


          WaterAssemblerRenderableComponent 提供頂點(diǎn)緩存的定制。


          在這個(gè)類里面,通過訪問粒子系統(tǒng)的每一個(gè)粒子的位置,生成4個(gè)單獨(dú)的頂點(diǎn):

          let?posBuff?=?particles.GetPositionBuffer();
          let?r?=?particles.GetRadius()?*?PHYSICS_2D_PTM_RATIO?*?3;
          ?
          for?(let?i?=?0;?i?????let?x?=?posBuff[i].x?*?PHYSICS_2D_PTM_RATIO;
          ????let?y?=?posBuff[i].y?*?PHYSICS_2D_PTM_RATIO;

          ????//?left-bottom
          ????vbuf[vertexOffset++]?=?x?-?r;?//x?
          ????vbuf[vertexOffset++]?=?y?-?r;?//y
          ????vbuf[vertexOffset++]?=?0;?//?z?
          ????vbuf[vertexOffset++]?=?x;?//?u
          ????vbuf[vertexOffset++]?=?y;?//?v
          ???...
          }


          最后計(jì)算索引緩存:

          //?fill?indices
          const?ibuf?=?buffer.iData!;
          for?(let?i?=?0;?i?????ibuf[indicesOffset++]?=?vertexId;
          ????ibuf[indicesOffset++]?=?vertexId?+?1;
          ????ibuf[indicesOffset++]?=?vertexId?+?2;
          ????ibuf[indicesOffset++]?=?vertexId?+?1;
          ????ibuf[indicesOffset++]?=?vertexId?+?3;
          ????ibuf[indicesOffset++]?=?vertexId?+?2;
          ????vertexId?+=?4;
          }


          頂點(diǎn)緩存描述了頂點(diǎn)的數(shù)據(jù),索引緩存指定了頂點(diǎn)的繪制順序。


          這樣就生成了一個(gè)基于粒子中心點(diǎn)的矩形。但是我們最終看到的是圓形,這里的魔法就是通過材質(zhì)和 Effect 系統(tǒng)來解決的。


          材質(zhì)和 Shader 解析


          7f0cc985c2028605b3ab1220ff05c981.webp


          模擬時(shí),需要使用 effect.effect 特效倆來模擬。


          注意這里選擇的是 transparent 的 technique:


          a1c6d6eb2531dede3164c5da7488bb21.webp


          effect.effectvert 函數(shù)內(nèi),計(jì)算了兩個(gè)傳輸?shù)?frag 的變量:v_cornerv_center ,這兩個(gè)變量代表的是粒子位置的中心點(diǎn)和角落的位置:

          ??out?vec2?v_corner;
          ??out?vec2?v_center;

          ??vec4?vert?()?{
          ????vec4?pos?=?vec4(a_position.xy,?0,?1);????

          ????//?no?a_corner?in?web?version
          ????//?use?a_position?instead?of?a_corner
          ????v_corner?=?a_position.xy?*?reverseRes;
          ????//?由于粒子是純色的,texCoord?里面記錄的是粒子的中心點(diǎn)位置
          ????v_center?=?a_texCoord.xy?*?reverseRes;

          ????v_corner.y?*=?yratio;
          ????v_center.y?*=?yratio;

          ????return?cc_matViewProj?*?pos;
          ??}


          這兩個(gè)變量在 frag 里面通過 smoothstep 進(jìn)行插值的計(jì)算:

          smoothstep(edge0,?edge1,?x)?
          這個(gè)函數(shù)會(huì)根據(jù)x計(jì)算赫爾米特插值:
          t?=?clamp((x?-?edge0)?/?(edge1?-?edge0),?0.0,?1.0);
          return?t?*?t?*?(3.0?-?2.0?*?t);


          9233b425fa2236809dfcc5e4d9197501.webp


          在?frag()?函數(shù)內(nèi)通過計(jì)算像素位置和粒子中心的距離,使用 smoothstep 進(jìn)行插值,粒子的半徑就會(huì)被控制在3倍到1倍半徑之間。同時(shí)由于是根據(jù)中心和半徑計(jì)算,粒子也會(huì)從矩形變成圓形:

          ??in?vec2?v_corner;
          ??in?vec2?v_center;

          ??vec4?frag?()?{
          ????float?mask?=?smoothstep(radius?*?3.,?radius,?distance(v_corner,?v_center));
          ????return?vec4(1.0,?1.0,?1.0,?mask);
          ??}


          此時(shí)繪制出來的粒子顏色是白色:


          e98de26751980c4caee2e027f969b78e.webp


          最后通過 display.effect 配合 render texture 將其渲染為藍(lán)色:


          9097a8fadafd72a6f8766e04fc6e0887.webp


          display.effect 使用了屬性查看器內(nèi)傳入的顏色 color

          ??in?vec4?color;

          ??#if?USE_TEXTURE
          ????in?vec2?uv0;
          ????#pragma?builtin(local)
          ????layout(set?=?2,?binding?=?10)?uniform?sampler2D?cc_spriteTexture;
          ??#endif

          ??vec4?frag?()?{
          ????vec4?o?=?vec4(1,?1,?1,?1);

          ????#if?USE_TEXTURE
          ??????o?*=?CCSampleWithAlphaSeparated(cc_spriteTexture,?uv0);
          ??????#if?IS_GRAY
          ????????float?gray??=?0.2126?*?o.r?+?0.7152?*?o.g?+?0.0722?*?o.b;
          ????????o.r?=?o.g?=?o.b?=?gray;
          ??????#endif
          ????#endif

          ????o.a?=?smoothstep(0.95,?1.0,?o.a);
          ????o?*=?color;

          ????ALPHA_TEST(o);
          ????return?o;
          ??}
          ??


          這個(gè)時(shí)候由于 alpha 的問題會(huì)出現(xiàn)一些毛邊:


          232b5b42900b1c67390b413c46533859.webp


          因此通過 smoothstep(0.95, 1.0, o.a) ,將像素的 alpha 值都控制在0.95到1之間。


          通過這個(gè)渲染我們可以看到,其實(shí)做游戲不一定非要真實(shí)的去模擬,我們只要騙過眼睛就能做出很好的效果了!




          本期分享就到這里,DEMO 地址見評(píng)論。在之前我們也和大家分享過 Cocos Creator 3.x 的?2D 動(dòng)態(tài)光照、2D 實(shí)時(shí)陰影的技術(shù)實(shí)現(xiàn)方案,更多方案與 DEMO 請(qǐng)移步論壇集中貼,如果有想了解的技術(shù)或效果實(shí)現(xiàn),歡迎在評(píng)論區(qū)留言,后續(xù)我們會(huì)更新更多關(guān)于游戲引擎的技術(shù)分享。


          論壇集中貼

          https://forum.cocos.org/t/topic/124637


          參考鏈接&擴(kuò)展閱讀

          Box2D 開源:

          https://github.com/erincatto/box2d

          LiquidFun 官網(wǎng):

          https://github.com/google/liquidfun

          LiquidFun 參考文檔:

          https://google.github.io/liquidfun/Programmers-Guide/html/index.html

          SmoothStep:

          https://en.wikipedia.org/wiki/Smoothstep


          往期精彩

          7670239be91030e50d359893e0f73ace.webp

          ef6dcfe7ae21ef612ed2458159efaa7b.webp

          738fa87425919b3ef8dd5c5303c33108.webp

          瀏覽 704
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一区一区国家精品 | 黄色三级片免费看 | 久久国产精品久久久 | 色婷亚州五月天 | 国产精品男女 |