Adding bombs and magic cubes.
This commit is contained in:
parent
c6b5ed65d9
commit
9ecf924a8c
@ -2,9 +2,9 @@ import 'dart:math';
|
|||||||
import 'package:flame/components.dart';
|
import 'package:flame/components.dart';
|
||||||
import 'package:flame/effects.dart';
|
import 'package:flame/effects.dart';
|
||||||
import 'package:flame/game.dart';
|
import 'package:flame/game.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'tile.dart';
|
import 'tile.dart';
|
||||||
import 'package:flame/sprite.dart';
|
import 'package:flame/sprite.dart';
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'swap_notifier.dart';
|
import 'swap_notifier.dart';
|
||||||
|
|
||||||
class Board extends FlameGame {
|
class Board extends FlameGame {
|
||||||
@ -17,6 +17,7 @@ class Board extends FlameGame {
|
|||||||
int? selectedRow;
|
int? selectedRow;
|
||||||
int? selectedCol;
|
int? selectedCol;
|
||||||
bool animating = false;
|
bool animating = false;
|
||||||
|
Tile? lastMovedTile;
|
||||||
|
|
||||||
Board({required this.sprites, required this.swapNotifier});
|
Board({required this.sprites, required this.swapNotifier});
|
||||||
|
|
||||||
@ -64,7 +65,7 @@ class Board extends FlameGame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int _randomElement() {
|
int _randomElement() {
|
||||||
return Random().nextInt(7);
|
return Random().nextInt(sprites.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _removeInitialMatches() {
|
void _removeInitialMatches() {
|
||||||
@ -97,11 +98,12 @@ class Board extends FlameGame {
|
|||||||
} else {
|
} else {
|
||||||
tiles[selectedRow!][selectedCol!]?.deselect();
|
tiles[selectedRow!][selectedCol!]?.deselect();
|
||||||
if (_isAdjacent(selectedRow!, selectedCol!, row, col)) {
|
if (_isAdjacent(selectedRow!, selectedCol!, row, col)) {
|
||||||
swapTiles(tiles[selectedRow!]![selectedCol!]!, tiles[row]![col]!, true);
|
lastMovedTile = tiles[selectedRow!][selectedCol!];
|
||||||
|
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(
|
swapTiles(
|
||||||
tiles[row]![col]!, tiles[selectedRow!]![selectedCol!]!, true);
|
tiles[row][col]!, tiles[selectedRow!][selectedCol!]!, true);
|
||||||
}
|
}
|
||||||
selectedRow = null;
|
selectedRow = null;
|
||||||
selectedCol = null;
|
selectedCol = null;
|
||||||
@ -138,12 +140,13 @@ class Board extends FlameGame {
|
|||||||
|
|
||||||
if (targetTile != null) {
|
if (targetTile != null) {
|
||||||
animating = true;
|
animating = true;
|
||||||
|
lastMovedTile = tile;
|
||||||
swapTiles(tile, targetTile, true);
|
swapTiles(tile, targetTile, true);
|
||||||
|
|
||||||
await Future.delayed(const Duration(milliseconds: 300));
|
await Future.delayed(const Duration(milliseconds: 300));
|
||||||
|
|
||||||
if (!checkMatches()) {
|
if (!checkMatches()) {
|
||||||
swapTiles(tile, targetTile!, true);
|
swapTiles(tile, targetTile, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
animating = false;
|
animating = false;
|
||||||
@ -213,59 +216,89 @@ class Board extends FlameGame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
int _removeMatchedElements(int row, int col) {
|
int _removeMatchedElements(int row, int col) {
|
||||||
int score = 0;
|
int score = 0;
|
||||||
final value = tiles[row]?[col]?.spriteIndex;
|
final int? value = tiles[row][col]?.spriteIndex;
|
||||||
|
bool transformToBomb = false;
|
||||||
|
|
||||||
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) {
|
||||||
int matchLength = right - left + 1;
|
int matchLength = right - left + 1;
|
||||||
score += _calculateScore(matchLength);
|
score += _calculateScore(matchLength);
|
||||||
|
|
||||||
|
if (matchLength == 4 &&
|
||||||
|
lastMovedTile != null &&
|
||||||
|
lastMovedTile!.row == row &&
|
||||||
|
lastMovedTile!.col == col) {
|
||||||
|
transformToBomb = true;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = left; i <= right; i++) {
|
for (int i = left; i <= right; i++) {
|
||||||
if (tiles[row]?[i] != null) {
|
if (tiles[row][i] != null &&
|
||||||
_animateRemoveTile(tiles[row]![i]!);
|
(!transformToBomb || tiles[row][i] != lastMovedTile)) {
|
||||||
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) {
|
||||||
int matchLength = bottom - top + 1;
|
int matchLength = bottom - top + 1;
|
||||||
score += _calculateScore(matchLength);
|
score += _calculateScore(matchLength);
|
||||||
|
|
||||||
|
if (matchLength == 4 &&
|
||||||
|
lastMovedTile != null &&
|
||||||
|
lastMovedTile!.row == row &&
|
||||||
|
lastMovedTile!.col == col) {
|
||||||
|
transformToBomb = true;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = top; i <= bottom; i++) {
|
for (int i = top; i <= bottom; i++) {
|
||||||
if (tiles[i]?[col] != null) {
|
if (tiles[i][col] != null &&
|
||||||
_animateRemoveTile(tiles[i]![col]!);
|
(!transformToBomb || tiles[i][col] != lastMovedTile)) {
|
||||||
tiles[i]![col] = null;
|
_animateRemoveTile(tiles[i][col]!);
|
||||||
|
tiles[i][col] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (transformToBomb && lastMovedTile != null) {
|
||||||
|
_createBomb(lastMovedTile!.row, lastMovedTile!.col);
|
||||||
|
} else {
|
||||||
|
if (tiles[row][col] != null) {
|
||||||
|
_animateRemoveTile(tiles[row][col]!);
|
||||||
|
tiles[row][col] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,6 +307,8 @@ class Board extends FlameGame {
|
|||||||
return 50;
|
return 50;
|
||||||
} else if (matchLength == 4) {
|
} else if (matchLength == 4) {
|
||||||
return 100;
|
return 100;
|
||||||
|
} else if (matchLength == 5) {
|
||||||
|
return 200;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -284,16 +319,113 @@ class Board extends FlameGame {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _createBomb(int row, int col) {
|
||||||
|
final tile = tiles[row][col]!;
|
||||||
|
tile.isBomb = true;
|
||||||
|
|
||||||
|
tile.add(
|
||||||
|
OpacityEffect.to(
|
||||||
|
0.5,
|
||||||
|
EffectController(
|
||||||
|
duration: 0.5,
|
||||||
|
infinite: true,
|
||||||
|
reverseDuration: 0.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
tile.add(
|
||||||
|
ColorEffect(
|
||||||
|
Colors.orange,
|
||||||
|
EffectController(
|
||||||
|
duration: 0.5,
|
||||||
|
infinite: true,
|
||||||
|
reverseDuration: 0.5,
|
||||||
|
),
|
||||||
|
opacityFrom: 0.5,
|
||||||
|
opacityTo: 1.0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _createMagicCube(int row, int col) {
|
||||||
|
final tile = tiles[row][col]!;
|
||||||
|
tile.isMagicCube = true;
|
||||||
|
|
||||||
|
tile.add(
|
||||||
|
OpacityEffect.to(
|
||||||
|
0.5,
|
||||||
|
EffectController(
|
||||||
|
duration: 0.5,
|
||||||
|
infinite: true,
|
||||||
|
reverseDuration: 0.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
tile.add(
|
||||||
|
ColorEffect(
|
||||||
|
Colors.purple,
|
||||||
|
EffectController(
|
||||||
|
duration: 0.5,
|
||||||
|
infinite: true,
|
||||||
|
reverseDuration: 0.5,
|
||||||
|
),
|
||||||
|
opacityFrom: 0.5,
|
||||||
|
opacityTo: 1.0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void explodeBomb(Tile bombTile) {
|
||||||
|
final bombPosition = bombTile.position;
|
||||||
|
final bombRow = bombTile.row;
|
||||||
|
final bombCol = bombTile.col;
|
||||||
|
|
||||||
|
for (int rowOffset = -1; rowOffset <= 1; rowOffset++) {
|
||||||
|
for (int colOffset = -1; colOffset <= 1; colOffset++) {
|
||||||
|
final row = bombRow + rowOffset;
|
||||||
|
final col = bombCol + colOffset;
|
||||||
|
|
||||||
|
if (row >= 0 && row < rows && col >= 0 && col < cols) {
|
||||||
|
final tile = tiles[row][col];
|
||||||
|
if (tile != null && tile != bombTile) {
|
||||||
|
_animateRemoveTile(tile);
|
||||||
|
tiles[row][col] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bombTile.add(RemoveEffect(
|
||||||
|
delay: 0.5,
|
||||||
|
onComplete: () => remove(bombTile),
|
||||||
|
));
|
||||||
|
|
||||||
|
final explosion = CircleComponent(
|
||||||
|
radius: tileSize / 2,
|
||||||
|
paint: Paint()..color = Colors.orange.withOpacity(0.7),
|
||||||
|
position: bombPosition,
|
||||||
|
);
|
||||||
|
add(explosion);
|
||||||
|
|
||||||
|
explosion.add(ScaleEffect.to(
|
||||||
|
Vector2.all(2),
|
||||||
|
EffectController(duration: 0.5),
|
||||||
|
onComplete: () => explosion.removeFromParent(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
void _applyGravity() {
|
void _applyGravity() {
|
||||||
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) {
|
||||||
for (int k = row - 1; k >= 0; k--) {
|
for (int k = row - 1; k >= 0; k--) {
|
||||||
if (tiles[k]?[col] != null) {
|
if (tiles[k][col] != null) {
|
||||||
tiles[row]![col] = tiles[k]![col]!;
|
tiles[row][col] = tiles[k][col]!;
|
||||||
tiles[k]![col] = null;
|
tiles[k][col] = null;
|
||||||
tiles[row]![col]!.row = row;
|
tiles[row][col]!.row = row;
|
||||||
tiles[row]![col]!.animateMoveTo(
|
tiles[row][col]!.animateMoveTo(
|
||||||
Vector2(col * tileSize, row * tileSize), () {});
|
Vector2(col * tileSize, row * tileSize), () {});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -306,7 +438,7 @@ 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],
|
||||||
|
@ -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:match_magic/game/sprite_loader.dart';
|
import 'package:match_magic/game/sprite_loader.dart';
|
||||||
import 'package:match_magic/main.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'board.dart';
|
import 'board.dart';
|
||||||
import 'swap_notifier.dart';
|
import 'swap_notifier.dart';
|
||||||
@ -48,6 +47,9 @@ class _MatchMagicGameScreenState extends State<MatchMagicGameScreen> {
|
|||||||
sprites: sprites, swapNotifier: context.read<SwapNotifier>());
|
sprites: sprites, swapNotifier: context.read<SwapNotifier>());
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 50,
|
||||||
|
),
|
||||||
const ScoreDisplay(),
|
const ScoreDisplay(),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Center(
|
child: Center(
|
||||||
@ -59,8 +61,14 @@ class _MatchMagicGameScreenState extends State<MatchMagicGameScreen> {
|
|||||||
),
|
),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: _restartGame,
|
onPressed: _restartGame,
|
||||||
child: const Text('Restart'),
|
child: const Text(
|
||||||
|
'Restart',
|
||||||
|
style: TextStyle(color: Colors.black),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 50,
|
||||||
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -72,19 +80,6 @@ class _MatchMagicGameScreenState extends State<MatchMagicGameScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MatchMagicGame extends StatelessWidget {
|
|
||||||
final List<Sprite> sprites;
|
|
||||||
|
|
||||||
const MatchMagicGame({required this.sprites, Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return GameWidget(
|
|
||||||
game: Board(sprites: sprites, swapNotifier: context.read<SwapNotifier>()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ScoreDisplay extends StatelessWidget {
|
class ScoreDisplay extends StatelessWidget {
|
||||||
const ScoreDisplay({Key? key}) : super(key: key);
|
const ScoreDisplay({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@ -96,7 +91,8 @@ class ScoreDisplay extends StatelessWidget {
|
|||||||
builder: (context, notifier, child) {
|
builder: (context, notifier, child) {
|
||||||
return Text(
|
return Text(
|
||||||
'Score: ${notifier.score}',
|
'Score: ${notifier.score}',
|
||||||
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
style: const TextStyle(
|
||||||
|
fontSize: 24, fontWeight: FontWeight.bold, color: Colors.white),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -2,6 +2,7 @@ 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:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'board.dart';
|
||||||
|
|
||||||
class Tile extends SpriteComponent with TapCallbacks, DragCallbacks {
|
class Tile extends SpriteComponent with TapCallbacks, DragCallbacks {
|
||||||
int row;
|
int row;
|
||||||
@ -10,6 +11,8 @@ class Tile extends SpriteComponent with TapCallbacks, DragCallbacks {
|
|||||||
final void Function(Tile) onTileTap;
|
final void Function(Tile) onTileTap;
|
||||||
final void Function(Tile, Vector2) onSwipe;
|
final void Function(Tile, Vector2) onSwipe;
|
||||||
bool isSelected = false;
|
bool isSelected = false;
|
||||||
|
bool isBomb = false;
|
||||||
|
bool isMagicCube = false;
|
||||||
Vector2? startDragPosition;
|
Vector2? startDragPosition;
|
||||||
|
|
||||||
Tile({
|
Tile({
|
||||||
@ -25,18 +28,30 @@ class Tile extends SpriteComponent with TapCallbacks, DragCallbacks {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool onTapDown(TapDownEvent event) {
|
bool onTapDown(TapDownEvent event) {
|
||||||
onTileTap(this);
|
if (parent is Board && (parent as Board).animating) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isBomb) {
|
||||||
|
(parent as Board).explodeBomb(this);
|
||||||
|
} else {
|
||||||
|
onTileTap(this);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool onDragStart(DragStartEvent event) {
|
bool onDragStart(DragStartEvent event) {
|
||||||
|
super.onDragStart(event);
|
||||||
|
if (parent is Board && (parent as Board).animating) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
startDragPosition = event.localPosition;
|
startDragPosition = event.localPosition;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool onDragEnd(DragEndEvent event) {
|
bool onDragEnd(DragEndEvent event) {
|
||||||
|
super.onDragEnd(event);
|
||||||
if (startDragPosition != null) {
|
if (startDragPosition != null) {
|
||||||
final delta = event.velocity;
|
final delta = event.velocity;
|
||||||
onSwipe(this, delta);
|
onSwipe(this, delta);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
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:flutter/services.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'game/match_magic_game.dart';
|
import 'game/match_magic_game.dart';
|
||||||
import 'game/swap_notifier.dart';
|
import 'game/swap_notifier.dart';
|
||||||
@ -14,6 +13,7 @@ void main() {
|
|||||||
child: const MyApp(),
|
child: const MyApp(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
@ -27,6 +27,7 @@ class MyApp extends StatelessWidget {
|
|||||||
primarySwatch: Colors.blue,
|
primarySwatch: Colors.blue,
|
||||||
),
|
),
|
||||||
home: const Scaffold(
|
home: const Scaffold(
|
||||||
|
backgroundColor: Colors.black,
|
||||||
body: MatchMagicGameScreen(),
|
body: MatchMagicGameScreen(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user