一统天下 flutter - 游戏: 俄罗斯方块
一统天下 flutter - 游戏: 俄罗斯方块
示例如下:
lib\game\tetris\tetris.dart
/*
* 俄罗斯方块
*
* 使用了 flame 库,在 pubspec.yaml 中做如下配置,然后 flutter pub get
* dependencies:
* flame: ^1.7.3
*/
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
import 'package:flutter_demo/game/tetris/shape/shape.dart';
import 'package:flutter_demo/game/tetris/shape/square.dart';
import 'package:flutter_demo/game/tetris/shape/t.dart';
import 'config.dart';
import 'controller.dart';
import 'core.dart';
class Tetris extends StatefulWidget {
const Tetris({Key? key}) : super(key: key);
@override
TetrisState createState() => TetrisState();
}
class TetrisState extends State<Tetris> {
final controller = Controller();
@override
Widget build(BuildContext context) {
return Material(
color: Colors.orange,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
width: Config.mainMatrixWidth * Config.squareWidth,
height: Config.mainMatrixHeight * Config.squareWidth,
child: GameWidget(
game: MyGame(controller: controller),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_button1(
Icons.arrow_back, () { controller.left(); }, () { },
),
_button1(
Icons.arrow_forward, () { controller.right(); }, () { },
),
_button1(
Icons.rotate_right_rounded, () { controller.rotate(); }, () { },
),
_button1(
Icons.arrow_downward, () { controller.downPressed = true; }, () { controller.downPressed = false; },
),
],
),
],
),
);
}
Ink _button1(IconData iconData, VoidCallback onDown, VoidCallback onUp) {
return Ink(
padding: EdgeInsets.zero,
width: 36,
height: 36,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(18),
),
child: InkWell(
child: Icon(iconData, color: Colors.white,),
onTapDown: (details) { onDown(); },
onTapUp: (details) { onUp(); },
),
);
}
}
class MyGame extends FlameGame {
MyGame({required this.controller});
final Controller controller;
bool shapeUpdated = false;
List<Shape> shapeList = <Shape>[];
List<List<Square?>> mainMatrix = <List<Square?>>[];
@override
Future<void>? onLoad() async {
shapeList.add(Core.createShape());
Core.initMainMatrix(mainMatrix);
controller.addListener(() {
if (controller.rotateTimes > 0) {
shapeList[0].rotate();
controller.rotateTimes--;
} else if (controller.leftTimes > 0) {
shapeList[0].left();
controller.leftTimes--;
} else if (controller.rightTimes > 0) {
shapeList[0].right();
controller.rightTimes--;
}
shapeUpdated = true;
});
return super.onLoad();
}
@override
void render(Canvas canvas) {
super.render(canvas);
shapeList[0].render(canvas);
for (var j = 0; j < mainMatrix.length; j++) {
for (var i = 0; i < mainMatrix[j].length; i++) {
var square = mainMatrix[j][i];
if (square != null) {
var offsetCenter = Offset(square.width / 2 + i * square.width, square.width / 2 + j * square.width);
canvas.drawRect(Rect.fromCenter(center: offsetCenter, width: square.width, height: square.width), square.borderPaint);
canvas.drawRect(Rect.fromCenter(center: offsetCenter, width: square.width - square.borderWidth, height: square.width - square.borderWidth), square.paint);
}
}
}
}
@override
void update(double dt) {
super.update(dt);
if (controller.downPressed) {
Config.speed = 500;
} else {
Config.speed = 20;
}
var shape = shapeList[0];
if (shapeUpdated) {
shapeUpdated = false;
} else {
shape.update(dt);
}
var collisionType = Core.checkCollision(shape, mainMatrix);
if (collisionType == CollisionType.bottom) {
shape.loadPrev();
Core.pinShape(shape, mainMatrix);
controller.downPressed = false;
Core.removeLineShape(mainMatrix);
shapeList.removeAt(0);
var newShape = Core.createShape();
shapeList.add(newShape);
if (Core.checkCollision(newShape, mainMatrix) == CollisionType.bottom) {
Core.initMainMatrix(mainMatrix); // 死了重来
}
} else if (collisionType == CollisionType.edge) {
shape.loadPrev();
}
}
}
lib\game\tetris\core.dart
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_demo/game/tetris/shape/i.dart';
import 'package:flutter_demo/game/tetris/shape/l.dart';
import 'package:flutter_demo/game/tetris/shape/l2.dart';
import 'package:flutter_demo/game/tetris/shape/n.dart';
import 'package:flutter_demo/game/tetris/shape/n2.dart';
import 'package:flutter_demo/game/tetris/shape/o.dart';
import 'package:flutter_demo/game/tetris/shape/shape.dart';
import 'package:flutter_demo/game/tetris/shape/square.dart';
import 'package:flutter_demo/game/tetris/shape/t.dart';
import 'config.dart';
enum CollisionType { none, edge, bottom }
class Core {
static var random = Random();
static CollisionType checkCollision(Shape shape, List<List<Square?>> mainMatrix) {
var shapeMatrix = shape.matrixList[shape.currentIndex];
for (var j = 0; j < shapeMatrix.length; j++) { // 纵轴是 j 横轴是 i
for (var i = 0; i < shapeMatrix[j].length; i++) {
var cell = shapeMatrix[j][i];
if (cell == 1) {
var leftTopPoint = shape.leftTopPoint;
var prevLeftTopPoint = shape.prevLeftTopPoint;
var mi = i + leftTopPoint.dx.toInt();
var mj = j + leftTopPoint.dy.toInt();
if (mi < 0 || mi >= mainMatrix[0].length) {
return CollisionType.edge;
}
if (mj >= mainMatrix.length) {
return CollisionType.bottom;
}
var square = mainMatrix[mj][mi];
if (square != null) {
if (leftTopPoint.dx == prevLeftTopPoint.dx) {
return CollisionType.bottom;
} else {
return CollisionType.edge;
}
}
}
}
}
return CollisionType.none;
}
static void pinShape(Shape shape, List<List<Square?>> mainMatrix) {
var shapeMatrix = shape.matrixList[shape.currentIndex];
for (var j = 0; j < shapeMatrix.length; j++) { // 纵轴是 j 横轴是 i
for (var i = 0; i < shapeMatrix[j].length; i++) {
var cell = shapeMatrix[j][i];
if (cell == 1) {
var mi = i + shape.leftTopPoint.dx.toInt();
var mj = j + shape.leftTopPoint.dy.toInt();
if (mi < 0 || mi >= mainMatrix[0].length) {
return;
}
if (mj < 0 || mj >= mainMatrix.length) {
return;
}
mainMatrix[mj][mi] = Square(shape.square.paint.color, shape.square.borderPaint.color);
}
}
}
}
static void removeLineShape(List<List<Square?>> mainMatrix) {
for (var j = 0; j < mainMatrix.length; j++) { // 纵轴是 j 横轴是 i
var line = true;
for (var i = 0; i < mainMatrix[j].length; i++) {
var square = mainMatrix[j][i];
if (square == null) {
line = false;
continue;
}
}
if (line) {
mainMatrix.removeAt(j);
var row = <Square?>[];
mainMatrix.insert(0, row);
for (var i = 0; i < 10; i++) {
row.add(null);
}
}
}
}
static void initMainMatrix(List<List<Square?>> mainMatrix) {
mainMatrix.clear();
for (var i = 0; i < Config.mainMatrixHeight; i++) {
var row = <Square?>[];
mainMatrix.add(row);
for (var j = 0; j < Config.mainMatrixWidth; j++) {
row.add(null);
}
}
}
static Shape createShape() {
var shapeList = <Shape>[I(), L(), L2(), N(), N2(), O(), T()];
var shape = shapeList[random.nextInt(shapeList.length)];
return shape;
}
}
lib\game\tetris\config.dart
class Config {
static double mainMatrixWidth = 10;
static double mainMatrixHeight = 20;
static double squareWidth = 20;
static double squareBorderWidth = 2;
static double speed = 50;
}
lib\game\tetris\controller.dart
import 'package:flutter/material.dart';
class Controller extends ChangeNotifier {
int rotateTimes = 0;
int leftTimes = 0;
int rightTimes = 0;
bool downPressed = false;
void left() {
leftTimes++;
notifyListeners();
}
void right() {
rightTimes++;
notifyListeners();
}
void rotate() {
rotateTimes++;
notifyListeners();
}
}
lib\game\tetris\shape\square.dart
import 'package:flutter/material.dart';
import '../config.dart';
class Square {
late Paint paint;
late Paint borderPaint;
double width = Config.squareWidth;
double borderWidth = Config.squareBorderWidth;
Square(Color color, Color borderColor) {
paint = Paint()..color = color;
borderPaint = Paint()..color = borderColor;
}
}
lib\game\tetris\shape\shape.dart
import 'package:flutter/material.dart';
import 'package:flutter_demo/game/tetris/shape/square.dart';
import '../config.dart';
class Shape {
Shape(this.square) {
leftTopOffset = Offset(3 * Config.squareWidth, 0);
prevLeftTopOffset = Offset(3 * Config.squareWidth, 0);
}
Square square;
Offset leftTopOffset = const Offset(0, 0);
int currentIndex = 0;
int maxIndex = 3;
Offset prevLeftTopOffset = const Offset(0, 0);
int prevCurrentIndex = 0;
Offset get leftTopPoint {
return _getLeftTopPoint(leftTopOffset);
}
Offset get prevLeftTopPoint {
return _getLeftTopPoint(prevLeftTopOffset);
}
Offset _getLeftTopPoint(Offset offset) {
var x = offset.dx % square.width == 0 ? offset.dx ~/ square.width : offset.dx ~/ square.width + 1;
var y = offset.dy % square.width == 0 ? offset.dy ~/ square.width : offset.dy ~/ square.width + 1;
return Offset(x.toDouble(), y.toDouble());
}
List<List<List<int>>> get matrixList => [
[
[],
],
];
void render(Canvas canvas) {
var matrix = matrixList[currentIndex];
for (var j = 0; j < matrix.length; j++) {
for (var i = 0; i < matrix[j].length; i++) {
var cell = matrix[j][i];
if (cell == 1) {
var offsetCenter = leftTopOffset.translate(square.width / 2 + i * square.width, square.width / 2 + j * square.width);
canvas.drawRect(Rect.fromCenter(center: offsetCenter, width: square.width, height: square.width), square.borderPaint);
canvas.drawRect(Rect.fromCenter(center: offsetCenter, width: square.width - square.borderWidth, height: square.width - square.borderWidth), square.paint);
}
}
}
}
void update(double dt) {
savePrev();
leftTopOffset = leftTopOffset.translate(0, dt * Config.speed);
}
void rotate() {
savePrev();
if (currentIndex < maxIndex) {
currentIndex ++;
} else {
currentIndex = 0;
}
}
void left() {
savePrev();
leftTopOffset = leftTopOffset.translate(-square.width, 0);
}
void right() {
savePrev();
leftTopOffset = leftTopOffset.translate(square.width, 0);
}
void savePrev() {
prevLeftTopOffset = leftTopOffset;
prevCurrentIndex = currentIndex;
}
void loadPrev() {
leftTopOffset = prevLeftTopOffset;
currentIndex = prevCurrentIndex;
}
}
lib\game\tetris\shape\i.dart
import 'package:flutter/material.dart';
import 'package:flutter_demo/game/tetris/shape/square.dart';
import 'shape.dart';
class I extends Shape {
I() : super(Square(Colors.red, Colors.white70));
@override
List<List<List<int>>> get matrixList => [
[
[0,1,0,0],
[0,1,0,0],
[0,1,0,0],
[0,1,0,0],
],
[
[0,0,0,0],
[1,1,1,1],
[0,0,0,0],
[0,0,0,0],
],
];
@override
int get maxIndex => 1;
}
lib\game\tetris\shape\l.dart
import 'package:flutter/material.dart';
import 'package:flutter_demo/game/tetris/shape/square.dart';
import 'shape.dart';
class L extends Shape {
L() : super(Square(Colors.green[200]!, Colors.white70));
@override
List<List<List<int>>> get matrixList => [
[
[0,1,0,0],
[0,1,0,0],
[0,1,1,0],
[0,0,0,0],
],
[
[0,0,0,0],
[1,1,1,0],
[1,0,0,0],
[0,0,0,0],
],
[
[1,1,0,0],
[0,1,0,0],
[0,1,0,0],
[0,0,0,0],
],
[
[0,0,1,0],
[1,1,1,0],
[0,0,0,0],
[0,0,0,0],
],
];
@override
int get maxIndex => 3;
}
lib\game\tetris\shape\l2.dart
import 'package:flutter/material.dart';
import 'package:flutter_demo/game/tetris/shape/square.dart';
import 'shape.dart';
class L2 extends Shape {
L2() : super(Square(Colors.green[800]!, Colors.white70));
@override
List<List<List<int>>> get matrixList => [
[
[0,0,1,0],
[0,0,1,0],
[0,1,1,0],
[0,0,0,0],
],
[
[0,1,0,0],
[0,1,1,1],
[0,0,0,0],
[0,0,0,0],
],
[
[0,0,1,1],
[0,0,1,0],
[0,0,1,0],
[0,0,0,0],
],
[
[0,0,0,0],
[0,1,1,1],
[0,0,0,1],
[0,0,0,0],
],
];
@override
int get maxIndex => 3;
}
lib\game\tetris\shape\n.dart
import 'package:flutter/material.dart';
import 'package:flutter_demo/game/tetris/shape/square.dart';
import 'shape.dart';
class N extends Shape {
N() : super(Square(Colors.blue[200]!, Colors.white70));
@override
List<List<List<int>>> get matrixList => [
[
[0,0,1,0],
[0,1,1,0],
[0,1,0,0],
[0,0,0,0],
],
[
[0,1,1,0],
[0,0,1,1],
[0,0,0,0],
[0,0,0,0],
],
];
@override
int get maxIndex => 1;
}
lib\game\tetris\shape\n2.dart
import 'package:flutter/material.dart';
import 'package:flutter_demo/game/tetris/shape/square.dart';
import 'shape.dart';
class N2 extends Shape {
N2() : super(Square(Colors.blue[800]!, Colors.white70));
@override
List<List<List<int>>> get matrixList => [
[
[0,1,0,0],
[0,1,1,0],
[0,0,1,0],
[0,0,0,0],
],
[
[0,0,1,1],
[0,1,1,0],
[0,0,0,0],
[0,0,0,0],
],
];
@override
int get maxIndex => 1;
}
lib\game\tetris\shape\o.dart
import 'package:flutter/material.dart';
import 'package:flutter_demo/game/tetris/shape/square.dart';
import 'shape.dart';
class O extends Shape {
O() : super(Square(Colors.yellow, Colors.white70));
@override
List<List<List<int>>> get matrixList => [
[
[0,1,1,0],
[0,1,1,0],
[0,0,0,0],
[0,0,0,0],
],
];
@override
int get maxIndex => 0;
}
lib\game\tetris\shape\t.dart
import 'package:flutter/material.dart';
import 'package:flutter_demo/game/tetris/shape/square.dart';
import 'shape.dart';
class T extends Shape {
T() : super(Square(Colors.purple, Colors.white70));
@override
List<List<List<int>>> get matrixList => [
[
[0,1,0,0],
[1,1,1,0],
[0,0,0,0],
[0,0,0,0],
],
[
[0,1,0,0],
[0,1,1,0],
[0,1,0,0],
[0,0,0,0],
],
[
[0,0,0,0],
[1,1,1,0],
[0,1,0,0],
[0,0,0,0],
],
[
[0,1,0,0],
[1,1,0,0],
[0,1,0,0],
[0,0,0,0],
],
];
@override
int get maxIndex => 3;
}