Home 신기록을 알아차리기 그리고 폭죽터뜨리기
Post
Cancel

신기록을 알아차리기 그리고 폭죽터뜨리기

1. 신기록 갱신

우선 신기록을 갱신할때마다 알려줘야한다. 그 기능을 구현하고 폭죽을 구현해보자. 그럼 알고리즘을 어떻게 짜면 될까? 맨처음엔 어떻게 생각했냐면 html 에 first ranking 의 innerHtml이 바뀔때 마다 알려주는 API가 없을까 라고 검색하던 도중 발견한게 하나 있다. 바로 MutationObserver 라는 API이다. 근데 사실은, paint함수때문에 게임이 최초 실행될때마다 , 또는 게임이 끝날때 마다 first ranking이 다시 새로쓰여지기 때문에 매번 trigger 될것이다. 따라서 의미 없다.(그래도 유용하게 쓰일지 몰라 나중에 새로알게된 문법에 정리하려고 한다.)

그럼 알고리즘을 짜야한다. 어떻게 짜야할까?

1. 게임을 다 완료한 순간 걸리는 시간을 계산(new record 라고 하자).

2. 이전에 1등했던 기록을 가져옴(old record 라고 하자)).

3. new record - old record 가 0보다 작으면 신기록이라고 하면 된다.

4. 혹시 처음 기록을 쓰는 경우 innerHtml = ‘‘라고 가정하면 된다.

2. 기능 구현하기

recordToggle.js

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
  updateRecord() {
    this.timeRecord = this.aggregateRecord();
    this._compareRecord(this.timeRecord);
    this._firstRankingDetector(this.timeRecord);
    this._paintRecord(this.recordList);
    this._saveRecord();
  }

  _firstRankingDetector(record) {
    let firstRankingNumberOnly = this.firstRankingSpan.innerHTML.substring(
      5,
      7
    );
    let difference = record - firstRankingNumberOnly;
    if (this.firstRankingSpan.innerHTML === '') {
      console.log('first record!');
      startFireworks();
      sound.playfireworksSound();
    } else if (difference > 0) {
      console.log('not that special');
    } else if (difference === 0) {
      console.log('same record');
    } else if (difference < 0) {
      console.log('new record');
      startFireworks();
      sound.playfireworksSound();
    }
  }

first record / new record 에서 폭죽이 터지도록 했다. 그럼 폭죽을 구현해보자.

3. 폭죽구현

폭죽은 내가 구현하지 못하니 검색을 통해 이미 구현되어있는 것을 가져오기로 했다. 그러던중 좋은것을 발견하였다. 코드도 길지않았고 간단했다.

fireworks.js

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
const max_fireworks = 10,
  max_sparks = 10;
let canvas = document.getElementById("myCanvas");
let context = canvas.getContext("2d");

var fireworkId;
let num = 0;

context.canvas.width = window.innerWidth;
context.canvas.height = window.innerHeight - 5;

let fireworks = [];

function makeFireworks() {
  num = 0;

  for (let i = 0; i < max_fireworks; i++) {
    let firework = {
      sparks: [],
    };
    for (let n = 0; n < max_sparks; n++) {
      let spark = {
        vx: Math.random() * 5 + 0.5,
        vy: Math.random() * 5 + 0.5,
        weight: Math.random() * 0.3 + 0.03,
        red: Math.floor(Math.random() * 199),
        green: Math.floor(Math.random() * 199),
        blue: Math.floor(Math.random() * 199),
      };
      if (Math.random() > 0.5) spark.vx = -spark.vx;
      if (Math.random() > 0.5) spark.vy = -spark.vy;
      firework.sparks.push(spark);
    }
    fireworks.push(firework);
    resetFirework(firework);
  }
}

function resetFirework(firework) {
  firework.x = Math.floor(Math.random() * canvas.width);
  firework.y = canvas.height;
  firework.age = 0;
  firework.phase = "fly";
}

function explode() {
  num++;
  context.clearRect(0, 0, canvas.width, canvas.height);
  fireworks.forEach((firework, index) => {
    if (firework.phase == "explode") {
      firework.sparks.forEach((spark) => {
        for (let i = 0; i < 10; i++) {
          let trailAge = firework.age + i;
          let x = firework.x + spark.vx * trailAge;
          let y =
            firework.y +
            spark.vy * trailAge +
            spark.weight * trailAge * spark.weight * trailAge;
          let r = Math.floor(spark.red * 2);
          let g = Math.floor(spark.green * 2);
          let b = Math.floor(spark.blue * 2);

          context.beginPath();
          context.fillStyle = "rgba(" + r + "," + g + "," + b + ",1)";
          context.rect(x, y, 4, 4);
          context.fill();
        }
      });
      firework.age++;
      if (firework.age > 100 && Math.random() < 0.05) {
        resetFirework(firework);
      }
    } else {
      firework.y = firework.y - 10;
      for (let spark = 0; spark < 15; spark++) {
        context.beginPath();
        context.fillStyle = "rgba(" + index * 50 + "," + spark * 17 + ",0,1)";
        context.rect(
          firework.x + Math.random() * spark - spark / 2,
          firework.y + spark * 4,
          4,
          4
        );
        context.fill();
      }
      if (Math.random() < 0.001 || firework.y < 200) firework.phase = "explode";
    }
  });

  fireworkId = window.requestAnimationFrame(explode);
}
makeFireworks();
fireworkId = window.requestAnimationFrame(explode);

캔버스로 구현했다. 굉장히 복잡해보이지만 원리는 간단하다. 일단 makefireworks로 폭죽 스파크를 랜덤으로 만든다. 그리고 explode로 그것을 1초에 60번 표시하면서 애니메이션을 구현해낸다. 폭죽의 갯수와 스파크의 갯수도 조절할 수 있고 색깔또한 조절할 수 있다. 그리고 캔버스의 크기는 css로 조절할 수 없다. 따라서 화면에 꽉차게 하기 위해서 width / height를 위와 같이 window api를 이용해서 지정해줬다. 반응형이다 히히

그럼 html에 myCanvas를 추가하고 css로 z-index를 조절해주자.

1
<canvas id="myCanvas"></canvas>
1
2
3
4
#myCanvas {
  position: absolute;
  z-index: -3;
}

그리고 fireworks를 조금 손봐야한다. 폭죽이 시작되서 중지되기 까지 로직을 짜봤다.

1. 폭죽이 시작되면 z-index가 커지면서 표시가 되게 해야한다.

2. 시간이 지나면서 서서히 없어져야하고 다 없어지면 애니메이션이 중지가 되어야한다.

3. 중지가 되면 z-index가 낮아지면서 게임을 클릭가능하게 해야한다.

폭죽을 수정하는건 다음글에서:)

This post is licensed under CC BY 4.0 by the author.