|
@@ -13,11 +13,20 @@
|
|
|
import { v1 as uuid } from "uuid";
|
|
|
import {SCanvasView, SColor, SPainter, STextAlign} from "@persagy-web/draw/lib";
|
|
|
|
|
|
+ /**
|
|
|
+ * 俄罗斯方块视图
|
|
|
+ *
|
|
|
+ * @author 郝建龙
|
|
|
+ * */
|
|
|
class TestView extends SCanvasView {
|
|
|
+ /** 背景表示数组 */
|
|
|
map: number[][] = [];
|
|
|
- fkType: number = Number((Math.random() * 6).toFixed()); // 0-6
|
|
|
- dir: number = Number((Math.random() * 2).toFixed()); // 0-3
|
|
|
- fk: number[][][]=[
|
|
|
+ /** 方块类型索引 */
|
|
|
+ boxType: number = Number(Math.floor(Math.random() * 7)); // 0-6
|
|
|
+ /** 方块变形索引 */
|
|
|
+ dir: number = Number(Math.floor(Math.random() * 4)); // 0-3
|
|
|
+ /** 所有方块集合 */
|
|
|
+ box: number[][][] = [
|
|
|
[
|
|
|
[0, 0, 0, 0],
|
|
|
[0, 1, 1, 0],
|
|
@@ -187,66 +196,87 @@
|
|
|
[0, 0, 0, 0],
|
|
|
]
|
|
|
];
|
|
|
+ /** 方块初始位置x坐标 */
|
|
|
x = 5;
|
|
|
+ /** 方块初始位置y坐标 */
|
|
|
y = 0;
|
|
|
+ /** 记录上次刷新时间 */
|
|
|
t = Date.now();
|
|
|
+ /** 是否游戏结束 */
|
|
|
gameOver: boolean = false;
|
|
|
- xzColor: SColor = new SColor();
|
|
|
+ /** 方块颜色 */
|
|
|
+ boxColor: SColor = new SColor();
|
|
|
|
|
|
+ /**
|
|
|
+ * 构造函数
|
|
|
+ *
|
|
|
+ * @param id canvas DOM id
|
|
|
+ */
|
|
|
constructor(id: string) {
|
|
|
super(id);
|
|
|
this.initMap();
|
|
|
this.initColor();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 键盘按下事件
|
|
|
+ *
|
|
|
+ * @param event 事件对象
|
|
|
+ */
|
|
|
onKeyDown(event: KeyboardEvent): void {
|
|
|
if (this.gameOver) {
|
|
|
return;
|
|
|
}
|
|
|
+
|
|
|
if (event.code == "KeyW") {
|
|
|
- if (!this.isPz(this.x, this.y, (this.dir + 1) % 4)) {
|
|
|
+ if (!this.checkKnocked(this.x, this.y, (this.dir + 1) % 4)) {
|
|
|
this.dir = (this.dir + 1) % 4;
|
|
|
}
|
|
|
} else if (event.code == "KeyA") {
|
|
|
- if (!this.isPz(this.x - 1, this.y, this.dir)) {
|
|
|
+ if (!this.checkKnocked(this.x - 1, this.y, this.dir)) {
|
|
|
this.x--;
|
|
|
}
|
|
|
} else if (event.code == "KeyD") {
|
|
|
- if (!this.isPz(this.x + 1, this.y, this.dir)) {
|
|
|
+ if (!this.checkKnocked(this.x + 1, this.y, this.dir)) {
|
|
|
this.x++;
|
|
|
}
|
|
|
- }
|
|
|
- else if (event.code == "KeyS") {
|
|
|
- if (!this.isPz(this.x, this.y + 1, this.dir)) {
|
|
|
+ } else if (event.code == "KeyS") {
|
|
|
+ if (!this.checkKnocked(this.x, this.y + 1, this.dir)) {
|
|
|
this.y++;
|
|
|
} else {
|
|
|
this.fullMap();
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
this.update();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 键盘按下事件
|
|
|
+ *
|
|
|
+ * @param painter 绘制对象
|
|
|
+ */
|
|
|
onDraw(painter: SPainter): void {
|
|
|
- if(this.gameOver){
|
|
|
+ if (this.gameOver) {
|
|
|
painter.brush.color = SColor.Blue;
|
|
|
painter.font.size = 20;
|
|
|
painter.font.textAlign = STextAlign.Center;
|
|
|
- painter.drawText('GAME OVER', 180, 30);
|
|
|
- return
|
|
|
+ painter.drawText("GAME OVER", 180, 30);
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- painter.clearRect(0,0,this.width, this.height);
|
|
|
+ painter.clearRect(0, 0, this.width, this.height);
|
|
|
|
|
|
- painter.pen.color = new SColor('#0bc0ff');
|
|
|
+ painter.pen.color = new SColor("#0bc0ff");
|
|
|
painter.brush.color = new SColor("#2accc7");
|
|
|
this.drawMap(painter);
|
|
|
|
|
|
- painter.pen.color = this.xzColor;
|
|
|
- painter.brush.color = this.xzColor;
|
|
|
- this.drawFk(painter);
|
|
|
+ painter.pen.color = this.boxColor;
|
|
|
+ painter.brush.color = this.boxColor;
|
|
|
+ this.drawBox(painter);
|
|
|
|
|
|
if (Date.now() - this.t > 500) {
|
|
|
- if (!this.isPz(this.x, this.y + 1, this.dir)) {
|
|
|
+ if (!this.checkKnocked(this.x, this.y + 1, this.dir)) {
|
|
|
this.y++;
|
|
|
} else {
|
|
|
this.fullMap();
|
|
@@ -256,14 +286,17 @@
|
|
|
this.update();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 初始化背景
|
|
|
+ */
|
|
|
initMap(): void {
|
|
|
this.gameOver = false;
|
|
|
this.map = [];
|
|
|
for (let row = 0; row < 22; row++) {
|
|
|
- const m1:number[] = [];
|
|
|
+ const m1: number[] = [];
|
|
|
for (let col = 0; col < 14; col++) {
|
|
|
if (row > 19 || col < 2 || col > 11) {
|
|
|
- m1.push(2);
|
|
|
+ m1.push(-1);
|
|
|
} else {
|
|
|
m1.push(0);
|
|
|
}
|
|
@@ -272,49 +305,91 @@
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- initXZ(){
|
|
|
+ /**
|
|
|
+ * 初始化方块形状
|
|
|
+ */
|
|
|
+ initBox(): void {
|
|
|
this.x = 5;
|
|
|
this.y = 0;
|
|
|
- this.fkType = Number((Math.random() * 6).toFixed());
|
|
|
- this.dir = Number((Math.random() * 2).toFixed());
|
|
|
+ this.boxType = Number(Math.floor(Math.random() * 7));
|
|
|
+ this.dir = Number(Math.floor(Math.random() * 4));
|
|
|
this.initColor();
|
|
|
}
|
|
|
|
|
|
- initColor(){
|
|
|
- const a = Number((200*Math.random()).toFixed());
|
|
|
- const b = Number((200*Math.random()).toFixed());
|
|
|
- const c = Number((200*Math.random()).toFixed());
|
|
|
- this.xzColor = new SColor(a, b, c);
|
|
|
+ /**
|
|
|
+ * 初始化方块颜色
|
|
|
+ */
|
|
|
+ initColor(): void {
|
|
|
+ const a = Number(Math.floor(Math.random() * 128));
|
|
|
+ const b = Number(Math.floor(Math.random() * 128));
|
|
|
+ const c = Number(Math.floor(Math.random() * 128));
|
|
|
+ this.boxColor = new SColor(a, b, c);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 绘制背景
|
|
|
+ *
|
|
|
+ * @param painter 绘制对象
|
|
|
+ */
|
|
|
private drawMap(painter: SPainter): void {
|
|
|
for (let row = 0; row < 22; row++) {
|
|
|
for (let col = 0; col < 14; col++) {
|
|
|
- const n = this.map[row][col];
|
|
|
- if (n == 1 || n == 2) {
|
|
|
- painter.drawRoundRect(col * 30 + 11 - 60, row * 30 + 11, 28, 28, 6);
|
|
|
- }
|
|
|
+ this.drawShape(painter, row, col, this.map[row][col]);
|
|
|
// if (n == 0) {
|
|
|
// painter.drawRect(col * 30 + 11 - 60, row * 30 + 11, 28, 28);
|
|
|
// }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 绘制实体图形
|
|
|
+ *
|
|
|
+ * @param painter 绘制对象
|
|
|
+ * @param row 行
|
|
|
+ * @param col 列
|
|
|
+ * @param type 图类型
|
|
|
+ */
|
|
|
+ private drawShape(painter: SPainter, row: number, col: number, type: number): void {
|
|
|
+ const x = col * 30 + 11 - 60;
|
|
|
+ const y = row * 30 + 11;
|
|
|
+ switch (type) {
|
|
|
+ case 0:
|
|
|
+ break;
|
|
|
+ case -1:
|
|
|
+ painter.drawRoundRect(x, y, 28, 28, 6);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ painter.drawRoundRect(x, y, 28, 28, 6);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- private drawFk(painter: SPainter): void {
|
|
|
+ /**
|
|
|
+ * 绘制方块
|
|
|
+ *
|
|
|
+ * @param painter 绘制对象
|
|
|
+ */
|
|
|
+ private drawBox(painter: SPainter): void {
|
|
|
for (let row = 0; row < 4; row++) {
|
|
|
for (let col = 0; col < 4; col++) {
|
|
|
- if (this.fk[this.fkType * 4 + this.dir][row][col] == 1) {
|
|
|
- painter.drawRoundRect((col + this.x) * 30 + 11 - 60, (row + this.y) * 30 + 11, 28, 28, 6);
|
|
|
- }
|
|
|
+ this.drawShape(painter, row + this.y, col + this.x, this.box[this.boxType * 4 + this.dir][row][col]);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private isPz(x: number, y: number, dir: number): boolean {
|
|
|
+ /**
|
|
|
+ * 是否碰撞
|
|
|
+ *
|
|
|
+ * @param x x坐标
|
|
|
+ * @param y y坐标
|
|
|
+ * @param dir 方块变形索引
|
|
|
+ * @return 是否碰撞
|
|
|
+ */
|
|
|
+ private checkKnocked(x: number, y: number, dir: number): boolean {
|
|
|
for (let row = 0; row < 4; row++) {
|
|
|
for (let col = 0; col < 4; col++) {
|
|
|
- if (this.fk[this.fkType * 4 + dir][row][col] == 1 && this.map[y + row][x + col] != 0) {
|
|
|
+ if (this.box[this.boxType * 4 + dir][row][col] != 0 && this.map[y + row][x + col] != 0) {
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
@@ -322,25 +397,32 @@
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- private fullMap() {
|
|
|
+ /**
|
|
|
+ * 将方块合到背景中
|
|
|
+ */
|
|
|
+ private fullMap(): void {
|
|
|
for (let row = 0; row < 4; row++) {
|
|
|
for (let col = 0; col < 4; col++) {
|
|
|
- if (this.fk[this.fkType * 4 + this.dir][row][col] == 1) {
|
|
|
+ if (this.box[this.boxType * 4 + this.dir][row][col] == 1) {
|
|
|
this.map[this.y + row][this.x + col] = 1;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- this.initXZ();
|
|
|
+ this.initBox();
|
|
|
|
|
|
- if(this.isPz(this.x, this.y, this.dir)){
|
|
|
+ if (this.checkKnocked(this.x, this.y, this.dir)) {
|
|
|
this.gameOver = true;
|
|
|
}
|
|
|
- this.xc();
|
|
|
+
|
|
|
+ this.remove();
|
|
|
}
|
|
|
|
|
|
- private xc(): void{
|
|
|
- let xcSum = 1;
|
|
|
+ /**
|
|
|
+ * 消除满行
|
|
|
+ */
|
|
|
+ private remove(): void {
|
|
|
+ let removeCount = 1;
|
|
|
for (let row = 0; row < 20; row++) {
|
|
|
let flag = true;
|
|
|
for (let col = 0; col < 14; col++) {
|
|
@@ -349,21 +431,33 @@
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
- if(flag){
|
|
|
- this.map.splice(row,1);
|
|
|
- this.map.unshift([2,2, 0,0,0,0,0,0,0,0,0,0,2,2]);
|
|
|
- xcSum++;
|
|
|
+
|
|
|
+ if (flag) {
|
|
|
+ this.map.splice(row, 1);
|
|
|
+ this.map.unshift(this.randomRow(10, 0));
|
|
|
+ removeCount++;
|
|
|
}
|
|
|
}
|
|
|
- if (xcSum > 1) {
|
|
|
- this.$emit('score', xcSum);
|
|
|
+
|
|
|
+ if (removeCount > 1) {
|
|
|
+ this.$emit("score", removeCount);
|
|
|
this.map.shift();
|
|
|
- this.map.splice(19, 0, [2,2, this.random(),this.random(),this.random(),this.random(),this.random(),this.random(),this.random(),this.random(),this.random(),this.random(),2,2])
|
|
|
+ this.map.splice(19, 0, this.randomRow());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private random():number{
|
|
|
- return Number(Math.random().toFixed())
|
|
|
+ /**
|
|
|
+ * 随机数组生成
|
|
|
+ *
|
|
|
+ * @return 数组
|
|
|
+ */
|
|
|
+ private randomRow(len: number = 10, num: number = 2): number[] {
|
|
|
+ let array = new Array(len + 4).fill(2);
|
|
|
+ for (let i = 2; i < array.length - 2; i++) {
|
|
|
+ array[i] = Math.floor(num * Math.random());
|
|
|
+ }
|
|
|
+
|
|
|
+ return array;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -375,7 +469,7 @@
|
|
|
view: TestView | undefined = undefined;
|
|
|
mounted(): void {
|
|
|
this.view = new TestView(this.id);
|
|
|
- this.view.connect('score', this, this.changeScore)
|
|
|
+ this.view.connect("score", this, this.changeScore);
|
|
|
document.getElementById(this.id)!!.focus();
|
|
|
}
|
|
|
|
|
@@ -385,7 +479,7 @@
|
|
|
|
|
|
reset(){
|
|
|
this.view!!.initMap();
|
|
|
- this.view!!.initXZ();
|
|
|
+ this.view!!.initBox();
|
|
|
this.score = 0;
|
|
|
this.view!!.update();
|
|
|
document.getElementById(this.id)!!.focus();
|