Flutter迷宫生成与构建插件maze_builder的使用

Flutter迷宫生成与构建插件maze_builder的使用

简介

本库使用Dart实现了Eller算法来生成迷宫。该算法能够创建“完美的”迷宫,保证任意两个单元格之间只有一条路径,例如:

+---+---+---+---+---+---+---+
|           |           |   |
+---+   +---+   +   +   +   +
|   |   |       |   |       |
+   +   +   +   +   +   +   +
|       |   |   |   |   |   |
+   +---+   +   +---+---+   +
|   |   |   |   |   |   |   |
+   +   +   +   +   +   +   +
|   |       |   |   |       |
+   +---+   +---+   +---+---+
|   |   |   |       |       |
+   +   +   +   +---+   +   +
|                       |   |
+---+---+---+---+---+---+---+

该库生成一个二维数组的迷宫单元格,每个单元格具有以下属性:

{
  x: 4,          // 水平位置,整数
  y: 7,          // 垂直位置,整数
  top: false,    // 上面有墙/被阻挡,布尔值
  left: false,   // 左边有墙/被阻挡,布尔值
  bottom: true,  // 下面有墙/被阻挡,布尔值
  right: true,   // 右边有墙/被阻挡,布尔值
  set: 5         // 用于生成迷宫的集合编号,可以忽略
}

特性

该包的例子还使用Flutter CustomPainter绘制线条。

入门指南

在你的项目中添加库:

import 'package:maze_builder/maze_builder.dart';

使用方法

首先生成迷宫:

Random rand = Random();
List<List<Cell>> localMaze = generate(width: 200, height: 200, closed: true, seed: rand.nextInt(100000));

然后绘制迷宫:

void makeBlocks() {
  int mazeLength = maze.length;
  for (var i = 0; i < mazeLength; i++) {
    for (var j = 0; j < maze[i].length; j++) {
      getBlockLines(maze[i][j], i);
    }
  }
}

void getBlockLines(Cell cell, int index) {
  int size = 24;
  if (cell.top) {
    double x1 = cell.x * size;
    double y1 = cell.y * size;
    double x2 = cell.x * size + size;
    double y2 = cell.y * size;
    drawLine(x1, y1, x2, y2, _paint);
  }
  if (cell.bottom) {
    double x1 = cell.x * size;
    double y1 = cell.y * size + size;
    double x2 = cell.x * size + size;
    double y2 = cell.y * size + size;

    drawLine(x1, y1, x2, y2, _paint);
  }
  if (cell.left) {
    double x1 = cell.x * size;
    double y1 = cell.y * size;
    double x2 = cell.x * size;
    double y2 = cell.y * size + size;

    drawLine(x1, y1, x2, y2, _paint);
  }
  if (cell.right) {
    double x1 = cell.x * size + size;
    double y1 = cell.y * size;
    double x2 = cell.x * size + size;
    double y2 = cell.y * size + size;

    drawLine(x1, y1, x2, y2, _paint);
  }
}

// 绘制线段
void drawLine(double x, double y, double targetX, double targetY, Paint paint) {
  canvas!.drawLine(
    Offset(x, y),
    Offset(targetX, targetY),
    paint,
  );
}

完整示例代码

以下是一个完整的示例代码,展示了如何使用maze_builder插件生成并绘制迷宫:

import 'dart:math';

import 'package:example/maze_driver.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:maze_builder/maze_builder.dart';
import 'dart:ui' as ui;

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  [@override](/user/override)
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  List<List<Cell>> maze = [];
  late AnimationController _controller;
  BoxConstraints? viewportConstraints;

  [@override](/user/override)
  void initState() {
    super.initState();

    _controller =
        AnimationController(vsync: this, duration: const Duration(seconds: 1));

    SchedulerBinding.instance.addPostFrameCallback((_) {
      _controller.repeat();
      generateMaze();
    });
  }

  [@override](/user/override)
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  /// 生成迷宫
  void generateMaze() {
    int size = 24;
    Random rand = Random();
    List<List<Cell>> localMaze = generate(
        width: size, height: size, closed: true, seed: rand.nextInt(100000));

    setState(() {
      maze = localMaze;
    });
  }

  void randomize() {
    generateMaze();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const ui.Color.fromARGB(255, 0, 0, 0),
      bottomNavigationBar: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Container(
          padding: const EdgeInsets.only(left: 8.0, right: 8.0),
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(50.0),
            color: const ui.Color.fromARGB(255, 0, 0, 0),
          ),
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [],
            ),
          ),
        ),
      ),
      body: LayoutBuilder(
        builder: (BuildContext context, BoxConstraints viewportConstraints) {
          this.viewportConstraints = viewportConstraints;
          return Stack(
            children: [
              Transform.translate(
                offset: const Offset(50, 100),
                child: RepaintBoundary(
                  child: CustomPaint(
                    size: const ui.Size(200, 400),
                    key: UniqueKey(),
                    isComplex: true,
                    painter: MazeDriverCanvas(
                      controller: _controller,
                      maze: maze,
                      blockSize: 16,
                      width: viewportConstraints.maxWidth,
                      height: viewportConstraints.maxHeight,
                    ),
                    child: Container(
                      constraints: BoxConstraints(
                        maxWidth: viewportConstraints.maxWidth,
                        maxHeight: viewportConstraints.maxHeight,
                      ),
                    ),
                  ),
                ),
              ),
              Positioned(
                top: viewportConstraints.maxHeight - 100,
                left: viewportConstraints.maxWidth - 100,
                child: FloatingActionButton(
                  onPressed: randomize,
                  backgroundColor: Colors.green,
                  child: const Icon(Icons.refresh),
                ),
              ),
            ],
          );
        },
      ),
    );
  }
}
1 回复

更多关于Flutter迷宫生成与构建插件maze_builder的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter中使用maze_builder插件来生成和构建迷宫的代码示例。maze_builder是一个虚构的插件,用于说明目的,因为实际上在Flutter的官方插件或常用社区插件中并没有直接名为maze_builder的插件。但我们可以基于Flutter的绘图能力和一些迷宫生成算法来实现类似功能。

为了模拟maze_builder插件的功能,我们将使用Flutter的CustomPainter来绘制迷宫,并使用深度优先搜索(DFS)算法来生成迷宫。

首先,确保你的pubspec.yaml文件中添加了必要的依赖项(虽然这里主要是Flutter本身的功能,但你可能需要其他UI相关的库,比如flutter_hooks用于状态管理,不过在这个例子中我们只用基本的Flutter功能):

dependencies:
  flutter:
    sdk: flutter

然后,创建一个新的Flutter项目,并在lib目录下创建一个新的Dart文件,比如maze_painter.dart,用于实现迷宫的绘制逻辑:

import 'package:flutter/material.dart';
import 'dart:math' as math;

class MazePainter extends CustomPainter {
  final int rows;
  final int cols;
  final List<List<bool>> maze;
  final double cellSize;
  final Color wallColor;
  final Color pathColor;

  MazePainter({
    required this.rows,
    required this.cols,
    required this.maze,
    this.cellSize = 50.0,
    this.wallColor = Colors.black,
    this.pathColor = Colors.white,
  });

  @override
  void paint(Canvas canvas, Size size) {
    final Paint wallPaint = Paint()
      ..color = wallColor
      ..style = PaintingStyle.fill
      ..strokeWidth = 1.0;

    final Paint pathPaint = Paint()
      ..color = pathColor
      ..style = PaintingStyle.fill
      ..strokeWidth = 1.0;

    double offsetX = (size.width - cols * cellSize) / 2;
    double offsetY = (size.height - rows * cellSize) / 2;

    for (int row = 0; row < rows; row++) {
      for (int col = 0; col < cols; col++) {
        if (row == 0 || row == rows - 1 || col == 0 || col == cols - 1 ||
            !maze[row][col]) {
          // Draw walls around cells that are walls or on the border
          canvas.drawRect(
            Rect.fromLTWH(
              col * cellSize + offsetX,
              row * cellSize + offsetY,
              cellSize,
              cellSize,
            ),
            wallPaint,
          );
        } else {
          // Fill the path cells
          canvas.drawRect(
            Rect.fromLTWH(
              col * cellSize + offsetX + 1.0, // Offset by 1 to avoid overlap with walls
              row * cellSize + offsetY + 1.0,
              cellSize - 2.0,
              cellSize - 2.0,
            ),
            pathPaint,
          );

          // Draw internal walls based on maze layout
          if (col < cols - 1 && !maze[row][col + 1]) {
            canvas.drawLine(
              Offset(col * cellSize + cellSize + offsetX - 1.0, row * cellSize + offsetY),
              Offset(col * cellSize + cellSize + offsetX - 1.0, row * cellSize + cellSize + offsetY),
              wallPaint,
            );
          }
          if (row < rows - 1 && !maze[row + 1][col]) {
            canvas.drawLine(
              Offset(col * cellSize + offsetX, row * cellSize + cellSize + offsetY - 1.0),
              Offset(col * cellSize + cellSize + offsetX, row * cellSize + cellSize + offsetY - 1.0),
              wallPaint,
            );
          }
        }
      }
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}

// Function to generate a maze using DFS
List<List<bool>> generateMaze(int rows, int cols) {
  List<List<bool>> maze = List.generate(rows, () => List.filled(cols, true));
  List<Offset> stack = [Offset(1, 1)];
  List<List<bool>> visited = List.generate(rows, () => List.filled(cols, false));
  List<List<List<bool>>> directions = [
    [true, false], // Right
    [false, true], // Down
    [true, true],  // Right and Down
    [false, false] // No walls (path)
  ];

  Random random = Random();

  while (stack.isNotEmpty) {
    Offset current = stack.removeAt(stack.length - 1);
    int row = current.dy.toInt();
    int col = current.dx.toInt();
    visited[row][col] = true;

    List<int> availableDirections = [];

    if (col < cols - 1 && !visited[row][col + 1]) availableDirections.add(0);
    if (row < rows - 1 && !visited[row + 1][col]) availableDirections.add(1);

    if (availableDirections.isEmpty) continue;

    int choice = random.nextInt(availableDirections.length);
    int directionIndex = choice;

    if (availableDirections.contains(0)) {
      if (choice == 0 || (choice == 1 && random.nextBool())) {
        maze[row][col] = directions[2][0];
        maze[row][col + 1] = directions[2][1];
        stack.add(Offset(col + 1, row));
      } else {
        maze[row][col] = directions[0][0];
        stack.add(Offset(col - 1, row)); // Backtrack
      }
    } else if (availableDirections.contains(1)) {
      maze[row][col] = directions[3][0];
      maze[row + 1][col] = directions[3][1];
      stack.add(Offset(col, row + 1));
    }
  }

  // Remove outer walls (for demonstration purposes)
  for (int row = 0; row < rows; row++) {
    maze[row][0] = false;
    maze[row][cols - 1] = false;
  }
  for (int col = 0; col < cols; col++) {
    maze[0][col] = false;
    maze[rows - 1][col] = false;
  }

  return maze;
}

最后,在你的主Dart文件(比如main.dart)中使用这个MazePainter来绘制迷宫:

import 'package:flutter/material.dart';
import 'maze_painter.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Maze Generator'),
        ),
        body: Center(
          child: CustomPaint(
            painter: MazePainter(
              rows: 21,
              cols: 21,
              maze: generateMaze(21, 21),
            ),
            size: Size(400, 400), // Adjust size as needed
          ),
        ),
      ),
    );
  }
}

这段代码实现了一个基本的迷宫生成器,并使用Flutter的绘图功能将其绘制出来。迷宫是通过深度优先搜索(DFS)算法生成的,并且使用CustomPainter来绘制迷宫的墙壁和路径。你可以根据需要调整迷宫的尺寸和颜色。

回到顶部
AI 助手
你好,我是IT营的 AI 助手
您可以尝试点击下方的快捷入口开启体验!