Changes
This commit is contained in:
parent
f2fe5dcd93
commit
2c5aec71cb
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
@ -1,14 +0,0 @@
|
|||||||
import 'package:flame/flame.dart';
|
|
||||||
|
|
||||||
Future<void> loadImages() async {
|
|
||||||
final imageNames = [
|
|
||||||
'crystal1.png',
|
|
||||||
'crystal2.png',
|
|
||||||
'crystal3.png',
|
|
||||||
'crystal4.png',
|
|
||||||
'crystal5.png',
|
|
||||||
'crystal6.png',
|
|
||||||
'crystal7.png',
|
|
||||||
];
|
|
||||||
await Future.wait(imageNames.map((name) => Flame.images.load(name)));
|
|
||||||
}
|
|
226
lib/game/board.dart
Normal file
226
lib/game/board.dart
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flame/game.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'tile.dart';
|
||||||
|
import 'swap_notifier.dart';
|
||||||
|
import 'package:flame/sprite.dart';
|
||||||
|
|
||||||
|
class Board extends FlameGame {
|
||||||
|
final List<Sprite> sprites;
|
||||||
|
static const int rows = 8;
|
||||||
|
static const int cols = 8;
|
||||||
|
late double tileSize;
|
||||||
|
List<List<Tile?>> tiles = [];
|
||||||
|
int? selectedRow;
|
||||||
|
int? selectedCol;
|
||||||
|
|
||||||
|
Board({required this.sprites});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> onLoad() async {
|
||||||
|
super.onLoad();
|
||||||
|
|
||||||
|
tileSize = size.x / cols;
|
||||||
|
_initializeGrid();
|
||||||
|
_removeInitialMatches();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _initializeGrid() {
|
||||||
|
for (int row = 0; row < rows; row++) {
|
||||||
|
List<Tile?> rowTiles = [];
|
||||||
|
for (int col = 0; col < cols; col++) {
|
||||||
|
int spriteIndex = _randomElement();
|
||||||
|
var tile = Tile(
|
||||||
|
sprite: sprites[spriteIndex],
|
||||||
|
spriteIndex: spriteIndex,
|
||||||
|
size: Vector2.all(tileSize),
|
||||||
|
position: Vector2(col * tileSize, row * tileSize),
|
||||||
|
row: row,
|
||||||
|
col: col,
|
||||||
|
onTileTap: handleTileTap,
|
||||||
|
);
|
||||||
|
rowTiles.add(tile);
|
||||||
|
add(tile);
|
||||||
|
}
|
||||||
|
tiles.add(rowTiles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int _randomElement() {
|
||||||
|
return Random().nextInt(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _removeInitialMatches() {
|
||||||
|
bool hasMatches;
|
||||||
|
do {
|
||||||
|
hasMatches = false;
|
||||||
|
for (int row = 0; row < rows; row++) {
|
||||||
|
for (int col = 0; col < cols; col++) {
|
||||||
|
if (_hasMatch(row, col)) {
|
||||||
|
int spriteIndex = _randomElement();
|
||||||
|
tiles[row][col]!.sprite = sprites[spriteIndex];
|
||||||
|
tiles[row][col]!.spriteIndex = spriteIndex;
|
||||||
|
hasMatches = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (hasMatches);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleTileTap(Tile tappedTile) {
|
||||||
|
int row = tappedTile.row;
|
||||||
|
int col = tappedTile.col;
|
||||||
|
|
||||||
|
if (selectedRow == null || selectedCol == null) {
|
||||||
|
selectedRow = row;
|
||||||
|
selectedCol = col;
|
||||||
|
} else {
|
||||||
|
if (_isAdjacent(selectedRow!, selectedCol!, row, col)) {
|
||||||
|
swapTiles(tiles[selectedRow!][selectedCol!]!, tiles[row][col]!);
|
||||||
|
Future.delayed(const Duration(milliseconds: 300), () {
|
||||||
|
if (!_checkMatches()) {
|
||||||
|
swapTiles(tiles[row][col]!, tiles[selectedRow!][selectedCol!]!);
|
||||||
|
}
|
||||||
|
selectedRow = null;
|
||||||
|
selectedCol = null;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
selectedRow = row;
|
||||||
|
selectedCol = col;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isAdjacent(int row1, int col1, int row2, int col2) {
|
||||||
|
return (row1 == row2 && (col1 - col2).abs() == 1) ||
|
||||||
|
(col1 == col2 && (row1 - row2).abs() == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void swapTiles(Tile tile1, Tile tile2) {
|
||||||
|
final tempPosition = tile1.position;
|
||||||
|
final tempRow = tile1.row;
|
||||||
|
final tempCol = tile1.col;
|
||||||
|
|
||||||
|
tile1.position = tile2.position;
|
||||||
|
tile1.row = tile2.row;
|
||||||
|
tile1.col = tile2.col;
|
||||||
|
|
||||||
|
tile2.position = tempPosition;
|
||||||
|
tile2.row = tempRow;
|
||||||
|
tile2.col = tempCol;
|
||||||
|
|
||||||
|
tiles[tile1.row][tile1.col] = tile1;
|
||||||
|
tiles[tile2.row][tile2.col] = tile2;
|
||||||
|
|
||||||
|
tile1.animateMoveTo(tile1.position, () {});
|
||||||
|
tile2.animateMoveTo(tile2.position, () {});
|
||||||
|
|
||||||
|
_checkMatches();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _checkMatches() {
|
||||||
|
final matches = <List<int>>[];
|
||||||
|
for (int row = 0; row < rows; row++) {
|
||||||
|
for (int col = 0; col < cols; col++) {
|
||||||
|
if (_hasMatch(row, col)) {
|
||||||
|
matches.add([row, col]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches.isNotEmpty) {
|
||||||
|
for (final match in matches) {
|
||||||
|
_removeMatchedElements(match[0], match[1]);
|
||||||
|
}
|
||||||
|
_applyGravity();
|
||||||
|
Future.delayed(const Duration(milliseconds: 300), () {
|
||||||
|
_fillEmptySpaces();
|
||||||
|
_checkMatches();
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _hasMatch(int row, int col) {
|
||||||
|
final value = tiles[row][col]!.spriteIndex;
|
||||||
|
|
||||||
|
int count = 1;
|
||||||
|
for (int i = col + 1; i < cols && tiles[row][i]!.spriteIndex == value; i++)
|
||||||
|
count++;
|
||||||
|
for (int i = col - 1; i >= 0 && tiles[row][i]!.spriteIndex == value; i--)
|
||||||
|
count++;
|
||||||
|
if (count >= 3) return true;
|
||||||
|
|
||||||
|
count = 1;
|
||||||
|
for (int i = row + 1; i < rows && tiles[i][col]!.spriteIndex == value; i++)
|
||||||
|
count++;
|
||||||
|
for (int i = row - 1; i >= 0 && tiles[i][col]!.spriteIndex == value; i--)
|
||||||
|
count++;
|
||||||
|
return count >= 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _removeMatchedElements(int row, int col) {
|
||||||
|
final value = tiles[row][col]!.spriteIndex;
|
||||||
|
|
||||||
|
int left = col;
|
||||||
|
while (left > 0 && tiles[row][left - 1]!.spriteIndex == value) left--;
|
||||||
|
int right = col;
|
||||||
|
while (right < cols - 1 && tiles[row][right + 1]!.spriteIndex == value)
|
||||||
|
right++;
|
||||||
|
if (right - left + 1 >= 3) {
|
||||||
|
for (int i = left; i <= right; i++) {
|
||||||
|
tiles[row][i] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int top = row;
|
||||||
|
while (top > 0 && tiles[top - 1][col]!.spriteIndex == value) top--;
|
||||||
|
int bottom = row;
|
||||||
|
while (bottom < rows - 1 && tiles[bottom + 1][col]!.spriteIndex == value)
|
||||||
|
bottom++;
|
||||||
|
if (bottom - top + 1 >= 3) {
|
||||||
|
for (int i = top; i <= bottom; i++) {
|
||||||
|
tiles[i][col] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _applyGravity() {
|
||||||
|
for (int col = 0; col < cols; col++) {
|
||||||
|
int emptyRow = rows - 1;
|
||||||
|
for (int row = rows - 1; row >= 0; row--) {
|
||||||
|
if (tiles[row][col] != null) {
|
||||||
|
if (row != emptyRow) {
|
||||||
|
tiles[emptyRow][col] = tiles[row][col];
|
||||||
|
tiles[emptyRow][col]!.row = emptyRow;
|
||||||
|
tiles[row][col] = null;
|
||||||
|
}
|
||||||
|
emptyRow--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _fillEmptySpaces() {
|
||||||
|
for (int col = 0; col < cols; col++) {
|
||||||
|
for (int row = rows - 1; row >= 0; row--) {
|
||||||
|
if (tiles[row][col] == null) {
|
||||||
|
int spriteIndex = _randomElement();
|
||||||
|
var tile = Tile(
|
||||||
|
sprite: sprites[spriteIndex],
|
||||||
|
spriteIndex: spriteIndex,
|
||||||
|
size: Vector2.all(tileSize),
|
||||||
|
position: Vector2(col * tileSize, row * tileSize),
|
||||||
|
row: row,
|
||||||
|
col: col,
|
||||||
|
onTileTap: handleTileTap,
|
||||||
|
);
|
||||||
|
tiles[row][col] = tile;
|
||||||
|
add(tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,117 +1,23 @@
|
|||||||
import 'package:flame/components.dart';
|
import 'package:flame/components.dart';
|
||||||
import 'package:flame/events.dart';
|
|
||||||
import 'package:flame/flame.dart';
|
|
||||||
import 'package:flame/game.dart';
|
import 'package:flame/game.dart';
|
||||||
import 'package:match_magic/models/game_state.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'dart:ui';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'sprite_loader.dart';
|
||||||
|
import 'board.dart';
|
||||||
|
import 'swap_notifier.dart';
|
||||||
|
|
||||||
class MatchMagicGame extends FlameGame with TapDetector {
|
class MatchMagicGame extends StatelessWidget {
|
||||||
final GameState gameState;
|
final List<Sprite> sprites;
|
||||||
|
|
||||||
MatchMagicGame(this.gameState);
|
const MatchMagicGame({required this.sprites, Key? key}) : super(key: key);
|
||||||
|
|
||||||
late double tileSize;
|
|
||||||
late double gridWidth;
|
|
||||||
late double gridHeight;
|
|
||||||
late double gridOffsetX;
|
|
||||||
late double gridOffsetY;
|
|
||||||
final Map<String, Sprite> sprites = {};
|
|
||||||
late SpriteComponent selectedTile;
|
|
||||||
bool hasSelectedTile = false;
|
|
||||||
final selectedPaint = Paint()..color = Colors.white.withOpacity(0.5);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> onLoad() async {
|
Widget build(BuildContext context) {
|
||||||
await super.onLoad();
|
return ChangeNotifierProvider(
|
||||||
await loadImages();
|
create: (_) => SwapNotifier(),
|
||||||
loadGrid();
|
child: GameWidget(
|
||||||
}
|
game: Board(sprites: sprites),
|
||||||
|
),
|
||||||
Future<void> loadImages() async {
|
);
|
||||||
final imageNames = [
|
|
||||||
'crystal1.png',
|
|
||||||
'crystal2.png',
|
|
||||||
'crystal3.png',
|
|
||||||
'crystal4.png',
|
|
||||||
'crystal5.png',
|
|
||||||
'crystal6.png',
|
|
||||||
'crystal0.png',
|
|
||||||
];
|
|
||||||
for (var name in imageNames) {
|
|
||||||
final sprite = Sprite(await Flame.images.load(name));
|
|
||||||
sprites[name] = sprite;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void loadGrid() {
|
|
||||||
tileSize = size.x / gameState.cols;
|
|
||||||
gridWidth = tileSize * gameState.cols;
|
|
||||||
gridHeight = tileSize * gameState.rows;
|
|
||||||
|
|
||||||
gridOffsetX = (size.x - gridWidth) / 2;
|
|
||||||
gridOffsetY = (size.y - gridHeight) / 2;
|
|
||||||
|
|
||||||
addGrid();
|
|
||||||
}
|
|
||||||
|
|
||||||
void addGrid() {
|
|
||||||
children
|
|
||||||
.whereType<SpriteComponent>()
|
|
||||||
.forEach((component) => component.removeFromParent());
|
|
||||||
|
|
||||||
for (var row = 0; row < gameState.rows; row++) {
|
|
||||||
for (var col = 0; col < gameState.cols; col++) {
|
|
||||||
final value = gameState.grid[row][col];
|
|
||||||
final spriteName = 'crystal$value.png';
|
|
||||||
final sprite = sprites[spriteName];
|
|
||||||
final spriteComponent = SpriteComponent()
|
|
||||||
..sprite = sprite
|
|
||||||
..width = tileSize
|
|
||||||
..height = tileSize
|
|
||||||
..x = gridOffsetX + col * tileSize
|
|
||||||
..y = gridOffsetY + row * tileSize;
|
|
||||||
add(spriteComponent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void render(Canvas canvas) {
|
|
||||||
super.render(canvas);
|
|
||||||
if (hasSelectedTile) {
|
|
||||||
canvas.drawRect(
|
|
||||||
Rect.fromLTWH(
|
|
||||||
selectedTile.x,
|
|
||||||
selectedTile.y,
|
|
||||||
tileSize,
|
|
||||||
tileSize,
|
|
||||||
),
|
|
||||||
selectedPaint,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onTapDown(TapDownInfo info) {
|
|
||||||
final x = info.eventPosition.global.x;
|
|
||||||
final y = info.eventPosition.global.y;
|
|
||||||
final col = ((x - gridOffsetX) / tileSize).floor();
|
|
||||||
final row = ((y - gridOffsetY) / tileSize).floor();
|
|
||||||
|
|
||||||
if (col >= 0 && col < gameState.cols && row >= 0 && row < gameState.rows) {
|
|
||||||
if (hasSelectedTile) {
|
|
||||||
gameState.selectElement(row, col);
|
|
||||||
hasSelectedTile = false;
|
|
||||||
addGrid();
|
|
||||||
} else {
|
|
||||||
selectedTile = SpriteComponent()
|
|
||||||
..x = gridOffsetX + col * tileSize
|
|
||||||
..y = gridOffsetY + row * tileSize
|
|
||||||
..width = tileSize
|
|
||||||
..height = tileSize;
|
|
||||||
hasSelectedTile = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
lib/game/sprite_loader.dart
Normal file
11
lib/game/sprite_loader.dart
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import 'package:flame/components.dart';
|
||||||
|
|
||||||
|
class SpriteLoader {
|
||||||
|
static Future<List<Sprite>> loadSprites() async {
|
||||||
|
List<Sprite> sprites = [];
|
||||||
|
for (int i = 1; i <= 7; i++) {
|
||||||
|
sprites.add(await Sprite.load('crystal$i.png'));
|
||||||
|
}
|
||||||
|
return sprites;
|
||||||
|
}
|
||||||
|
}
|
26
lib/game/swap_notifier.dart
Normal file
26
lib/game/swap_notifier.dart
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'board.dart';
|
||||||
|
import 'tile.dart';
|
||||||
|
|
||||||
|
class SwapNotifier extends ChangeNotifier {
|
||||||
|
Tile? selectedTile;
|
||||||
|
|
||||||
|
void selectTile(Tile tile, Board board) {
|
||||||
|
if (selectedTile == null) {
|
||||||
|
selectedTile = tile;
|
||||||
|
} else {
|
||||||
|
if (_isNeighbor(selectedTile!, tile)) {
|
||||||
|
board.swapTiles(selectedTile!, tile);
|
||||||
|
selectedTile = null;
|
||||||
|
} else {
|
||||||
|
selectedTile = tile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isNeighbor(Tile tile1, Tile tile2) {
|
||||||
|
return (tile1.row == tile2.row && (tile1.col - tile2.col).abs() == 1) ||
|
||||||
|
(tile1.col == tile2.col && (tile1.row - tile2.row).abs() == 1);
|
||||||
|
}
|
||||||
|
}
|
36
lib/game/tile.dart
Normal file
36
lib/game/tile.dart
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flame/effects.dart';
|
||||||
|
import 'package:flame/events.dart';
|
||||||
|
import 'package:flame/input.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class Tile extends SpriteComponent with TapCallbacks {
|
||||||
|
int row;
|
||||||
|
int col;
|
||||||
|
int spriteIndex;
|
||||||
|
final void Function(Tile) onTileTap;
|
||||||
|
|
||||||
|
Tile({
|
||||||
|
required Sprite sprite,
|
||||||
|
required this.spriteIndex,
|
||||||
|
required Vector2 size,
|
||||||
|
required Vector2 position,
|
||||||
|
required this.row,
|
||||||
|
required this.col,
|
||||||
|
required this.onTileTap,
|
||||||
|
}) : super(sprite: sprite, size: size, position: position);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool onTapDown(TapDownEvent event) {
|
||||||
|
onTileTap(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void animateMoveTo(Vector2 newPosition, VoidCallback onComplete) {
|
||||||
|
add(MoveEffect.to(
|
||||||
|
newPosition,
|
||||||
|
EffectController(duration: 0.5),
|
||||||
|
onComplete: onComplete,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,16 @@
|
|||||||
|
import 'package:flame/sprite.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:match_magic/game/sprite_loader.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:match_magic/screen/game_screen.dart';
|
import 'game/match_magic_game.dart';
|
||||||
import 'package:match_magic/models/game_state.dart';
|
import 'game/swap_notifier.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(
|
runApp(
|
||||||
ChangeNotifierProvider(
|
MultiProvider(
|
||||||
create: (context) => GameState(),
|
providers: [
|
||||||
|
ChangeNotifierProvider(create: (_) => SwapNotifier()),
|
||||||
|
],
|
||||||
child: const MyApp(),
|
child: const MyApp(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -22,7 +26,34 @@ class MyApp extends StatelessWidget {
|
|||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
primarySwatch: Colors.blue,
|
primarySwatch: Colors.blue,
|
||||||
),
|
),
|
||||||
home: const GameScreen(),
|
home: const Scaffold(
|
||||||
|
body: MatchMagicGameScreen(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MatchMagicGameScreen extends StatelessWidget {
|
||||||
|
const MatchMagicGameScreen({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FutureBuilder<List<Sprite>>(
|
||||||
|
future: SpriteLoader.loadSprites(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
|
if (snapshot.hasError) {
|
||||||
|
return Center(child: Text('Error: ${snapshot.error}'));
|
||||||
|
} else if (!snapshot.hasData || snapshot.data == null) {
|
||||||
|
return const Center(child: Text('No sprites found'));
|
||||||
|
} else {
|
||||||
|
final sprites = snapshot.data!;
|
||||||
|
return MatchMagicGame(sprites: sprites);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,163 +0,0 @@
|
|||||||
import 'dart:math';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class GameState extends ChangeNotifier {
|
|
||||||
final int rows = 8;
|
|
||||||
final int cols = 8;
|
|
||||||
late List<List<int>> grid;
|
|
||||||
int? selectedRow;
|
|
||||||
int? selectedCol;
|
|
||||||
|
|
||||||
GameState() {
|
|
||||||
_initializeGrid();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _initializeGrid() {
|
|
||||||
grid = List.generate(
|
|
||||||
rows, (i) => List.generate(cols, (j) => _randomElement()));
|
|
||||||
_removeInitialMatches();
|
|
||||||
}
|
|
||||||
|
|
||||||
int _randomElement() {
|
|
||||||
return Random().nextInt(6) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _removeInitialMatches() {
|
|
||||||
bool hasMatches;
|
|
||||||
do {
|
|
||||||
hasMatches = false;
|
|
||||||
for (int i = 0; i < rows; i++) {
|
|
||||||
for (int j = 0; j < cols; j++) {
|
|
||||||
if (_hasMatch(i, j)) {
|
|
||||||
grid[i][j] = _randomElement();
|
|
||||||
hasMatches = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (hasMatches);
|
|
||||||
}
|
|
||||||
|
|
||||||
void selectElement(int row, int col) {
|
|
||||||
if (selectedRow == null || selectedCol == null) {
|
|
||||||
selectedRow = row;
|
|
||||||
selectedCol = col;
|
|
||||||
} else {
|
|
||||||
if (_isAdjacent(selectedRow!, selectedCol!, row, col)) {
|
|
||||||
swapElements(selectedRow!, selectedCol!, row, col);
|
|
||||||
Future.delayed(const Duration(milliseconds: 300), () {
|
|
||||||
if (!_checkMatches()) {
|
|
||||||
swapElements(row, col, selectedRow!, selectedCol!);
|
|
||||||
}
|
|
||||||
selectedRow = null;
|
|
||||||
selectedCol = null;
|
|
||||||
notifyListeners();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
selectedRow = row;
|
|
||||||
selectedCol = col;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _isAdjacent(int row1, int col1, int row2, int col2) {
|
|
||||||
return (row1 == row2 && (col1 - col2).abs() == 1) ||
|
|
||||||
(col1 == col2 && (row1 - row2).abs() == 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void swapElements(int row1, int col1, int row2, int col2) {
|
|
||||||
final temp = grid[row1][col1];
|
|
||||||
grid[row1][col1] = grid[row2][col2];
|
|
||||||
grid[row2][col2] = temp;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _checkMatches() {
|
|
||||||
final matches = <List<int>>[];
|
|
||||||
for (int i = 0; i < rows; i++) {
|
|
||||||
for (int j = 0; j < cols; j++) {
|
|
||||||
if (_hasMatch(i, j)) {
|
|
||||||
matches.add([i, j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matches.isNotEmpty) {
|
|
||||||
for (final match in matches) {
|
|
||||||
_removeMatchedElements(match[0], match[1]);
|
|
||||||
}
|
|
||||||
_applyGravity();
|
|
||||||
Future.delayed(const Duration(milliseconds: 300), () {
|
|
||||||
_fillEmptySpaces();
|
|
||||||
_checkMatches();
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _hasMatch(int row, int col) {
|
|
||||||
final value = grid[row][col];
|
|
||||||
if (value == 0) return false;
|
|
||||||
|
|
||||||
int count = 1;
|
|
||||||
for (int i = col + 1; i < cols && grid[row][i] == value; i++) count++;
|
|
||||||
for (int i = col - 1; i >= 0 && grid[row][i] == value; i--) count++;
|
|
||||||
if (count >= 3) return true;
|
|
||||||
|
|
||||||
count = 1;
|
|
||||||
for (int i = row + 1; i < rows && grid[i][col] == value; i++) count++;
|
|
||||||
for (int i = row - 1; i >= 0 && grid[i][col] == value; i--) count++;
|
|
||||||
return count >= 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _removeMatchedElements(int row, int col) {
|
|
||||||
final value = grid[row][col];
|
|
||||||
|
|
||||||
int left = col;
|
|
||||||
while (left > 0 && grid[row][left - 1] == value) left--;
|
|
||||||
int right = col;
|
|
||||||
while (right < cols - 1 && grid[row][right + 1] == value) right++;
|
|
||||||
if (right - left + 1 >= 3) {
|
|
||||||
for (int i = left; i <= right; i++) {
|
|
||||||
grid[row][i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int top = row;
|
|
||||||
while (top > 0 && grid[top - 1][col] == value) top--;
|
|
||||||
int bottom = row;
|
|
||||||
while (bottom < rows - 1 && grid[bottom + 1][col] == value) bottom++;
|
|
||||||
if (bottom - top + 1 >= 3) {
|
|
||||||
for (int i = top; i <= bottom; i++) {
|
|
||||||
grid[i][col] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _applyGravity() {
|
|
||||||
for (int col = 0; col < cols; col++) {
|
|
||||||
int emptyRow = rows - 1;
|
|
||||||
for (int row = rows - 1; row >= 0; row--) {
|
|
||||||
if (grid[row][col] != 0) {
|
|
||||||
if (row != emptyRow) {
|
|
||||||
grid[emptyRow][col] = grid[row][col];
|
|
||||||
grid[row][col] = 0;
|
|
||||||
}
|
|
||||||
emptyRow--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _fillEmptySpaces() {
|
|
||||||
for (int col = 0; col < cols; col++) {
|
|
||||||
for (int row = rows - 1; row >= 0; row--) {
|
|
||||||
if (grid[row][col] == 0) {
|
|
||||||
grid[row][col] = _randomElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
import 'package:flame/game.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:match_magic/models/game_state.dart';
|
|
||||||
import 'package:match_magic/game/match_magic_game.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
|
|
||||||
class GameScreen extends StatelessWidget {
|
|
||||||
const GameScreen({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
body: Consumer<GameState>(
|
|
||||||
builder: (context, gameState, child) {
|
|
||||||
return GameWidget<MatchMagicGame>(
|
|
||||||
game: MatchMagicGame(gameState),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -68,7 +68,7 @@ flutter:
|
|||||||
- assets/images/crystal4.png
|
- assets/images/crystal4.png
|
||||||
- assets/images/crystal5.png
|
- assets/images/crystal5.png
|
||||||
- assets/images/crystal6.png
|
- assets/images/crystal6.png
|
||||||
- assets/images/crystal0.png
|
- assets/images/crystal7.png
|
||||||
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
# https://flutter.dev/assets-and-images/#resolution-aware
|
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||||
|
Loading…
Reference in New Issue
Block a user