Trung Vo
Trung Vo, web expert with 10 years of experience, Google Developer Expert in Angular, fosters web dev communities and speaks globally, based in Singapore.
Frontend Engineer
Angular Kenya - 05 Feb 2021
Hi, My name is Trung 😊
What is Tetris?
What and why Angular Tetris?
Techstack
Development Challenge
Tetris Game Loop
Piece/Tetrominos
Board
Animation/Timer
Keyboard
Sounds
It is the most important part of the game
“ Code is like humor. When you have to explain it, it’s bad.” - Cory House
I ended up using @chrum/ngx-tetris
I did write some additional functionality
_gameInterval: Subscription; auto(delay: number) { this._gameInterval = timer(0, delay).subscribe(() => { this._update(); }); }
export class Piece { x: number; y: number; rotation = PieceRotation.Deg0; type: PieceTypes; shape: Shape; next: Shape; private _shapes: Shapes; private _lastConfig: Partial<Piece>; constructor(x: number, y: number) { this.x = x; this.y = y; } protected setShapes(shapes: Shapes) { this._shapes = shapes; this.shape = shapes[this.rotation]; } }
const ShapesL: Shapes = []; ShapesL[PieceRotation.Deg0] = [ [0, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0], [1, 1, 0, 0] ]; ShapesL[PieceRotation.Deg90] = [ [0, 0, 0, 0], [0, 0, 0, 0], [1, 1, 1, 0], [1, 0, 0, 0] ]; export class PieceL extends Piece { constructor(x: number, y: number) { super(x, y); this.type = PieceTypes.L; this.next = [ [0, 0, 1, 0], [1, 1, 1, 0] ]; this.setShapes(ShapesL); } }
const ShapesF: Shapes = []; ShapesF[PieceRotation.Deg0] = [ [1, 0, 0, 0], [1, 1, 0, 0], [1, 0, 0, 0], [1, 1, 0, 0] ]; export class PieceF extends Piece { constructor(x, y) { super(x, y); this.type = PieceTypes.F; this.next = [ [1, 0, 1, 0], [1, 1, 1, 1] ]; this.setShapes(ShapesF); } }
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
I rewrote the animation with RxJS
div.b { transform: scale(-1, 1); }
div.cube { width: 150px; height: 80px; background-color: yellow; } div.a { transform: scale(1, 1); }
.dragon { width: 80px; height: 86px; margin: 0 auto; background-position: 0 -100px; &.l1 { background-position: 0 -100px; } &.l1 transform: scale(-1, 1); } }
.dragon { width: 80px; height: 86px; margin: 0 auto; background-position: 0 -100px; &.r1 { background-position: 0 -100px; } }
eyes() { return timer(0, 500).pipe( startWith(0), map((x) => x + 1), takeWhile((x) => x < 6), tap((x) => { let state = x % 2 === 0 ? 1 : 2; this.className = `l${ state }`; }) ); }
run() { let side = 'r'; return timer(0, 100).pipe( startWith(0), map((x) => x + 1), takeWhile((x) => x <= 40), tap((x) => { if (x === 10 || x === 20 || x === 30) { side = side === 'r' ? 'l' : 'r'; } let state = x % 2 === 0 ? 3 : 4; this.className = `${ side }${ state }`; }), finalize(() => { this.className = `${ side }1`; }) ); }
r 1s -> l 1s -> r1s -> l 1s -> end with {side}1 ~ l1
The Concat operator concatenates the output of multiple Observables so that they act like a single Observable, with all of the items emitted by the first Observable being emitted before any of the items emitted by the second Observable
ngOnInit(): void { concat(this.run(), this.eyes()) .pipe( delay(5000), repeat(1000), untilDestroyed(this) ) .subscribe(); }
The actual result doesn't look very identical but it is good enough in my standard.
export enum TetrisKeyboard { Up = 'arrowup', Down = 'arrowdown', Left = 'arrowleft', Right = 'arrowright', Space = 'space', P = 'p', R = 'r', S = 's' } @HostListener(`${KeyDown}.${TetrisKeyboard.Left}`) keyDownLeft() { this._soundManager.move(); this._keyboardService.setKeỵ({ left: true }); if (this.hasCurrent) { this._tetrisService.moveLeft(); } else { this._tetrisService.decreaseLevel(); } }
See more ➡️ @HostListener
Some browsers use deprecated properties and method names that are not present in standards-compliant browsers
By Trung Vo
Deck for Angular APAC Dec 2020. https://www.angularnation.net/
Trung Vo, web expert with 10 years of experience, Google Developer Expert in Angular, fosters web dev communities and speaks globally, based in Singapore.