Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 男人日女人的逼视频,亚洲区一二三四区2021,国产成人免费视频

          整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          這款中國(guó)程序員開源的游戲引擎,讓你用不到100行代碼就寫出“憤怒的小鳥”

          家好,我是一個(gè)游戲引擎技術(shù)探索者,同時(shí)也是一名做過(guò)不少前端開發(fā)工作的程序員。如果你想知道如何從編寫網(wǎng)頁(yè)到開發(fā)游戲,那你來(lái)對(duì)地方了!

          今天我們聊聊如何使用 Dora SSR,一個(gè)支持 TSX 且跨平臺(tái)在 native 運(yùn)行的游戲引擎,助你輕松跨入游戲開發(fā)的世界。

          不必?fù)?dān)心,說(shuō)到游戲引擎并不是啥高不可攀的技術(shù),反而和我們熟悉的前端開發(fā)工具可以有驚人相似之處。


          一、游戲客戶端開發(fā)也可以是一種前端開發(fā)

          首先,讓我們解釋一下什么是游戲引擎。
          簡(jiǎn)單來(lái)說(shuō),游戲引擎就是一套工具和庫(kù)的集合,幫助開發(fā)者構(gòu)建游戲,管理圖形、聲音、物理計(jì)算或碰撞檢測(cè)等。
          對(duì)于前端開發(fā)者來(lái)說(shuō),你可以把它想象成就是一種特殊的瀏覽器,專門用來(lái)運(yùn)行游戲。
          Dora SSR 的游戲場(chǎng)景管理使用了類似于 HTML DOM 的樹形結(jié)構(gòu),這對(duì)我們來(lái)說(shuō)再熟悉不過(guò)了。
          想象一下,將 div 元素?fù)Q成游戲中的各種對(duì)象,CSS 動(dòng)畫換成游戲動(dòng)畫,概念也差不多,代碼寫法上可能也差不多,是不是覺得有點(diǎn)意思了?


          二、從 TypeScript 到 TSX:前端技術(shù)在游戲中的應(yīng)用

          許多前端開發(fā)者都熟悉 TypeScript 和 React 的 JSX 語(yǔ)法。
          在 Dora SSR 開源游戲引擎中,我們通過(guò)支持 TSX,提供了與前端開發(fā)編程模式相似的游戲開發(fā)接口。
          是的你沒聽錯(cuò),就是那個(gè) TSX。
          使用 TSX 開發(fā)游戲,意味著你可以利用已有的前端技術(shù)棧 — 組件、模塊和其他現(xiàn)代前端技術(shù),直接在游戲開發(fā)中復(fù)用這些概念。
          而且,Dora SSR 的性能優(yōu)化確保了即使是在復(fù)雜的游戲場(chǎng)景中,也能保持流暢的運(yùn)行。


          三、挑戰(zhàn) 100 行代碼以內(nèi),教你寫一個(gè) “憤怒的小鳥” like 的游戲

          好了,理論知識(shí)夠多了,讓我們來(lái)點(diǎn)實(shí)際操作吧。
          來(lái)看看如何在 Dora SSR 中用 100 行以內(nèi)的 TSX 代碼編寫一個(gè)類似 “憤怒的小鳥” 的小游戲。
          當(dāng)然,在開始之前還是要準(zhǔn)備開發(fā)環(huán)境,做這個(gè)事用 Dora SSR 就很簡(jiǎn)單:我有一個(gè)安裝包一裝,我有一個(gè)瀏覽器一開,嗯,開始寫代碼運(yùn)行吧。
          安裝啟動(dòng)參見:Dora 啟動(dòng)!
          (https://dora-ssr.net/zh-Hans/docs/tutorial/quick-start)
          不小心裝成了APK包在手機(jī)上?那就在同局域網(wǎng)下訪問(wèn),直接在手機(jī)上進(jìn)行開發(fā)調(diào)試吧

          1. 編寫最簡(jiǎn)單游戲場(chǎng)景

          在編寫實(shí)際的代碼之前,我們可以先寫一個(gè)有特別功能的注釋,它可以告訴 Dora SSR 的 Web IDE 在我們按下 Ctrl + S 保存文件時(shí),自動(dòng)熱更新運(yùn)行的代碼,以實(shí)現(xiàn)代碼運(yùn)行結(jié)果的實(shí)時(shí)預(yù)覽功能。
          // @preview-file on
          然后,我們引入必要的庫(kù)和組件。
          當(dāng)然我們的代碼編輯器也會(huì)提示輔助我們自動(dòng)引入需要的模塊,可以放到后面編碼過(guò)程中再完成:
          import { React, toNode, useRef } from 'DoraX';
          import { Body, BodyMoveType, Ease, Label, Line, Scale, TypeName, Vec2, tolua } from 'Dora';

          在 Dora SSR 中顯示一個(gè)圖片很簡(jiǎn)單,只要使用 <sprite> 標(biāo)簽,最后通過(guò) toNode() 函數(shù)將標(biāo)簽實(shí)例化為一個(gè)游戲?qū)ο缶涂梢粤恕?/span>

          toNode(<sprite file='Image/logo.png' scaleX={0.2} scaleY={0.2}/>);
          好的,至此你已經(jīng)基本掌握大部分 Dora SSR 游戲開發(fā)的訣竅了,開始做你自己的游戲吧(認(rèn)真)。

          2. 編寫游戲箱子組件

          接下來(lái)我們?cè)谟螒蛑信鲎驳南渥訒?huì)由 Box 組件定義,它接受
          numxychildren 等屬性:
          interface BoxProps {
          num: number;
          x?: number;
          y?: number;
          children?: any | any[];
          }

          const Box = (props: BoxProps) => {
          const numText = props.num.toString();
          return (
          <body type={BodyMoveType.Dynamic} scaleX={0} scaleY={0} x={props.x} y={props.y} tag={numText}>
          <rect-fixture width={100} height={100}/>
          <draw-node>
          <rect-shape width={100} height={100} fillColor={0x8800ffff} borderWidth={1} borderColor={0xff00ffff}/>
          </draw-node>
          <label fontName='sarasa-mono-sc-regular' fontSize={40}>{numText}</label>
          {props.children}
          </body>
          );
          };
          我們使用仿 React 的函數(shù)組件的寫法來(lái)完成我們箱子組件的定義,其中:
          • body 組件的 tag 屬性:用于存儲(chǔ)箱子的分?jǐn)?shù)。
          • rect-fixture :定義了箱子的碰撞形狀。
          • draw-node :用于繪制箱子的外觀。
          • label :用于顯示盒子的分?jǐn)?shù)。

          3. 創(chuàng)建 TSX 實(shí)例化后的對(duì)象引用

          使? useRef 創(chuàng)建兩個(gè)引?變量進(jìn)行備用,分別指向??和分?jǐn)?shù)標(biāo)簽:
          const bird = useRef<Body.Type>();
          const score = useRef<Label.Type>();

          4. 創(chuàng)建發(fā)射線

          發(fā)射線由 line 變量創(chuàng)建,并添加觸摸(同時(shí)也是鼠標(biāo)點(diǎn)擊)的事件處理:
          let start = Vec2.zero;
          let delta = Vec2.zero;
          const line = Line();

          toNode(
          <physics-world
          onTapBegan={(touch) => {
          start = touch.location;
          line.clear();
          }}
          onTapMoved={(touch) => {
          delta = delta.add(touch.delta);
          line.set([start, start.add(delta)]);
          }}
          onTapEnded={() => {
          if (!bird.current) return;
          bird.current.velocity = delta.mul(Vec2(10, 10));
          start = Vec2.zero;
          delta = Vec2.zero;
          line.clear();
          }}
          >
          {/* ...在物理世界下創(chuàng)建其它游戲元素 ... */}
          </physics-world>
          );
          • onTapBegan 事件中,記錄觸摸開始的位置并清除發(fā)射線。
          • onTapMoved 事件中,計(jì)算觸摸移動(dòng)的距離并更新發(fā)射線。
          • onTapEnded 事件中,根據(jù)觸摸移動(dòng)的距離設(shè)置小鳥的發(fā)射速度并清除發(fā)射線。

          5. 創(chuàng)建其它游戲元素

          接下來(lái),我們以 <physics-world> 作為游戲場(chǎng)景的父級(jí)標(biāo)簽,在它下面繼續(xù)創(chuàng)建游戲場(chǎng)景中的各個(gè)元素:

          5.1 地面

          首先,我們使用 body 組件創(chuàng)建一個(gè)地面,并將其設(shè)置為靜態(tài)剛體:
          <body type={BodyMoveType.Static}>
          <rect-fixture centerY={-200} width={2000} height={10}/>
          <draw-node>
          <rect-shape centerY={-200} width={2000} height={10} fillColor={0xfffbc400}/>
          </draw-node>
          </body>
          • type={BodyMoveType.Static}:表明這是一個(gè)靜態(tài)剛體,不會(huì)受到物理模擬的影響。
          • rect-fixture:定義地面碰撞形狀為一個(gè)矩形。
          • draw-node:用于繪制地面的外觀。
          • rect-shape:繪制一個(gè)矩形,顏色為黃色。

          5.2 箱子

          接下來(lái),我們使用之前寫好的 Box 組件創(chuàng)建 5 個(gè)箱子,并設(shè)置不同的初始位置和分?jǐn)?shù),在創(chuàng)建時(shí)播放出場(chǎng)動(dòng)畫:
          {
          [10, 20, 30, 40, 50].map((num, i) => (
          <Box num={num} x={200} y={-150 + i * 100}>
          <sequence>
          <delay time={i * 0.2}/>
          <scale time={0.3} start={0} stop={1}/>
          </sequence>
          </Box>
          ))
          }
          • map 函數(shù):用于遍歷分?jǐn)?shù)數(shù)組從 10 到 50,并為每個(gè)分?jǐn)?shù)創(chuàng)建一個(gè)需要小鳥撞擊的箱子。
          • Box 組件:用于創(chuàng)建箱子,并傳入以下屬性:
            • num={num}:箱子的分?jǐn)?shù),對(duì)應(yīng)數(shù)組中的數(shù)字。
            • x={200}:箱子的初始 x 軸位置,為 200。
            • y={-150 + i * 100}:箱子的初始 y 軸位置,根據(jù)創(chuàng)建序號(hào)遞增。
          • sequence 組件:用于創(chuàng)建要在父節(jié)點(diǎn)上播放的動(dòng)畫序列,包含以下動(dòng)畫:
            • delay time={i * 0.2}:延遲播放動(dòng)畫,延遲時(shí)間根據(jù)創(chuàng)建序號(hào)遞增。
            • scale time={0.3} start={0} stop={1}:縮放動(dòng)畫,從不顯示到完全顯示,耗時(shí) 0.3 秒。

          5.3 小鳥

          最后,我們使用 body 組件創(chuàng)建小鳥,并設(shè)置碰撞形狀、外觀和分?jǐn)?shù)標(biāo)簽:

          <body ref={bird} type={BodyMoveType.Dynamic} x={-200} y={-150} onContactStart={(other) => {
          if (other.tag !== '' && score.current) {
          // 累加積分
          const sc = parseFloat(score.current.text) + parseFloat(other.tag);
          score.current.text = sc.toString();
          // 清除被撞箱子上的分?jǐn)?shù)
          const label = tolua.cast(other.children?.last, TypeName.Label);
          if (label) label.text = '';
          other.tag = '';
          // 播放箱子被撞的動(dòng)畫
          other.perform(Scale(0.2, 0.7, 1.0));
          }
          }}>
          <disk-fixture radius={50}/>
          <draw-node>
          <dot-shape radius={50} color={0xffff0088}/>
          </draw-node>
          <label ref={score} fontName='sarasa-mono-sc-regular' fontSize={40}>0</label>
          <scale time={0.4} start={0.3} stop={1.0} easing={Ease.OutBack}/>
          </body>
          • ref={bird}:使用 ref 創(chuàng)建引用變量,方便后續(xù)操控小鳥。
          • type={BodyMoveType.Dynamic}:表明這是一個(gè)動(dòng)態(tài)剛體,會(huì)受到物理模擬的影響。
          • onContactStart={(other) => {}}:小鳥的物理體接觸到其它物體時(shí)觸發(fā)的回調(diào)處理函數(shù)。
          • disk-fixture:定義小鳥形狀為一個(gè)圓盤。
          • draw-node :用于繪制小鳥的外觀。
          • label :用于顯示小鳥的累積分?jǐn)?shù)。
          • scale :用于播放小鳥的出場(chǎng)動(dòng)畫。

          6. 完成游戲邏輯

          至此,我們已經(jīng)完成了小游戲的核心邏輯。你可以根據(jù)自己的想法進(jìn)一步完善游戲邏輯和增加功能。
          完整的 demo 代碼可以見這個(gè)鏈接:
          https://github.com/IppClub/Dora-SSR/blob/main/Assets/Script/Test/Birdy.tsx
          下面是一些運(yùn)行效果的截圖。
          拖拽屏幕發(fā)射了“憤怒的小鳥”

          高超的技巧,使我一擊獲得了所有得分


          四、簡(jiǎn)單揭秘一下

          1. 是鹿還是馬

          事實(shí)上我們寫的這段游戲代碼,在 Dora SSR 引擎的能力下是可以確保在跨 Linux、Android、iOS、macOS 和 Windows 獲得一致的運(yùn)行結(jié)果。
          但是為了運(yùn)行這段代碼,我們的 Dora SSR 引擎甚至都沒有做 JavaScript 運(yùn)行環(huán)境的支持……(你說(shuō)什么?)
          是的,Dora SSR 的底層技術(shù)實(shí)現(xiàn)其實(shí)是基于 Lua 和 WASM 虛擬機(jī)作為腳本語(yǔ)言運(yùn)行環(huán)境的。
          對(duì) TypeScript 的支持其實(shí)是通過(guò)整合了 TypescriptToLua 這個(gè)編譯器提供的。
          https://github.com/TypeScriptToLua/TypeScriptToLua
          TSTL 通過(guò)重新編寫了 TypeScript 語(yǔ)言編譯器的后端,將 TS 和 TSX 的代碼編譯為了等價(jià)運(yùn)行的 Lua 代碼,從而使得 TS 代碼可以在 Dora 上加載運(yùn)行。
          在 Dora 自帶的 Web IDE 的代碼編輯器下,可以幫助大家做 TS 的語(yǔ)言檢查和補(bǔ)全以及 Dora 內(nèi)置庫(kù) API 的提示。
          最終的使用體驗(yàn),大家就可以不用管最后是鹿還是馬,只要代碼能通過(guò)了 TS 的編譯檢查,拉出來(lái)那都是一樣的跑啦。

          2. 和 React 有關(guān)系嗎

          這個(gè)問(wèn)題的答案目前是:可以有(所以截至發(fā)文前還沒有)。
          React 最重要的能力是通過(guò) Virtual DOM 和執(zhí)行 Tree Diff 處理的過(guò)程來(lái)進(jìn)行渲染組件和業(yè)務(wù)數(shù)據(jù)的狀態(tài)同步,目前這個(gè)機(jī)制還沒有在 Dora SSR 中實(shí)現(xiàn),所以大家目前看到的用 TSX 編寫出的類似 VDOM 的構(gòu)建代碼只會(huì)在運(yùn)行時(shí)做一次性的游戲渲染對(duì)象的構(gòu)建,往后都是底層 C++ 實(shí)現(xiàn)的引擎功能在負(fù)責(zé)繼續(xù)處理。
          也許有一天我們會(huì)為游戲 UI 的開發(fā),提供仿 React 通過(guò)執(zhí)行 Tree Diff 做狀態(tài)同步的能力,或是仿 SolidJS 基于 TSX 實(shí)現(xiàn)其它的渲染組件狀態(tài)同步的機(jī)制。
          所以在這里也誠(chéng)摯地邀請(qǐng)廣大前端開發(fā)的朋友,加入我們,一起玩 Dora SSR 項(xiàng)目,一起研究怎么運(yùn)用前端開發(fā)技術(shù)思想,為游戲開發(fā)也引入更多好用便捷的輪子吧。
          最后我們的 Q 群在這里,歡迎過(guò)來(lái)玩:
          512620381


          作者介紹

          李瑾:金融行業(yè)大數(shù)據(jù)工程師,Dora SSR 和 Yuescript 開源軟件作者。

          項(xiàng)目介紹

          Dora SSR(多蘿珍奇引擎)是一個(gè)用于多種設(shè)備上快速開發(fā)2D游戲的游戲引擎。
          它內(nèi)置易用的開發(fā)工具鏈,支持在手機(jī)、開源掌機(jī)等設(shè)備上直接進(jìn)行游戲開發(fā)。


          項(xiàng)目倉(cāng)庫(kù)

          https://gitee.com/pig/Dora-SSR
          https://github.com/IppClub/Dora-SSR


          END

          OM是Document Object Model的縮寫,中文名稱是文檔對(duì)象模型。

          DOM是處理HTML頁(yè)面的標(biāo)準(zhǔn)編程接口,DOM可被JavaScript用來(lái)讀取、改變HTML的內(nèi)容和結(jié)構(gòu)。

          前端三大件指HTML、CSS、JavaScript,其中JavaScript最重要的組成部分就是DOM。

          一:我們?yōu)槭裁匆獙W(xué)習(xí)DOM呢?

          1,DOM可以讓用戶對(duì)網(wǎng)頁(yè)元素進(jìn)行交互操作

          比如,當(dāng)我點(diǎn)擊了一個(gè)按鈕,彈出一個(gè)對(duì)話框等操作。

          2,DOM可以用來(lái)做網(wǎng)頁(yè)游戲

          比如,現(xiàn)在比較流行的游戲,我們完全可以拿JavaScript操作DOM來(lái)實(shí)現(xiàn)。

          3,DOM是ajax的重要基礎(chǔ)

          比如,我們通過(guò)ajax獲取了一些數(shù)據(jù),你要顯示給用戶,這就需要用到DOM了。

          二:我們是這么講解DOM的

          1,首先我們會(huì)講解什么是DOM,就是之前說(shuō)的文檔對(duì)象模型

          DOM是處理HTML的標(biāo)準(zhǔn)編程接口,DOM可被JavaScript用來(lái)讀取、改變HTML的內(nèi)容和結(jié)構(gòu)。

          2,會(huì)講解什么是DOM樹

          對(duì)象與對(duì)象間的層次結(jié)構(gòu)。

          3,會(huì)講解什么是DOM節(jié)點(diǎn)

          根據(jù) W3C 的 HTML DOM 標(biāo)準(zhǔn),HTML 文檔中的所有內(nèi)容都是節(jié)點(diǎn):

          整個(gè)文檔是一個(gè)文檔節(jié)點(diǎn)

          每個(gè) HTML 元素是元素節(jié)點(diǎn)

          HTML 元素內(nèi)的文本是文本節(jié)點(diǎn)

          每個(gè) HTML 屬性是屬性節(jié)點(diǎn)

          注釋是注釋節(jié)點(diǎn)

          4,會(huì)講解什么是事件驅(qū)動(dòng)

          即做了什么操作,執(zhí)行什么事件。

          5,會(huì)講解什么是2級(jí)DOM

          1級(jí)DOM、2級(jí)DOM、3級(jí)DOM分別指什么?怎樣實(shí)現(xiàn)。

          6,會(huì)講解什么是事件流

          會(huì)講解什么叫做事件冒泡,什么叫做事件捕獲,根據(jù)事件流的特點(diǎn)能實(shí)現(xiàn)什么功能,及如何阻止事件的傳播。

          7,會(huì)講解什么是Event

          Event 對(duì)象的屬性提供了有關(guān)事件的細(xì)節(jié)。

          三:模擬畫筆案例

          在網(wǎng)頁(yè)中,通過(guò)JavaScript操作DOM來(lái)模擬畫筆功能,比如鼠標(biāo)按下后,在屏幕中拖動(dòng),會(huì)形成痕跡,那么整個(gè)過(guò)程,就是在模擬畫筆。

          • 認(rèn)為 WebAssembly (WASM) 只用于圖像處理、復(fù)雜的數(shù)學(xué)計(jì)算或者 Web 上的小小應(yīng)用嗎?
          • 你是否經(jīng)常將 WASM 與 Web Workers 和 Service Workers 的概念混淆?
          • 你對(duì) WASM 不感興趣,是因?yàn)槟阏J(rèn)為現(xiàn)在的 Web 應(yīng)用程序在未來(lái) 10 年里依舊是 JavaScript 主導(dǎo)?
          • 你是否想過(guò)用 JS 以外的語(yǔ)言做 Web 前端開發(fā)?

          如果你不想細(xì)讀,你可以看下我做的 demo 頁(yè)面或者直接看下 go-wasm-cat-game-on-canvas-with-docker 這個(gè)項(xiàng)目,我會(huì)講的簡(jiǎn)潔一些,盡量不浪費(fèi)你的時(shí)間。以下是我這個(gè)項(xiàng)目的一些關(guān)鍵的代碼解析。

          故事開始了

          我們的目標(biāo)是給貓 做一個(gè)簡(jiǎn)單的小游戲:做一個(gè)小紅點(diǎn)在手機(jī)上不停的移動(dòng),整個(gè)過(guò)程還有 HiFi 音樂 還有震動(dòng)。整個(gè)項(xiàng)目我們會(huì)用 Golang (Go)這門語(yǔ)言來(lái)實(shí)現(xiàn),包括 DOM 操作、邏輯還有相關(guān)的狀態(tài)。

          而而而而而且,由于貓咪不會(huì)使用鼠標(biāo),我們還需要給貓爪 做一些點(diǎn)擊觸摸的交互。

          說(shuō)一下我的理解!

          把 WASM 想象成一個(gè) 通用虛擬機(jī)(UVM, Universal Virtual Machine) 或者一個(gè)沙箱,你只需編寫一次任何代碼,它便可以在任何地方運(yùn)行。

          WASM 是一個(gè)編譯目標(biāo),而不是一種語(yǔ)言。就像你要同時(shí)針對(duì) Windows,Mac OS 和 Linux 進(jìn)行編譯一樣!

          我不認(rèn)為 WASM 會(huì)廢棄 JS,你可以有其他選擇而不用付出任何代價(jià)。

          想象一下使用 Go,Swift,Rust,Ruby,C ++,OCaml 或者其他語(yǔ)言的開發(fā)人員。現(xiàn)在,他們可以使用自己喜歡的語(yǔ)言來(lái)創(chuàng)建交互式,聯(lián)網(wǎng),快速,具有脫機(jī)功能的網(wǎng)站和Web 應(yīng)用。

          你是否曾經(jīng)參與過(guò)類似「一個(gè)項(xiàng)目是用一個(gè)代碼倉(cāng)庫(kù)管理還是多個(gè)代碼倉(cāng)庫(kù)管理?」問(wèn)題的討論?

          好吧,不管你有沒有,你現(xiàn)在也要想一下現(xiàn)在這個(gè)項(xiàng)目打算用一門語(yǔ)言實(shí)現(xiàn)還是多門語(yǔ)言實(shí)現(xiàn)了。

          當(dāng)大家可以使用相同的技術(shù)棧時(shí),一切都會(huì)變得更加容易,尤其是團(tuán)隊(duì)之間的溝通。

          你可以依舊使用 React 或者 Vue,但你現(xiàn)在開始也可以不用使用 JS 來(lái)開發(fā)了。

          WASM 跟 Service Workers 還有 Web Workers 有什么區(qū)別?

          Service Workers 還有 Web Workers 允許應(yīng)用在后臺(tái)運(yùn)行,也可以做到離線運(yùn)行和緩存。它們模仿線程,無(wú)法訪問(wèn)DOM,并且不能共享數(shù)據(jù)(僅能通過(guò)消息傳遞),只能在單獨(dú)的上下文中運(yùn)行。咦,其實(shí)我們甚至可以在其中運(yùn)行 WASM 而不是 JS。對(duì)我來(lái)說(shuō),它們只提供一些具有特殊特權(quán)的抽象層,沒有人說(shuō)這些層必須執(zhí)行 JS。

          Service Workers 還有 Web Workers 是瀏覽器上的功能,不是 JS 的專有功能。

          設(shè)置開發(fā)環(huán)境

          我們將使用 WASM,Go,JS 和 Docker(這個(gè)是可選的) 來(lái)進(jìn)行開發(fā)。

          如果您不了解Go,但了解 JS,請(qǐng) 點(diǎn)擊這里學(xué)習(xí) Go,然后再回來(lái)繼續(xù)閱讀。讓我們從 Go WASM Wiki 開始。

          你可以使用安裝在電腦本地的 go 版本,在這里我使用 Docker 的 golang:1.12-rc 鏡像。只需在此處為 go 編譯器設(shè)置兩個(gè) WASM 標(biāo)志。在 main.go 中創(chuàng)建一個(gè)簡(jiǎn)單的 hello world 進(jìn)行測(cè)試。

          $ GOOS=js GOARCH=wasm go build -o game.wasm main.go
          
          build_go:
           docker run --rm \
           -v `pwd`/src:/game \
           --env GOOS=js --env GOARCH=wasm \
           golang:1.12-rc \
           /bin/bash -c "go build -o /game/game.wasm /game/main.go; cp /usr/local/go/misc/wasm/wasm_exec.js /game/wasm_exec.js"
          

          現(xiàn)在,讓我們利用好 Go 團(tuán)隊(duì)提供的 wasm_exec.js 代碼。代碼里的全局變量 Go 對(duì) WASM 進(jìn)行了初始化操作,我們不必自己從頭開始做好任何 DOM 的實(shí)現(xiàn)。等我們編譯好 wasm 文件后,它會(huì)獲取 .wasm 文件并運(yùn)行我們的游戲。

          總而言之,它應(yīng)該看起來(lái)像這樣:

          <!DOCTYPE html>
          <html>
            <head>
              <meta charset="utf-8" />
              <meta name="viewport" content="width=device-width,initial-scale=1.0" />
              <style>body{height:100%;width:100%;padding:0;margin:0;background-color:#000000;color:#FFFFFF;font-family:Arial,Helvetica,sans-serif}</style>
              <script type="text/javascript" src="./wasm_exec.js"></script>
              <script type="text/javascript">
                async function run(fileUrl) {
                  try {
                    const file = await fetch(fileUrl);
                    const buffer = await file.arrayBuffer();
                    const go = new Go();
                    const { instance } = await WebAssembly.instantiate(buffer, go.importObject);
                    go.run(instance);
                  } catch (err) {
                    console.error(err);
                  }
                }
                setTimeout(() => run("./game.wasm"));
              </script>
            </head>
            <body></body>
          </html>
          

          放碼過(guò)來(lái)!(當(dāng)然是 Go 的碼)

          要渲染我們的這個(gè)小游戲,<canvas> 這個(gè)標(biāo)簽應(yīng)該足夠了。我們可以直接從 Go 代碼創(chuàng)建 DOM 結(jié)構(gòu)和元素!這個(gè) syscall/js 文件 (包含在標(biāo)準(zhǔn) Go 庫(kù)中)為我們處理了與 DOM 交互的方法。

          main() 方法

          我敢打賭,你很久沒見過(guò) main() 方法了 。

          package main
          
          import (
           // https://github.com/golang/go/tree/master/src/syscall/js
           "syscall/js"
          )
          
          var (
           // js.Value 可以是任意的 JS 對(duì)象、類型或者構(gòu)造函數(shù)
           window, doc, body, canvas, laserCtx, beep js.Value
           windowSize struct{ w, h float64 }
          )
          
          func main() {
           setup()
          }
          
          func setup() {
           window = js.Global()
           doc = window.Get("document")
           body = doc.Get("body")
          
           windowSize.h = window.Get("innerHeight").Float()
           windowSize.w = window.Get("innerWidth").Float()
          
           canvas = doc.Call("createElement", "canvas")
           canvas.Set("height", windowSize.h)
           canvas.Set("width", windowSize.w)
           body.Call("appendChild", canvas)
          
           // 這個(gè)是小紅點(diǎn)   Canvas 對(duì)象
           laserCtx = canvas.Call("getContext", "2d")
           laserCtx.Set("fillStyle", "red")
          
           // http://www.iandevlin.com/blog/2012/09/html5/html5-media-and-data-uri/
           beep = window.Get("Audio").New("data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU2LjI1LjEwMQAAAAAAAAAAAAAA/+NAwAAAAAAAAAAAAFhpbmcAAAAPAAAAAwAAA3YAlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw////////////////////////////////////////////AAAAAExhdmYAAAAAAAAAAAAAAAAAAAAAACQAAAAAAAAAAAN2UrY2LgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/jYMQAEvgiwl9DAAAAO1ALSi19XgYG7wIAAAJOD5R0HygIAmD5+sEHLB94gBAEP8vKAgGP/BwMf+D4Pgh/DAPg+D5//y4f///8QBhMQBgEAfB8HwfAgIAgAHAGCFAj1fYUCZyIbThYFExkefOCo8Y7JxiQ0mGVaHKwwGCtGCUkY9OCugoFQwDKqmHQiUCxRAKOh4MjJFAnTkq6QqFGavRpYUCmMxpZnGXJa0xiJcTGZb1gJjwOJDJgoUJG5QQuDAsypiumkp5TUjrOobR2liwoGBf/X1nChmipnKVtSmMNQDGitG1fT/JhR+gYdCvy36lTrxCVV8Paaz1otLndT2fZuOMp3VpatmVR3LePP/8bSQpmhQZECqWsFeJxoepX9dbfHS13/////aysppUblm//8t7p2Ez7xKD/42DE4E5z9pr/nNkRw6bhdiCAZVVSktxunhxhH//4xF+bn4//6//3jEvylMM2K9XmWSn3ah1L2MqVIjmNlJtpQux1n3ajA0ZnFSu5EpX////uGatn///////1r/pYabq0mKT//TRyTEFNRTMuOTkuNaqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq/+MQxNIAAANIAcAAAKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqg==")
          }
          

          看起來(lái)是不是很像 JS 代碼?

          是的,這就是與 DOM 交互所需的全部?jī)?nèi)容!現(xiàn)在只需要幾個(gè) get 方法還有調(diào)用函數(shù)即可。

          在這一點(diǎn)上,我問(wèn)自己:在某種程度上,我仍然在寫 JS … 這怎么算是升級(jí)?因?yàn)槲覀冞€不能直接訪問(wèn) DOM,所以我們必須(通過(guò) JS)調(diào)用 DOM 來(lái)做任何事情。想象一下如何用 JSX / React 來(lái)抽象化它。

          實(shí)際上,已經(jīng)可以做到了,請(qǐng)期待我的下篇文章 。

          「渲染」還有事件處理

          直接使用 syscall / js 庫(kù),這個(gè)寫法看起來(lái)有點(diǎn)像 ES5 的回調(diào)。但我們能夠監(jiān)聽 DOM 事件,而且那些靜態(tài)類型看起來(lái)很干凈!

          func main() {
           setup()
          
            // 在編譯時(shí)聲明渲染器
           var renderer js.Func
           // 沒有錯(cuò),看起來(lái)很像 JS 的回調(diào)  
           renderer = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
            updateGame()
            // 實(shí)現(xiàn) 60FPS 的動(dòng)畫
            window.Call("requestAnimationFrame", renderer)
            return nil
           })
           window.Call("requestAnimationFrame", renderer)
          
           // 讓我們處理下 鼠標(biāo)/手勢(shì) 點(diǎn)擊事件
           var mouseEventHandler js.Func = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
            updatePlayer(args[0])
            return nil
           })
           window.Call("addEventListener", "pointerdown", mouseEventHandler)
          }
          
          func updatePlayer(event js.Value) {}
          func updateGame() {}
          

          日志記錄、音頻播放以及「異步」執(zhí)行

          在 Go 中,有一個(gè)慣例是把所有的函數(shù)都寫成同步的方式,由調(diào)用者決定函數(shù)的執(zhí)行是否是異步的。異步運(yùn)行函數(shù)非常簡(jiǎn)單,只要在前面加上 go 就行了!它使用自己的上下文創(chuàng)建一個(gè)線程,你仍然可以將父級(jí)上下文綁定給它,不要擔(dān)心哈。

          func updatePlayer(event js.Value) {
           mouseX := event.Get("clientX").Float()
           mouseY := event.Get("clientY").Float()
           
            // `go` 關(guān)鍵字是主要用來(lái)實(shí)現(xiàn)線程、異步、并行的功能
           // TODO 與 Web Workers 的區(qū)別
           // TODO 與 Service Workers 的區(qū)別
           // https://gobyexample.com/goroutines
           go log("mouseEvent", "x", mouseX, "y", mouseY)
          
           // 下一個(gè)關(guān)鍵點(diǎn)
           if isLaserCaught(mouseX, mouseY, gs.laserX, gs.laserY) {
            go playSound()
           }
          }
          
          // 不要以為我用了什么黑魔法,這里直接使用了 HTML5 的 API
          // https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement#Basic_usage
          func playSound() {
           beep.Call("play")
           window.Get("navigator").Call("vibrate", 300)
          }
          
          // 這里主要用了 JS 的解構(gòu)賦值語(yǔ)法
          // 這里的 `...interface{}` 有點(diǎn)像 TS 的 `any` 語(yǔ)法
          // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters#Description
          func log(args ...interface{}) {
           window.Get("console").Call("log", args...)
          }
          

          讓游戲一直跑下去!

          該代碼創(chuàng)建一個(gè)非緩沖通道,并嘗試從該通道接收數(shù)據(jù)。因?yàn)闆]有人向它發(fā)送任何東西,它本質(zhì)上是一個(gè)永久的阻塞操作,允許我們永遠(yuǎn)運(yùn)行我們的程序。

          func main() {
           // https://stackoverflow.com/a/47262117
           // 創(chuàng)建空通道
           runGameForever := make(chan bool)
          
           setup()
          
           // 嘗試從空通道接收
           // 由于沒有人向它發(fā)送任何數(shù)據(jù),它本質(zhì)上是一個(gè)永久阻塞操作
            // 我們有一個(gè) daeomon / service / background 程序
            // 在 WASM 里,我們的游戲會(huì)一直運(yùn)行  
           <-runGameForever
          }
          

          更新游戲狀態(tài)并移動(dòng)小紅點(diǎn)

          這里沒有狀態(tài)管理,只有一個(gè)簡(jiǎn)單的聲明類型的結(jié)構(gòu)體,它不允許在內(nèi)部傳遞任何不正確的值。

          import (
           "math"
          )
          
          type gameState struct{ laserX, laserY, directionX, directionY, laserSize float64 }
          
          var (
           // gs 處于最高范圍,小于這個(gè)范圍小紅點(diǎn)   都能都能訪問(wèn)
           gs = gameState{laserSize: 35, directionX: 3.7, directionY: -3.7, laserX: 40, laserY: 40}
          )
          
          func updateGame() {
           // 邊界判斷
           if gs.laserX+gs.directionX > windowSize.w-gs.laserSize || gs.laserX+gs.directionX < gs.laserSize {
            gs.directionX = -gs.directionX
           }
           if gs.laserY+gs.directionY > windowSize.h-gs.laserSize || gs.laserY+gs.directionY < gs.laserSize {
            gs.directionY = -gs.directionY
           }
          
           // 移動(dòng)小紅點(diǎn)  
           gs.laserX += gs.directionX
           gs.laserY += gs.directionY
          r/> // 清除畫布
           laserCtx.Call("clearRect", 0, 0, windowSize.w, windowSize.h)
          r/> //畫一個(gè)小紅點(diǎn)  
           laserCtx.Call("beginPath")
           laserCtx.Call("arc", gs.laserX, gs.laserY, gs.laserSize, 0, math.Pi*2, false)
           laserCtx.Call("fill")
           laserCtx.Call("closePath")
          }r/>
          // 判斷點(diǎn)擊的點(diǎn)是不是在小紅點(diǎn)   內(nèi)部
          func isLaserCaught(mouseX, mouseY, laserX, laserY float64) bool {r/> // 直接這樣返回是不行的r/> // return laserCtx.Call("isPointInPath", mouseX, mouseY).Bool()
          > 
           // 所以這里我通過(guò)勾股定理   來(lái)實(shí)現(xiàn)r/> // 同時(shí)我給 laserSize 屬性的值加上 15,讓貓爪更容易點(diǎn)擊   
           return (math.Pow(mouseX-laserX, 2) + math.Pow(mouseY-laserY, 2)) < math.Pow(gs.laserSize+15, 2)
          }
          

          總結(jié)

          事實(shí)上,WASM 仍然被認(rèn)為是一個(gè) [MVP](https://hacks.mozilla.org/2018/10/webassembly -post- MVP -future/) (MAP),你可以不用編寫一行 JS,就能創(chuàng)建一個(gè)像這樣的游戲。驚不驚訝!CanIUse 上 WASM 的支持已經(jīng)是一片綠色了,沒有人可以阻止你去創(chuàng)建基于 WASM 的網(wǎng)站和應(yīng)用。

          你可以組合所有你想要的語(yǔ)言,像是把 JS 轉(zhuǎn)成 WASM。最后,它們都將編譯成 WASM 字節(jié)碼。如果你需要在他們之間分享任何東西,也沒問(wèn)題,因?yàn)樗鼈兛梢怨蚕碓純?nèi)存。

          我擔(dān)心的是,在最近的新聞中,我們關(guān)注到 微軟正在開發(fā) Chromium 瀏覽器 還有 Firefox市場(chǎng)份額低于9%。這使谷歌在 WASM 上有了致命的切換能力。如果他們不愿意配合,大眾可能永遠(yuǎn)不會(huì)知道有這個(gè)特性。

          現(xiàn)在都有誰(shuí)在用 WASM?

          你必須得承認(rèn),我的項(xiàng)目已經(jīng)在用了。這個(gè)項(xiàng)目?jī)H僅是畫了一個(gè)全屏的畫布,這里有一些更高級(jí)的例子,它們關(guān)注于語(yǔ)義 Web awesome-wasm#web-frameworks-libraries

          同時(shí),也有相當(dāng)多的項(xiàng)目已經(jīng)上了 WASM 的車了。我對(duì) Spotify、Twitch 和 FigmaEWASM 更感興趣。

          Web3 時(shí)代的 WASM

          現(xiàn)在,如果你想在手機(jī)上使用以太坊錢包(Ethereum wallet),你必須從應(yīng)用商店下載一個(gè)類似于 http://Status.im 的移動(dòng)端錢包 App,并且信任所有商家。

          如果有一個(gè)先進(jìn)的 Web App,可以運(yùn)行 geth (Go Ethereum 客戶端),并且能在 WebRTC 上光速同步,這會(huì)怎么樣?它可以使用 Service Worker 來(lái)更新它的 WASM 代碼并在后臺(tái)運(yùn)行,可以托管在 IPFS/Dat 上。

          一些有用的關(guān)于 WASM 的文章、資源還有學(xué)習(xí)資料

          • WebAssembly is more than the web
          • WebAssembly and Go: A look at the future 還有 HN comments
          • Mozilla HacksHacker News 發(fā)布的文章
          • WebAssembly architecture for Goawesome-wasm , awesome-wasm-langs , gowasm-experiments , WasmWeekly , WasmRocks , SPA with C++ , better DOM bindings for Go

          感謝 twifkak 在 Android Chrome 上對(duì) Go 的優(yōu)化!

          原標(biāo)題:The world’s easiest introduction to WebAssembly
          原文鏈接:The world’s easiest introduction to WebAssembly - freeCodeCamp.org - Medium
          作者:Martin Olsansky (olso)


          主站蜘蛛池模板: 国产福利电影一区二区三区久久久久成人精品综合 | 国产成人精品一区二区三区| 日韩有码一区二区| 中文字幕乱码一区久久麻豆樱花| 一区二区三区四区在线播放 | 国产午夜精品一区二区三区小说 | 性色AV一区二区三区无码| 中文字幕精品无码一区二区| 在线播放国产一区二区三区 | 毛片一区二区三区无码| 成人无码精品一区二区三区| 精品视频一区二区三区四区五区| 学生妹亚洲一区二区| 在线精品亚洲一区二区三区| 国产激情精品一区二区三区| 九九无码人妻一区二区三区| 91视频国产一区| 国产精品高清一区二区人妖| 麻豆国产在线不卡一区二区 | 免费高清av一区二区三区| 亚洲一区二区三区久久久久| 日韩av无码一区二区三区| 夜夜嗨AV一区二区三区| 精品国产一区二区22| 本免费AV无码专区一区| 亚洲AV永久无码精品一区二区国产 | 国产午夜精品一区二区三区极品 | 国产日韩AV免费无码一区二区 | 一区二区在线免费观看| 91久久精品国产免费一区| 中文字幕精品一区二区2021年| 国产自产对白一区| 精品国产一区二区三区久| 天天看高清无码一区二区三区| 成人国产精品一区二区网站| 又硬又粗又大一区二区三区视频| 国产精品熟女一区二区| 国产怡春院无码一区二区| 波多野结衣一区在线| 中文字幕在线观看一区二区| 亚洲一区二区三区首页|