Changed the behaviour of elements on the field.

This commit is contained in:
Alex Vasilev 2024-08-06 21:00:51 +03:00
parent 2c5aec71cb
commit 5d08e84407
5 changed files with 108 additions and 38 deletions

View File

@ -1,9 +1,8 @@
import 'dart:math'; import 'dart:math';
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:flame/effects.dart';
import 'package:flame/game.dart'; import 'package:flame/game.dart';
import 'package:provider/provider.dart';
import 'tile.dart'; import 'tile.dart';
import 'swap_notifier.dart';
import 'package:flame/sprite.dart'; import 'package:flame/sprite.dart';
class Board extends FlameGame { class Board extends FlameGame {
@ -14,6 +13,7 @@ class Board extends FlameGame {
List<List<Tile?>> tiles = []; List<List<Tile?>> tiles = [];
int? selectedRow; int? selectedRow;
int? selectedCol; int? selectedCol;
bool animating = false;
Board({required this.sprites}); Board({required this.sprites});
@ -69,23 +69,30 @@ class Board extends FlameGame {
} }
void handleTileTap(Tile tappedTile) { void handleTileTap(Tile tappedTile) {
if (animating) return;
int row = tappedTile.row; int row = tappedTile.row;
int col = tappedTile.col; int col = tappedTile.col;
if (selectedRow == null || selectedCol == null) { if (selectedRow == null || selectedCol == null) {
tappedTile.select();
selectedRow = row; selectedRow = row;
selectedCol = col; selectedCol = col;
} else { } else {
tiles[selectedRow!][selectedCol!]?.deselect();
if (_isAdjacent(selectedRow!, selectedCol!, row, col)) { if (_isAdjacent(selectedRow!, selectedCol!, row, col)) {
swapTiles(tiles[selectedRow!][selectedCol!]!, tiles[row][col]!); swapTiles(tiles[selectedRow!]![selectedCol!]!, tiles[row]![col]!, true);
Future.delayed(const Duration(milliseconds: 300), () { Future.delayed(const Duration(milliseconds: 300), () {
if (!_checkMatches()) { if (!_checkMatches()) {
swapTiles(tiles[row][col]!, tiles[selectedRow!][selectedCol!]!); swapTiles(
tiles[row]![col]!, tiles[selectedRow!]![selectedCol!]!, true);
} }
selectedRow = null; selectedRow = null;
selectedCol = null; selectedCol = null;
}); });
} else { } else {
tiles[selectedRow!][selectedCol!]?.deselect();
tappedTile.select();
selectedRow = row; selectedRow = row;
selectedCol = col; selectedCol = col;
} }
@ -97,7 +104,7 @@ class Board extends FlameGame {
(col1 == col2 && (row1 - row2).abs() == 1); (col1 == col2 && (row1 - row2).abs() == 1);
} }
void swapTiles(Tile tile1, Tile tile2) { void swapTiles(Tile tile1, Tile tile2, bool animate) {
final tempPosition = tile1.position; final tempPosition = tile1.position;
final tempRow = tile1.row; final tempRow = tile1.row;
final tempCol = tile1.col; final tempCol = tile1.col;
@ -113,13 +120,16 @@ class Board extends FlameGame {
tiles[tile1.row][tile1.col] = tile1; tiles[tile1.row][tile1.col] = tile1;
tiles[tile2.row][tile2.col] = tile2; tiles[tile2.row][tile2.col] = tile2;
tile1.animateMoveTo(tile1.position, () {}); if (animate) {
tile2.animateMoveTo(tile2.position, () {}); tile1.animateMoveTo(tile1.position, () {});
tile2.animateMoveTo(tile2.position, () {});
_checkMatches(); }
} }
bool _checkMatches() { bool _checkMatches() {
if (animating) return false;
animating = true;
final matches = <List<int>>[]; final matches = <List<int>>[];
for (int row = 0; row < rows; row++) { for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) { for (int col = 0; col < cols; col++) {
@ -133,71 +143,92 @@ class Board extends FlameGame {
for (final match in matches) { for (final match in matches) {
_removeMatchedElements(match[0], match[1]); _removeMatchedElements(match[0], match[1]);
} }
_applyGravity();
Future.delayed(const Duration(milliseconds: 300), () { Future.delayed(const Duration(milliseconds: 300), () {
_fillEmptySpaces(); _applyGravity();
_checkMatches(); Future.delayed(const Duration(milliseconds: 300), () {
_fillEmptySpaces();
Future.delayed(const Duration(milliseconds: 300), () {
animating = false;
_checkMatches();
});
});
}); });
return true; return true;
} }
animating = false;
return false; return false;
} }
bool _hasMatch(int row, int col) { bool _hasMatch(int row, int col) {
final value = tiles[row][col]!.spriteIndex; final value = tiles[row]?[col]?.spriteIndex;
int count = 1; int count = 1;
for (int i = col + 1; i < cols && tiles[row][i]!.spriteIndex == value; i++) for (int i = col + 1; i < cols && tiles[row]?[i]?.spriteIndex == value; i++)
count++; count++;
for (int i = col - 1; i >= 0 && tiles[row][i]!.spriteIndex == value; i--) for (int i = col - 1; i >= 0 && tiles[row]?[i]?.spriteIndex == value; i--)
count++; count++;
if (count >= 3) return true; if (count >= 3) return true;
count = 1; count = 1;
for (int i = row + 1; i < rows && tiles[i][col]!.spriteIndex == value; i++) for (int i = row + 1; i < rows && tiles[i]?[col]?.spriteIndex == value; i++)
count++; count++;
for (int i = row - 1; i >= 0 && tiles[i][col]!.spriteIndex == value; i--) for (int i = row - 1; i >= 0 && tiles[i]?[col]?.spriteIndex == value; i--)
count++; count++;
return count >= 3; return count >= 3;
} }
void _removeMatchedElements(int row, int col) { void _removeMatchedElements(int row, int col) {
final value = tiles[row][col]!.spriteIndex; final value = tiles[row]?[col]?.spriteIndex;
int left = col; int left = col;
while (left > 0 && tiles[row][left - 1]!.spriteIndex == value) left--; while (left > 0 && tiles[row]?[left - 1]?.spriteIndex == value) left--;
int right = col; int right = col;
while (right < cols - 1 && tiles[row][right + 1]!.spriteIndex == value) while (right < cols - 1 && tiles[row]?[right + 1]?.spriteIndex == value)
right++; right++;
if (right - left + 1 >= 3) { if (right - left + 1 >= 3) {
for (int i = left; i <= right; i++) { for (int i = left; i <= right; i++) {
tiles[row][i] = null; if (tiles[row]?[i] != null) {
_animateRemoveTile(tiles[row]![i]!);
tiles[row]![i] = null;
}
} }
} }
int top = row; int top = row;
while (top > 0 && tiles[top - 1][col]!.spriteIndex == value) top--; while (top > 0 && tiles[top - 1]?[col]?.spriteIndex == value) top--;
int bottom = row; int bottom = row;
while (bottom < rows - 1 && tiles[bottom + 1][col]!.spriteIndex == value) while (bottom < rows - 1 && tiles[bottom + 1]?[col]?.spriteIndex == value)
bottom++; bottom++;
if (bottom - top + 1 >= 3) { if (bottom - top + 1 >= 3) {
for (int i = top; i <= bottom; i++) { for (int i = top; i <= bottom; i++) {
tiles[i][col] = null; if (tiles[i]?[col] != null) {
_animateRemoveTile(tiles[i]![col]!);
tiles[i]![col] = null;
}
} }
} }
} }
void _animateRemoveTile(Tile tile) {
tile.animateRemove(() {
remove(tile);
});
}
void _applyGravity() { void _applyGravity() {
for (int col = 0; col < cols; col++) { for (int col = 0; col < cols; col++) {
int emptyRow = rows - 1;
for (int row = rows - 1; row >= 0; row--) { for (int row = rows - 1; row >= 0; row--) {
if (tiles[row][col] != null) { if (tiles[row]?[col] == null) {
if (row != emptyRow) { for (int k = row - 1; k >= 0; k--) {
tiles[emptyRow][col] = tiles[row][col]; if (tiles[k]?[col] != null) {
tiles[emptyRow][col]!.row = emptyRow; tiles[row]![col] = tiles[k]![col]!;
tiles[row][col] = null; tiles[k]![col] = null;
tiles[row]![col]!.row = row;
tiles[row]![col]!.animateMoveTo(
Vector2(col * tileSize, row * tileSize), () {});
break;
}
} }
emptyRow--;
} }
} }
} }
@ -206,19 +237,20 @@ class Board extends FlameGame {
void _fillEmptySpaces() { void _fillEmptySpaces() {
for (int col = 0; col < cols; col++) { for (int col = 0; col < cols; col++) {
for (int row = rows - 1; row >= 0; row--) { for (int row = rows - 1; row >= 0; row--) {
if (tiles[row][col] == null) { if (tiles[row]?[col] == null) {
int spriteIndex = _randomElement(); int spriteIndex = _randomElement();
var tile = Tile( var tile = Tile(
sprite: sprites[spriteIndex], sprite: sprites[spriteIndex],
spriteIndex: spriteIndex, spriteIndex: spriteIndex,
size: Vector2.all(tileSize), size: Vector2.all(tileSize),
position: Vector2(col * tileSize, row * tileSize), position: Vector2(col * tileSize, -tileSize),
row: row, row: row,
col: col, col: col,
onTileTap: handleTileTap, onTileTap: handleTileTap,
); );
tiles[row][col] = tile; tiles[row][col] = tile;
add(tile); add(tile);
tile.animateMoveTo(Vector2(col * tileSize, row * tileSize), () {});
} }
} }
} }

View File

@ -2,7 +2,6 @@ import 'package:flame/components.dart';
import 'package:flame/game.dart'; import 'package:flame/game.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'sprite_loader.dart';
import 'board.dart'; import 'board.dart';
import 'swap_notifier.dart'; import 'swap_notifier.dart';

View File

@ -8,12 +8,15 @@ class SwapNotifier extends ChangeNotifier {
void selectTile(Tile tile, Board board) { void selectTile(Tile tile, Board board) {
if (selectedTile == null) { if (selectedTile == null) {
selectedTile = tile; selectedTile = tile;
tile.select();
} else { } else {
if (_isNeighbor(selectedTile!, tile)) { if (_isNeighbor(selectedTile!, tile)) {
board.swapTiles(selectedTile!, tile); board.swapTiles(selectedTile!, tile, true);
selectedTile = null; selectedTile = null;
} else { } else {
selectedTile?.deselect();
selectedTile = tile; selectedTile = tile;
tile.select();
} }
} }
notifyListeners(); notifyListeners();

View File

@ -1,7 +1,6 @@
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:flame/effects.dart'; import 'package:flame/effects.dart';
import 'package:flame/events.dart'; import 'package:flame/events.dart';
import 'package:flame/input.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class Tile extends SpriteComponent with TapCallbacks { class Tile extends SpriteComponent with TapCallbacks {
@ -9,6 +8,7 @@ class Tile extends SpriteComponent with TapCallbacks {
int col; int col;
int spriteIndex; int spriteIndex;
final void Function(Tile) onTileTap; final void Function(Tile) onTileTap;
bool isSelected = false;
Tile({ Tile({
required Sprite sprite, required Sprite sprite,
@ -26,10 +26,41 @@ class Tile extends SpriteComponent with TapCallbacks {
return true; return true;
} }
void select() {
isSelected = true;
updateBorder();
}
void deselect() {
isSelected = false;
updateBorder();
}
void updateBorder() {
if (isSelected) {
add(RectangleComponent(
size: size,
paint: Paint()
..color = Colors.yellow
..style = PaintingStyle.stroke
..strokeWidth = 3,
));
} else {
children.removeWhere((child) => child is RectangleComponent);
}
}
void animateMoveTo(Vector2 newPosition, VoidCallback onComplete) { void animateMoveTo(Vector2 newPosition, VoidCallback onComplete) {
add(MoveEffect.to( add(MoveEffect.to(
newPosition, newPosition,
EffectController(duration: 0.5), EffectController(duration: 0.3),
onComplete: onComplete,
));
}
void animateRemove(VoidCallback onComplete) {
add(RemoveEffect(
delay: 0.5,
onComplete: onComplete, onComplete: onComplete,
)); ));
} }

View File

@ -48,7 +48,12 @@ class MatchMagicGameScreen extends StatelessWidget {
return const Center(child: Text('No sprites found')); return const Center(child: Text('No sprites found'));
} else { } else {
final sprites = snapshot.data!; final sprites = snapshot.data!;
return MatchMagicGame(sprites: sprites); return Center(
child: AspectRatio(
aspectRatio: 1,
child: MatchMagicGame(sprites: sprites),
),
);
} }
} else { } else {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());