<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>

          臥槽,3D錯(cuò)覺!這腦洞簡(jiǎn)直了...

          共 5086字,需瀏覽 11分鐘

           ·

          2022-06-01 13:55

          引言:去年,獨(dú)立游戲《籠中窺夢(mèng)》獨(dú)特的美術(shù)表現(xiàn)令無(wú)數(shù)玩家贊嘆。游戲利用「視錯(cuò)覺」來(lái)設(shè)計(jì)謎題關(guān)卡,創(chuàng)造了一個(gè)奇妙的立方體世界。本次,「Cocos Star Writer」Nowpaper 將在 v3.5 中實(shí)現(xiàn)同款視錯(cuò)效果,源碼和視頻教程見文末。


          「視錯(cuò)覺」解密游戲《籠中窺夢(mèng)》


          《籠中窺夢(mèng)》有很多值得考究的技術(shù)細(xì)節(jié)點(diǎn),那么今天,我們將在?Cocos Creator 3.5 中使用可渲染紋理和自定義著色器實(shí)現(xiàn)一下這個(gè)游戲的方盒視錯(cuò)功能,一窺其技術(shù)實(shí)現(xiàn)思路。


          Demo 最終效果


          這一次的技術(shù)實(shí)現(xiàn)靈感來(lái)自于我正在制作的另外一個(gè)技術(shù)視頻,那是探討如何在 v3.x 中實(shí)現(xiàn)瞄準(zhǔn)鏡和額外屏幕技術(shù)(PS.這個(gè)方案很快也會(huì)分享給大家)。偶然發(fā)現(xiàn)其中的部分,只要結(jié)合之前的傳送門技術(shù)實(shí)現(xiàn)方法,就能做出《籠中窺夢(mèng)》的方盒視錯(cuò),于是率先完成了這個(gè)方案,第一時(shí)間分享給大家。


          實(shí)現(xiàn)瞄準(zhǔn)鏡和額外屏幕技術(shù)


          場(chǎng)景準(zhǔn)備,實(shí)現(xiàn)分析


          《籠中窺夢(mèng)》中的盒子,各個(gè)面展示了完全不同的場(chǎng)景,玩家通過(guò)旋轉(zhuǎn)攝像機(jī)找到合適的位置,組合達(dá)成過(guò)關(guān)條件。因此我們需要解決的是:如何讓盒子的各個(gè)面顯示不同的畫面。


          正常來(lái)說(shuō),我們將使用5個(gè)攝像機(jī),渲染不同的畫面到方盒的5個(gè)面上,這里使用 RenderToTexture 可以很容易地完成。我準(zhǔn)備了一些素材搭建了幾個(gè)不同的場(chǎng)景,需要注意的是,需要注意的是,這里將場(chǎng)景按節(jié)點(diǎn)分割,方便管理。



          首先搭建一個(gè)基礎(chǔ)場(chǎng)景,中間擺放一個(gè)盒子,并且建立對(duì)應(yīng)各方向的面片。但如果五個(gè)表面都制作,那么場(chǎng)景搭建將會(huì)異常復(fù)雜,為了降低復(fù)雜度,我們先做出兩個(gè)表面,對(duì)應(yīng)的也得做出來(lái)2個(gè)場(chǎng)景用來(lái)當(dāng)謎題。


          這些場(chǎng)景搭建并不復(fù)雜,但是如果真的要實(shí)現(xiàn)完整的關(guān)卡,這就得需要精細(xì)設(shè)計(jì)了。本文主要探討技術(shù)實(shí)現(xiàn),就不做那么細(xì)致了。


          在項(xiàng)目設(shè)置中,新增6個(gè) Layer 用來(lái)標(biāo)記渲染分組,因?yàn)閷?duì)應(yīng)的攝像機(jī),只需要渲染指定的渲染層即可,不用全部都渲染出來(lái)。


          現(xiàn)在為它們每個(gè)組的根節(jié)點(diǎn)設(shè)置對(duì)應(yīng)的 Layer,以方便將來(lái)攝像機(jī)的設(shè)置。將基本場(chǎng)景的根節(jié)點(diǎn)設(shè)置成為 Base,而其他的兩個(gè)場(chǎng)景根節(jié)點(diǎn)分別設(shè)置為 Layer 1 和2。如此一來(lái)準(zhǔn)備工作就完成了。



          攝像機(jī)的控制代碼


          接著我們需要先實(shí)現(xiàn)一個(gè)攝像機(jī)的渲染控制代碼,讓攝像機(jī)圍繞著一個(gè)物體進(jìn)行旋轉(zhuǎn)。代碼相對(duì)來(lái)說(shuō)比較簡(jiǎn)單,通過(guò)監(jiān)聽 TouchMove 事件處理旋轉(zhuǎn),具體的請(qǐng)參看論壇中一些大佬的分享。

          import?{?_decorator,?Component,?Node,??input,?Input,?EventTouch,?Vec3,?v3,?Quat,?math,?quat?}?from?'cc';

          const?{?ccclass,?property?}?=?_decorator;

          @ccclass('CameraControl')

          export?class?CameraControl?extends?Component?{

          ????@property(Node)

          ????target:?Node?=?null;

          ????public?yMinLimit?=?-90;//相機(jī)向下最大角度

          ????public?yMaxLimit?=?0;//相機(jī)向上最大角度

          ????private?targetX?=?0;

          ????private?targetY?=?0;

          ????private?xSpeed?=?25;

          ????private?ySpeed?=?12;

          ????private?disance:?Vec3?=?v3();

          ????start()?{

          ????????let?angles?=?this.node.eulerAngles;

          ????????this.targetX?=?angles.y;

          ????????this.targetY?=?angles.x;

          ????????input.on(Input.EventType.TOUCH_MOVE,?this.onTouchMove,?this);

          ????????Vec3.subtract(this.disance,?this.node.worldPosition,?this.target.worldPosition);

          ????????this.disance?=?v3(0,0,this.disance.length());

          ????}

          ????private?onTouchMove(touch:?EventTouch)?{

          ????????this.targetX?-=?touch.getDeltaX()?*?this.xSpeed?*?0.02;

          ????????this.targetY?-=?touch.getDeltaY()?*?this.ySpeed?*?0.02;

          ????????this.targetY?=?this.ClampAngle(this.targetY,?this.yMinLimit,?this.yMaxLimit);

          ???????

          ????}

          ????private?_quat?=?quat();

          ????private?_vec3?=?v3();

          ????update?(deltaTime:?number)?{

          ????????Quat.fromEuler(this._quat,?this.targetY,?this.targetX,?0);

          ????????Vec3.transformQuat(this._vec3,?this.disance,?this._quat);

          ????????Vec3.add(this._vec3,?this.target.worldPosition,?this._vec3);

          ????????this.node.worldPosition?=?this._vec3.clone();

          ????????this.node.worldRotation?=?this._quat.clone();

          ????}

          ????ClampAngle(angle,?min,?max)?{

          ????????if?(angle?-360)?angle?+=?360;

          ????????if?(angle?>?360)?angle?-=?360;

          ????????return?math.clamp(angle,?min,?max);

          ????}

          }


          回到編輯器中,為主攝像機(jī)添加腳本,并且指定目標(biāo),測(cè)試旋轉(zhuǎn)效果。



          新建一個(gè)攝像機(jī),命名為 Camera 1。然后復(fù)制主攝像機(jī)節(jié)點(diǎn)參數(shù),粘貼給新建的攝像機(jī),調(diào)整到合適的位置,用來(lái)拍攝第一個(gè)場(chǎng)景。設(shè)置這個(gè)攝像機(jī)的可見性掩碼,只勾選 Layer 1 這個(gè) Layer 層。



          然后再實(shí)現(xiàn)一個(gè)攝像機(jī)同步的代碼,用來(lái)處理面攝像機(jī)和主攝像機(jī)的同步。實(shí)現(xiàn)同步只需要在開始的時(shí)候,計(jì)算兩個(gè)攝像機(jī)的偏移值,同步位置和旋轉(zhuǎn)時(shí)候修正即可。

          import?{?_decorator,?Component,?Node,?Vec3,?v3?}?from?'cc';

          const?{?ccclass,?property?}?=?_decorator;

          //?同步攝像機(jī)腳本,事實(shí)上如果監(jiān)聽主攝像機(jī)坐標(biāo)、旋轉(zhuǎn)差值會(huì)更加合理

          @ccclass('SyncCamera')

          export?class?SyncCamera?extends?Component?{

          ????@property(Node)

          ????mainCamera:?Node?=?null;

          ????offset:?Vec3?=?v3();

          ????_vec3?=?v3();

          ????start()?{

          ????????Vec3.subtract(this.offset,?this.node.position,?this.mainCamera.position);

          ????}

          ????update(deltaTime:?number)?{

          ????????Vec3.add(this._vec3,this.mainCamera.position,this.offset);

          ????????this.node.position?=?this._vec3.clone();

          ????????this.node.rotation?=?this.mainCamera.rotation;

          ????}

          }


          先實(shí)現(xiàn)一個(gè)面


          現(xiàn)在我們先實(shí)現(xiàn)一個(gè)面看看效果。


          新建一個(gè)材質(zhì),選擇著色器為內(nèi)置 builtin-unlit,勾選 Use Texture,新建一個(gè)可渲染紋理,設(shè)置寬高為 512x512,把剛剛新建的可渲染紋理,拖到上面完成引用。



          在場(chǎng)景中選擇 surface 1 的面片,將它材質(zhì)替換,就可以看到攝像機(jī)畫面,渲染到表面1上,現(xiàn)在我們運(yùn)行一下看看效果,由于有攝像機(jī)同步腳本,當(dāng)主攝像機(jī)調(diào)整的時(shí)候輔攝像機(jī)也會(huì)跟著變動(dòng)。



          接著用同樣的方式建立第二個(gè)表面的渲染畫面。


          但是很快我們會(huì)發(fā)現(xiàn)有個(gè)不對(duì)勁的地方:這個(gè)畫面看起來(lái)非常的平,根本就不像是透視過(guò)去的畫面。這是因?yàn)楝F(xiàn)在表面是直接將拍攝到的渲染畫面顯示出來(lái),這樣肯定是沒(méi)有透視效果的。



          這個(gè)時(shí)候就需要一些特殊的處理。在我之前有關(guān)傳送門的分享中,這個(gè)問(wèn)題也沒(méi)有解決得很完美,后來(lái)在 Cocos 的 Panda 大佬的幫助下,通過(guò)修改著色器 Shader 達(dá)成了完美的傳送門效果,本方案就將用到這個(gè)著色器文件,大家也可以到 Cocos Store 中免費(fèi)下載。


          「?jìng)魉烷T實(shí)現(xiàn)」源碼免費(fèi)下載:

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

          也可以參考 youtube 大佬 Sebastian Lague 的有關(guān)視頻,他對(duì)透視計(jì)算 Shader 和原理的講解非常詳細(xì),本文中的實(shí)現(xiàn)只是他的簡(jiǎn)化版本。

          https://www.youtube.com/watch?v=cWpFZbjtSQg


          核心代碼如下:

          float?flip?=?cc_cameraPos.w?==?0.0???-1.0?:?1.0;

          v_screenPos?=?pos?*?0.5;

          v_screenPos.xy?=?vec2(v_screenPos.x,?v_screenPos.y?*?flip)?+?v_screenPos.w;

          v_screenPos.zw?=?pos.zw;


          將修改過(guò)后的著色器添加到工程,然后選擇 surface 的材質(zhì),修改 Effect 點(diǎn)擊保存,運(yùn)行就可以看到效果,現(xiàn)在透視基本上 OK 了。



          不過(guò)好像還有一些小細(xì)節(jié)可以調(diào)整,比如畫質(zhì)發(fā)生了變化、顯示的內(nèi)容更近了,這個(gè)需要我們自行對(duì)攝像機(jī)進(jìn)行調(diào)整。但是為了更好的設(shè)計(jì)關(guān)卡,兩個(gè)攝像機(jī)的有關(guān)參數(shù)應(yīng)該保持一致,否則可能會(huì)出現(xiàn)奇怪的視覺錯(cuò)誤。


          視錯(cuò)謎題


          經(jīng)過(guò)參數(shù)調(diào)整,效果已經(jīng)非常不錯(cuò)了,那么我們也設(shè)計(jì)一個(gè)簡(jiǎn)單的謎題到其中。


          這里有一幅畫,放進(jìn)第一個(gè)場(chǎng)景中,然后再往第二個(gè)場(chǎng)景中添加一個(gè),此時(shí)需要一些空間視覺感,來(lái)調(diào)整對(duì)應(yīng)的位置和角度。這里可能需要我們反復(fù)對(duì)比攝像機(jī)呈現(xiàn)的畫面和最終運(yùn)行結(jié)果,才能調(diào)出滿意的效果。


          運(yùn)行一下看看效果。一般情況下,我們透過(guò)兩個(gè)表面看到的畫并不能完全重合,經(jīng)過(guò)拖動(dòng)旋轉(zhuǎn)可以讓其完全重合。



          結(jié)語(yǔ)


          好了,剩下的就是關(guān)卡設(shè)計(jì)的問(wèn)題了,您可以利用本篇中的技巧,使用 Cocos Creator 完成一個(gè)自己的《籠中窺夢(mèng)》游戲。今天這個(gè)技術(shù)實(shí)現(xiàn)本質(zhì)上是將 RenderToTexture 給用活了,只要稍微調(diào)整一下,你也能做出令人嘆為觀止的創(chuàng)意。


          我一直認(rèn)為,技術(shù)工具都是為創(chuàng)意服務(wù),人類的創(chuàng)意總是沒(méi)有盡頭,如何實(shí)現(xiàn)心中所想,除了對(duì)它的熱愛,工具的熟練度和技術(shù)的沉淀,才是創(chuàng)意發(fā)揮的基礎(chǔ)。


          資源鏈接


          • 點(diǎn)擊文末【閱讀原文】前往 Cocos Store 下載本項(xiàng)目源碼(含三個(gè)基礎(chǔ)場(chǎng)景資源、三個(gè)面的實(shí)現(xiàn)場(chǎng)景,可以很方便地進(jìn)行擴(kuò)展):

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


          • 視頻教程(UP 主:Nowpaper)

          https://www.bilibili.com/video/BV1D3411G7jy


          • 論壇討論帖:

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


          今天的文章就到這里了,我是 Nowpaper,一個(gè)混跡游戲行業(yè)的老爸,如果您喜歡我的分享,不妨多多點(diǎn)贊留言,也歡迎關(guān)注我的 B 站,您的支持是我更新的動(dòng)力,下次再見!


          Nowpaper 往期分享

          《守望先鋒》同級(jí)的槍彈射擊體驗(yàn)

          《時(shí)空幻境》時(shí)間倒放玩法

          《饑荒》同款視覺表現(xiàn)

          用 RenderTexture 實(shí)現(xiàn)小地圖與傳送門


          瀏覽 93
          點(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>
                  青青草成人在线免费观看 | 天天澡天天爽日日AV | 久爱免费视频 | 亚洲美国日本中文字幕 | 99玖玖视频 |