249 lines
6.1 KiB
Dart
249 lines
6.1 KiB
Dart
import 'dart:async';
|
|
import 'dart:math' as math;
|
|
import 'dart:ui' as ui;
|
|
import 'dart:typed_data';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:aesthetica_wallpaper/models/drag_puzzle_game.dart';
|
|
|
|
class DragPuzzleProvider extends ChangeNotifier {
|
|
DragPuzzleGame? _currentGame;
|
|
Timer? _timer;
|
|
|
|
DragPuzzleGame? get currentGame => _currentGame;
|
|
|
|
/// 创建拖拽式拼图游戏
|
|
Future<void> createDragPuzzle({
|
|
required String imagePath,
|
|
required DragPuzzleDifficulty difficulty,
|
|
}) async {
|
|
// 停止之前的计时器
|
|
_timer?.cancel();
|
|
|
|
// 分割图片
|
|
final pieces = await _splitImageForDrag(
|
|
imagePath,
|
|
difficulty.rows,
|
|
difficulty.cols,
|
|
);
|
|
|
|
// 打乱拼图块顺序
|
|
pieces.shuffle();
|
|
|
|
// 创建游戏
|
|
_currentGame = DragPuzzleGame(
|
|
pieces: pieces,
|
|
gridRows: difficulty.rows,
|
|
gridCols: difficulty.cols,
|
|
imagePath: imagePath,
|
|
);
|
|
|
|
// 开始计时
|
|
_startTimer();
|
|
|
|
notifyListeners();
|
|
}
|
|
|
|
/// 分割图片为拖拽式拼图块
|
|
Future<List<DragPuzzlePiece>> _splitImageForDrag(
|
|
String imagePath,
|
|
int rows,
|
|
int cols,
|
|
) async {
|
|
final pieces = <DragPuzzlePiece>[];
|
|
|
|
// 加载图片
|
|
final ByteData data = await rootBundle.load(imagePath);
|
|
final codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
|
|
final frame = await codec.getNextFrame();
|
|
final image = frame.image;
|
|
|
|
final pieceWidth = image.width ~/ cols;
|
|
final pieceHeight = image.height ~/ rows;
|
|
|
|
// 分割图片
|
|
for (int row = 0; row < rows; row++) {
|
|
for (int col = 0; col < cols; col++) {
|
|
final id = row * cols + col;
|
|
|
|
// 创建图片块
|
|
final recorder = ui.PictureRecorder();
|
|
final canvas = Canvas(recorder);
|
|
|
|
canvas.drawImageRect(
|
|
image,
|
|
Rect.fromLTWH(
|
|
col * pieceWidth.toDouble(),
|
|
row * pieceHeight.toDouble(),
|
|
pieceWidth.toDouble(),
|
|
pieceHeight.toDouble(),
|
|
),
|
|
Rect.fromLTWH(0, 0, pieceWidth.toDouble(), pieceHeight.toDouble()),
|
|
Paint(),
|
|
);
|
|
|
|
final picture = recorder.endRecording();
|
|
final pieceImage = await picture.toImage(pieceWidth, pieceHeight);
|
|
|
|
// 将图片转换为字节数据
|
|
final byteData = await pieceImage.toByteData(
|
|
format: ui.ImageByteFormat.png,
|
|
);
|
|
final uint8List = byteData!.buffer.asUint8List();
|
|
|
|
pieces.add(
|
|
DragPuzzlePiece(
|
|
id: id,
|
|
correctRow: row,
|
|
correctCol: col,
|
|
image: MemoryImage(uint8List),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
return pieces;
|
|
}
|
|
|
|
/// 将拼图块放置到指定位置
|
|
void placePiece(int pieceId, int targetRow, int targetCol) {
|
|
if (_currentGame == null || _currentGame!.isComplete) return;
|
|
|
|
final pieces = List<DragPuzzlePiece>.from(_currentGame!.pieces);
|
|
final pieceIndex = pieces.indexWhere((p) => p.id == pieceId);
|
|
|
|
if (pieceIndex == -1) return;
|
|
|
|
final piece = pieces[pieceIndex];
|
|
|
|
// 检查目标位置是否已被占用
|
|
final existingPiece = _currentGame!.getPieceAt(targetRow, targetCol);
|
|
if (existingPiece != null) {
|
|
// 如果目标位置有其他块,将其移回底部
|
|
final existingIndex = pieces.indexWhere((p) => p.id == existingPiece.id);
|
|
pieces[existingIndex] = existingPiece.copyWith(
|
|
isPlaced: false,
|
|
currentRow: null,
|
|
currentCol: null,
|
|
);
|
|
}
|
|
|
|
// 放置当前块
|
|
pieces[pieceIndex] = piece.copyWith(
|
|
isPlaced: true,
|
|
currentRow: targetRow,
|
|
currentCol: targetCol,
|
|
);
|
|
|
|
// 增加移动次数
|
|
final newMoves = _currentGame!.moves + 1;
|
|
|
|
// 更新游戏状态
|
|
_currentGame = _currentGame!.copyWith(pieces: pieces, moves: newMoves);
|
|
|
|
// 检查是否完成
|
|
if (_currentGame!.checkComplete()) {
|
|
_completeGame();
|
|
}
|
|
|
|
notifyListeners();
|
|
}
|
|
|
|
/// 将拼图块移回底部
|
|
void removePiece(int pieceId) {
|
|
if (_currentGame == null || _currentGame!.isComplete) return;
|
|
|
|
final pieces = List<DragPuzzlePiece>.from(_currentGame!.pieces);
|
|
final pieceIndex = pieces.indexWhere((p) => p.id == pieceId);
|
|
|
|
if (pieceIndex == -1) return;
|
|
|
|
final piece = pieces[pieceIndex];
|
|
|
|
// 只有已放置的块才能移除
|
|
if (!piece.isPlaced) return;
|
|
|
|
pieces[pieceIndex] = piece.copyWith(
|
|
isPlaced: false,
|
|
currentRow: null,
|
|
currentCol: null,
|
|
);
|
|
|
|
_currentGame = _currentGame!.copyWith(
|
|
pieces: pieces,
|
|
moves: _currentGame!.moves + 1,
|
|
);
|
|
|
|
notifyListeners();
|
|
}
|
|
|
|
/// 开始计时
|
|
void _startTimer() {
|
|
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
|
if (_currentGame != null && !_currentGame!.isComplete) {
|
|
_currentGame = _currentGame!.copyWith(
|
|
elapsedTime: _currentGame!.elapsedTime + const Duration(seconds: 1),
|
|
);
|
|
notifyListeners();
|
|
}
|
|
});
|
|
}
|
|
|
|
/// 完成游戏
|
|
void _completeGame() {
|
|
_timer?.cancel();
|
|
_currentGame = _currentGame!.copyWith(isComplete: true);
|
|
}
|
|
|
|
/// 重新开始游戏
|
|
void restartGame() {
|
|
if (_currentGame == null) return;
|
|
|
|
_timer?.cancel();
|
|
|
|
// 重置所有拼图块
|
|
final pieces = _currentGame!.pieces
|
|
.map(
|
|
(piece) => piece.copyWith(
|
|
isPlaced: false,
|
|
currentRow: null,
|
|
currentCol: null,
|
|
),
|
|
)
|
|
.toList();
|
|
|
|
// 重新打乱
|
|
pieces.shuffle();
|
|
|
|
_currentGame = _currentGame!.copyWith(
|
|
pieces: pieces,
|
|
elapsedTime: Duration.zero,
|
|
moves: 0,
|
|
isComplete: false,
|
|
);
|
|
|
|
_startTimer();
|
|
notifyListeners();
|
|
}
|
|
|
|
/// 提示功能
|
|
void showHint() {
|
|
if (_currentGame == null || _currentGame!.isComplete) return;
|
|
|
|
// 找到第一个未正确放置的块
|
|
final unplacedPieces = _currentGame!.unplacedPieces;
|
|
if (unplacedPieces.isNotEmpty) {
|
|
final hintPiece = unplacedPieces.first;
|
|
print(
|
|
'提示:将块 ${hintPiece.id} 放到位置 (${hintPiece.correctRow}, ${hintPiece.correctCol})',
|
|
);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_timer?.cancel();
|
|
super.dispose();
|
|
}
|
|
}
|