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

大家好,我是一個遊戲引擎技術探索者,同時也是一名做過不少前端開發工作的程序員。如果你想知道如何從編寫網頁到開發遊戲,那你來對地方了!

今天我們聊聊如何使用 Dora SSR,一個支持 TSX 且跨平臺在 native 運行的遊戲引擎,助你輕鬆跨入遊戲開發的世界。

不必擔心,說到遊戲引擎並不是啥高不可攀的技術,反而和我們熟悉的前端開發工具可以有驚人相似之處。


一、遊戲客戶端開發也可以是一種前端開發

首先,讓我們解釋一下什麼是遊戲引擎。
簡單來說,遊戲引擎就是一套工具和庫的集合,幫助開發者構建遊戲,管理圖形、聲音、物理計算或碰撞檢測等。
對於前端開發者來說,你可以把它想象成就是一種特殊的瀏覽器,專門用來運行遊戲。
Dora SSR 的遊戲場景管理使用了類似於 HTML DOM 的樹形結構,這對我們來說再熟悉不過了。
想象一下,將 div 元素換成遊戲中的各種對象,CSS 動畫換成遊戲動畫,概念也差不多,代碼寫法上可能也差不多,是不是覺得有點意思了?


二、從 TypeScript 到 TSX:前端技術在遊戲中的應用

許多前端開發者都熟悉 TypeScript 和 React 的 JSX 語法。
在 Dora SSR 開源遊戲引擎中,我們通過支持 TSX,提供了與前端開發編程模式相似的遊戲開發接口。
是的你沒聽錯,就是那個 TSX。
使用 TSX 開發遊戲,意味着你可以利用已有的前端技術棧 — 組件、模塊和其他現代前端技術,直接在遊戲開發中複用這些概念。
而且,Dora SSR 的性能優化確保了即使是在複雜的遊戲場景中,也能保持流暢的運行。


三、挑戰 100 行代碼以內,教你寫一個 “憤怒的小鳥” like 的遊戲

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

1. 編寫最簡單遊戲場景

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

在 Dora SSR 中顯示一個圖片很簡單,只要使用  標籤,最後通過 toNode() 函數將標籤實例化爲一個遊戲對象就可以了。

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

2. 編寫遊戲箱子組件

接下來我們在遊戲中碰撞的箱子會由 Box 組件定義,它接受
 
numxy 和 children 等屬性:
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 的函數組件的寫法來完成我們箱子組件的定義,其中:
  • body 組件的 tag 屬性:用於存儲箱子的分數。
  • rect-fixture :定義了箱子的碰撞形狀。
  • draw-node :用於繪製箱子的外觀。
  • label :用於顯示盒子的分數。

3. 創建 TSX 實例化後的對象引用

使⽤ useRef 創建兩個引⽤變量進行備用,分別指向⼩⻦和分數標籤:
const bird = useRef();
const score = useRef();

4. 創建發射線

發射線由 line 變量創建,並添加觸摸(同時也是鼠標點擊)的事件處理:
let start = Vec2.zero;
let delta = Vec2.zero;
const line = Line();

toNode(

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();
}}
>
{/* ...在物理世界下創建其它遊戲元素 ... */}

);
  • 在 onTapBegan 事件中,記錄觸摸開始的位置並清除發射線。
  •  onTapMoved 事件中,計算觸摸移動的距離並更新發射線。
  • 在 onTapEnded 事件中,根據觸摸移動的距離設置小鳥的發射速度並清除發射線。

5. 創建其它遊戲元素

接下來,我們以  作爲遊戲場景的父級標籤,在它下面繼續創建遊戲場景中的各個元素:

5.1 地面

首先,我們使用 body 組件創建一個地面,並將其設置爲靜態剛體:
<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}:表明這是一個靜態剛體,不會受到物理模擬的影響。
  • rect-fixture:定義地面碰撞形狀爲一個矩形。
  • draw-node:用於繪製地面的外觀。
  • rect-shape:繪製一個矩形,顏色爲黃色。

5.2 箱子

接下來,我們使用之前寫好的 Box 組件創建 5 個箱子,並設置不同的初始位置和分數,在創建時播放出場動畫:
{
[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 函數:用於遍歷分數數組從 10 到 50,併爲每個分數創建一個需要小鳥撞擊的箱子。
  • Box 組件:用於創建箱子,並傳入以下屬性:
    • num={num}:箱子的分數,對應數組中的數字。
    • x={200}:箱子的初始 x 軸位置,爲 200。
    • y={-150 + i * 100}:箱子的初始 y 軸位置,根據創建序號遞增。
  • sequence 組件:用於創建要在父節點上播放的動畫序列,包含以下動畫:
    • delay time={i * 0.2}:延遲播放動畫,延遲時間根據創建序號遞增。
    • scale time={0.3} start={0} stop={1}:縮放動畫,從不顯示到完全顯示,耗時 0.3 秒。

5.3 小鳥

最後,我們使用 body 組件創建小鳥,並設置碰撞形狀、外觀和分數標籤:

<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();
// 清除被撞箱子上的分數
const label = tolua.cast(other.children?.last, TypeName.Label);
if (label) label.text = '';
other.tag = '';
// 播放箱子被撞的動畫
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}>0label>
<scale time={0.4} start={0.3} stop={1.0} easing={Ease.OutBack}/>
body>
  • ref={bird}:使用 ref 創建引用變量,方便後續操控小鳥。
  • type={BodyMoveType.Dynamic}:表明這是一個動態剛體,會受到物理模擬的影響。
  • onContactStart={(other) => {}}:小鳥的物理體接觸到其它物體時觸發的回調處理函數。
  • disk-fixture:定義小鳥形狀爲一個圓盤。
  • draw-node :用於繪製小鳥的外觀。
  • label :用於顯示小鳥的累積分數。
  • scale :用於播放小鳥的出場動畫。

6. 完成遊戲邏輯

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

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


四、簡單揭祕一下

1. 是鹿還是馬

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

2. 和 React 有關係嗎

這個問題的答案目前是:可以有(所以截至發文前還沒有)。
React 最重要的能力是通過 Virtual DOM 和執行 Tree Diff 處理的過程來進行渲染組件和業務數據的狀態同步,目前這個機制還沒有在 Dora SSR 中實現,所以大家目前看到的用 TSX 編寫出的類似 VDOM 的構建代碼只會在運行時做一次性的遊戲渲染對象的構建,往後都是底層 C++ 實現的引擎功能在負責繼續處理。
也許有一天我們會爲遊戲 UI 的開發,提供仿 React 通過執行 Tree Diff 做狀態同步的能力,或是仿 SolidJS 基於 TSX 實現其它的渲染組件狀態同步的機制。
所以在這裏也誠摯地邀請廣大前端開發的朋友,加入我們,一起玩 Dora SSR 項目,一起研究怎麼運用前端開發技術思想,爲遊戲開發也引入更多好用便捷的輪子吧。
最後我們的 Q 羣在這裏,歡迎過來玩:
512620381

延伸閱讀:用Rust開發跨平臺遊戲是怎樣的體驗?


作者介紹

李瑾:金融行業大數據工程師,Dora SSR 和 Yuescript 開源軟件作者。

項目介紹

Dora SSR(多蘿珍奇引擎)是一個用於多種設備上快速開發2D遊戲的遊戲引擎。
它內置易用的開發工具鏈,支持在手機、開源掌機等設備上直接進行遊戲開發。


項目倉庫

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


END


熱門文章

- 想體驗Xcode 16的“AI編程”?16GB內存起步——“蘋果不等式”破防了

拜登政府全面禁售知名殺軟“卡巴斯基”

“鴨子數據庫”正式發佈1.0穩定版:C++引擎代碼超30萬行

Linus眼中“很爛”的C++擊敗了C語言

- 開發者直接複製ChatGPT生成的代碼,導致公司損失10000美元