天天看點

閑魚Flutter互動引擎系列——整體設計篇

作者:閑魚技術-然道

0. 什麼是Candy引擎

​ Candy引擎是閑魚技術團隊設計開發的一款APP嵌入式的、輕量級的、易于開發、性能穩定的互動引擎。其中遊戲系列符合業界标準,繪制系統高度融合Flutter體系,遊戲場景和Flutter UI可以無縫混排,動畫系統對主流格式的支援友好且易擴充。本文從緣起開始講解下我們為什麼要做這款引擎以及我們是如何設計這款引擎的。

1. 緣起

​ 最近APP遊戲化成為了一個新的風口,把在遊戲中一些好玩的、能吸引使用者的娛樂方式或場景應用在應用當中,以達到增加使用者粘性,提升DAU的效果,成本較低。同時在一些需要對使用者有引導性的場景,遊戲化還可以使使用者更易于接受并完成引導性任務,并通過激勵的形式鼓勵使用者持續沉浸在任務當中,形成良性循環。

​ 目前APP内嵌小遊戲一般采用H5小遊戲的方式,而這個方式存在一些隐患,并不被很多應用商店推薦。是以我們需要尋找一種新的安全的方式來實作APP内嵌小遊戲,并且我們希望這個方式開發友好、性能穩定、功能齊全;是以我們遵循這三點去尋找一種新的方式。

2. 思考

我們主要通過下面三種思路來探讨APP内嵌小遊戲:

  1. 采用Native的遊戲能力

​ 目前Native開發遊戲生态并不是特别成熟,而且采用Native開發,就必須面臨雙端兩套代碼的問題,開發成本和後續維護成本都會比較高。

  1. 采用遊戲引擎,比如Cocos-2dx、Unity等

​ 雖然遊戲引擎目前非常成熟,但是遊戲引擎一般用于開發重度遊戲,是以引擎大小一般比較大,引入遊戲引擎會導緻包大小增幅不小。而且遊戲引擎比較複雜,是以引擎啟動耗時較多,比較難做到遊戲頁面秒開;遊戲引擎加載進來後記憶體消耗都會比較大。遊戲引擎和APP間的通信互動相對較為麻煩,目前沒有比較好的混合棧支援。遊戲引擎的UI能力較弱,無法勝任複雜的APP UI邏輯,若采用遊戲引擎開發内嵌小遊戲,無法融合小遊戲頁面内遊戲場景和Feeds等UI。

  1. 采用Flutter的輕量級互動引擎

​ Flutter本身是基于Skia這個2D繪制引擎實作的跨端APP解決方案,是以它天然具備2D繪制能力,是以采用Flutter來實作App内嵌小遊戲存在可能。目前Flutter存在一些輕量級遊戲引擎,比如Flame,這款引擎支援簡單遊戲邏輯和動畫能力,同時整個遊戲是以一個Widget的形式最終插入到APP中,可以讓小遊戲頁面中遊戲部分和UI部分完美融合。

綜上考慮,我們決定采用Flutter的輕量級互動引擎。

3. Flame還是自主設計

Flame引擎

目前是Flutter生态中比較不錯的一款小遊戲引擎,但是依然存在很多問題:

  1. 遊戲系統不完善:引擎隻有Game和Componet,沒有Scene和GameObject概念,這樣會導緻遊戲對象嵌套複雜,對多場景不友好。
  2. 引擎完全采用Canvas來實作,遊戲場景中無法實作局部重新整理,存在性能隐患。
  3. 缺少GUI系統,場景内嵌套UI比較難。
  4. 缺少手勢事件系統。
  5. 動畫支援格式不主流:骨骼動畫是通過Flare支援的,不支援DragonBones。粒子動畫最近才上,對主流格式支援也不太友好。
  6. 資源管理存在記憶體問題,資源加載後一直不會釋放。
  7. 缺少機型适配能力。

基于這些考慮,我們決定重新設計一款Flutter互動引擎:

  1. 對标集團的EVA引擎和業界的Unity引擎,完善遊戲系統。
  2. 複用Flutter局部重新整理。
  3. 複用Flutter UI作為GUI。
  4. 複用Flutter手勢管理。
  5. 實作支援主流格式的骨骼動畫和粒子動畫。
  6. 複用APP資源庫(圖檔庫)。
  7. 實作全局750适配。

其中2~4點本質上是将互動引擎的繪制系統融合入Flutter的繪制體系中,本文下面按解決上面問題的思路依次介紹我們的引擎設計。

4. Candy引擎設計

4.1 架構設計

​ 首先分析遊戲化業務需要哪些能力,分析我們的業務場景,得出遊戲化業務需要圖4-1所示的能力:

閑魚Flutter互動引擎系列——整體設計篇

​ 拆解後,互動引擎需要有遊戲系統、繪制系統、生命周期系統、GUI系統、實體系統、動畫系統、資源系統、事件系統(手勢管理)。

​ 根據我們之前的定位,互動遊戲繪制融合到Flutter繪制體系中來,基于這個思路,我們可以複用Flutter的UI系統,同時需要融合Flutter和遊戲的手勢管理。最終我們得出如圖4-2所示的架構圖:

閑魚Flutter互動引擎系列——整體設計篇

整個互動引擎架構共分為四部分:

  1. 接口層

對外暴露的遊戲接口,主要包含建立遊戲、建立遊戲對象、添加遊戲元件等接口,同時還封裝了一些常用遊戲對象、常用遊戲元件的工廠接口。

  1. 遊戲系統

遊戲世界的管理系統,主要管理Game、Scene、GameObject和Compoent間的組織關系,還控制遊戲子系統和繪制系統的啟動與關閉。

  1. 遊戲子系統

遊戲化能力補充,主要包含生命周期系統、實體系統、動畫系統和資源系統,被遊戲系統調用。

  1. 繪制系統

負責遊戲的繪制,本引擎的繪制系統會高度和Flutter繪制邏輯融合,是以相容了GUI系統和事件系統(手勢管理)。

4.2 遊戲系統

​ 對标Unity設計,遊戲系統有下列四大元素:

  1. Game:遊戲類,負責整個遊戲的管理,Scene的加載管理以及各子系統管理與排程。
  2. Scene:遊戲場景類,負責遊戲場景中各遊戲對象的管理。
  3. GameObject:遊戲對象類,遊戲世界中遊戲對象的最小機關,遊戲世界中的任何物體都是GameObject。
  4. Component:遊戲元件類,表示遊戲對象的能力屬性,比如SpriteComponent表示精靈元件,表示繪制精靈的能力。

​ GameObject通過組合Component的形式來讓自己擁有各種能力,不同的組合讓GameObject互相之間不一樣。整個遊戲系統的組織關系如圖4-3所示:

閑魚Flutter互動引擎系列——整體設計篇

4.3 生命周期

對标Unity和Flutter特性,我們設計了如表4-1所示的生命周期,共有八個回調,基本可以滿足互動遊戲業務開發。

閑魚Flutter互動引擎系列——整體設計篇

4.4 渲染系統

​ 基于融合Flutter繪制體系思考,我們就不能全盤用Canvas來做整個遊戲的繪制管理,我們需要将遊戲對象和Flutter的繪制對象RenderObject結合起來,如圖4-4所示:

閑魚Flutter互動引擎系列——整體設計篇

​ 首先是Game的對象數和Flutter的三顆樹有效融合,是以每一個GameObject必須對應一個Widget、Element和RenderObject。

​ 融合過程主要需要解決以下問題:

  1. 遊戲的坐标系與Flutter的布局轉換融合。
  2. 動态添加和删除遊戲對象的處理。
  3. 動态修改遊戲繪制深度的處理。
  4. Flutter Inspector對遊戲對象的支援。

​ 整個繪制融合相對複雜,需要解決很多BadCase,後續會另撰文詳述互動引擎繪制融合Flutter繪制體系的過程,本文不再贅述。

4.5 GUI系統

​ 由于繪制已經融合到Flutter體系,GameObject都會對應Widget,是以我們可以設計一個特殊的GameObject,支援插入一段Flutter Widget樹,這樣我們就不需要另外實作GUI了,複用Flutter UI作為GUI即可。這個邏輯和繪制融合思路比較一緻,将插入的Widget樹作為GUIWidget的孩子即可,在GUIRenderObject中打通layout、paint和hitTest邏輯即可。

​ 這裡給一段我們GUI的示例執行個體代碼,開發過程相對簡單:

final GUIObject gui = IdleFishGame.createGUI(
  'gui',
  child: GestureDetector(
    child: Container(
      width: 100.0,
      height: 60.0,
      decoration: BoxDecoration(
        color: const Color(0xFFA9CCE3),
        borderRadius: const BorderRadius.all(
          Radius.circular(10.0),
        ),
      ),
      child: const Center(
        child: Text(
          'Flutter UI示例',
          style: TextStyle(
            fontSize: 14.0,
          ),
        ),
      ),
    ),
    behavior: HitTestBehavior.opaque,
    onTap: () {
      print('UI被點選了');
    },
  ),
  position: Position(100, 100),
);
game.scene.addChild(gui);           

4.6 事件系統

​ 在繪制融合到Flutter體系的基礎上,我們融合了事件系統,增加了手勢處理元件GestureBoxComponent,如圖4-5所示:

閑魚Flutter互動引擎系列——整體設計篇

整個融合過程分下列幾步:

  1. GestureBoxComponent将開發者注冊手勢回調方法注冊到手勢識别器中。
  2. GameObject對應的RenderObject複寫hitTest邏輯,按Flutter規範來處理點選的hitTest。通過GestureBoxComponent來判斷點選是否該被消費。
  3. GameObject複寫handEvent來将點選事件傳遞給GestureBoxComponent消費。
  4. GestureBoxComponent收到點選事件後,傳遞給手勢識别器處理。
  5. 手勢識别器在将點選傳給手勢競技場開始手勢競技,最終将勝出的手勢傳回手勢識别器,最終傳回并做出手勢事件響應,當然這一步屬于Flutter邏輯了。

4.7 其他子系統

4.7.1 動畫系統

​ 我們目前動畫主要支援骨骼動畫和粒子動畫,骨骼動畫資源目前支援DragonBones,粒子動畫資源目前支援EgretFeather。本文篇幅有限,動畫的具體實作我們後續撰文專門講述。

4.7.2 資源系統

​ 目前互動引擎的資源系統相對簡單,本文就簡單介紹下。資源系統的設計思路是複用APP的資源系統,確定整個APP隻有一份資源庫,減少記憶體開銷和增大資源複用率。資源系統架構如圖4-6所示,在遊戲系統和資源系統中間增加了一層代理,相容APP資源系統和兜底資源系統。若我們沒有注冊APP的資源系統,系統會自動調用兜底的資源系統。

​ 兜底資源系統目前分兩部分:

​ 兜底圖檔庫,複用Flutter的ImageCache,複用Flutter的能力做記憶體管理。

​ 動畫JSON資源管理,目前隻實作了JSON讀取邏輯,由于JSON複用性不高,是以目前并沒有實作緩存管理。

閑魚Flutter互動引擎系列——整體設計篇

​ 目前資源系統沒有做遠端加載和預加載的能力,這部分在我們的後續規劃中,後續我們再撰文分享具體設計實作。

5. 最後

​ 本次主要從全局上分析介紹了我們正在做的Flutter互動引擎設計,後續我們會通過《Candy引擎系列》的系列文章分别來詳述一些具體系統的設計,如渲染系統設計、動畫系統等。

​ 本文主要講述了Candy互動引擎的設計,而我們在設計實作過程中遇到了很多問題,如發現了Flutter在繪制過程中存在一定的記憶體洩露,記憶體回收不及時等,我們後續會撰文詳述這些問題的排查與解決,同時還會對Candy引擎的性能與穩定性方面做詳細測試分析,敬請關注。