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),
),
),
],
);
},
),
);
}
}
更多关于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
来绘制迷宫的墙壁和路径。你可以根据需要调整迷宫的尺寸和颜色。