Learn RxJS
Search…
Flappy Bird Game
By adamlubek
This recipe demonstrates RxJs implementation of Flappy Bird like game.

Example Code

Flappy Bird

index.ts

1
// RxJS v6+
2
import { interval, merge, combineLatest, fromEvent } from 'rxjs';
3
import { tap, scan, takeWhile } from 'rxjs/operators';
4
import { paint } from './html-renderer';
5
6
const gamePipe = (x, y) => ({ x, y, checked: false });
7
const gameSize = 10;
8
const createPipes = y =>
9
(random =>
10
Array.from(Array(gameSize).keys())
11
.map(e => gamePipe(e, y))
12
.filter(e => e.x < random || e.x > random + 2))(
13
Math.floor(Math.random() * Math.floor(gameSize))
14
);
15
16
const gamePipes$ = interval(500).pipe(
17
scan < any,
18
any >
19
(acc =>
20
(acc.length < 2 ? [...acc, createPipes(gameSize)] : acc)
21
.filter(c => c.some(e => e.y > 0))
22
.map(cols => cols.map(e => gamePipe(e.x, e.y - 1))),
23
[createPipes(gameSize / 2), createPipes(gameSize)])
24
);
25
26
const fly = xPos => (xPos > 0 ? (xPos -= 1) : xPos);
27
const fall = xPos => (xPos < gameSize - 1 ? (xPos += 1) : gameSize - 1);
28
const bird$ = merge(interval(300), fromEvent(document, 'keydown')).pipe(
29
scan < any,
30
any >
31
((xPos, curr) => (curr instanceof KeyboardEvent ? fly(xPos) : fall(xPos)),
32
gameSize - 1)
33
);
34
35
const updateGame = (bird, pipes) =>
36
(game => (
37
pipes.forEach(col => col.forEach(v => (game[v.x][v.y] = 2))),
38
(game[bird][0] = 1),
39
game
40
))(
41
Array(gameSize)
42
.fill(0)
43
.map(e => Array(gameSize).fill(0))
44
);
45
46
const valueOnCollisionFor = pipes => ({
47
when: predicate =>
48
!pipes[0][0].checked && predicate ? ((pipes[0][0].checked = true), 1) : 0
49
});
50
51
combineLatest(bird$, gamePipes$)
52
.pipe(
53
scan < any,
54
any >
55
((state, [bird, pipes]) => ({
56
bird: bird,
57
pipes: pipes,
58
lives:
59
state.lives -
60
valueOnCollisionFor(pipes).when(
61
pipes.some(c => c.some(c => c.y === 0 && c.x === bird))
62
),
63
score:
64
state.score + valueOnCollisionFor(pipes).when(pipes[0][0].y === 0)
65
}),
66
{ lives: 3, score: 0, bird: 0, pipes: [] }),
67
tap(state =>
68
paint(updateGame(state.bird, state.pipes), state.lives, state.score)
69
),
70
takeWhile(state => state.lives > 0)
71
)
72
.subscribe();
Copied!

html-renderer.ts

1
const createElem = col => {
2
const elem = document.createElement('div');
3
elem.classList.add('board');
4
elem.style.display = 'inline-block';
5
elem.style.marginLeft = '10px';
6
elem.style.height = '6px';
7
elem.style.width = '6px';
8
elem.style['background-color'] =
9
col === 0
10
? 'white'
11
: col === 1
12
? 'cornflowerblue'
13
: col === 2
14
? 'gray'
15
: 'silver';
16
elem.style['border-radius'] = '90%';
17
return elem;
18
};
19
20
export const paint = (game: number[][], lives, score) => {
21
document.body.innerHTML = `Lives: ${lives}, Score: ${score}`;
22
23
game.forEach(row => {
24
const rowContainer = document.createElement('div');
25
row.forEach(col => rowContainer.appendChild(createElem(col)));
26
document.body.appendChild(rowContainer);
27
});
28
};
Copied!

Operators Used

Last modified 1yr ago