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

YEONGHUN KO·2021년 11월 11일
0
post-thumbnail

1. 신기록 갱신

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

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

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

2. 이전에 1등했던 기록을 가져옴(old record 라고 하자)).
{: .notice--success}

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

4. 혹시 처음 기록을 쓰는 경우 innerHtml = ''라고 가정하면 된다.
{: .notice--success}

2. 기능 구현하기

recordToggle.js


  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

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를 조절해주자.

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

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

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

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

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

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

profile
'과연 이게 최선일까?' 끊임없이 생각하기

0개의 댓글