Skip to content
Snippets Groups Projects
Commit 00dba30b authored by Benoît Harrault's avatar Benoît Harrault
Browse files

Add animations on board game

parent 50a369ca
Branches
Tags Release_1.0.55_56
1 merge request!47Resolve "Add animations on board"
Pipeline #4878 passed
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
app.versionName=1.0.39
app.versionCode=40
app.versionName=1.0.40
app.versionCode=41
......@@ -2,15 +2,21 @@ import 'dart:convert';
import 'dart:math';
class GameDataItem {
final int value;
final int? value;
const GameDataItem({
required this.value,
});
factory GameDataItem.fromValue(int? value) {
return GameDataItem(
value: value,
);
}
factory GameDataItem.fromJson(Map<String, dynamic>? json) {
return GameDataItem(
value: (json?['value'] != null) ? (json?['value'] as int) : 0,
value: (json?['value'] != null) ? (json?['value'] as int) : null,
);
}
......@@ -26,10 +32,12 @@ class GameDataItem {
}
class GameData {
final bool isReady;
final int boardSize;
final List<List<GameDataItem>> board;
const GameData({
required this.isReady,
required this.boardSize,
required this.board,
});
......@@ -40,7 +48,7 @@ class GameData {
for (var y = 0; y < boardSize; y++) {
final List<GameDataItem> line = [];
for (var x = 0; x < boardSize; x++) {
final GameDataItem item = new GameDataItem(value: 0);
final GameDataItem item = new GameDataItem(value: null);
line.add(item);
}
......@@ -48,6 +56,7 @@ class GameData {
}
return GameData(
isReady: true,
boardSize: boardSize,
board: cells,
);
......@@ -71,13 +80,27 @@ class GameData {
}
return GameData(
isReady: true,
boardSize: boardSize,
board: cells,
);
}
GameDataItem getCell(int x, int y) {
return this.board[y][x];
}
int? getCellValue(int x, int y) {
return this.getCell(x, y).value;
}
void updateCellValue(int x, int y, int? value) {
this.board[y][x] = new GameDataItem.fromValue(value);
}
factory GameData.fromJson(Map<String, dynamic>? json) {
return GameData(
isReady: (json?['isReady'] != null) ? (json?['isReady'] as bool) : false,
boardSize: (json?['boardSize'] != null) ? (json?['boardSize'] as int) : 0,
board: (json?['board'] != null) ? (json?['board'] as List<List<GameDataItem>>) : [],
);
......@@ -85,6 +108,7 @@ class GameData {
Map<String, dynamic>? toJson() {
return <String, dynamic>{
'isReady': this.isReady,
'boardSize': this.boardSize,
'board': this.board,
};
......
......@@ -58,6 +58,6 @@ class CellPainter extends CustomPainter {
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
return true;
}
}
......@@ -13,12 +13,89 @@ class GamePage extends StatefulWidget {
State<GamePage> createState() => _GamePageState();
}
void createNewGame(GameCubit gameCubit, int boardSize) {
final GameData newGame = GameData.createRandom(boardSize);
gameCubit.updateGameState(newGame);
}
class _GamePageState extends State<GamePage> with TickerProviderStateMixin {
static const boardSize = 6;
List<List<Animation<double>?>> animations = List.generate(
boardSize,
(i) => List.generate(
boardSize,
(i) => null,
),
);
void resetAnimations() {
animations = List.generate(
boardSize,
(i) => List.generate(
boardSize,
(i) => null,
),
);
}
void createNewGame(GameCubit gameCubit) {
final GameData newGame = GameData.createRandom(boardSize);
gameCubit.updateGameState(newGame);
}
void removeCell(GameCubit gameCubit, int x, int y) {
final GameData newGame = gameCubit.state.game ?? GameData.createRandom(boardSize);
// "remove" cell
newGame.updateCellValue(x, y, null);
setState(() {});
// "move down" cells
final controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 750),
)..addListener(() {
if (mounted) {
setState(() {});
}
});
if (mounted) {
setState(() {});
}
Animation<double> animation = Tween(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
curve: Curves.bounceOut,
parent: controller,
))
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
for (var i = 0; i < y; i++) {
newGame.updateCellValue(x, (y - i), newGame.getCellValue(x, (y - i) - 1));
}
newGame.updateCellValue(x, 0, null);
resetAnimations();
setState(() {});
controller.dispose();
}
});
for (var i = 0; i < y; i++) {
animations[(y - i) - 1][x] = animation;
}
controller.forward().orCancel;
}
void updateCellValue(GameCubit gameCubit, int x, int y, int value) {
final GameData newGame = gameCubit.state.game ?? GameData.createRandom(boardSize);
newGame.updateCellValue(x, y, value);
gameCubit.updateGameState(newGame);
setState(() {});
}
class _GamePageState extends State<GamePage> {
@override
Widget build(BuildContext context) {
return BlocBuilder<GameCubit, GameState>(
......@@ -29,19 +106,36 @@ class _GamePageState extends State<GamePage> {
final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
if (gameState.game?.isReady != true) {
createNewGame(gameCubit);
}
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
gameState.game != null
? GameBoardWidget(
gameData: gameState.game!,
size: Size(boardWidgetWidth, boardWidgetHeight),
)
: SizedBox.shrink(),
GestureDetector(
child: gameState.game != null
? GameBoardWidget(
gameData: gameState.game!,
size: Size(boardWidgetWidth, boardWidgetHeight),
animations: animations,
)
: SizedBox.shrink(),
onTapUp: (details) {
double xTap = details.localPosition.dx;
double yTap = details.localPosition.dy;
int x = (xTap / boardWidgetWidth * boardSize).toInt();
int y = (yTap / boardWidgetHeight * boardSize).toInt();
print('[' + x.toString() + ',' + y.toString() + ']');
removeCell(gameCubit, x, y);
},
),
IconButton(
onPressed: () {
createNewGame(gameCubit, boardSize);
createNewGame(gameCubit);
},
icon: Icon(UniconsSolid.star),
color: Colors.white,
......
......@@ -3,64 +3,64 @@ import 'package:flutter/material.dart';
import 'package:random/models/game_data.dart';
import 'package:random/ui/painters/cell_painter.dart';
class GameBoardWidget extends StatefulWidget {
class GameBoardWidget extends StatelessWidget {
const GameBoardWidget({
super.key,
required this.gameData,
required this.size,
required this.animations,
});
final GameData gameData;
final Size size;
final List<List<Animation<double>?>> animations;
@override
State<GameBoardWidget> createState() => _GameBoardWidgetState();
}
class _GameBoardWidgetState extends State<GameBoardWidget> {
@override
Widget build(BuildContext context) {
final widgetWidth = widget.size.width;
final widgetHeight = widget.size.height;
final widgetWidth = this.size.width;
final widgetHeight = this.size.height;
final rowsCount = widget.gameData.board.length;
final columnsCount = widget.gameData.board[0].length;
print('counts: rows=' + rowsCount.toString() + ' / columns=' + columnsCount.toString());
final rowsCount = this.gameData.board.length;
final columnsCount = this.gameData.board[0].length;
final cellWidth = widgetWidth / columnsCount;
final cellHeight = widgetHeight / rowsCount;
print('cell: width=' + cellWidth.toString() + ' / height=' + cellHeight.toString());
final List<Widget> cells = [];
for (var y = 0; y < rowsCount; y++) {
for (var x = 0; x < columnsCount; x++) {
final GameDataItem item = widget.gameData.board[y][x];
final GameDataItem item = this.gameData.board[y][x];
int? value = item.value;
if (value != null) {
final Animation<double>? translation = this.animations[y][x];
final Widget cellContent = CustomPaint(
size: Size(cellWidth, cellHeight),
willChange: false,
painter: CellPainter(value: item.value),
);
final Widget cellContent = CustomPaint(
size: Size(cellWidth, cellHeight),
willChange: false,
painter: CellPainter(value: value),
);
final Widget cellWidget = Positioned(
left: (x * cellWidth).toDouble(),
top: (y * cellHeight).toDouble(),
child: Container(
width: cellWidth,
height: cellHeight,
child: cellContent,
),
);
final Widget cellWidget = Positioned(
left: (x * cellWidth).toDouble(),
top: ((y + (translation?.value ?? 0)) * cellHeight).toDouble(),
child: Container(
width: cellWidth,
height: cellHeight,
child: cellContent,
),
);
cells.add(cellWidget);
cells.add(cellWidget);
}
}
}
return Container(
width: widgetWidth,
height: widgetHeight,
color: Colors.grey,
color: Colors.black,
child: Stack(
children: cells,
),
......
......@@ -3,7 +3,7 @@ description: A random application, for testing purpose only.
publish_to: 'none'
version: 1.0.39+40
version: 1.0.40+41
environment:
sdk: '^3.0.0'
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment