Skip to main content

22 posts tagged with "The Key of Huanche"

View All Tags

· 12 min read
Wei Ji

前情提要

進度

先講結論,今天 (2024-08-31) 我終於把這幾個要素組裝在一起了:

  • Three.js
  • 後端渲染/無頭 (Headless) 渲染
  • ECS (Entity Component System)
  • 開發用瀏覽窗口

實作:https://github.com/FlySkyPie/polis-node/commit/aca8f24012848de30bef2b17cd07c1f2b32fc53b

技術選擇與技術堆疊的困難

3D x Javascript

不論是在工作上還是 side project,我其實經歷不少機會去使用不熟悉的語言,因此我意識到在原型開發 (Prototyping) 這一目標上,熟悉語言的經驗有時候能夠彌補語言特性帶來的缺陷,舉例來說:使用熟悉的語言因此知道某些需求需要使用特定的實作或是對於某些需求基於語言限制必須要使用某種 by pass;比起使用不熟悉的語言而在未知中摸索甚至踩到語言特性的坑。時間並沒有使用在開發上反而花在學習與試錯。

用 Javascript 來寫 3D 的東西聽起來很反直覺,但是我手上有的卡牌就是:

  • 接近 4 年的前端 (Javascript/Typescript) 開發經驗
  • 工作或業餘經手過超過 5 個以上的 Three.js 專案

比起重新學習另外一種技術棧 (Tech Stack),使用熟悉的工具不只可以加速開發,更可以在過程中累積的經驗值回過來貢獻在工作上使用的技能。

不過這是基於我的手牌採取的遊戲策略,並不見得適合其他人。

後端 X 3D (Three.js)

很明顯的,WebGL 是為了網頁瀏覽器而生的東西,而 Three.js 更是建立在此之上的抽象層,本身並不支援在 Node.js 這樣的後端環境執行。舉例來說,在 Web API 的設計之中 WebGLRenderingContext 是和 <canvas> 元素榜定的,但是後端的環境中根本沒有 DOM 實做 <canvas>

另外一個問題是後端環境是無頭 (headless) 的,在缺乏視覺反饋的情況下,很難對程式進行 debug,因此一個可以「窺見」程式內 3D 渲染結果的手段是十分必要的。

開發用瀏覽窗口

「如何窺見 3D 的後端程式」這件事情產生另外一個問題,就是如何實現快速(低延遲)的向外串流?

為了避免開發與維護另外一個讀取並顯示串流的 Client 端,基於 Web 的方案對我而言是相對合理的。於是就得出了「使用 WebRTC 來串流用於 debug 的畫面」這個結論。

ECS x Three.js

ECS 是我計畫用來管理複雜遊戲環境的架構,不過當它需要跟其他函式庫組合使用就會產生一些問提,諸如:

  • Three.js
  • Rapier.js

原因在於大部分的 Javascript ECS 實現把重心放在「密集的資料」這個 ECS 特性,因此這些函式庫只能用來儲存諸如浮點數、整數之類的基本資料型別,並不能方便的和其他基於 OOP 邏輯的函式庫組合使用,使用那些 ECS 函式庫的邏輯再現其他函式庫的功能顯得不合乎成本。

回顧

串流

串流算是第一個急著解決的問題,畢竟就算我能在後端渲染的東西,只要不能把畫面丟出來我就不能 debug。

在考慮了 FLV (Flash Video), RTMP (Real-Time Messaging Protocol), HLS (HTTP Live Streaming) 和 MPEG-DASH (Dynamic Adaptive Streaming over HTTP) 等方案之後,我選擇使用 WebRTC。

考慮延遲以及瀏覽器支援兩個要素下,WebRTC 都是一個比較合理的方案。

https://www.wowza.com/wp-content/uploads/latency-continuum-2021-with-protocols-700x300-1.png

瀏覽器上的 Javascript 並沒有能力建立 TCP Socket,大部分網路連線需求都是由 HTTP 完成,即便是 WebSocket 實際上也是建立在 HTTP 連線之上。而 WebRTC 則是會由瀏覽器實做,實際上會依情況使用 TCP 或 UDP 連線。在這先天特性之下,WebRTC 能夠比其他基於 HTTP 的手段來得低延遲。

去年 (2023) 在我短暫的學習串流相關技術之後,

我使用 WebRTC 寫了一個小範例。

後端渲染

網路上關於如何在後端 (Node.js) 渲染 Three.js 的方案並沒有很多,其中還不乏用重新實做 WebGL 兼容界面然後用 CPU 算圖的方法。甚至一度考慮使用 Deno,不過最後也做罷了,這些細節在我前一篇文章都有紀錄。

在 2023-08-16 的時候我試著用 Electron 實做伺服器

不過把資訊從 Browser 在 Node.js 之間轉發是一件很麻煩的事情,大大的提高了程式的複雜度。引此在完成測試之後就沒有什麼進展了。

之後 (~2024-08) 有試著嘗試了這裡提供的範例:

SSR - Three.js Tutorials

它使用 headless-gl 實現後端渲染,不過它的前置作業相對複雜,除了要用 jsdom 補足 DOM 的實作之外,光是安裝套件就要處理 node-gyp 帶來的一些問題,因此沒有完全說服我使用它。

我在網路上瞎逛的時候發現了 node-3d/3d-core-raub,它提供另外一種簡單粗暴的解法:把 GLFW binding 到 Node.js 上去再加上 Three.js 的兼容實作。能夠在 Linux 開啟一個視窗顯示渲染結果更是大大的減少專案初期的阻礙。

跟線有的 WebRTC 實作整合之後,確發現畫面不太正常,起初我以為是延遲的問題,於是想在裡面 Three.js 裡面放個時鐘,沒想到卻是另外一個坑。

Three.js x Text

在 Three.js 裡面渲染文字並不是一個被非常重視的功能,因為瀏覽器上已經有 HTML + CSS 這樣一個方便顯示文字的方法了。

在經過一番折騰之後,我得到了一個像這樣的問題清單:

  • three-bmfont-text
    • 沒有型別定義
    • THREE is not defined
  • troika-three-text
    • 仰賴 XMLHttpRequestwindow.document
  • TextGeometry
    • 花費 60ms 建立新的 Geometry
  • three-spritetext
    • 仰賴 ctx.measureText
    • TypeError: ctx.measureText is not a function
  • three-text2d
    • TypeError: Class constructor Object3D cannot be invoked without 'new'
    • build target 的語法過於老舊造成
    • 仰賴 document
  • three-mesh-ui
    • ctx.canvas.width = 1;
    • TypeError: Cannot set properties of undefined (setting 'width')

在電腦圖學中(特別是 3D 遊戲領域),有一門叫做 Texture Atlas 的技術,就是把文字先烘培成貼圖與幾何資訊,在程式中再把這兩者組合起來在顯示卡的 context 中顯示文字。

這個過程需要仰賴額外的工具(例如:Angelcode’s bmfont),或是由遊戲引擎內建的工具承擔(例如:Unity3D, Godot),因此在 Three.js 開發圈更常用的手段是在 runtime 時產生這些貼圖,而過程仰賴瀏覽器的渲染引擎對文字進行運算及排版,而使用這種手段的函式庫會無法在後端 (Node.js) 環境中使用。

這麼一看 three-bmfont-text 似乎是最有潛力的選擇,最後我透過:

解決了問題並且完成一個數位時鐘的實作:

ECS

在正式開始之前,我選擇了一個小題目來練手:

其實原本想再練一個題目的,

只是從 mohsenheydari/three-fps fork 之後用 Typescript 重構、更新 Ammo.js 之後,發現它的實作邏輯有點複雜(物件互相呼叫的時序),就暫時放棄嘗試把它轉成 ECS 了。

Monorepo

試著使用 pnpm 提供的 monorepo 功能來整合前後端的專案,大大的提高了開發體驗。沒有使用 nx 的原因是它看起來配置較為複雜,學習成本是一個阻礙。

下一步

Logger 系統的改善

在目前的程式碼之中有一些我為了 debug WebRTC 保留下來的 log 點,我想把它們移到 winstonpino 之類的專門函式庫去。

畢竟在後端環境中,未來還要面對更複雜的遊戲實作,這樣的機制來紀錄並整理 log 是非常必要的。

自由的觀戰者

目前觀戰者並沒有移動能力,需要透過 requestPointerLock 之類的手段錨定滑鼠來提供 FPV (First Person View) 的瀏覽體驗。

整合 Voxel 實作

既然前置作業已經完成,我就可以把我之前 Fork 的 Voxel 實作整合過來了:

· 10 min read
Wei Ji
info

稍微盤點一下我所知道的 Minecraft

The Minecraft 時期 (2012)

Minecraft 這款遊戲本身我已經很久沒有點開遊玩它了。實際上呢,我最熟悉的版本是 Beta 1.8,釀造系統(包含)以後的所有更新我其實都不是那麼的熟悉。比較熱衷玩 Minecraft 的日子大概是這樣的:

  • Yami Odymel 開的伺服器活動
  • 因為耍白痴被 BAN 了
  • 自己試著開了跑酷伺服器
  • 自己試著開了 Solar Apocalypse 伺服器

一種人類玩家與 AI 對抗的構想 (2013)

當時 Minecraft 伺服器的架設者為了提高遊戲的趣味程度,通常透過插件加入一些常見的功能:

  • 經濟系統:允許玩家持有遊戲內的虛擬貨幣。
  • RPG: 賦予玩家等級制的成長系統。
  • 更強的怪物: 提高怪物的血量、速度、集體行動;甚至賦予特殊技能,創造類似小 BOSS 一般的存在。

但是怪物不論在數值上有多強,只要有固定的行動模式就能被玩家破解。Minecraft 內最令人敬畏的敵人是誰?人類玩家。

透過創造與人類玩家別無二致的 NPC 敵人

這樣的想法在我腦海油然而生。

我試著學習撰寫插件來生成結構化的建築物,2013-04-06 的截圖,我與插件的生成物(背景):

info

具體的實做方式就是透過插件的 hook 取得指令事件、讀取玩家座標、使用預先儲存的三維矩陣把周遭的方塊填入遊戲世界中。

在我的構想中,人類玩家是破壞者,這些「敵對 NPC」是創造者,它們以蟲群意識行動,並不斷的建造巨構建築吞噬地圖,就像 BLAME! 中的建造者一樣。

SyntheticEmpire (2018)

五年後我重拾這個被我放置的概念,這次我從「實做 NPC」開始。經過對函式庫的調查,我理解到 Minecraft 處理「玩家實體」的方式是放置一個虛假的幻影,然後透過一些方式來操作這些幻影,使得「玩家實體」與其他遊戲中的「生物實體」截然不同。

於是當時我選中了殭屍作為 NPC 的基礎類別,因為它是少數人形的怪物,而且服裝最接近 Steve。

關於實體的操作這種類型的函式庫屬於 NMS (net.minecraft.serve) 的部份,並不是插件的函式庫特別關照的區塊,因此仍然殘留大量反編譯留下的非語意化變數名稱,當時花了好一番功夫去讀懂這些函數與變數的功能。最後成功附加一些玩家的特性到特製的殭屍實體去了。

遺憾的是當時撰寫的專案並沒有保留程式碼,只有一個輸出的 .jar 檔案

(點圖開啟影片)

那些跟 Minecraft 有關的 YouTuber

info

2b2t: 一個無政府主義 (Anarchism) 伺服器。有別於一般的 Minecraft 伺服器會透過規則、區分世界、權限插件等機制約束玩家並限制玩家破壞,Anarchy 伺服器沒有規則:開外掛是基本配件、終界水晶互炸很常見、把 bug 武器化(舉例來說在地圖物件上堆疊大量的箭頭來耗盡玩家的顯示卡資源)來攻擊敵對陣營也是再正常不過的事情。

info

Far Lands: Minecraft 地形生產演算法在座標的浮點數逼近特定數值時的浮點數誤差會使算法受到破壞性的影響而不再能生成正常的地形。

那些跟 Minecraft 有關的 AI 專案

  • Baritone
    • Minecraft 的機器人外掛
  • MineDojo/Voyager
    • 一個訓練 GPT (OpenAI) 玩 Minecraft 的專案
  • Mineflayer
    • 跟 AI 沒有直接關係,它是 Voyager 專案中 AI 操作 Minecraft 的界面
    • 它是以 Javascript 實做的無頭 (headless) Minecraft 客戶端,換句話說你可以透過撰寫程式碼來連線到 Minecraft 伺服器「遊玩」,而伺服器會以為你是用 Minecraft 連線過來的。
  • minerkasbs/minerl
    • Minecraft 鑽石挑戰 (MineRL Competition) 的套件包
    • 關於鑽石挑戰我翻譯過兩篇 2021 年度的比賽規則:
  • Malmo
    • MineRL 的前身,由微軟釋出並主辦了幾場沒有創意的比賽,然後就沒有然後了。
    • 我有寫一篇介紹 Project Malmo 的文章。

The Master Pieces

以遊戲軟體工程的角度來說 Minecraft 是個巨作,它身上有很多值得學習的東西,這裡有個影片點出其中幾個比較知名的要點(點圖開啟影片):

  • 用 Greedy Meshing 減少三角面
  • 用 Perlin Noise 實現程序化生成地形
  • 用 Perlin Worm 生成洞穴
  • 用參數表定義生態系
  • 用基於 Voxel 的算法處理光源

Minecraft 也有一些軟體設計上的缺陷值得借鏡,諸如:

  • 原生的伺服器程式基本上是單執行緒的
  • OOP 的程式架構造成錯綜複雜的 Side Effect 造成難以除錯 (Debugging)

「真正的 Minecraft」

Beta 1.7 是一個特別的 Minecraft 版本,Minecraft 圈子有一群懷舊的玩家堅持這是最後一個真正的 Minecraft。原因在於 Beta 1.8 的更新中加入了諸如飢餓系統與奔跑等遊戲機制,大幅度改變遊戲體驗。

那些跟 Minecraft 有關的野生專案

  • ClassiCube
    • 以 C 語言復刻 Minecraft Classic
  • Eaglercraft
    • 盜版 Minecraft 的網頁移植版
    • 使用 TeaVM 在網頁上執行 Java 生態的程式

那些「啟發自」Minecraft 的開源專案

  • fogleman/Craft
    • 總共只有大約 3k 行 C 語言實做「Voxel 遊戲的基本功能」。
  • satoshinm/NetCraft
    • fogleman/Craft 的 fork
    • 透過 emscripten 移植到網頁上
  • Minetest
    • C++ 實做的「Voxel 遊戲引擎」
    • 支援 Lua 插件
    • 可以透過插件來逼近 Minecraft 的遊戲內容
    • 市面上流通不少「山寨版 Minecraft」都是這專案的 fork
  • Voxel.js
    • 一個作者從 WebGL 開始刻的網頁 Voxel 專案。

那些跟 Minecraft 有關的開源專案

特修斯的 Minecraft

當使用完全開源的 Client 端實做,例如:Mineflayer; 搭配上完全開源的 Server 端實做,例如:Cuberite; Minecraft 就昇華成純粹的協定,整個過程不涉及原本的 Minecraft 了。

· One min read
Wei Ji

這個問題一直佔用我的大腦資源,然而目前我正在忙於其他 Side Project,無心處理這個問題,所以我想寫一篇文章先把這堆東西倒出去,讓腦根子清靜一點,日後再回頭來處理。

這是 Polis 預計使用的架構:

因為 Canvas 提供一個 captureStream() 方法允許把 Canvas 的畫面轉換成 MediaStream 物件,而 MediaStream 又能夠抽出 MediaStreamTrack 透過 WebRTC 的 API 串流出去。

但是最近我發現一個問題,就是 WebGL (Three.js) 存在一些限制:

一個 WebGLRenderingContext 被綁定到一個 Canvas

而 GL context 之間是無法互通的,因此複數個 Canvas 無法共用同一組 GPU Context。

· 7 min read
Wei Ji

前情提要

  • Tellus環驅之鑰下的子專案,專案目的為「建立儲存 Voxel 世界資料的系統」。
  • 2021 年的時候,我受到 MBTiles 的啟發寫了一個基於 SQLite 檔案的 Voxel 儲存格式1

背景

最近 (2024 二月)買了 TerraTech ,玩著玩著、在遊戲內轉著方塊,突然靈光一閃,想要處理旋轉方向的資料格式問題。

原本(2021 年寫的那份格式)我打算採取儲存 Voxel 的格式是用任意無損壓縮的點陣圖檔來儲存,舉例來說一個 16×16×16 Chunk 的資料會長得像這樣:

也就是一個 Pixel 等於一個 Voxel,但是這種方式單純是把一個 Voxel 當成一個像素點紀錄,無法支援像 Minecraft 那樣方塊有方向性的情況。

儲存的哲學

我無意針對儲存 Voxel 資料去創造一個二進制文件的定義,而是使用一般的點陣圖檔,其原因源自於 Unix 哲學2

Store data in flat text files.

二維點陣圖檔是一種常見且十分普及的資料格式,不論是透過圖形化界面的軟體還是函式庫,對其進行讀寫都是一件簡單的事情,我認為在這個意義上它與「純文字檔案」無異,而且具有更好的資料密度(壓縮率)。

在這個基礎上,我打算拿 8 個 Pixels 來描述一個 Block,令其中夾帶「方塊方向」的資訊,就結果而言可能過於浪費記憶體,不過作為一個(預計)遊戲的開發者,資料儲存的效率不是我當下最在意的事情,況且不少點陣圖片的檔案格式就有各自的無損壓縮演算法了,這個想法的基礎同樣來自 Unix 哲學2

Make each program do one thing well.

單位晶胞

單位晶胞 (Unit cell) 是固態物理學使用的一種概念,與之相對的是另外一個名為晶格 (Crystal Lattice) 的概念:

https://www.expii.com/t/crystal-lattice-structure-formation-7999

晶格是討論的結晶材料時候的最小單位,因為我們可以從它的結構與方向去探討這些參數如何影響宏觀特性,但是當我們把它切得更小會得到一個可以透過旋轉而構成晶格的最小不重複單元,那就是單位晶胞。

於是我把原本設計的資料格式再往下切割,用 8 個 Voxels 去構成一個 Block:

透過解析 8 個 Pixels 的排列模式就能定義出一個 Block 不同的旋轉方向。

次生 Voxel

接下來我會明確的區分這三個用語:

  • Pixel
    • 二維空間的色點。
  • Voxel
    • 三圍空間的色點,沒有方向性。
  • Block
    • 由 8 個 Voxel 構成的方塊,有方向性。

對於一個 3 Bytes 的像素空間而言(RGB 三個頻道個佔 1 個 Byte),能夠表達的 Voxel 種類其實足夠可觀了,即便拿 8 個排列組合來描述一種 Block 也綽綽有餘:

28×28×288=256×256×2568=2,097,152\frac{2^8 \times 2^8 \times 2^8 }{8} = \frac{256 \times 256 \times 256}{8} = 2,097,152

假定一個 Block 的基礎 Voxel 值為 #000000,為了描述其方向而衍生的 7 個 Voxel 則為次生 Voxel:

  • #000001
  • #000002
  • #000003
  • #000004
  • #000005
  • #000006
  • #000007

旋轉模式

不同旋轉模式所需要的次生 Voxel 數量並不相同,目前我定義了幾種模式:

  • 固體 (Solid)
  • Y 軸對稱 (XZ-Reflection / Y-Symmetric)
  • Y 軸旋轉
  • Y 軸旋轉對稱 (Y-Symmetric-Rotation)

固體 (Solid)

只需要 1 個次生 Voxel,只有 1 種排列組合(方向性),類似 Minecraft 的石頭 (Stone)

Y 軸對稱 (XZ-Reflection / Y-Symmetric)

我不確定稱它為「 Y 軸對稱」還是「XZ 平面對稱」比較精確。需要 2 個次生 Voxel,有 2 種排列組合(上與下),類似 Minecraft 的半磚 (Slab)

Y 軸旋轉

需要 4 個次生 Voxel,有 4 種排列組合(東南西北),類似 Minecraft 的儲物箱 (Chest)

Y 軸旋轉對稱 (Y-Symmetric-Rotation)

需要 8 個次生 Voxel,有 16 種排列組合(東南西北×上下),類似 Minecraft 的階梯角 (Corner Stairs)

Demo

為了呈現這個概念,我寫了一個編輯器,因為只是演示作用,不保證可靠性:

另外,在這個 Demo 中我試著嘗試了幾個之前沒用過得東西:

  • Tailwind
    • 因為有幾個同事很喜歡用,所以我想試試看這東西建構 Prototype 的速度如何。
  • PrimeReact
    • MUI 用習慣了,試試其他 Component 庫建構 Prototype 的速度如何。
  • React Hook Form
    • 之前寫了一份 React 問卷,發現我沒有用過表單管理套件,趁著這個專案摸摸看。
  • Dexie.js (React Hook)
    • 因為前一陣子研究 TiddlyWiki 的時候得知 PouchDB 這類東西,對前端儲存方案有些興趣就趁著這個專案摸摸看了。
  • Presite
    • 久違的想試試看預渲染 (pre-render),不過 react-snap 貌似壞掉了(不支援 React 18),就找了個類似的替代品。

Footnotes

  1. Voxel Terrain Tiles Specification. (FlyPie). Retrieved 2024-03-24, from https://github.com/FlySkyPie/voxtt-spec.

  2. Mike Gancarz: The UNIX Philosophy. (n.d.). Retrieved 2024-03-24, from https://en.wikipedia.org/wiki/Unix_philosophy#Mike_Gancarz:_The_UNIX_Philosophy 2

· 15 min read
Wei Ji

前情提要

關於我的 Side Project 「環驅之鑰」其實是一個很複雜、很難三言兩語說清楚東西,試著用一些像是條目的東西來解釋它,作為我現場跟其他人解釋的參考資料算是蠻方便的輔助,但是讓人自行閱讀一目了然似乎還是稍嫌不足。

於是我決定試試用其他方式向一個可能沒什麼背景知識的解釋我想做的東西:先把其中一個主線用輕小說一般標題展開,再根據其中的內容慢慢解釋。

ECS (Entity Component System)

關於我想用 ECS 實作 Minecraft 以分散式架構運行並在其中用增強式學習訓練完全視覺化之遞迴布林類神經網路以產出二維條碼人造語言那檔事

ECS 是種軟體架構,鬥陣特攻 (Overwatch) 的團隊在遊戲上使用並且在 GDC 2017 中介紹而遊戲開發圈廣為人知,Unity3D 更在 2018 將 ECS 框架引入遊戲引擎中。它的核心概念是不使用原本流行的物件導向 (OOP, Object-oriented programming) 的軟體抽象化方式轉而使用資料導向 (Data Oriented)。

OOP 的邏輯是:把程式邏輯抽象化成一個一個的「物件」,然後透過「方法」去操作物件,隨著專案越來越複雜,這些物件通常會一層包一層。而 ECS 的邏輯是:把資料跟運算邏輯分離,Component 只有資料、Entity 是 Component 的索引、System 則不儲存狀態專注於運算邏輯。

關於 ECS 的介紹我就點到這裡為止,至於我想在專案中使用這種架構的原因源自 2018 年我試著寫一個 Minecraft 插件的時候經歷了非常痛苦的開發體驗,以我當時想修改的殭屍實體為例:

Minecraft 裡充滿了多層繼承 (Multilevel Inheritance),這使得釐清物件之間如何交互作用的認知負荷變得非常高,雪上加霜的是 Minecraft 插件源自逆向工程的產物,並不是所有變數和方法都是人類可讀的。

雖然當時體驗的困難並不是所有都是軟體架構造成的,然而後來我才知道這種開發風格在遊戲開發圈也算是很有爭議的主題之一,在知道 Minecraft 的程式碼有多難除錯,看著它那滿坑滿谷的 Bug 清單,當中不少還直接被標記「這個不會被修復」似乎也不奇怪了。

作為一個想開發類似於 Minecraft 這等高度複雜的沙盒開放世界遊戲的後進者,自然會想避免步上 Minecraft 的後塵,而 ECS 便是我目前優先考慮的架構。

分散式架構

關於我想用 ECS 實作 Minecraft 以分散式架構運行並在其中用增強式學習訓練完全視覺化之遞迴布林類神經網路以產出二維條碼人造語言那檔事

Minecraft 大部分的運算是在同一個執行緒 (Thread) 上完成的,這在 Minecraft 的圈子也算是很有名的特性,因為你不能單純拿著多核心處理器去架遊戲伺服器就很神奇的可以容納更多的玩家。

這限制源自於它的遊戲運算不能簡單的被化為獨立的任務並分配成平行運算,作為一個寫網站的工程師,我並沒有足夠的知識與經驗去設計一個多執行緒的架構或系統,但是作為一個 Web 工程師我知道一種叫做微服務 (Microservices) 的東西,那麼我何不把遊戲拆成一組 Microservices 叢集,使其成為一個方便水平擴充的系統。

增強式學習 (RL, Reinforcement learning)

關於我想用 ECS 實作 Minecraft 以分散式架構運行並在其中用增強式學習訓練完全視覺化之遞迴布林類神經網路以產出二維條碼人造語言那檔事

https://ictinstitute.nl/ai-machine-learning-and-neural-networks-explained/

增強式學習是機器學習 (Machine Learning) 領域的一個子集(分支),相較於其他著重資料分析、統計之類的機器學習主題,RL 著重於玩家 (Agent) 與環境 (Environment) 互動 (Action/Observe),透過獎勵 (Reward) 來最佳化策略 (Policy)。RL 可以說是「遊戲感」最重的機器學習分支。

遞迴布林類神經網路 (RBNN, Recurrent Boolean Neural Network)

關於我想用 ECS 實作 Minecraft 以分散式架構運行並在其中用增強式學習訓練完全視覺化之遞迴布林類神經網路以產出二維條碼人造語言那檔事

人工智慧 (AI, Artificial Intelligence)與深度學習 (Deep Learning) 算是近幾年被用到爛大街的詞彙了,起因來自於一個被稱作類神經網路 (Neural Network) 的不新鮮的數學模型在晶片性能提昇以及算法改善的現代而終於獲得實用化,又因為 AlphaGo 在 2016 擊敗人類棋手等事件而形成話題才終於進入大眾的視野。

而布林類神經網路 (BNN)1 則是參考了類神經網路的設計,但是不使用實數而是使用布林代數的一種算法,這個選擇並不是基於性能或實用性考量,老實說因為不是使用實數體系的代數,因此不能用微積分作弊(倒傳遞算法 Backpropagation),也不能直接以類神經模型主流的方式用顯示卡加速運算。

而是一種基於哲學上的選擇,私以為「因為類神經網路是對人類神經元的數學建模,因此類神經網路是通往『通用型人工智慧』的道路」是一種身為人類的傲慢。如果基於二進制的電腦能夠產生「通用智慧」,那它應該回歸二進制運算的本質,即布林代數本身,而不是透過浮點數 (IEEE 754) 運算去模仿神經元,這種模仿終究只能得到偽物。

根據奧卡姆剃刀 (Occam's razor),簡單的解釋通常比較接近真理,因此我想嘗試這條用布林代數去探索的道路。

RBNN 單純是參考了遞迴類神經網路 (RNN) 加入了遞迴的要素使該演算法具備記憶性。

完全視覺化

關於我想用 ECS 實作 Minecraft 以分散式架構運行並在其中用增強式學習訓練完全視覺化之遞迴布林類神經網路以產出二維條碼人造語言那檔事

拿 Minecraft 來做機器學習訓練 AI 的專案不少,不過它們並不是沈浸式 (Immersive Experience) 的。在 Minecraft 之中除了第一人稱視角 (PFV, First-person view) 之外還有大量基於滑鼠操作的 UX。

對增強式學習而言,玩家與環境的互動來自操作 (Action) 與觀察 (Observe),對人類而言可以觀察到 Minecraft 的兩個資訊:第一人稱 3D 渲染與 HUD/UI;而操作同樣有兩個:第一人稱控制器與滑鼠。

但是對大多數的 Minecraft AI 專案而言,AI 觀察與操作都與人類大相逕庭。以 Baritone (知名 Minecraft 自動外掛)為例,程式是直接讀取地圖上的方塊資料來規劃路徑,操作則是直接丟封包給伺服器,所以可以作到諸如隔著牆壁打開箱子之類的事情。

另外一個機器學習專案 Voyager則是包了一層的 Minecraft Client,真正操作與觀察的程式是一個叫做 Mineflayer 的 Javascript 函式庫,與 Baritone 相同,直接讀取地圖資訊並呼叫函式來丟封包給伺服器完成操作。不同的是 Voyager 透過機器學習的 GPT-4 模型負責撰寫 Javascript 程式碼來控制玩家。

據我所知唯一使用視覺化資料的是一個名為 MineRL 的專案,它其實是一個 AI 競賽,目標就是用 AI 玩 Minecraft 挖到鑽石,但是 AI 只能跟玩家一樣觀察到一張 3D 渲染的第一人稱畫面,即便如此,它的操作依然不是沈浸式的2

它直接提供一個函式,當玩家附近有工作台並且身上有足夠的原料,就直接完成合成,以人類可以理解的體驗就像是:

你桌上放著一排按鈕,只要附近有工作台,按下對應的按鈕自動完成合成,過程中於需操作遊戲中的那個操作界面用滑鼠拖拉材料。

因為 MineRL 設計上就是為了挖到鑽石,因此只需要提供部份必要的界面 (Interface) 供玩家操作即可。

「完全視覺化」意味著在這個專案中,視覺資料是 AI 唯一能夠獲得環境資訊的方式,FPV 控制器是 AI 唯一能和環境互動的手段,因此,沒有血量或體力值的變數、沒有額外的裝備欄變數、沒有任何基於滑鼠操作的彈出視窗、沒有快捷的 function call。

「完全視覺化」意味著必須重新設計一套完全基於 FPV 控制器的遊戲體驗。

二維條碼人造語言

關於我想用 ECS 實作 Minecraft 以分散式架構運行並在其中用增強式學習訓練完全視覺化之遞迴布林類神經網路以產出二維條碼人造語言那檔事

其實我還期望從這個專案中獲得其他產物,不過「二維條碼人造語言」算是這個專案的主線之一。

如果你看過或是知道異星入境 (Arrival) 這部電影,要理解這個概念就會顯得很容易,沒有沒有也沒關係。

可能涉及「異星入境」的劇透,請斟酌點閱
劇中的外星人不具備基於聲音的「語言」,
它們透過操控懸浮的沙粒構成文字來傳達意思:
https://brand-new-life.org/b-n-l/learning-from-time/

我希望訓練 AI 在遊戲中表現出「部落」的特性,也就是複數個獨立的 AI 能夠溝通並且協調作業,在這個基礎上透過解析它們的溝通方式來實現「人類對某種數位智慧進行溝通」這件事。

一般玩家在 Minecraft 內使用文字頻道溝通,但是這有違前述的「完全視覺化」原則,因此我需要另外一種能夠讓玩家在遊戲內溝通的方式。在遊戲內重建聲學模型又過於複雜。

「基於視覺化的語言」這樣的概念便自然而然的被到這個專案之中,現實中其實也有這種東西:手語,而我所想像在 Minecraft 中的視覺化語言會像這個樣子:

後來想想,基於二進制的智能體,溝通用的語言是二維條碼好像也很合理?

Footnotes

  1. Boolean Neural Network. (R. Kohut, B. Steinbach). Retrieved 2023-10-08, from https://www.semanticscholar.org/paper/Boolean-Neural-Networks-Kohut-Steinbach/1c472945ab2970a709efe97f81d9a5e7bf37baae

  2. MineRL 0.3.0 documentation. (William H. Guss, Brandon Houghton). Retrieved 2023-10-08, from https://minerl.io/docs/environments/index.html#id20

· 12 min read
Wei Ji

稍微把 Polis 挖坑至今 (2023-08-05) 累積的嘗試做個紀錄。

基本概念

整個開放世界會被拆分成不同的區域,每個區域由獨立的節點 (Polis Node) 負責運算,節點跟節點之間會構成 P2P 網路,形成一個可以橫向擴展的架構,而 Client 端則會在由 Polis Node 構成的 P2P 網路所運算的開放世界中進行漫遊 (Roaming)。其實這種分散式的架構在 ITUGV 的時宜就令我十分著迷,不過那就是另外一段故事了,在這裡稍微提及只是為了讓讀者理解筆者對分散式架構抱有特別的偏好,並且這種構想早就存在於早期的其他專案之中。

POC (Proof Of Concept)

雖然 Polis 作為 Hakoniwa 的子專案,目的一樣是為了建造一個程序化生成 Voxel 開放世界,但是 Polis 將會把重點放在兩個目標上:

  • 建立 P2P 網路
  • 能夠透過 Client 程式連線

因此在 Polis 的第一個原型中,達到這些條件就算成功完成了概念驗證:

2023-05-13 ~ 2023-05-26

在這個階段我想用 Deno 作為實作 Polis Node 的手段,有幾個原因:

  1. Typescript 目前是我最熟練的語言。
  2. WebGPU 已經納入 Web API 中,而部份版本的 Deno 已經實作,因此我能使用 Typescript 解決 server side render 的需求,這麼部份是 Node.js 無法達成的。
  3. Deno 內建跨平台編譯 (Cross Compiling),因此寫一次程式我就能把節點佈署到除了 Linux 以外的 Windows 或是 macOS 上。

網路連接技術

在挖了坑並且對技術棧 (Technology Stack) 有了粗略的方向之後,首先我想先處理 Polis 中最關鍵的問題:

如何建立 Polis 網路 (P2P 網路)?

在 2020-03-22 的時候我就有使用 ZeroMQ 進行過傳輸相關的實驗,也是我在 The Key of Huanche 中一直考慮投入使用的函式庫。直到我因為 Polis 的分散式架構回過頭去翻閱它的官方文件的時候,才發現了一件事:

And this is the world we’re targeting with ZeroMQ. When we talk of “scale”, we don’t mean hundreds of computers, or even thousands. Think of clouds of tiny smart and perhaps self-replicating machines surrounding every person, filling every space, covering every wall, filling the cracks and eventually, becoming so much a part of us that we get them before birth and they follow us to death. 1

ZeroMQ 在官方文件 Chapter 8 - A Framework for Distributed Computing 中描述了他們那野心勃勃的願景,老實說這非常打動我。

然而調查下來,發現 Deno 在這方面並沒有多少函式庫可以使用、專案的活躍度也都很低:

資料傳輸載體

Protobuf (Protocol Buffers) 是另外一個我有意投入 The Key of Huanche 使用的 Technology Stack,第一次知道它的存在是工作上使用 Mapbox 的並因此接觸 Mapbox Vector Tile 的時候發現它所使用的資料格式。簡言之就是一種預先定義資料結構後,把資料壓成密度比較高的格式進行傳輸,到達目的地之後再用預先定義的資料結構進行解碼,比起使用 JSON 傳輸可以省下很多頻寬。

然而在 Deno 的生態戲中依然有類似的問題,能選用的函式庫很少、而且專案活躍路很低:

如何分辨序列化資料 Protobuf 的型別?

Polis Node 之間的通訊基本上是一個事件系統,而事件的種類千奇百怪,需要的資料結構當然也是五花八門,但是它們之間會使用同一個序列化的通道,接收端街收到一組序列化資料後要怎麼區分 Protobuf 的類型並使用不同的結構來解碼呢?

沒有辦法直接區分,但是可以透過幾個方法解決這個問題2

壓垮 Deno (對我的專案而言)的最後一根稻草

我其實很難找到支援 WebGPU 或是活躍的 WebGL 的 Javascript 函式庫進行 "denolize"。

WebGPU 在那個當下只有 0.04% 的覆蓋率,就算打開先行版 Chrome 的 WebGPU flag 也是充滿破圖,因此鮮少有函式庫有支援 WebGPU。

活躍的 Javascript 3D 繪圖函式庫,諸如:Three.js, Babylon.js,即便當下是基於 WebGL 實作,在 WebGPU 逐漸普及下應該也會逐步支援 WebGPU 才對,但是它們多是基於 Canvas 的繪圖方式,而 Deno 環境下的 WebGPU 實作並不包含 Canvas 相關方法。

加上 WebGPU 的 API 在 1.8 被加入 Deno 之後3,又在 1.32.0 中基於性能考量被移除了4

也就是「在 Deno 中使用 WebGPU」這件是的前景並不樂觀,我可能必須要面對「使用舊版的 Deno 和相對底層的 WebGPU 手刻整個應用」的問題。

Deno 的 Cross Compiling 的能力是真的很吸引我,但是在繪圖跟網路連線兩面不討好的情況下,加上 IDE 在寫 Deno 程式的時候開發體驗並不好,我決定放棄這條路。

2023-05-27 ~ 2023-06-11

在 2022-10-06 的時候我就已經嘗試過把網頁上渲染的 Canvas 畫面壓成 Webp 並透過 WebSocket 傳送給伺服器播放了5,但是當時只是寫了 POC 等級的程式,並不能很好的管理每次連線的 Session,因此良好的串流機制就成了我下一個研究目標。

串流方案

https://www.wowza.com/wp-content/uploads/latency-continuum-2021-with-protocols-700x300-1.png

花了一點時間 (2023-05-27~2023-05-28) 查資料了解有哪些可能的方案能夠實現我的需求:

  • Websocket
    • ✅ Web API 十分普及
    • ❌ 需額外處理影像的解碼與渲染,消費更多的運算與延遲
  • WebRTC
    • ✅ Web API 十分普及
    • ✅ 原生設計支援低延遲
    • ❌ 協定複雜
    • ❌ P2P 架構,雖然可能可以透過在伺服器實作底層來欺騙 client 端,但是需付出額外開發成本
  • WebTransport/QUIC
    • ✅ 支援 UDP,可降低延遲
    • ✅ 次世代 API
    • ❌ Web API 尚未普及
      • 瀏覽器支援覆蓋率低
      • Nodejs 僅實驗性支援 QUIC
      • Deno 尚不支援
  • HTML5 video tag
    • ✅ Web API 十分普及
    • ❌ 高延遲
  • Video.js
  • MPEG-DASH
    • ✅ 開放的標準
    • ❌ 有點複雜
  • FLV
    • ✅ 歷史性因素,仍有許多資源
    • ❌ 過時

Node-Media-Server

之後 (2023-06-04) 發現了 Node-Media-Server 這個專案,透過 RTMP (Real-Time Messaging Protocol) 向 Node-Media-Server 串流影像,接著能夠使用 Node-Media-Server-Admin 打開串流,而且能夠流暢的關閉與重新開啟串流,Session 的管理便是我在 POC 階段所缺失的,並且它能夠使用以 Javascript 撰寫的 code base 對外進行串流,那我只要有辦法用 Javascript 產生 RTMP 流,就能整合成符合我需求的程式。

於是我便試著透過用 Typescript 重構專案的方式理解它是如何實現串流推流的。

2023-06-11 ~ 2023-06-25

RTMP

接著我發現單靠程式碼無法理解它解析 RTMP 封包的邏輯,於是我便打開 RTMP 的規範文件並搭配一些教學文章試著理解它,不過大約只讀到 Header 的部份,我就意識到就算我能製造 RTMP 堆流,還是需要把影像做編碼,然而 Javascript 的生態系在做這件事情上有點貧弱,於是我把目光轉而投向 WebRTC。

至於 RTMP 的學習筆記我應該會發到另外一篇獨立的文章去,雖然因為我其實沒有把 Spec 完整看完,發個不完整的筆記感覺怪彆扭的。=w="

2023-06-25 ~ 2023-08-05

WebRTC

7 月份因為工作上的壓力比較大,加上充滿了親戚過世、Steam 夏季特賣、COSUP...等等事件,所以學習 WebRTC 的進度可以說是其極緩慢,不過終於在昨天 (2023-08-05) 理解如何使用這個 Web API 並把範例做出來了。

同樣的,關於 WebRTC 的學習筆記我會放在另外一篇獨立的文章。


創用 CC 授權條款
Wei Ji 以創用CC 姓名標示-相同方式分享 4.0 國際 授權條款釋出。

Footnotes

  1. Chapter 8 - A Framework for Distributed Computing. Retrieved 2023-08-05, from https://zguide.zeromq.org/docs/chapter8

  2. c# - Protocol buffers detect type from raw message - Stack Overflow. Retrieved 2023-08-06, from https://stackoverflow.com/a/9125119

  3. Deno 1.8 Release Notes. Retrieved 2023-08-05, from https://deno.com/blog/v1.8

  4. Release v1.32.0 · denoland/deno. Retrieved 2023-08-05, from https://github.com/denoland/deno/releases/tag/v1.32.0

  5. Day 22 Streaming POC - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天. Retrieved 2023-08-05, from https://ithelp.ithome.com.tw/articles/10305097

· 3 min read
Wei Ji

  • 一種去中心化叢集運算架構,用以建構運算 Hakoniwa 的網路。
  • Polis 是「古希臘城邦」的意思。

基本概念 Concept

技術痛點

具我所知 Minecraft 在軟體層面有兩個瓶頸:

  • 複雜的 OOP 結構
  • 伺服器端使用單執行緒

以前我試著寫 Minecraft 插件的時候,因為有使用到 NMS (net.minecraft.server) 的 API,因此大概知道 Minecraft 的程式碼大量使用了多層繼承 (Multilevel Inheritance),然而這種 OOP 模式不只增加了開發者的負擔(本人認為多層繼承是一種反模式),更會造成在處理 Minecraft 這樣複雜的遊戲內容時,狀態的管理不容易控制而造成 bug 並且難以排除。

於是我打算使用 ECS (Entity Component System) 架構來解決,其概念就是單純化資料容器 (Entity 與 Component),由 System 執行邏輯運算,透過分離資料和邏輯達到更清晰的程式碼,並且複數 System 有序執行的方式讓狀態管理單純化。然而這個架構有個問題,就是 System 有序執行的特性讓我不知道如何搭配多執行緒來提高軟體對硬體資源的利用率。

遊戲運算平行化

其中一個合理的解方是以地圖的 Chunk 為單位切割最小的運算區塊 (Game Block),一個 Game Block 由一個執行緒處理運算,Game Block 之間(也就是執行緒之間)的通訊則由事件驅動。

舉例來說,有一支箭矢飛越運算區塊的邊界:

來源 Game Block 會把箭矢的資訊包裝成一個事件,發送給目標 Game Block,並且從資料集中移除。目標 Game Block 則是根據事件創建一個箭矢實體並繼續運算。兩個 Game Block 使用獨立的執行緒,並且運算週期並不同步,因此不會凍結彼此。

何不乾脆 P2P?

不知道為什麼,我直覺覺得比起寫成一個 APP 管理一堆 Game Block,不如乾脆全部做成微服務,讓它們自己構成一個叢集比較好。於是一個新的坑 (Polis) 就產生了。


創用 CC 授權條款
Wei Ji 以創用CC 姓名標示-相同方式分享 4.0 國際 授權條款釋出。

· 9 min read
Wei Ji

前情提要

如果對我的 Side Project 「底火的芬芳」不清楚的讀者可能需要先閱讀底火的芬芳 - 專案起源才能理解這場旅程是怎麼開始的。而最近 (2022-12-19) 在逛 Github 的時候晃到一個 repo,它是少女前線這款遊戲的數值模擬器,往程式語言構成一看是用 Typescript 寫的立刻勾起了我的興趣。

neko-gg/gfl-combat-simulator

2022-12-19 在 Github 上閒逛的時候,看到一個 repo

喔啦啦,這不是 Typescript 嗎? 喔啦啦,這不是 React 嗎? 是我的主場呢 = =+

於是就 clone 下來研究研究。

後來(約 2022-12-20)照著 README.md 的指示:

yarn update-resources
yarn update-assets

找到了 ResourcesUpdaterAssetsUpdater 這兩個程式,發現它把遊戲素材放在另外一個 repo。以及會從遊戲官方的 cdn 下載名為 ResData.ab 的檔案,因為不知道是什麼檔案類型,就用 Hex 瀏覽器打開來看:

啊啦啦,原來是 Unity ResData Bundle。

另外,從 ResourcesUpdater 看到它呼叫了來自 ChibiUpdater.ts 的方法,稍微研究一下內容發現它使用 Puppeteer 打開一個網站並做了某些事,不過因為程式碼已經有點過時了,網站已經被移到其他地方,DOM 的操作也和原本腳本內的方式有點落差。研究了它使用 Puppeteer 的方式,看得出來目的是取得一張 Chibi 站立的圖片。

  • 原始的 Chibi (程式內對人偶的稱呼)檔案是將人偶拆件,並透過組態檔組裝的。
  • naganeko 的網站能夠上傳 Spine 檔案,並播放動畫。
  • naganeko 的網站也能下載組裝後的圖檔。

試著逆向工程 naganeko 的網站,發現它應該是使用了 Spine 的 Runtime。

2D 骨架動畫

在 3D 模型/動畫中,會使用骨架 (Armature) 綁定模型節點,並透過權重、關鍵影格等方式讓模型執行動畫。

透過把模型壓扁,將這種技術應用在 2D 場景就是 2D 骨架動畫,可以解決傳統 Sprite 的一些限制。另外還有三渲二之類在遊戲開發上常見的手段,不過不在本次題目的討論範圍內。

Spine

Spine 是目前極為普及的 2D 骨架動畫 tool chain。用工業建模比喻的話就像 SolidWork,或是美術產業中的 PhotoShop 以及 Illustrator。原因在於其應用端成功的布局,包含 Unity3D, 又或是網頁遊戲開發 Three.js, Phaser, PixiJS...之類的執行環境,有著非常廣泛的 Runtime 支援。換句話使用它的產品,佈署目標的選擇十分多元。

然而這個格式終究是商業產品,編輯器本身需要付費,使用者(如:遊戲開發者)也需要購買授權。即便有 Runtime 的原始碼,其授權範圍仍不算開源的範疇,對我而言尋找替代方案就很重要了,因為我只是想寫寫 side project。

替代方案

與 Spine 類似的產品還有 Creature AnimationRive 之類的,不過它們跟 Spine 一樣都有一股商業味,自然就排除在我的選項之外了。

往開源的生態系望去,則有 Blender 的生態系內有個名為 COA Tools 的工具能夠製作 2D 動畫。或是 Godot 作為開源的遊戲引擎也有內建的 2D 骨架動畫系統

只是我懶得自己重頭綁骨架跟刷權重,能夠直接用撿到的少女前線檔案轉檔是在好不過了。

DragonBones

最後 (2022-12-22) 決定使用 DragonBones 的格式,雖然編輯軟體本身沒開源而且只能在 Windows 執行,但是至少是免費的而且能夠用 Wine 跑起來。這個工具背後的中國公司的 Tech Stack 算是從 Flash 長出來的,後來往 Javascript 轉型。因此它的 runtime 支援不少諸如 Three.js Pixi, Phaser 等知名的 Javascript 的套件。

最重要的是,它能夠匯入 Spine 格式的檔案,並輸出成 DragonBones 的格式。~~這樣我就可以直接撿 Chibi 的檔案來用囉~~~還有其他考量就是,如果編輯器也是基於 Javascript 的話,日後逆向工程起來比較容易。

DragonBonesJS 重構

然而 DragonBonesJS 的 Javascript Runtime 自 2020 就沒有維護了,而且並不支援 ES6 的模組載入方式。從程式碼看得出來它使用的設計是有一個處理事務邏輯的核心,以及針對各種不同套件實作的 Runtime 。因此即便我重構了核心,也無法直接測試,必須選用一個 Runtime 作為測試用途。

原本支援的 Runtime 中,Three.js 是我的目標,但是沒有充足的 Demo code 能夠測試重構,當中的 Phaser 是我有接觸過得遊戲引擎,但是它的實作較為複雜,而 PixiJS 的版本我雖然沒使用過,但是實作比較簡單,只有 4 個檔案,於是重構的任務就拆成了 3 個部份:

  • Core
    • 真正處理動畫邏輯的部份
  • PixiJS Runetime
    • 主要是為了完整的 Demo 範例,好測試 Core 的重構運作正不正常
  • Three.js Runtime
    • 目標,因為我想把 Chibi 丟進 3D 的空間中

Core 跟 PixiJS Runtime 的重構過程沒什麼好紀錄的,就是要轉換成 ES6 的寫法跟解決 tsconfig 設定不同的問題。Three.js Runtime 的重構過程倒是問題不少。

Three.js Runtime 重構

DragonBones 提供的 Demo 中,只有線性變換的部份(Translate, Rotation, Scale),並沒有包含網格變形,當我匯入 Chibi 的檔案時,得到了破圖的結果:

慶幸的是在 issues 撿到野生的 patch

接著要處理的要件是升級 Three.js 的版本,DragonBones 遺留的程式碼僅支援到 0.89.0,而目前版本是 0.148.0。升級的過程遇到一個問題是 Three.js 在 0.125.0 的時候棄用了一個名為 Geometry 的物件,而在 DragonBones 的實作中是仰賴這個物件的,即使可以透過 Legacy Code 拿來使用,卻會有性能問題。

重構成果

Three.js Chibi Game Sample

就只是想做個概念驗證:

註解

Sprite 限制

傳統 Sprite 是透過切換數張點陣圖達到視覺暫留的動畫效果,點陣圖除了繪製費工之外、檔案較大還有幀數受限制等問題。

與之相對的 3D 模型是用點數據構成的,也就是所謂的向量圖檔。透過綁定骨架就重複使用同一張貼圖製作不同的動畫。

Spine 授權

Spine 的授權規則很哆嗦:


創用 CC 授權條款
Wei Ji以創用CC 姓名標示-相同方式分享 4.0 國際 授權條款釋出。

· 4 min read
Wei Ji

大約 2019 年四月的時候,身邊有玩少女前線的朋友,並且當時又接觸一個名為 Dental Defender: Saga of the Candy Horde小遊戲,它使用 Phaser 引擎撰寫,於是我稍微試著了解一下 Phaser 引擎的運作方式與 Tiled 劃地圖的方法並分別在 FB 留下了兩篇貼文

2020 年六月份的時候在 Github 晃到一個 repo,它是一個有點像 diep.io 的簡單小遊戲,只是主軸是人類玩家對抗一堆 BOT,並且這些 BOT 透過簡單的機器學習理(論上應該)會越來越強,然後當玩家輸掉時遊戲還會嘲諷「你真是人類之恥」。因為我很好奇這個機器學習的效果如何,於是就簡單的改寫程式變成 BOT 4v4 的形式。

接著我又想「何不把機器學習包裝成類似少女前線這樣的角色蒐集遊戲?角色的強度依靠機器學習的性能而不是由開發商設定參數決定」,並且把這個概念寫成簡單的 UI。

2020 年七月底,透過重構 Dental Defender 來熟悉 Typescript 與 Phaser 引擎,就是為了實作這個概念而做的準備。也大約是這個時間,這個專案的名稱敲定為「底火的芬芳」。然而後來 Sprite 畫得有點煩加上工作忙碌再加上又跑去追其他蝴蝶,因為跟 AI 相關,這個專案就併入 The Key Of Huanche 作為子專案並暫時被冷凍了起來。


後來斷斷續續有畫一些素材也是為了這個專案:

名詞解釋

Dental Defender: Saga of the Candy Horde

遊戲開發圈有一種活動叫做 Game Jam,就是聚集一群愛好者在數小時到數天之內完成一款遊戲。而這個小遊戲來自一場名為 candyjam 的 Game Jam,原因是一間名為 King.com 的公司(就是 Candy Crush 的那間公司)在 2014 年將 "candy" 一詞申請商標,並指控一些獨立開發公司侵犯他們的商標。這一舉動激怒了獨立開發圈,於是他們舉辦一場 Game Jam 專門製作 "Cahndy" 相關的遊戲來對 King.com 表示抗議。1

蝴蝶

本人有嚴重的閃亮事物症候群,習慣將自己跑去玩其他 Side Project 比喻成追蝴蝶。


創用 CC 授權條款
Wei Ji以創用CC 姓名標示-相同方式分享 4.0 國際 授權條款釋出。

Footnotes

  1. Indie Developers Troll King Games Hard With 'Candy Jam' | WIRED. Retrieved 2023-01-17, from https://www.wired.com/2014/02/candy-crush/

· 2 min read
Wei Ji

未採用的幾何方案

原本我打算從波函數中尋找用來描述仿星環的函數,比如電子軌域或是電磁波:

https://wifflegif.com/gifs/496745-quantum-mechanics-atomic-orbitals-gif

https://www.researchgate.net/project/Open-Source-Tools-for-FEM-and-FDTD-Simulations

但是這些模型的產物都是張量,要另外設定條件把線(面)畫出來是很困難的。

摸索的過程中也有朋友建議一個接近的函數:腎形線,可惜它中間不是貫穿的。

幾何參數

最後採用尺規作圖直接刻的方式,簡單暴力。

接著計算 Voxel 平面的長度,也就是經度線H(紅)

H=0.5(2π0.5R)+2πR0.25+2π(2R)0.25+0.5(2π0.5R)=2.5πR\begin{align} H &= 0.5(2\pi \cdot 0.5R) + 2\pi R\cdot 0.25 + 2 \pi (2R) \cdot 0.25 + 0.5(2\pi \cdot 0.5R) \\ &= 2.5 \pi R \end{align}

與 Voxel 平面的寬度;緯度線W(藍),取小圓周與大園周的平均值:

W=2π(R)+2π(2R)2=3πR\begin{align} W &= \frac{2 \pi (R) + 2 \pi (2R)}{2} \\ &=3 \pi R \end{align}

因此平面面積為

A=HW=7.5π2R2A = H \cdot W = 7.5 \pi^2 R^2

等效仿星環

一個球體行星的表面積為:

S=4πRs2S = 4\pi {R_s}^{2}

若要將仿星環近似已知行星的表面積;如地球,則可透過下述關係達成:

S=A4πRs2=7.5π2R211.875πRs=R\begin{align} S &= A \\ 4\pi {R_s}^{2} &= 7.5 \pi^2 R^2 \\ \sqrt{\frac{1}{1.875 \pi}} R_s &= R \end{align}

地球的半徑為 6,371 km,因此等效仿星環的 R 為 2,625 km