RxJS 배우기
  • 소개
  • RxJS 배우기
    • 연산자
      • Combination
        • combineAll
        • combineLatest
        • concat
        • concatAll
        • endWith
        • forkJoin
        • merge
        • mergeAll
        • pairwise
        • race
        • startWith
        • withLatestFrom
        • zip
      • Conditional
        • defaultIfEmpty
        • every
        • iif
        • sequenceEqual
      • Creation
        • ajax
        • create
        • defer
        • empty
        • from
        • fromEvent
        • generate
        • interval
        • of
        • range
        • throw
        • timer
      • Error Handling
        • catch / catchError
        • retry
        • retryWhen
      • Multicasting
        • publish
        • multicast
        • share
        • shareReplay
      • Filtering
        • audit
        • auditTime
        • debounce
        • debounceTime
        • distinct
        • distinctUntilChanged
        • distinctUntilKeyChanged
        • filter
        • find
        • first
        • ignoreElements
        • last
        • sample
        • single
        • skip
        • skipUntil
        • skipWhile
        • take
        • takeLast
        • takeUntil
        • takeWhile
        • throttle
        • throttleTime
      • Transformation
        • buffer
        • bufferCount
        • bufferTime
        • bufferToggle
        • bufferWhen
        • concatMap
        • concatMapTo
        • exhaustMap
        • expand
        • groupBy
        • map
        • mapTo
        • mergeMap / flatMap
        • mergeScan
        • partition
        • pluck
        • reduce
        • scan
        • switchMap
        • switchMapTo
        • toArray
        • window
        • windowCount
        • windowTime
        • windowToggle
        • windowWhen
      • Utility
        • tap / do
        • delay
        • delayWhen
        • dematerialize
        • finalize / finally
        • let
        • repeat
        • timeInterval
        • timeout
        • timeoutWith
        • toPromise
      • 전체 목록
    • Subjects
      • AsyncSubject
      • BehaviorSubject
      • ReplaySubject
      • Subject
    • 사용예시
      • Alphabet Invasion Game
      • Battleship Game
      • Breakout Game
      • Car Racing Game
      • Catch The Dot Game
      • Click Ninja Game
      • Flappy Bird Game
      • Game Loop
      • Horizontal Scroll Indicator
      • Http Polling
      • Lockscreen
      • Matrix Digital Rain
      • Memory Game
      • Mine Sweeper Game
      • Platform Jumper Game
      • Progress Bar
      • Save Indicator
      • Smart Counter
      • Space Invaders Game
      • Stop Watch
      • Swipe To Refresh
      • Tank Battle Game
      • Tetris Game
      • Type Ahead
      • Uncover Image Game
    • 개념
      • RxJS 입문서
      • RxJS v5 -> v6 업그레이드
      • 시간 기반의 연산자 비교
      • 연산자 imports의 이해
Powered by GitBook
On this page
  • Example Code
  • index.ts
  • interfaces.ts
  • constants.ts
  • html-renderer.ts
  • Operators Used

Was this helpful?

  1. RxJS 배우기
  2. 사용예시

Breakout Game

PreviousBattleship GameNextCar Racing Game

Last updated 5 years ago

Was this helpful?

By

This recipe demonstrates an RxJS implementation of Breakout game.

Example Code

( )

Breakout Game

index.ts

// RxJS v6+
import { fromEvent, of, interval, combineLatest, generate, noop } from 'rxjs';
import { map, mergeMap, pluck, startWith, scan, toArray, takeWhile, tap } from 'rxjs/operators';
import { gameSize } from './constants';
import { Player, Ball, GameObject } from './interfaces';
import { render } from './html-renderer';

const createGameObject = (x, y) => ({ x, y });

const player$ = combineLatest(
  of({ ...createGameObject(gameSize - 2, (gameSize / 2) - 1), score: 0, lives: 3 }),
  fromEvent(document, 'keyup').pipe(startWith({ code: '' }), pluck('code'))
).pipe(
  map(([player, key]) => (
    key === 'ArrowLeft'
      ? player.y -= 1
      : key === 'ArrowRight'
        ? player.y += 1
        : noop
    , player)
  )
)

const ball$ = combineLatest(
  of({ ...createGameObject(gameSize / 2, (gameSize - 3)), dirX: 1, dirY: 1 }),
  interval(150)
).pipe(
  map(([ball, _]: [Ball, number]) => (
    ball.dirX *= ball.x > 0 ? 1 : -1,
    ball.dirY *= (ball.y > 0 && ball.y < gameSize - 1) ? 1 : -1,
    ball.x += 1 * ball.dirX,
    ball.y -= 1 * ball.dirY,
    ball)
  )
)

const bricks$ = generate(1, x => x < 8, x => x + 1)
  .pipe(
    mergeMap(r => generate(r % 2 === 0 ? 1 : 0, x => x < gameSize, x => x + 2)
      .pipe(map(c => createGameObject(r, c)))
    ),
    toArray()
  )

const processGameCollisions = (_, [player, ball, bricks]: [Player, Ball, GameObject[]])
  : [Player, Ball, GameObject[]] => (
    (collidingBrickIndex => collidingBrickIndex > -1
      ? (bricks.splice(collidingBrickIndex, 1), ball.dirX *= -1, player.score++)
      : noop
    )(bricks.findIndex(e => e.x === ball.x && e.y === ball.y)),
    ball.dirX *= player.x === ball.x && player.y === ball.y ? -1 : 1,
    ball.x > player.x ? (player.lives-- , ball.x = (gameSize / 2) - 3) : noop,
    [player, ball, bricks]
  )

combineLatest(player$, ball$, bricks$)
  .pipe(
    scan<[Player, Ball, GameObject[]], [Player, Ball, GameObject[]]>(processGameCollisions),
    tap(render),
    takeWhile(([player]: [Player, Ball, GameObject[]]) => player.lives > 0)
  ).subscribe()

interfaces.ts

export interface GameObject {
  x: number;
  y: number;
}
export interface Player extends GameObject {
  score: number;
  lives: number;
}
export interface Ball extends GameObject {
  dirX: number;
  dirY: number;
}

constants.ts

export const gameSize = 20;

html-renderer.ts

import { gameSize } from './constants';
import { Player, Ball, GameObject } from './interfaces';

const empty = 0;
const plyer = 1;
const bll = 2;
const brick = 3;

const createElem = col => {
  const elem = document.createElement('div');
  elem.classList.add('board');
  elem.style.display = 'inline-block';
  elem.style.marginLeft = '10px';
  elem.style.height = '6px';
  elem.style.width = '6px';
  elem.style['background-color'] =
    col === empty
      ? 'white'
      : col === plyer
      ? 'cornflowerblue'
      : col === bll
      ? 'gray'
      : 'silver';
  elem.style['border-radius'] = col === bll ? '100%' : '0%';
  return elem;
};

export const render = ([player, ball, bricks]: [
  Player,
  Ball,
  GameObject[]
]) => {
  const game = Array(gameSize)
    .fill(0)
    .map(e => Array(gameSize).fill(0));
  game[player.x][player.y] = plyer;
  game[ball.x][ball.y] = bll;
  bricks.forEach(b => (game[b.x][b.y] = brick));

  document.body.innerHTML = `Score: ${player.score} Lives: ${player.lives} <br/>`;
  game.forEach(r => {
    const rowContainer = document.createElement('div');
    r.forEach(c => rowContainer.appendChild(createElem(c)));
    document.body.appendChild(rowContainer);
  });
};

Operators Used

combineLatest
fromEvent
generate
interval
mergeMap
of
pluck
scan
startWith
takeWhile
tap
toArray
adamlubek
StackBlitz