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

Merge branch '55-fix-freeze-on-end-game-animation' into 'master'

Resolve "Fix freeze on end game animation"

Closes #55

See merge request !50
parents 1a753ff3 ae2ed563
No related branches found
No related tags found
1 merge request!50Resolve "Fix freeze on end game animation"
Pipeline #2023 passed
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.
Finish editing this message first!
Please register or to comment