如何使用Flutter构建2D游戏?分步指南

2021年11月30日02:19:07 发表评论 845 次浏览
如何使用Flutter构建2D游戏?分步指南
Flutter构建2D游戏实例

Flutter 的出现和成长利用了跨平台游戏设计的发展;Flutter 游戏只需几行代码即可创建设计和逻辑,同时保持出色的 UI/UX。

Flutter 能够以高达 60FPS 的速度渲染。你可以利用该功能构建简单的 2D 甚至 3D 游戏。请记住,在 Flutter 中开发更复杂的游戏并不是一个好主意,因为大多数开发人员会倾向于为复杂的应用程序进行原生开发。

如何使用Flutter构建2D游戏?在本教程中,我们将重新创建有史以来最早的电脑游戏之一:Pong,Pong是一个简单的游戏,所以它是一个很好的起点。本文分为两个主要部分:游戏逻辑和用户界面,通过分别关注重要部分,使构建更加清晰。

在我们进入构建之前,让我们回顾一下先决条件和设置。

Flutter 2D游戏构建教程预先准备

要理解本课程并编写代码,你将需要以下内容:

  • Flutter安装在你的机器上
  • Dart 和 Flutter 的工作知识
  • 一个文本编辑器

Flutter构建2D游戏实例入门

在这篇文章中,我们将用作屏幕 X 轴和 Y 轴位置的表示,这将有助于开发游戏的物理特性。我们还将为我们的一些变量创建无状态小部件,并在文件中声明它们,以使代码体积更小且易于理解。Alignment(x,y)Vector(x,y)homepage.dart

首先,创建一个 Flutter 项目。清除文件中的默认代码,并导入用于在应用程序中包含Material 小部件的包。main.dartmaterial.dart

接下来,创建一个名为并返回的类,然后创建一个并将其传递给如下所示的参数:MyApp()MaterialApp()statefulWidget HomePage()homeMaterialApp()

import 'package:flutter/material.dart';
import 'package:pong/homePage.dart';
void main() {
 runApp(MyApp());
}
class MyApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
  return MaterialApp(
   debugShowCheckedModeBanner:false,
   home: HomePage(),
  );
 }
}

游戏逻辑

如何使用Flutter构建2D游戏?在HomePage()中,我们需要编写一些函数和方法来处理数学和物理相关的操作。这些包括处理碰撞、加速或减速以及游戏中的导航。

但首先,我们需要声明一些参数,这些参数将代表球、球员的位置对齐以及双方球员的初始得分。参数的代码应该放在 下_HomePageState,我们将在后面的帖子中引用:

//player variations
double playerX = -0.2;
double brickWidth = 0.4;
int playerScore = 0;
// enemy variable
double enemyX = -0.2;
int enemyScore = 0;
//ball
double ballx = 0;
double bally = 0;
var ballYDirection = direction.DOWN;
var ballXDirection = direction.RIGHT;
bool gameStarted = false;
...

然后,我们提供球和砖块运动方向的枚举:

enum direction { UP, DOWN, LEFT, RIGHT }
...

为了让这个游戏工作,我们需要创建人工重力,这样当球碰到顶部砖块 (0.9) 或底部砖块 (-0.9) 时,它会朝着相反的方向前进。否则,如果它没有击中任何一块砖块,而是进入了比赛场地的顶部 (1) 或底部 (-1),则将其记录为玩家的损失。

当球撞到左边 (1) 或右边 (-1) 的墙壁时,它会朝着相反的方向前进:

void startGame() {
 gameStarted = true;
 Timer.periodic(Duration(milliseconds: 1), (timer) {
  updatedDirection();
  moveBall();
  moveEnemy();
  if (isPlayerDead()) {
   enemyScore++;
   timer.cancel();
   _showDialog(false);
   // resetGame();
  }
   if (isEnemyDead()) {
   playerScore++;
   timer.cancel();
   _showDialog(true);
   // resetGame();
  }
 });
}
...

在上面的代码,我们开始与函数从而改变了布尔来,之后我们称之为以一秒的持续时间。startGame()gameStartedtrueTimer()

在计时器内,函数如、和与检查任一玩家是否失败的语句一起传递。如果是,则累积分数,取消计时器,并显示一个对话框。updatedDirection()moveBall()moveEnemy()if

Flutter构建2D游戏实例:以下函数确保球不会超出0.9对齐,并且球在与砖块接触时只会向相反的方向移动:

void updatedDirection() {
 setState(() {
  //update vertical dirction
  if (bally >= 0.9 && playerX + brickWidth>= ballx && playerX <= ballx) {
   ballYDirection = direction.UP;
  } else if (bally <= -0.9) {
   ballYDirection = direction.DOWN;
  }
  // update horizontal directions
  if (ballx >= 1) {
   ballXDirection = direction.LEFT;
  } else if (ballx <= -1) {
   ballXDirection = direction.RIGHT;
  }
 });
}
void moveBall() {
 //vertical movement
 setState(() {
  if (ballYDirection == direction.DOWN) {
   bally += 0.01;
  } else if (ballYDirection == direction.UP) {
   bally -= 0.01;
  }
 });
 //horizontal movement
 setState(() {
  if (ballXDirection == direction.LEFT) {
   ballx -= 0.01;
  } else if (ballXDirection == direction.RIGHT) {
   ballx += 0.01;
  }
 });
}
...

此外,如果球击中场地的左侧或右侧,则它会朝着相反的方向前进:

void moveLeft() {
 setState(() {
  if (!(playerX - 0.1 <= -1)) {
   playerX -= 0.1;
  }
 });
}
void moveRight() {
 if (!(playerX + brickWidth >= 1)) {
  playerX += 0.1;
 }
}
...

该和功能有助于控制我们的砖块运动使用键盘上的箭头从左向右。这些与声明一起使用,以确保积木不会超出场地两个轴的宽度。moveLeft()moveRight()if

该函数将球员和球返回到他们的默认位置:resetGame()

void resetGame() {
 Navigator.pop(context);
 setState(() {
  gameStarted = false;
  ballx = 0;
  bally = 0;
  playerX = -0.2;
  enemyX =- 0.2;
 });
}
...

接下来,我们创建两个函数和,它们返回一个布尔值。他们检查是否有任何一个球员输了(如果球已经击中了砖块后面的垂直部分):isEnemyDead()isPlayerDead()

bool isEnemyDead(){
 if (bally <= -1) {
  return true;
 }
 return false;
}
bool isPlayerDead() {
 if (bally >= 1) {
  return true;
 }
 return false;
}
...

Flutter 2D游戏构建教程:最后,_showDialog当任一玩家获胜时,该函数会显示一个对话框。它传递一个布尔值 ,enemyDied来区分玩家何时输球。然后,它宣布非失败玩家赢得了这一轮,并使用获胜玩家的颜色显示文本“再玩一次:”

void _showDialog(bool enemyDied) {
 showDialog(
   context: context,
   barrierDismissible: false,
   builder: (BuildContext context) {
    // return object of type Dialog
    return AlertDialog(
     elevation: 0.0,
     shape: RoundedRectangleBorder(
       borderRadius: BorderRadius.circular(10.0)),
     backgroundColor: Colors.purple,
     title: Center(
      child: Text(
       enemyDied?"Pink Wins": "Purple Wins",
       style: TextStyle(color: Colors.white),
      ),
     ),
     actions: [
      GestureDetector(
       onTap: resetGame,
       child: ClipRRect(
        borderRadius: BorderRadius.circular(5),
        child: Container(
          padding: EdgeInsets.all(7),
          color: Colors.purple[100],
          child: Text(
           "Play Again",
           style: TextStyle(color:enemyDied?Colors.pink[300]: Colors.purple[000]),
          )),
       ),
      )
     ],
    );
   });
}

用户界面

如何使用Flutter构建2D游戏?现在,我们将开始开发用户界面。

在文件的小部件build内,添加以下代码:homePage.dart

return RawKeyboardListener(
 focusNode: FocusNode(),
 autofocus: false,
 onKey: (event) {
  if (event.isKeyPressed(LogicalKeyboardKey.arrowLeft)) {
   moveLeft();
  } else if (event.isKeyPressed(LogicalKeyboardKey.arrowRight)) {  
moveRight();
  }
 },
 child: GestureDetector(
  onTap: startGame,
  child: Scaffold(
    backgroundColor: Colors.grey[900],
    body: Center(
      child: Stack(
     children: [
      Welcome(gameStarted),
      //top brick
      Brick(enemyX, -0.9, brickWidth, true),
      //scoreboard
      Score(gameStarted,enemyScore,playerScore),
      // ball
      Ball(ballx, bally),
      // //bottom brick
      Brick(enemyX, 0.9, brickWidth, false)
     ],
    ))),
 ),
);

在代码中,我们返回,它将在我们在网络上构建时提供从左到右的移动。这也可以为触摸屏设备复制。RawKeyboardListener()

小部件提供用于调用上面在逻辑中编写的函数的功能。还编写了一个子项来指定应用程序的背景颜色和正文。GestureDetector()onTapstartGameScaffold()

接下来,创建一个名为的类Welcome并传入一个布尔值来检查游戏是否已经开始。如果游戏还没有开始,“tap to play”的文字会变成可见的:

class Welcome extends StatelessWidget {

 final bool gameStarted;
 Welcome(this.gameStarted);
 @override
 Widget build(BuildContext context) {
  return Container(
    alignment: Alignment(0, -0.2),
    child: Text(
     gameStarted ? "": "T A P T O P L A Y",
     style: TextStyle(color: Colors.white),
    ));
 }
}

现在我们可以创建另一个类 ,使用Ball来处理球设计及其在场地中每个点的位置。我们通过构造函数传递这些参数以实现移动性,如下所示:Alignment(x,y)

class Ball extends StatelessWidget {
 final x;
 final y;
 Ball(this.x, this.y);
 @override
 Widget build(BuildContext context) {
  return Container(
   alignment: Alignment(x, y),
   child: Container(
    decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.white),
    width: 20,
    height: 20,
   ),
  );
 }
}

现在让我们设计Brick类来处理砖块设计、颜色、位置和玩家类型。

Flutter构建2D游戏实例:在这里,我们使用数学方程 ( ) 来传递 x 和 y 轴的位置:Alignment((2* x +brickWidth)/(2-brickWidth), y)

class Brick extends StatelessWidget {
 final x;
 final y;
 final brickWidth;
 final isEnemy;
 Brick( this.x, this.y, this.brickWidth, this.isEnemy);
 @override
 Widget build(BuildContext context) {
  return Container(
    alignment: Alignment((2* x +brickWidth)/(2-brickWidth), y),
    child: ClipRRect(
     borderRadius: BorderRadius.circular(10),
     child: Container(
       alignment: Alignment(0, 0),
       color: isEnemy?Colors.purple[500]: Colors.pink[300],
       height: 20,
       width:MediaQuery.of(context).size.width * brickWidth/ 2,
       ),
    ));
 }
}

最后,Score该类应直接放置build在文件中的小部件下方;它显示每个玩家的分数。homepage.dart

为变量创建一个构造函数enemyScoreplayerScore处理每个玩家的分数,并gameStarted检查游戏是否已经开始。这将显示, 或空的内容:Stack()Container()

class Score extends StatelessWidget {
 final gameStarted;
 final enemyScore;
 final playerScore;
 Score(this.gameStarted, this.enemyScore,this.playerScore, );
 @override
 Widget build(BuildContext context) {
  return gameStarted? Stack(children: [
   Container(
     alignment: Alignment(0, 0),
     child: Container(
      height: 1,
      width: MediaQuery.of(context).size.width / 3,
      color: Colors.grey[800],
     )),
   Container(
     alignment: Alignment(0, -0.3),
     child: Text(
      enemyScore.toString(),
      style: TextStyle(color: Colors.grey[800], fontSize: 100),
     )),
   Container(
     alignment: Alignment(0, 0.3),
     child: Text(
      playerScore.toString(),
      style: TextStyle(color: Colors.grey[800], fontSize: 100),
     )),
  ]): Container();
 }
}

下面的 gif 显示了游戏的测试:

如何使用Flutter构建2D游戏?分步指南
Flutter构建2D游戏实例

Flutter 2D游戏构建教程结论

如何使用Flutter构建2D游戏?在这篇文章中,我们介绍了alignmentRawKeyboardListener、小部件、布尔值、容器的 ClipRect 以及我们代码中的数学函数,所有这些都用于重新创建 Pong 游戏。游戏还可以通过增加球数或减少砖块长度来改进,使其更加复杂。

我希望这篇文章像构建和记录它一样有用和有趣。随意使用文章中的原理重新创建其他经典游戏,或发明一个新游戏。你可以在 GitHub 上找到这篇文章中的代码链接

木子山

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: