close

以下內容來自公眾號code小生,關注每日乾貨及時送達

文/ Very Good Ventures 團隊,5 月 11 日發表於 Flutter 官方博客

為了今年的 Google I/O 大會,Flutter 團隊使用 Flutter 以及 Firebase 構建了一款經典的彈球遊戲。下面將會介紹我們是如何通過 Flame 遊戲引擎將 I/O 彈球遊戲[1] 帶到 Web 端的。

遊戲開發要點

使用 Flutter 打造用戶交互類型的遊戲是一個很棒的選擇,例如拼圖或者文字遊戲這樣的遊戲。Flame[2] 是一個在 Flutter 上構建的 2D 遊戲引擎,當涉及到使用遊戲循環的遊戲時它會非常有用。I/O 彈球遊戲使用了 Flame 提供的一系列特性,例如動畫、物理引擎、碰撞檢測等等,同時還藉助了 Flutter 框架的基礎架構。如果你能用 Flutter 構建應用,你就獲得 Flame 構建遊戲所需的基礎。
遊戲循環
通常來說應用屏幕在沒有用戶交互事件的時候都會保持視覺靜止狀態。遊戲中則是相反的—— UI 會持續的渲染,而且遊戲狀態會不斷變化。Flame 提供了一個 game widget,它內部管理了一個遊戲循環,所以能恆定且高效地進行渲染。Game 類包含了遊戲組件以及其邏輯的實現,然後被交給 widget 樹中的 GameWidget。在 I/O 彈球遊戲中,遊戲循環反映了彈球在遊戲場的位置以及狀態,然後如果球與物體碰撞或跌出比賽則需要給出必要的反饋。
@overridevoidupdate(doubledt){super.update(dt);finaldirection=-parent.body.linearVelocity.normalized();angle=math.atan2(direction.x,-direction.y);size=(_textureSize/45)*parent.body.fixtures.first.shape.radius;}
使用 2D 組件渲染 3D 空間
在做 I/O 彈球遊戲的時候,其中遇到的一個挑戰即是如何使用 2D 元素渲染一個 3D 的交互體驗。組件需要知道在屏幕上渲染的前後順序。例如,當小球發射到斜坡上時,它的順序會向前,這樣就會讓它看起來出現在斜坡的頂部。
彈球、彈射活塞、擋板以及 Chrome 小恐龍等等這些元素都是可活動的,這意味着它應該遵循真實世界的物理規則。而且彈球也需要根據它在板子上的位置改變其大小。當彈球滾到頂部時,它應該越來越小,以讓它看着離用戶更遠。此外,重力還會讓彈球調整角度,讓它能在斜坡上更快地落下。
///Scalestheball'sbodyandspriteaccordingtoitspositionontheboard.classBallScalingBehaviorextendsComponentwithParentIsA<Ball>{@overridevoidupdate(doubledt){super.update(dt);finalboardHeight=BoardDimensions.bounds.height;constmaxShrinkValue=BoardDimensions.perspectiveShrinkFactor;finalstandardizedYPosition=parent.body.position.y+(boardHeight/2);finalscaleFactor=maxShrinkValue+((standardizedYPosition/boardHeight)*(1-maxShrinkValue));parent.body.fixtures.first.shape.radius=(Ball.size.x/2)*scaleFactor;finalballSprite=parent.descendants().whereType<SpriteComponent>();if(ballSprite.isNotEmpty){ballSprite.single.scale.setValues(scaleFactor,scaleFactor,);}}}
Forge 2D 的物理引擎
I/O 彈球遊戲很大程度依賴了 Flame 團隊維護的 forge2d[3] package。這個 package 將開源的 Box2D 物理引擎[4] 移植到 Dart 中,以便可以輕鬆集成到 Flutter。我們使用 forge2d 增強遊戲中的物理特性,例如物體(夾板)在遊戲場上的之間的碰撞檢測。使用 forge2D 能夠我們監聽夾板發生碰撞的時機。我們就可以在這裡向夾板添加交互的回調,當兩個物體發生碰撞的時候我們就能收到通知。例如,彈球(它是圓形的)與彈簧(它是橢圓形的)接觸時,我們就會增加它的得分。在這些回調中,我們可以清楚地設置接觸開始和結束的位置,以便當兩個物體相互接觸時,會發生碰撞。
@overrideBodycreateBody(){finalshape=CircleShape()..radius=size.x/2;finalbodyDef=BodyDef(position:initialPosition,type:BodyType.dynamic,userData:this,);returnworld.createBody(bodyDef)..createFixtureFromShape(shape,1);}
Sprite sheet 動畫
在彈球遊戲場中有一些小東西,例如 Android、Dash(Dart 吉祥物)、Sparky(Firebase 吉祥物)以及 Chrome 小恐龍,這些都是動畫。對於這些東西,我們使用了 sprite sheets,它已經包含在 Flame 引擎中了,叫做 SpriteAnimationComponent。對於每個元素,我們都有一個文件,其中包含不同方向的圖像、文件中的幀數以及幀之間的時間。使用這些數據,Flame 中的 SpriteAnimationComponent 能夠在一個循環中將所有圖像編在一起,使元素看起來在運動。
△ Sprite sheet 示例
△ Sprite sheet 示例
finalspriteSheet=gameRef.images.fromCache(Assets.images.android.spaceship.animatronic.keyName,);constamountPerRow=18;constamountPerColumn=4;finaltextureSize=Vector2(spriteSheet.width/amountPerRow,spriteSheet.height/amountPerColumn,);size=textureSize/10;animation=SpriteAnimation.fromFrameData(spriteSheet,SpriteAnimationData.sequenced(amount:amountPerRow*amountPerColumn,amountPerRow:amountPerRow,stepTime:1/24,textureSize:textureSize,),);
接下來詳細解析 I/O 彈球遊戲代碼。
來自 Firebase 的實時積分排行榜
I/O 彈球排行榜實時地顯示世界各地玩家的最高分數,玩家還可以在 Twitter 和 Facebook 上分享他們的分數。我們使用 Firebase Cloud Firestore[5] 記錄排名前十的分數,將其顯示在排行榜上。當一個新的分數計入排行榜時,一個 Cloud Function[6] 會將分數按降序排列並刪除目前不在前十的分數。
///Acquirestop10[LeaderboardEntryData]s.Future<List<LeaderboardEntryData>>fetchTop10Leaderboard()async{try{finalquerySnapshot=await_firebaseFirestore.collection(_leaderboardCollectionName).orderBy(_scoreFieldName,descending:true).limit(_leaderboardLimit).get();finaldocuments=querySnapshot.docs;returndocuments.toLeaderboard();}onLeaderboardDeserializationException{rethrow;}onExceptioncatch(error,stackTrace){throwFetchTop10LeaderboardException(error,stackTrace);}}
構建 Web 應用
與傳統應用相比,製作響應式的遊戲更容易。彈球遊戲只需要根據設備的大小進行縮放即可。對於 I/O 彈球遊戲,我們基於固定比例的設備大小進行縮放。確保了無論在何種顯示大小,坐標系統總是相同的。保證組件在不同設備之間的一致顯示和交互非常重要。
I/O 彈球遊戲也適配了移動和桌面瀏覽器。在移動瀏覽器上,用戶可以點擊啟動按鈕開始播放,也可以點擊屏幕左右兩側來控制相應的擋板。在桌面瀏覽器上,用戶可以使用鍵盤來發射彈球和控制擋板。
代碼架構
彈球代碼遵循分層架構,每個功能都在自己的文件夾中。在這個項目中,遊戲邏輯也與視覺組件分離。讓我們能獨立於遊戲邏輯輕鬆地更新視覺元素。彈球遊戲的主題取決於玩家在遊戲開始前選擇的角色。主題是由 CharacterThemeCubit 類控制的。根據角色的選擇,球的顏色、背景和其他元素都會更新。
///{@templatecharacter_theme}///Baseclassforcreatingcharacterthemes.//////Characterspecificgamecomponentsshouldhaveagetterspecifiedhereto///loadtheircorrespondingassetsforthegame.///{@endtemplate}abstractclassCharacterThemeextendsEquatable{///{@macrocharacter_theme}constCharacterTheme();///Nameofcharacter.Stringgetname;///Assetfortheball.AssetGenImagegetball;///Assetforthebackground.AssetGenImagegetbackground;///Iconasset.AssetGenImagegeticon;///Iconassetfortheleaderboard.AssetGenImagegetleaderboardIcon;///Assetforthetheidlecharacteranimation.AssetGenImagegetanimation;@overrideList<Object>getprops=>[name,ball,background,icon,leaderboardIcon,animation,];}
I/O 彈球的遊戲狀態是用 flam_bloc[7] 這個 package 處理的,這是一個組合了 bloc 和 Flame 組件的 package。例如,我們使用 flame_bloc 來記錄剩餘的遊戲回合數、遊戲中獲得的獎勵以及當前的遊戲分數。另外,在 wdget 樹頂層有一個 widget,它包含加載頁面的邏輯以及玩遊戲的說明。我們還遵循 行為型模式[8] 來封裝和隔離基於組件的遊戲功能元素。例如,保險槓在被球擊中時會發出聲音,所以我們實現了 BumperNoiseBehavior 類來處理這個問題。
classBumperNoiseBehaviorextendsContactBehavior{@overridevoidbeginContact(Objectother,Contactcontact){super.beginContact(other,contact);readProvider<PinballPlayer>().play(PinballAudio.bumper);}}
代碼庫還包含全面的單元測試、組件測試和黃金測試。測試遊戲會帶來一些挑戰,因為一個組件可能具有多個職責,使得它們很難單獨地進行測試。最終我們定義了更好的隔離和測試組件的模式,並將其改進整合到 flame_test[9] 這個 package 中。
組件沙盒
這個項目高度依賴於 Flame 組件帶來的仿真彈球體驗。代碼附帶了一個組件沙盒,它類似於 Flutter Gallery 中展示的 UI 組件庫[10]。在開發遊戲時,這是一個很有用的工具,因為它提供了獨立的每個遊戲組件以確保它們的外觀和行為符合預期,然後再將它們整合到遊戲中。
接下來
邀請你來 I/O Pinball[11] 試玩並獲取高分!關注積分排行榜並且在社交媒體上分享你的分數,也可以在 GitHub repo[12] 訪問並學習項目的開源代碼。
原文鏈接:
https://medium.com/flutter/i-o-pinball-powered-by-flutter-and-firebase-d22423f3f5d
本地化: CFUG 團隊
中文鏈接: https://flutter.cn/posts/i-o-pinball
文內鏈接
[1]

I/O 彈球遊戲主頁: https://pinball.flutter.dev/

[2]

Flame 開發文檔主頁: https://docs.flame-engine.org/

[3]

Flame 團隊維護的 package: forge2d: https://pub.flutter-io.cn/packages/forge2d

[4]

Box2D 物理引擎官網: https://box2d.org/

[5]

Firebase Cloud Firestore 文檔: https://firebase.google.cn/docs/firestore

[6]

Firebase Cloud Function 文檔: https://firebase.google.cn/docs/functions

[7]

Flutter package: flam_bloc 頁面: https://pub.flutter-io.cn/packages/flame_bloc

[8]

百度百科: 設計模式行為型模式: http://baike.baidu.com/l/i4znnfCN

[9]

Flutter package: flame_test 頁面: https://pub.flutter-io.cn/packages/flame_test

[10]

Flutter Gallery 網頁版: https://gallery.flutter.cn/#/

[11]

I/O Pinball 彈球遊戲: https://pinball.flutter.dev/

[12]

彈球遊戲源代碼倉庫: https://github.com/flutter/pinball


Flutter 毀了客戶端和 Web 開發!
為什麼那麼多公司鍾愛 Flutter ?
使用 Flutter 開發 3 年後,我總結了這些經驗

我是code小生,喜歡可以隨手點個在看、轉發給你的朋友,
謝謝~

覺得不錯,請點個在看

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()