欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

利用Flutter制作經典貪吃蛇游戲

 更新時間:2022年04月25日 08:34:55   作者:大前端之旅  
Flutter框架可以使用單一代碼庫為 Android、iOS、Web ,桌面平臺構建應用程序。本文將利用它制作經典的貪吃蛇游戲,感興趣的小伙伴可以了解一下

前言

Flutter (Channel stable, 2.10.3, on Microsoft Windows [Version 10.0.19044.1586], locale zh-CN)

Android toolchain - develop for Android devices (Android SDK version 30.0.3)

Flutter 框架讓你可以使用單一代碼庫為 Android、iOS、Web ,桌面平臺構建應用程序。盡管許多大公司都將 Flutter 用于他們蓬勃發(fā)展的應用程序,包括 Google Pay 和阿里巴巴閑魚,但沒有多少人正在探索使用 Flutter 進行游戲開發(fā)。這就是你將在本教程中所做的。

由于 Flutter 能夠以高達 60 FPS 的速度渲染 UI,你將利用該功能在 Flutter 中構建簡單的 2D Snake 游戲。

在此過程中,你將學習如何:

  • 使用 Flutter 作為游戲引擎
  • 移動對象
  • 控制運動
  • 構建游戲用戶界面
  • 添加游戲元素

啟動項目設置了競爭環(huán)境。你將在構建游戲時為其添加 UI 元素。入門項目還提供了一些有用的實用方法,因此你可以專注于更大的場景:編寫游戲。以下是你可以在項目中找到的預構建函數(shù),以及如何使用它們:

  • roundToNearestTens(int num):將傳遞的整數(shù)參數(shù)四舍五入到最接近的步長值。 這使你可以在假想的網(wǎng)格上獲得與當前位置相距步進單位的確切下一個位置。
  • getRandomPositionWithinRange() :在游戲區(qū)域范圍內的屏幕上生成一個隨機位置。你將使用此函數(shù)生成一條新蛇并為蛇提供食物。
  • showGameOverDialog() :當 Snake 與游戲區(qū)域的任何邊界發(fā)生碰撞時,顯示一個樣式化的對話框彈出窗口。此對話框顯示用戶的分數(shù)和重新開始游戲的按鈕。
  • getRandomDirection([String type]) :隨機返回四個方向之一:上、下、左或右。你主要使用此函數(shù)在 Snake 生成時將其沿隨機方向移動。它可選地接受一個參數(shù),指定你希望它返回的隨機方向是水平還是垂直。
  • getRandomPositionWithinRange() :返回游戲區(qū)域內屏幕上的隨機位置。它確保隨機位置位于蛇移動的網(wǎng)格上。
  • getControls() : 實現(xiàn)ControlPanel,一個在屏幕上顯示四個圓形按鈕以控制蛇的移動的小部件。
  • getPlayAreaBorder() :沿屏幕邊緣繪制邊框,表示屏幕移動的播放區(qū)域。

使用 Flutter 作為游戲引擎

游戲引擎是一套工具和服務,可幫助游戲開發(fā)者構建游戲。他們處理很多不同的事情,比如圖形、聲音、人工智能、用戶輸入、網(wǎng)絡等等。Flutter 也可以處理大部分這些事情。作為 Flutter 開發(fā)人員,你可以訪問整個世界的 Dart 插件。另外,如果不存在,你始終可以自己編寫代碼。

要從 Flutter 作為游戲開發(fā)引擎開始,你將使用框架的渲染功能來為蛇、食物和游戲分數(shù)等游戲對象設置動畫。在這種情況下,你將使用計時器每秒重新生成 60 次 UI,并在屏幕上顯示一條蛇。通過在改變其位置的同時以高達 60 FPS 的速度渲染蛇,你可以獲得非常平滑的移動動畫效果。

像 Unity 這樣的游戲引擎以類似的方式工作。這里的不同之處在于,過多地推動框架的限制可能最終導致需要大量資源的應用程序。然而,在測試這款游戲時,F(xiàn)lutter 的表現(xiàn)驚人地好,根本沒有加熱設備。

畫蛇

你的第一步是創(chuàng)建蛇。你將從啟動Piece項目中的小部件開始,它會在屏幕上呈現(xiàn)一個彩色圓圈。使用此小部件,你將逐個繪制蛇以及蛇食。

但是,在繪制蛇之前,有必要花點時間了解使用 Flutter 進行 2D 渲染的基礎知識。

2D 渲染的基礎

要渲染任何在屏幕上不斷改變其位置的東西,你需要使用 和 之類的小Stack部件Positioned。給定 x 和 y 坐標,這些小部件可以將子小部件放置在屏幕上的任何位置。

要記住的最重要的事情是,你正在開發(fā)一個移動應用程序,該應用程序需要在具有各種屏幕尺寸和縱橫比的設備上運行。這意味著你不能對游戲領域的任何維度進行硬編碼。相反,你將使用MediaQuery獲取用戶屏幕的寬度和高度,然后根據(jù)這些值計算位置。

例如,這是一個簡單的代碼片段,它聲明變量,然后將屏幕的寬度和高度分配給它們。

final screenSize = MediaQuery.of(context).size;
final screenWidth = screenSize.width;
final screenHeight = screenSize.height;

在上面的代碼片段中,MediaQuery繼承的小部件用于獲取當前設備屏幕的寬度和高度。這需要傳遞內容,因此,我們在其內部擁有此代碼build()。

既然你知道如何在屏幕上放置對象,就該畫蛇了。

創(chuàng)建蛇

你將使用它Piece來創(chuàng)建蛇。首先,你將根據(jù)蛇在屏幕上的位置進行創(chuàng)建。然后,你會將組成蛇的所有部分的位置存儲在被調用的。

首先將getPieces()位于lib/game.dart中的 替換為:

 List<Piece> getPieces() {
    final pieces = <Piece>[];
    draw();
    drawFood();
?
    // 1
    for (var i = 0; i < length; ++i) {
      // 2
      if (i >= positions.length) {
        continue;
      }
?
      // 3
      pieces.add(
        Piece(
          posX: positions[i].dx.toInt(),
          posY: positions[i].dy.toInt(),
          // 4
          size: step,
          color: Colors.red,
        ),
      );
    }
?
    return pieces;
}

這是上面代碼中發(fā)生的事情:

  • 你使用的 for loop一直運行到它覆蓋了蛇的整個長度。
  • 當蛇的長度與列表的長度不匹配時,if內部的塊處理for loop邊緣情況。當蛇在吃一些食物后長度增加時,就會發(fā)生這種情況。``
  • 對于每次迭代,它都會創(chuàng)建一個Piece具有正確位置的 a 并將其添加到pieces它返回的列表中。
  • 除了位置,你還通過size和。在這種情況下,大小是,這確保了 Snake 沿著網(wǎng)格移動,其中每個網(wǎng)格單元的大小都是。顏色值是個人喜好。隨意使用自己的顏色。``

保存文件并讓應用程序熱重新加載。到目前為止,什么都沒有發(fā)生,你也不會注意到 UI 中的任何變化。

填寫列表

你需要實施draw()以實際填寫positions列表。因此draw(),在同一文件中替換為以下內容:

  void draw() async {
    // 1
    if (positions.length == 0) {
      positions.add(getRandomPositionWithinRange());
    }
?
    // 2
    while (length > positions.length) {
      positions.add(positions[positions.length - 1]);
    }
?
    // 3
    for (var i = positions.length - 1; i > 0; i--) {
      positions[i] = positions[i - 1];
    }
?
    // 4
    positions[0] = await getNextPosition(positions[0]);
  }

以下是上述函數(shù)的工作原理:

  • 如果positions為空,則getRandomPositionWithinRange()生成一個隨機位置并開始該過程。
  • 如果蛇剛剛吃了食物,它length就會增加。增加了一個新的while loop位置,positions這樣length并且positions始終保持同步。
  • 它檢查positions的長度并移動每個位置。這會產生蛇在移動的錯覺。
  • 最后,getNextPosition()將第一塊蛇頭移動到一個新位置。

你在這里需要做的最后一件事是實現(xiàn) at getNextPosition()。

將蛇移動到下一個位置

將以下代碼添加到lib/game.dart中的函數(shù)中:

  Future<Offset> getNextPosition(Offset position) async {
    Offset nextPosition;
?
    if (direction == Direction.right) {
      nextPosition = Offset(position.dx + step, position.dy);
    } else if (direction == Direction.left) {
      nextPosition = Offset(position.dx - step, position.dy);
    } else if (direction == Direction.up) {
      nextPosition = Offset(position.dx, position.dy - step);
    } else if (direction == Direction.down) {
      nextPosition = Offset(position.dx, position.dy + step);
    }
?
    return nextPosition;
  }

以下是上面代碼的作用:

  • 根據(jù)對象的當前位置及其方向值創(chuàng)建對象的新位置。改變方向會導致對象朝不同的方向移動。稍后你將使用控制按鈕來執(zhí)行此操作。
  • 如果方向設置為right則增加 x 坐標值,如果方向設置為left則減小值。
  • 同樣,如果方向設置為向上,則增加 y 坐標的值,如果方向設置為向下,則減少它。

最后,

  return Scaffold(
    body: Container(
      color: Color(0XFFF5BB00),
      child: Stack(
        children: [
          Stack(
            children: getPieces(),
          ),
        ],
      ),
    ),
  );

在上面的代碼片段中,我們只是添加了getPieces()將小部件返回到堆棧的方法,因此我們可以在屏幕上看到一些 UI。請注意,如果你不將小部件添加到build(),屏幕上不會發(fā)生任何變化。

保存所有內容并重新啟動應用程序。你會看到的:

你可以看到一條蛇……它什么也沒做。那是因為你沒有添加任何東西來重建 UI。但是,一次又一次地保存文件并讓熱重載完成它的工作,你會看到蛇移動。

添加運動和速度

現(xiàn)在,讓蛇移動所需的只是一種重建 UI 的方法。每次build調用,都需要計算新的位置,并在Piece屏幕上渲染新的 s 列表。為此,你將使用Timer.

changeSpeed()在lib/game.dart中添加以下定義:

  void changeSpeed() {
    if (timer != null && timer.isActive) timer.cancel();
?
    timer = Timer.periodic(Duration(milliseconds: 200 ~/ speed), (timer) {
      setState(() {});
    });
  }

上面的代碼只是簡單地重置了timer一個包含speed. speed每次蛇吃食物時,你控制并增加它。最后,在計時器的每一個滴答聲中,你都會調用setState(),它會重建整個 UI。

changeSpeed()接下來,從restart()同一文件中調用:

  void restart() {
    changeSpeed();
  }

timer每次用戶重新啟動游戲時都會重新初始化。

保存所有文件并重新啟動應用程序。現(xiàn)在,每次重新啟動應用程序時,蛇都會向隨機方向移動。

添加控件

現(xiàn)在你有了一條移動的蛇,你需要添加更多的旋鈕和轉盤,以便用戶可以控制蛇的方向和速度。請記住以下幾點:

  • 你將使用 來控制運動和方向ControlPanel
  • 每次蛇吃食物時,速度都必須增加。
  • restart()重置蛇與邊界框碰撞時的速度、長度和方向。
  • 發(fā)生碰撞后,你將向用戶顯示Game Over警報。

改變方向

ControlPanel擁有讓用戶控制蛇的方向所需的一切。它由四個圓形按鈕組成,每個按鈕都會改變蛇的運動方向。該小部件位于lib/control_panel.dart中。隨意挖掘并查看實現(xiàn)。

現(xiàn)在,將以下代碼添加到getControls()lib /game.dart 中:

  Widget getControls() {
    return ControlPanel( // 1
      onTapped: (Direction newDirection) { // 2
        direction = newDirection; // 3
      },
    );
  }

在上面的代碼片段中:

  • 我們正在使用ControlPanel已經在啟動項目中為你創(chuàng)建的小部件。ControlPanel 小部件呈現(xiàn) 4 個按鈕,你將使用這些按鈕來控制蛇的移動。
  • 我們還使用onTapped接收蛇移動的新方向作為參數(shù)的方法。
  • 我們direction用新的方向更新變量newDirection。這將導致蛇改變方向。

此外,在文檔頂部添加以下導入:

  import 'control_panel.dart';

接下來,添加getControls()為外部Stackin的第二個孩子build()

@override
Widget build(BuildContext context) {
  // ...
  return Scaffold(
    body: Container(
      color: Color(0XFFF5BB00),
      child: Stack(
        children: [
          Stack(
            children: getPieces(),
          ),
          getControls(),
        ],
      ),
    ),
  );
}

上面的代碼將 getControls 方法返回的小部件添加到 Stack 內屏幕上的 UI,但位于 UI 的其余部分之上。

保存文件并重新啟動應用程序?,F(xiàn)在,你將看到一組控制蛇方向的按鈕。

點擊按鈕會改變蛇的方向。到目前為止,游戲在一個簡單的級別上運行,但你仍然需要添加玩家應該努力的東西。只是在屏幕上移動一條五顏六色的蛇并不是很有趣,對吧?所以你的下一步是給蛇一些食物來加速它。

吃東西和提高速度

要在屏幕上渲染食物,你將Piece再次使用,但你將更改其顏色。請記住,你不希望食物出現(xiàn)在任意位置。相反,它應該始終在游戲區(qū)域內的隨機位置渲染,并且它應該始終位于蛇移動的網(wǎng)格上。

現(xiàn)在,你將實現(xiàn)在屏幕上drawFood()呈現(xiàn)食物。Piece將以下代碼添加到drawFood()同一文件中:

  void drawFood() {
?
    // 1
    if (foodPosition == null) {
      foodPosition = getRandomPositionWithinRange();
    }
?
    // 2
    food = Piece(
      posX: foodPosition.dx.toInt(),
      posY: foodPosition.dy.toInt(),
      size: step,
      color: Color(0XFF8EA604),
      isAnimated: true,
    );
  }

這是上面發(fā)生的事情。

  • 上面的代碼創(chuàng)建 aPiece并將其存儲在food.
  • 它存儲對象food內部的位置 。最初,這是,因此你可以在游戲區(qū)域內的屏幕上任意位置隨機渲染食物。`

在屏幕上顯示食物

接下來,你需要添加foodbuildinsideStack以將其呈現(xiàn)在屏幕上。

@override
Widget build(BuildContext context) {
  //...
  return Scaffold(
    body: Container(
      color: Color(0XFFF5BB00),
      child: Stack(
        children: [
          Stack(
            children: getPieces(),
          ),
          getControls(),
          food,
        ],
      ),
    ),
  );
}

保存所有文件并重新啟動應用程序以查看實際更改。應用程序重新啟動后,你會在屏幕上看到綠色食物。

然而,如果你試圖讓蛇找到食物,什么都不會發(fā)生——它只會越過食物圖標。那是因為你沒有做任何事情讓蛇吃掉食物。接下來你會解決這個問題。

消耗和再生食物

現(xiàn)在,你需要檢查蛇和食物在 2D 空間中是否處于相同坐標。如果是,你將對蛇進行一些更改并在新位置呈現(xiàn)一些新食物。

drawFood()在第一個if塊結束后將以下代碼添加到:

  void drawFood() {
?
    // ...
?
    if (foodPosition == positions[0]) {
      length++;
      speed = speed + 0.25;
      score = score + 5;
      changeSpeed();
?
      foodPosition = getRandomPositionWithinRange();
    }
?
    // ...
  }

上面的代碼只是檢查你存儲foodPosition的位置和蛇的第一Piece個小部件的位置是否相同。如果它們匹配,你將length增加1、speed和。然后調用,它使用新設置重新初始化。0.25``score``5``changeSpeed()``timer

最后,你foodPosition在屏幕上使用新的隨機位置進行更新,從而呈現(xiàn)新的食物Piece。

保存文件并重新啟動應用程序以使更改生效。

蛇現(xiàn)在可以吃食物了。當它這樣做時,它的長度會大大增加。

檢測碰撞并顯示游戲結束對話框

到目前為止,這條蛇可以自由地四處走動——這就造成了一個問題。沒有什么可以阻止蛇走出矩形游樂區(qū)。你需要限制蛇的移動以保持在你在屏幕上定義的游戲區(qū)域內。

首先,使用 渲染該游戲區(qū)域getPlayAreaBorder(),這會為游戲區(qū)域添加輪廓。只需添加getPlayAreaBorder()到外部Stackin build()

@override
Widget build(BuildContext context) {
  //...
  return Scaffold(
      body: Container(
        color: Color(0XFFF5BB00),
        child: Stack(
          children: [
            getPlayAreaBorder(),
            Stack(
              children: getPieces(),
            ),
            getControls(),
            food,
          ],
        ),
      ),
    );
}

上面的代碼將food小部件添加到Stackinbuild()所以現(xiàn)在它將呈現(xiàn)在屏幕上。

接下來,將以下代碼添加到detectCollision()

  bool detectCollision(Offset position) {
?
    if (position.dx >= upperBoundX && direction == Direction.right) {
      return true;
    } else if (position.dx <= lowerBoundX && direction == Direction.left) {
      return true;
    } else if (position.dy >= upperBoundY && direction == Direction.down) {
      return true;
    } else if (position.dy <= lowerBoundY && direction == Direction.up) {
      return true;
    }
?
    return false;
  }

上面的函數(shù)檢查蛇是否到達了四個邊界中的任何一個。如果有,則返回true,否則返回false。它使用lowerBoundX、upperBoundX和變量lowerBoundYupperBoundY檢查蛇是否仍在游戲區(qū)域內。

接下來,你需要在每次生成蛇的下一個位置時使用detectCollision()inside來檢查碰撞。getNextPosition()將以下代碼添加到getNextPosition()的聲明之后nextPosition

Future<Offset> getNextPosition(Offset position) async {
  //...
  if (detectCollision(position) == true) {
      if (timer != null && timer.isActive) timer.cancel();
      await Future.delayed(
          Duration(milliseconds: 500), () => showGameOverDialog());
      return position;
    }
  //...
}

上面的代碼檢查碰撞。如果蛇與邊界框發(fā)生碰撞,代碼會取消計時器并向用戶顯示Game Over對話框showGameOverDialog()。

保存所有文件并熱重載應用程序。

這一次,如果蛇接觸到周圍的邊界框,你將立即看到一個對話框,通知用戶游戲結束并顯示他們的分數(shù)。點擊對話框中的重新啟動按鈕以關閉對話框并重新開始游戲。接下來你會這樣做。

添加一些收尾工作

比賽開始成形。你接下來的步驟是編寫代碼以重新啟動游戲,然后添加分數(shù)以賦予游戲競爭元素。

重啟游戲

接下來,你將在用戶點擊Restart按鈕時重新啟動游戲。將lib/game.dartrestart中的現(xiàn)有替換為:

  void restart() {
?
    score = 0;
    length = 5;
    positions = [];
    direction = getRandomDirection();
    speed = 1;
?
    changeSpeed();
  }

上面的代碼只是將所有內容重置為其初始值。它也會清除positions,因此蛇會失去它length并從頭開始重生。

顯示分數(shù)

接下來,你需要添加代碼以在屏幕右上角顯示分數(shù)。為此,請實施getScore()

  Widget getScore() {
    return Positioned(
      top: 50.0,
      right: 40.0,
      child: Text(
        "Score: " + score.toString(),
        style: TextStyle(fontSize: 24.0),
      ),
    );
  }

添加getScore()build()作為外部的最后一個孩子Stack在. 最后,你應該如下所示:`

@override
Widget build(BuildContext context) {
  //...
  return Scaffold(
    body: Container(
      color: Color(0XFFF5BB00),
      child: Stack(
        children: [
          getPlayAreaBorder(),
          Stack(
            children: getPieces(),
          ),
          getControls(),
          food,
          getScore(),
        ],
      ),
    ),
  );
}

保存所有文件并重新啟動應用程序。

現(xiàn)在,你應該會實時看到分數(shù)更新。

以上就是利用Flutter制作經典貪吃蛇游戲的詳細內容,更多關于Flutter貪吃蛇的資料請關注腳本之家其它相關文章!

相關文章

  • Android實現(xiàn)炫酷輪播圖效果

    Android實現(xiàn)炫酷輪播圖效果

    這篇文章主要為大家詳細介紹了Android實現(xiàn)炫酷輪播圖效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • android ndk程序獲取外置SD沙盒目錄的方法講解

    android ndk程序獲取外置SD沙盒目錄的方法講解

    今天小編就為大家分享一篇android ndk程序獲取外置SD沙盒目錄的方法講解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08
  • Android開發(fā)之permission動態(tài)權限獲取詳解

    Android開發(fā)之permission動態(tài)權限獲取詳解

    這篇文章主要為大家詳細介紹了Android開發(fā)之permission動態(tài)權限獲取,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • 利用flutter實現(xiàn)炫酷的list

    利用flutter實現(xiàn)炫酷的list

    這篇文章主要給大家介紹了關于利用flutter實現(xiàn)炫酷的list的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用flutter具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-06-06
  • Android中GIF動圖的播放控制和監(jiān)聽詳解

    Android中GIF動圖的播放控制和監(jiān)聽詳解

    android下播放gif圖片功能似乎并不常用,很多時候還是以展示靜態(tài)圖片為主,可能是由于gif圖體積比較大吧。不過像表情動畫什么的,可能還是需要gif圖的。本文主要給大家介紹了關于Android中GIF動圖的播放控制和監(jiān)聽的相關資料,需要的朋友可以參考下。
    2017-05-05
  • Android自定義EditText實現(xiàn)登錄界面

    Android自定義EditText實現(xiàn)登錄界面

    這篇文章主要為大家詳細介紹了Android自定義EditText實現(xiàn)登錄界面,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • Android ViewModel的使用總結

    Android ViewModel的使用總結

    ViewModel 是 Jetpack 的一部分。 ViewModel 類旨在以注重生命周期的方式存儲和管理界面相關的數(shù)據(jù)。ViewModel 類讓數(shù)據(jù)可在發(fā)生屏幕旋轉等配置更改后繼續(xù)留存。本文簡單講解ViewModel的使用
    2021-06-06
  • android圖像繪制(二)畫布上放大縮小問題

    android圖像繪制(二)畫布上放大縮小問題

    android中圖像在畫布上放大縮小時,圖像的邊框大小沒有改變,很是疑惑,應該怎樣解決呢?接下來為您詳細介紹,感興趣的的朋友可以了解下
    2013-01-01
  • Qt qml中l(wèi)istview 列表視圖控件(下拉刷新、上拉分頁、滾動軸)

    Qt qml中l(wèi)istview 列表視圖控件(下拉刷新、上拉分頁、滾動軸)

    這篇文章主要介紹了Qt qml中l(wèi)istview 列表視圖控件(下拉刷新、上拉分頁、滾動軸) 的相關資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-07-07
  • Android通用LoadingView加載框架詳解

    Android通用LoadingView加載框架詳解

    這篇文章主要為大家詳細介紹了Android通用LoadingView加載框架的相關資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-07-07

最新評論