【文章內容使用 Gemini 1.5 Pro 自動產生】
結合 AI 生成的文字和圖片,為這款使用 Flutter、Firebase 和 Google Cloud 建立的紙牌遊戲增添趣味
I/O FLIP 是一款由 Google 支援並使用 Flutter 建立的 AI 設計紙牌遊戲。它結合了許多 Google 產品和技術,包括 Flutter、Firebase、生成式 AI 工具、Muse 上的 Dreambooth、PaLM API 和 MakerSuite。玩家可以進行多場比賽,並將遊戲中的牌分享到社群媒體。
要玩遊戲,請訪問 http://flip.withgoogle.com。要開始遊戲並生成牌組,請回答幾個提示。要組牌並加入比賽,請選擇三張牌。您將使用這些牌與您的對手進行三輪比賽,每輪比賽以最高牌獲勝。當比賽結束時,樂趣不必結束。繼續進行比賽以提高您的連勝紀錄,並試圖登上排行榜!
讓我們深入探討我們如何使用 Flutter 和 Firebase 來建立 I/O FLIP 的技術細節。
使用 Flutter 建立的紙牌遊戲
I/O FLIP 的使用者介面、動畫、全息影像效果和後端,都使用 Flutter 和 Dart 建立。
首先,我們利用了 Flutter 休閒遊戲工具包。我們使用開箱即用的音訊功能、遊戲生命週期和 go_router 的應用程式導航作為基礎。從那裡,我們建立了遊戲邏輯和 UI。I/O FLIP 是一個 響應式 Web 應用程式。它根據玩家的螢幕調整其 UI。該應用程式還根據玩家使用的設備處理輸入。當在行動裝置或平板電腦上訪問時,它接收觸控輸入,而在桌上型電腦上接收滑鼠輸入。
撲克牌是 I/O FLIP 的關鍵元素。成千上萬的牌可能會出現在玩家的牌組中。每張牌都顯示一個 Google 吉祥物、元素和力量值。當元素彼此對抗時,這些元素就會發揮作用。例如,當有人打出一張火牌,而他們的對手打出一張水牌時,水牌會受到 10 分的懲罰。我們還使用紀錄,Dart 3 的新功能,根據牌的元素來渲染一個框架。
…
(String, SvgPicture) _mapSuitNameToAssets() {
switch (suitName) { case 'fire':
if (isRare) { return ( Assets.images.cardFrames.holos.cardFire.keyName, Assets.images.suits.card.fire.svg(), ); } return ( Assets.images.cardFrames.cardFire.keyName, Assets.images.suits.card.fire.svg(), ); …
使用 AI 生成的圖片和描述建立的牌
I/O FLIP 中的每張牌都是獨特的,因為它包含 AI 生成的圖片和描述。在遊戲開始時,玩家會回答兩個提示。這些提示有助於填充一副包含 12 張牌的牌組,這些牌組的圖片和描述是 AI 模型預先生成的。
Google 團隊使用了兩種技術來預先生成圖片:Muse,一個 Transformer 文字轉圖片模型,以及 Dreambooth。Dreambooth 使得能夠在不同的場景、姿勢、視角和光照條件下合成一個主體。每張牌都包含四個 Google 吉祥物之一:Dash、Sparky、Android 或 Dino,以及一個地點。吉祥物還有一個物品,用來指定他們的隊伍。「隊伍類型」提示在遊戲開始時就會被覆蓋,用於建立這個物品。例如,選擇「巫師」可能會導致一個角色戴著巫師帽、魔杖,或其他巫師般的事物!
Google 團隊使用 PaLM API 來預先生成牌的描述。PaLM API 訪問 Google 的大型語言模型。遊戲開始時的提示包含團隊類型和他們團隊的力量類型。假設您選擇了「巫師」團隊和「磁力」力量。當您的牌生成時,一張牌的描述包含與生成的圖片相關的上下文,包括角色的特殊力量。例如,「Dash 巫師和他的寵物龍住在一座城堡裡。他喜歡施法,讓大家開心。」
Flutter 透過 GameCard Widget 組合牌。這個 Widget 接收牌的資料:名稱、描述、圖片和力量。一旦它建立了牌,它就會應用邊框以描繪牌的套裝元素。如果一張牌是特殊牌,Flutter 會應用箔紋理效果。
若要進一步了解遊戲的生成式 AI 方面是如何建立的,請查看 這篇 Google 開發者部落格文章。
著色器為特殊牌添加箔效果
Flutter 支援 片段著色器。為了生成這些逐像素的視覺效果,Flutter 在裝置的 GPU 上運行 OpenGL 著色語言 (GLSL)。集換式卡牌遊戲的收藏家可能會記得打開一包卡牌時,找到一張有閃亮的全息箔的特別版卡牌的感覺 I/O FLIP 也包括特殊的箔牌。它們的點數為 100 分。普通牌的點數範圍為 10 到 99 分。我們使用自訂著色器來渲染箔效果。
我们在 foil.frag 文件中实现了箔着色器。该效果使用以下常量:
- STRENGTH. 这会将原始像素颜色与箔效果的颜色混合。它从 0.0(无效果)到 1.0(全效果)。
- SATURATION. 这会设置颜色的强度。它从 0.0(灰度或无颜色)到 1.0(全颜色或无黑色)。
- LIGHTNESS. 这从 0.0(全黑)到 1.0(全白)。
该着色器还通过 uniform 接收输入,在本例中为 resolution 和 offset。称为 tSource 的 uniform sampler2d 代表应用了着色器的卡牌图像。最终结果是带有箔效果的卡牌。
vec4 rainbowEffect(vec2 uv) { vec4 srcColor = texture(tSource, uv); float hue = uv.x / (1.75 + abs(offset.x)) + offset.x / 3.0; float lightness = LIGHTNESS + 0.25 * (0.5 + offset.y * (0.5 - uv.y)); hue = fract(hue); float c = (1.0 - abs(2.0 * lightness - 1.0)) * SATURATION; float x = c * (1.0 - abs(mod(hue / (1.0 / 6.0), 2.0) - 1.0)); float m = LIGHTNESS - c / 2.0; vec3 rainbowPrime; if (hue < 1.0 / 6.0) { rainbowPrime = vec3(c, x, 0.0); } else if (hue < 1.0 / 3.0) { rainbowPrime = vec3(x, c, 0.0); } else if (hue < 0.5) { rainbowPrime = vec3(0.0, c, x); } else if (hue < 2.0 / 3.0) { rainbowPrime = vec3(0.0, x, c); } else if (hue < 5.0 / 6.0) { rainbowPrime = vec3(x, 0.0, c); } else { rainbowPrime = vec3(c, 0.0, x); } vec3 rainbow = rainbowPrime + m; return mix(srcColor, vec4(rainbow, srcColor.a), STRENGTH); }
Firebase 啟用遊戲託管和分享到社群媒體
所有遊戲玩法通訊都是透過 Firebase 的 Cloud Firestore 實時進行的。Firebase 儲存空間託管生成玩家牌組的牌的資產。我們還使用 Cloud Firestore 來追蹤排行榜上的「最高連勝紀錄」。當排行榜添加新的領先者時,firedart 套件會將其添加到 Firestore 中。
Dart Frog 啟用後端和前端之間的程式碼共享
I/O FLIP 需要一個後端來防止作弊。這個伺服器授權的遊戲邏輯可以防止惡意的客戶端發送偽造的請求。Dart Frog 將遊戲邏輯(例如每輪的獲勝者)保留在後端。它還將此程式碼在 Flutter 前端和 Firestore 後端之間共享。共享程式碼有一些好處。它允許我們共享邏輯。例如,如果一個玩家贏得一輪,遊戲可以在不每次都需要查詢 Firebase 的情況下顯示獲勝動畫。共享程式碼還加快了開發速度,因為團隊可以使用同一種語言(Dart)撰寫後端和前端程式碼。我們將 I/O FLIP Dart Frog 伺服器部署到 Cloud Run。這表示伺服器程式碼在 Google Cloud 中運行,並且可以自動縮放,因此應用程式可以同時處理許多玩家。
FutureOr<Response> onRequest(RequestContext context) async { if (context.request.method == HttpMethod.post) { final cardsRepository = context.read<CardsRepository>(); final promptRepository = context.read<PromptRepository>(); final body = await context.request.json() as Map<String, dynamic>; final prompt = Prompt.fromJson(body); if (!await promptRepository.isValidPrompt(prompt)) { return Response(statusCode: HttpStatus.badRequest); } final characterClass = prompt.characterClass; if (characterClass == null) { return Response(statusCode: HttpStatus.badRequest); } final characterPower = prompt.power; if (characterPower == null) { return Response(statusCode: HttpStatus.badRequest); } final cards = await cardsRepository.generateCards( characterClass: characterClass, characterPower: characterPower, ); return Response.json( body: {'cards': cards.map((e) => e.toJson()).toList()}, ); } return Response(statusCode: HttpStatus.methodNotAllowed); }
Dart Frog 還促進了在社群媒體上的分享。當選擇牌時,玩家可以將單張牌分享到 Twitter 或 Facebook。當您將分數提交到排行榜時,您可以將手牌分享到 Twitter 或 Facebook。一旦使用者點擊分享,Dart Frog 會生成一個預先設定好的帖子。此帖子包含文字和指向具有您對應的手牌或牌的網頁的連結,以及一個按鈕,供訪客自己玩 I/O FLIP!
接下來是什麼
I/O FLIP 展示了如何在一個全球玩家都可以玩的有趣遊戲中組合 Flutter 和 Firebase,以及 Google 的生成式 AI 工具和技術。
製作方法:I/O FLIP 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。