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

Fix freeze on end game animation, clean some code, minor improvements

parent 1a753ff3
No related branches found
No related tags found
1 merge request!50Resolve "Fix freeze on end game animation"
Pipeline #1960 passed
This commit is part of merge request !50. Comments created here will be created in the context of that merge request.
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
app.versionName=0.0.45
app.versionCode=45
app.versionName=0.0.46
app.versionCode=46
Fix freeze on end game animation, clean some code, minor improvements
Correction du blocage sur l'animation de fin de partie, nettoyage de code, améliorations mineures
import 'package:flutter/material.dart';
import '../provider/data.dart';
import '../utils/board_animate.dart';
import '../utils/board_utils.dart';
class Cell {
int value;
bool isFixed;
int conflictsCount = 0;
bool isAnimated = false;
Cell(
......@@ -14,21 +16,16 @@ class Cell {
@required this.isFixed,
);
static Color cellBorderDarkColor = Colors.black;
static Color cellBorderLightColor = Colors.grey;
static Color cellBorderSelectedColor = Colors.red;
Container widget(Data myProvider, Border borders, int row, int col) {
String imageAsset = 'assets/skins/empty.png';
if (this.value > 0) {
int cellValue = myProvider.getTranslatedValueForDisplay(this.value);
imageAsset = 'assets/skins/' + myProvider.skin + '_' + cellValue.toString() + '.png';
}
/*
* Build widget for board cell, with interactions
*/
Container widget(Data myProvider, int row, int col) {
String imageAsset = this.getImageAssetName(myProvider);
return Container(
decoration: BoxDecoration(
color: this.getBackgroundColor(myProvider),
border: borders,
border: this.getCellBorders(myProvider, row, col),
),
child: GestureDetector(
child: AnimatedSwitcher(
......@@ -39,7 +36,7 @@ class Cell {
child: Image(
image: AssetImage(imageAsset),
fit: BoxFit.fill,
key: ValueKey<int>(this.value),
key: ValueKey<int>(imageAsset.hashCode),
),
),
onTap: () {
......@@ -55,69 +52,26 @@ class Cell {
);
}
Color getBackgroundColor(Data myProvider) {
Color editableCellColor = Colors.grey[100];
Color editableCellColorConflict = Colors.pink[100];
Color fixedCellColor = Colors.grey[300];
Color fixedCellColorConflict = Colors.pink[200];
Color editableSelectedValueColor = Colors.green[100];
Color fixedSelectedValueColor = Colors.green[300];
Color backgroundColor = editableCellColor;
if (this.isFixed) {
backgroundColor = fixedCellColor;
}
if (myProvider.showConflicts && (this.conflictsCount != 0)) {
if (this.isFixed) {
backgroundColor = fixedCellColorConflict;
} else {
backgroundColor = editableCellColorConflict;
}
}
if (myProvider.showConflicts && (this.value == myProvider.currentCellValue)) {
if (this.isFixed) {
backgroundColor = fixedSelectedValueColor;
} else {
backgroundColor = editableSelectedValueColor;
}
}
if (this.isAnimated) {
if (this.isFixed) {
backgroundColor = Colors.green[300];
} else {
backgroundColor = Colors.green[200];
}
}
return backgroundColor;
}
/*
* Build widget for select/update value cell, with interactions
*/
Container widgetUpdateValue(Data myProvider) {
if (this.value < 0) {
return Container();
}
String imageAsset = 'assets/skins/empty.png';
if (this.value > 0) {
int cellValue = myProvider.getTranslatedValueForDisplay(this.value);
imageAsset = 'assets/skins/' + myProvider.skin + '_' + cellValue.toString() + '.png';
}
String imageAsset = this.getImageAssetName(myProvider);
Color backgroundColor = Colors.grey[200];
List cells = myProvider.cells;
int blockSizeHorizontal = myProvider.blockSizeHorizontal;
int blockSizeVertical = myProvider.blockSizeVertical;
if (
myProvider.showConflicts
&& myProvider.currentCellCol != null
&& myProvider.currentCellRow != null
) {
List cells = myProvider.cells;
int blockSizeHorizontal = myProvider.blockSizeHorizontal;
int blockSizeVertical = myProvider.blockSizeVertical;
if (!BoardUtils.isValueAllowed(cells, blockSizeHorizontal, blockSizeVertical, myProvider.currentCellCol, myProvider.currentCellRow, this.value)) {
backgroundColor = Colors.pink[100];
}
......@@ -141,30 +95,102 @@ class Cell {
myProvider.updateCellValue(myProvider.currentCellCol, myProvider.currentCellRow, this.value);
}
myProvider.selectCell(null, null);
BoardUtils.computeConflictsInBoard(myProvider);
if (BoardUtils.checkBoardIsSolved(myProvider)) {
BoardAnimate.startAnimation(myProvider, 'win');
}
},
)
);
}
static Border getCellBorders(Data myProvider, int row, int col) {
/*
* Compute image asset name, from skin and cell value/state
*/
String getImageAssetName(Data myProvider) {
String imageAsset = 'assets/skins/empty.png';
if (this.value > 0) {
int cellValue = myProvider.getTranslatedValueForDisplay(this.value);
imageAsset = 'assets/skins/' + myProvider.skin + '_' + cellValue.toString() + '.png';
}
return imageAsset;
}
// Compute cell background color, from cell state
Color getBackgroundColor(Data myProvider) {
Color editableCellColor = Colors.grey[100];
Color editableCellColorConflict = Colors.pink[100];
Color fixedCellColor = Colors.grey[300];
Color fixedCellColorConflict = Colors.pink[200];
Color editableSelectedValueColor = Colors.green[100];
Color fixedSelectedValueColor = Colors.green[300];
Color editableAnimated = Colors.green[200];
Color fixedAnimated = Colors.green[300];
Color backgroundColor = editableCellColor;
if (this.isFixed) {
backgroundColor = fixedCellColor;
}
if (myProvider.showConflicts && (this.conflictsCount != 0)) {
if (this.isFixed) {
backgroundColor = fixedCellColorConflict;
} else {
backgroundColor = editableCellColorConflict;
}
}
if (myProvider.showConflicts && (this.value == myProvider.currentCellValue)) {
if (this.isFixed) {
backgroundColor = fixedSelectedValueColor;
} else {
backgroundColor = editableSelectedValueColor;
}
}
if (this.isAnimated) {
if (this.isFixed) {
backgroundColor = fixedAnimated;
} else {
backgroundColor = editableAnimated;
}
}
return backgroundColor;
}
// Compute cell borders, from board size and cell state
Border getCellBorders(Data myProvider, int row, int col) {
int blockSizeHorizontal = myProvider.blockSizeHorizontal;
int blockSizeVertical = myProvider.blockSizeVertical;
Color cellBorderDarkColor = Colors.black;
Color cellBorderLightColor = Colors.grey;
Color cellBorderSelectedColor = Colors.red;
Color cellBorderColor = cellBorderSelectedColor;
double cellBorderWidth = 4;
if (blockSizeVertical * blockSizeHorizontal > 8) {
// Reduce cell border width on big boards
int boardSize = blockSizeVertical * blockSizeHorizontal;
if (boardSize > 8) {
cellBorderWidth = 2;
if (blockSizeVertical * blockSizeHorizontal > 10) {
if (boardSize > 10) {
cellBorderWidth = 1;
}
}
if (!myProvider.gameIsRunning) {
cellBorderColor = Colors.green[700];
}
Border borders = Border.all(
color: cellBorderSelectedColor,
color: cellBorderColor,
width: cellBorderWidth,
);
// Update cell borders if not currently selected cell
if (col != myProvider.currentCellCol || row != myProvider.currentCellRow) {
borders = Border(
top: BorderSide(width: cellBorderWidth, color: ((row % blockSizeVertical) == 0) ? cellBorderDarkColor : cellBorderLightColor),
......@@ -176,5 +202,4 @@ class Cell {
return borders;
}
}
import 'dart:math';
import 'package:flutter/material.dart';
import '../entities/cell.dart';
import '../provider/data.dart';
import '../utils/board_utils.dart';
class Board {
static Container buildGameBoard(Data myProvider) {
Color borderColor = Colors.black;
return Container(
margin: EdgeInsets.all(2),
padding: EdgeInsets.all(2),
decoration: BoxDecoration(
color: Colors.black,
color: borderColor,
borderRadius: BorderRadius.circular(2),
border: Border.all(
color: Colors.black,
color: borderColor,
width: 2,
),
),
child: buildGameTileset(myProvider),
child: Column(
children: [
buildGameTileset(myProvider),
],
),
);
}
......@@ -38,7 +39,6 @@ class Board {
Column(children: [
cells[row][col].widget(
myProvider,
Cell.getCellBorders(myProvider, row, col),
row,
col
)
......
......@@ -5,7 +5,6 @@ import 'package:flutter/material.dart';
import '../entities/cell.dart';
import '../layout/board.dart';
import '../provider/data.dart';
import '../utils/board_animate.dart';
import '../utils/board_utils.dart';
import '../utils/game_utils.dart';
......@@ -13,24 +12,22 @@ class Game {
static Container buildGameWidget(Data myProvider) {
bool gameIsFinished = BoardUtils.checkBoardIsSolved(myProvider);
if (gameIsFinished) {
BoardAnimate.startAnimation(myProvider, 'win');
}
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Board.buildGameBoard(myProvider),
SizedBox(height: 2),
gameIsFinished ? Game.buildWinMessage(myProvider) : Game.buildSelectCellValueBar(myProvider),
gameIsFinished
? Game.buildEndGameMessage(myProvider)
: Game.buildSelectCellValueBar(myProvider),
],
),
);
}
static Container buildSelectCellValueBar(Data myProvider) {
List cells = myProvider.cells;
......@@ -74,15 +71,24 @@ class Game {
);
}
static Container buildWinMessage(Data myProvider) {
Column decorationImage = Column(
children: [
Image(
image: AssetImage('assets/icons/game_win.png'),
fit: BoxFit.fill
static FlatButton buildRestartGameButton(Data myProvider) {
return FlatButton(
child: Container(
child: Image(
image: AssetImage('assets/icons/button_back.png'),
fit: BoxFit.fill,
),
]
),
onPressed: () => GameUtils.resetGame(myProvider),
);
}
static Container buildEndGameMessage(Data myProvider) {
Image decorationImage = Image(
image: AssetImage(
'assets/icons/game_win.png'
),
fit: BoxFit.fill
);
return Container(
......@@ -94,21 +100,9 @@ class Game {
children: [
TableRow(
children: [
decorationImage,
Column(
children: [
FlatButton(
child: Container(
child: Image(
image: AssetImage('assets/icons/button_back.png'),
fit: BoxFit.fill
),
),
onPressed: () => GameUtils.resetGame(myProvider),
),
]
),
decorationImage,
Column(children: [ decorationImage ]),
Column(children: [ myProvider.animationInProgress ? decorationImage : buildRestartGameButton(myProvider) ]),
Column(children: [ decorationImage ]),
],
),
]
......
......@@ -26,7 +26,6 @@ class Parameters {
);
}
static Container buildStartGameButton(Data myProvider) {
Column decorationImage = Column(
children: [
......@@ -53,7 +52,7 @@ class Parameters {
child: Container(
child: Image(
image: AssetImage('assets/icons/button_start.png'),
fit: BoxFit.fill
fit: BoxFit.fill,
),
),
onPressed: () => GameUtils.startGame(myProvider),
......@@ -68,7 +67,6 @@ class Parameters {
);
}
static Table buildParameterSelector(Data myProvider, String parameterCode) {
List availableValues = myProvider.getParameterAvailableValues(parameterCode);
......@@ -89,7 +87,6 @@ class Parameters {
);
}
static FlatButton _buildParameterButton(Data myProvider, String parameterCode, String parameterValue) {
String currentValue = myProvider.getParameterValue(parameterCode).toString();
......@@ -109,12 +106,11 @@ class Parameters {
),
child: Image(
image: AssetImage(imageAsset),
fit: BoxFit.fill
fit: BoxFit.fill,
),
),
onPressed: () => myProvider.setParameterValue(parameterCode, parameterValue),
);
}
}
......@@ -20,23 +20,24 @@ class Data extends ChangeNotifier {
String _sizeDefault = '3x3';
String _skin = null;
String _skinDefault = 'default';
bool _showConflicts = false;
// Game data
bool _stateRunning = false;
bool _assetsPreloaded = false;
bool _gameIsRunning = false;
bool _animationInProgress = false;
int _blockSizeVertical = null;
int _blockSizeHorizontal = null;
List _cells = [];
List _cellsSolved = [];
List _shuffledCellValues = [];
int _currentCellCol = null;
int _currentCellRow = null;
int _currentCellValue = null;
bool _showConflicts = false;
int _givenTipsCount = 0;
bool _animationInProgress = false;
List _shuffledCellValues = [];
String get level => _level;
set updateLevel(String level) {
void updateLevel(String level) {
_level = level;
notifyListeners();
}
......@@ -44,7 +45,7 @@ class Data extends ChangeNotifier {
String get size => _size;
int get blockSizeVertical => _blockSizeVertical;
int get blockSizeHorizontal => _blockSizeHorizontal;
set updateSize(String size) {
void updateSize(String size) {
_size = size;
_blockSizeHorizontal = int.parse(_size.split('x')[0]);
_blockSizeVertical = int.parse(_size.split('x')[1]);
......@@ -52,17 +53,11 @@ class Data extends ChangeNotifier {
}
String get skin => _skin;
set updateSkin(String skin) {
void updateSkin(String skin) {
_skin = skin;
notifyListeners();
}
bool get showConflicts => _showConflicts;
set updateShowConflicts(bool showConflicts) {
_showConflicts = showConflicts;
notifyListeners();
}
getParameterValue(String parameterCode) {
switch(parameterCode) {
case 'difficulty': { return _level; }
......@@ -87,11 +82,11 @@ class Data extends ChangeNotifier {
setParameterValue(String parameterCode, String parameterValue) async {
switch(parameterCode) {
case 'difficulty': { updateLevel = parameterValue; }
case 'difficulty': { updateLevel(parameterValue); }
break;
case 'size': { updateSize = parameterValue; }
case 'size': { updateSize(parameterValue); }
break;
case 'skin': { updateSkin = parameterValue; }
case 'skin': { updateSkin(parameterValue); }
break;
}
final prefs = await SharedPreferences.getInstance();
......@@ -105,14 +100,25 @@ class Data extends ChangeNotifier {
setParameterValue('skin', prefs.getString('skin') ?? _skinDefault);
}
bool get gameIsRunning => _gameIsRunning;
void updateGameIsRunning(bool gameIsRunning) {
_gameIsRunning = gameIsRunning;
notifyListeners();
}
bool get assetsPreloaded => _assetsPreloaded;
void updateAssetsPreloaded(bool assetsPreloaded) {
_assetsPreloaded = assetsPreloaded;
}
List get cells => _cells;
set updateCells(List cells) {
void updateCells(List cells) {
_cells = cells;
notifyListeners();
}
List get cellsSolved => _cellsSolved;
set updateCellsSolved(List cells) {
void updateCellsSolved(List cells) {
_cellsSolved = cells;
}
......@@ -178,11 +184,14 @@ class Data extends ChangeNotifier {
notifyListeners();
}
bool get stateRunning => _stateRunning;
set updateStateRunning(bool stateRunning) {
_stateRunning = stateRunning;
bool get showConflicts => _showConflicts;
void updateShowConflicts(bool showConflicts) {
_showConflicts = showConflicts;
notifyListeners();
}
void toggleShowConflicts() {
updateShowConflicts(!showConflicts);
}
bool get animationInProgress => _animationInProgress;
void updateAnimationInProgress(bool animationInProgress) {
......
......@@ -23,13 +23,58 @@ class _HomeState extends State<Home> {
myProvider.initParametersValues();
}
List getImagesAssets(Data myProvider) {
List assets = [];
List gameImages = [
'button_back',
'button_help',
'button_show_conflicts',
'button_start',
'game_win',
];
myProvider.availableDifficultyLevels.forEach(
(difficulty) => gameImages.add('difficulty_' + difficulty)
);
myProvider.availableSizes.forEach(
(size) => gameImages.add('size_' + size)
);
gameImages.forEach(
(image) => assets.add('assets/icons/' + image + '.png')
);
List skinImages = [];
for (int value = 1; value <= 16; value++) {
skinImages.add(value.toString());
}
myProvider.availableSkins.forEach(
(skin) => skinImages.forEach(
(image) => assets.add('assets/skins/' + skin + '_' + image + '.png')
)
);
assets.add('assets/skins/empty.png');
return assets;
}
@override
Widget build(BuildContext context) {
Data myProvider = Provider.of<Data>(context);
if (!myProvider.assetsPreloaded) {
List assets = getImagesAssets(myProvider);
assets.forEach(
(asset) => precacheImage(AssetImage(asset), context)
);
myProvider.updateAssetsPreloaded(true);
}
List<Widget> menuActions = [];
if (myProvider.stateRunning) {
if (myProvider.gameIsRunning) {
menuActions = [
FlatButton(
child: Container(
......@@ -79,7 +124,7 @@ class _HomeState extends State<Home> {
),
),
onPressed: () {
myProvider.updateShowConflicts = !myProvider.showConflicts;
myProvider.toggleShowConflicts();
},
),
FlatButton(
......@@ -108,7 +153,7 @@ class _HomeState extends State<Home> {
),
body: SafeArea(
child: Center(
child: myProvider.stateRunning
child: myProvider.gameIsRunning
? Game.buildGameWidget(myProvider)
: Parameters.buildParametersSelector(myProvider)
),
......
import 'dart:async';
import 'dart:math';
import '../entities/cell.dart';
import '../provider/data.dart';
......@@ -40,7 +39,29 @@ class BoardAnimate {
for (var row = 0; row < boardSideLength; row++) {
List<bool> patternRow = [];
for (var col = 0; col < boardSideLength; col++) {
patternRow.add(row > (patternIndex - 3));
patternRow.add(row > (patternIndex - 4));
}
pattern.add(patternRow);
}
patterns.add(pattern);
}
return patterns;
}
// Default multi-purpose animation: sliding stripes, from top left to right bottom
static List createDefaultAnimationPatterns(Data myProvider) {
List<List> patterns = [];
int boardSideLength = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical;
int patternsCount = boardSideLength;
for (var patternIndex = 0; patternIndex < patternsCount; patternIndex++) {
List<List> pattern = [];
for (var row = 0; row < boardSideLength; row++) {
List<bool> patternRow = [];
for (var col = 0; col < boardSideLength; col++) {
patternRow.add(((patternIndex + row + col) % 4 == 0));
}
pattern.add(patternRow);
}
......@@ -60,6 +81,8 @@ class BoardAnimate {
case 'win':
patterns = createWinGameAnimationPatterns(myProvider);
break;
default:
patterns = createDefaultAnimationPatterns(myProvider);
}
int _patternIndex = patterns.length;
......
import 'dart:math';
import '../entities/cell.dart';
import '../utils/random_pick_grid.dart';
import '../provider/data.dart';
import '../utils/random_pick_grid.dart';
class BoardUtils {
......@@ -23,7 +23,6 @@ class BoardUtils {
print('');
}
static Future<void> pickGrid(Data myProvider) async {
String grid;
RandomPickGrid randomPickGrid;
......@@ -41,8 +40,8 @@ class BoardUtils {
if (grid.length == pow(blockSizeHorizontal * blockSizeVertical, 2)) {
print('Picked grid from template: ' + grid);
bool isSymetric = (blockSizeHorizontal == blockSizeVertical);
myProvider.updateCells = BoardUtils.createBoardFromTemplate(grid, isSymetric);
myProvider.updateCellsSolved = BoardUtils.getSolvedGrid(myProvider);
myProvider.updateCells(BoardUtils.createBoardFromTemplate(grid, isSymetric));
myProvider.updateCellsSolved(BoardUtils.getSolvedGrid(myProvider));
myProvider.selectCell(null, null);
printGrid(myProvider.cells, myProvider.cellsSolved);
......
......@@ -6,15 +6,15 @@ import '../utils/game_utils.dart';
class GameUtils {
static Future<void> resetGame(Data myProvider) async {
myProvider.updateStateRunning = false;
myProvider.updateGameIsRunning(false);
}
static Future<void> startGame(Data myProvider) async {
myProvider.updateSize = myProvider.size;
myProvider.updateStateRunning = true;
myProvider.updateSize(myProvider.size);
myProvider.updateGameIsRunning(true);
myProvider.resetGivenTipsCount();
myProvider.shuffleCellValues();
myProvider.updateCells = BoardUtils.createEmptyBoard(myProvider.blockSizeHorizontal * myProvider.blockSizeVertical);
myProvider.updateCells(BoardUtils.createEmptyBoard(myProvider.blockSizeHorizontal * myProvider.blockSizeVertical));
BoardUtils.pickGrid(myProvider);
BoardAnimate.startAnimation(myProvider, 'start');
}
......@@ -86,7 +86,9 @@ class GameUtils {
myProvider.updateCellValue(myProvider.currentCellCol, myProvider.currentCellRow, allowedValuesCount == 1 ? eligibleValue : 0);
myProvider.selectCell(null, null);
BoardUtils.computeConflictsInBoard(myProvider);
if (BoardUtils.checkBoardIsSolved(myProvider)) {
BoardAnimate.startAnimation(myProvider, 'win');
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment