SSAFY 13๐Ÿ“– ์‘์šฉ ๋ฐ ๋” ์•Œ์•„๋ณด๊ธฐ

๐Ÿ“– ์‘์šฉ ๋ฐ ๋” ์•Œ์•„๋ณด๊ธฐ

๐Ÿ“ฆ ๋กœ์ปฌ ์ €์žฅ์†Œ์™€ ํฌํƒˆ ์‚ฌ์šฉํ•˜๊ธฐ

ssafy13-localstorage-portal

๋กœ์ปฌ ์ €์žฅ์†Œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ, ํฌํƒˆ๋กœ ๋งต์„ ์ด๋™ํ•ด๋„ ๋™์ผํ•œ ์ •๋ณด๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ €์žฅํ•  ๊ฐ’์€ ๋‘ ๊ฐ€์ง€ ์ข…๋ฅ˜๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ณตํ†ต์ ์œผ๋กœ ์ €์žฅํ•ด์•ผ ํ•˜๋Š” ๊ฐ’๊ณผ ๊ฐœ๋ณ„์ ์œผ๋กœ ์ €์žฅํ•ด์•ผ ํ•˜๋Š” ๊ฐ’์ž…๋‹ˆ๋‹ค.

๊ณตํ†ต์ ์œผ๋กœ ์ €์žฅํ•ด์•ผ ํ•˜๋Š” ๊ฐ’

์ ‘๊ทผ์— ์šฉ์ดํ•˜๊ฒŒ ํŒ€๋ช…_๋ณ€์ˆ˜๋ช…์œผ๋กœ ์•„์ดํ…œ ์ด๋ฆ„์„ ์ง€์ • ๋ฐ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์ง€์ •ํ•œ ์•„์ดํ…œ ์ด๋ฆ„์„ ํ†ตํ•ด ๋กœ์ปฌ ์ €์žฅ์†Œ์—์„œ ์›ํ•˜๋Š” ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฒŒ์ž„์ด ์˜ˆ๊ธฐ์น˜ ๋ชปํ•˜๊ฒŒ ์ข…๋ฃŒ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋จน์„ ๋•Œ๋งˆ๋‹ค ์ฆ‰๊ฐ์ ์œผ๋กœ ๋กœ์ปฌ ์ €์žฅ์†Œ์— ์ €์žฅํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

  // ...
 
  // ์•„์ง ์ €์žฅ์ด ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ, null์„ ๋ฐ˜ํ™˜. null์ผ ๊ฒฝ์šฐ 0์œผ๋กœ ์ดˆ๊ธฐํ™”
  let gemCount = localStorage.getItem("teamDave_gemCount") ?? 0;
  const gemCountGUI = GUI.getObject("GEM_COUNT");
  gemCountGUI.setText(`GEM: ${gemCount}`);
 
  for(let i = 0; i < 6; i++) {
    const gem = WORLD.getObject(`GEM_${i}`);
    gem.onCollide(PLAYER, () => {
        gemCount++;
        gemCountGUI.setText(`GEM: ${gemCount}`);
        gem.kill();
        setTimeout(() => gem.revive(), 1000);
        // ๋จน๋Š” ์ฆ‰์‹œ, ๋กœ์ปฌ ์ €์žฅ์†Œ์— ์ €์žฅ
        localStorage.setItem("teamDave_gemCount", gemCount);
    });
  }
 
  // ...

๊ฐœ๋ณ„์ ์œผ๋กœ ์ €์žฅํ•ด์•ผ ํ•˜๋Š” ๊ฐ’

์›”๋“œ๋งˆ๋‹ค ๊ฐœ๋ณ„์ ์œผ๋กœ ์ €์žฅํ•ด์•ผ ํ•˜๋Š” ๊ฐ’์€ ์ค‘๋ณต๋  ์ˆ˜ ์—†๋Š” ๊ฐ’์ธ pid์— ๋ณ€์ˆ˜๋ช…์„ ๋ถ™์—ฌ ์•„์ดํ…œ ์ด๋ฆ„์„ ์ง€์ • ๋ฐ ์ €์žฅํ•ด์ค๋‹ˆ๋‹ค.

์›์‹œ๊ฐ’(์ˆซ์ž, ๋ฌธ์ž์—ด, Boolean, โ€ฆ)์ด ์•„๋‹Œ ๊ฐ’์€ JSON ๋ฌธ์ž์—ด ํ˜•ํƒœ๋กœ ์ €์žฅํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
์ด๋ฅผ ์œ„ํ•ด JSON.stringify()๋กœ ๋ณ€ํ™˜ํ•˜๊ณ , JSON.parse()๋กœ ๋‹ค์‹œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ๋กœ ๋ณต์›ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

  // ...
 
  // ํ˜„์žฌ ์‚ฌ์ดํŠธ์˜ URL์—์„œ pid ๋ถ€๋ถ„์„ ์ถ”์ถœ, ์ด๋ฅผ ํ†ตํ•ด ์•„์ดํ…œ ์ด๋ฆ„ ์ง€์ •
  const urlParams = new URLSearchParams(window.location.search);
  GLOBAL.PID = urlParams.get('pid');
 
  // JSON ๋ฌธ์ž์—ด๋กœ ์ €์žฅ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ฝ์–ด์™€์„œ, ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ๋กœ ๋ณ€๊ฒฝ
  const lastPosition = JSON.parse(localStorage.getItem(`${GLOBAL.PID}_lastPosition`));
  if(lastPosition) { 
      lastPosition.y += 3;
      PLAYER.position.copy(lastPosition);
      PLAYER.body.needUpdate = true;
  }
 
  const portal = WORLD.getObject("PORTAL");
  portal.onCollide(PLAYER, () => {
    // ํ”Œ๋ ˆ์ด์–ด์˜ ๋งˆ์ง€๋ง‰ ์œ„์น˜๋ฅผ JSON ๋ฌธ์ž์—ด ํ˜•ํƒœ๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ์ €์žฅ
    localStorage.setItem(`${GLOBAL.PID}_lastPosition`, JSON.stringify(PLAYER.position));
  });
 
  // ...

๐Ÿฅ‡ ๋žญํ‚น ์‹œ์Šคํ…œ ์ถ”๊ฐ€ํ•˜๊ธฐ

redbrick_rank-result

Redbrick์—์„œ ์ œ๊ณตํ•˜๋Š” ๋‚ด์žฅ ๋ชจ๋“ˆ์„ ํ†ตํ•ด ์†์‰ฝ๊ฒŒ ๋žญํ‚น ์‹œ์Šคํ…œ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
ํ˜„์žฌ ๋žญํ‚น ํ‘œ์‹œ ์œ ํ˜•์€ ์ผ๋ฐ˜์ ์ธ ์ ์ˆ˜์™€ ์‹œ๊ฐ„, ๋‘ ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์ ์ˆ˜ ์ €์žฅํ•˜๊ธฐ

โš ๏ธ

์ ์ˆ˜๋Š” ์ •์ˆ˜๋กœ ์ €์žฅ๋˜๋ฏ€๋กœ, ์ ์ˆ˜ ์‹œ์Šคํ…œ์„ ์„ค๊ณ„ํ•  ๋•Œ ์ •์ˆ˜๋กœ ๊ณ„์‚ฐ๋˜๋„๋ก ๊ณ ๋ คํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
์ ์ˆ˜๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ, ์†Œ์ˆ˜ ๋ถ€๋ถ„์€ ๋ฐ˜์˜ฌ๋ฆผ๋˜์–ด ์ •์ˆ˜๋กœ ๋ณ€ํ™˜๋ฉ๋‹ˆ๋‹ค.

1. ์ผ๋ฐ˜์ ์ธ ์ ์ˆ˜ ์ €์žฅํ•˜๊ธฐ

  // ... ์ ์ˆ˜ ๋ณ€์ˆ˜(myScore) ์„ ์–ธ ๋ฐ ์ •์˜๋˜์–ด ์žˆ๊ณ , ์–ด๋””์„ ๊ฐ€ ์ ์ˆ˜ ์ฆ๊ฐ€
  
  REDBRICK.Rank.saveScore({ score: myScore });
 
  // ... 

2. ์‹œ๊ฐ„์„ ์ ์ˆ˜๋กœ ์ €์žฅํ•˜๊ธฐ

์ ์ˆ˜๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ํฐ ์ˆ˜์—์„œ ์ž‘์€ ์ˆ˜๋กœ ์ •๋ ฌ๋˜์–ด ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ ํƒ€์ž„์–ดํƒ์˜ ๊ฒฝ์šฐ์—๋Š” ์ž‘์„์ˆ˜๋ก ์ˆœ์œ„๊ฐ€ ๋†’์•„์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ •๋ ฌ ์˜ต์…˜์„ ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. order: "ASC"
๋ณ„๋„์˜ ์ •๋ ฌ ์˜ต์…˜์„ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ, ๊ธฐ๋ณธ๊ฐ’์ธ order: "DESC"๋กœ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.

Time Attack
  // Redbrick ๋‚ด์žฅ ๋ชจ๋“ˆ Timer ์‚ฌ์šฉํ•˜๊ธฐ
  const timer = new REDRBICK.Timer();
  timer.start(); // ํƒ€์ด๋จธ ์‹œ์ž‘
  timer.pause(); // ํƒ€์ด๋จธ ์ผ์‹œ์ •์ง€
  
  // ์ ์ˆ˜๋Š” ์ •์ˆ˜ ์ €์žฅ, ์˜ˆ๋ฅผ ๋“ค์–ด ํƒ€์ด๋จธ๊ฐ€ 9.33์ดˆ๋ผ๋ฉด ๋ฐ˜์˜ฌ๋ฆผํ•˜์—ฌ 9์ดˆ๋กœ ์ €์žฅ
  REDBRICK.Rank.saveScore({ score: timer.getTime(), order: "ASC" });
 
  // ... 
๐Ÿšซ

์ ์ˆ˜ ์ €์žฅ์€ ํ•œ ๊ฒŒ์ž„์ด ๋๋‚˜๋ฉด ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
Update๋‚˜ setInterval๊ณผ ๊ฐ™์€ ๋ฃจํ”„์—์„œ ํ˜ธ์ถœํ•œ๋‹ค๋ฉด ์กฐ๊ฑด์„ ํ†ตํ•ด ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ์„ธ์š”.

๋žญํ‚น ๋ณด์—ฌ์ฃผ๊ธฐ

1. ์ผ๋ฐ˜์ ์ธ ์ ์ˆ˜ ๋žญํ‚น ๋ณด์—ฌ์ฃผ๊ธฐ

  REDBRICK.Rank.show();

2. ์‹œ๊ฐ„์œผ๋กœ ๋žญํ‚น ๋ณด์—ฌ์ฃผ๊ธฐ

์ ์ˆ˜๋ฅผ ์ €์žฅํ•  ๋•Œ, ์ ์šฉํ–ˆ๋˜ ์ •๋ ฌ ์˜ต์…˜์„ ๊ทธ๋Œ€๋กœ ์ ์šฉํ•ด์ฃผ์–ด์•ผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋žญํ‚น์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

Time Attack
  REDBRICK.Rank.show({ type: "time", order: "ASC" });

GUI์™€ ์—ฐ๊ฒฐํ•˜์—ฌ ๋žญํ‚น ๋ณด์—ฌ์ฃผ๊ธฐ

GUI์˜ onClick() ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋žญํ‚น์„ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Rank Button
  this.onClick(() => {
    REDBRICK.Rank.show({ type: "time", order: "ASC" });
  });

๋” ์ž์„ธํ•œ ๋‚ด์šฉ์€ Rank ํŽ˜์ด์ง€๋ฅผ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”!

๐Ÿ’ก onCollide() ์™ธ์— ๋˜ ๋‹ค๋ฅธ ์ถฉ๋Œ ๋ฐฉ์‹

๊ฑฐ๋ฆฌ ๊ณ„์‚ฐ์„ ํ†ตํ•ด ์ถฉ๋Œ ํŒ์ •์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
distanceTo() ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ„์‚ฐํ•˜๊ณ  ์ถฉ๋Œ ํŒ์ • ๊ฑฐ๋ฆฌ๋ณด๋‹ค ์ž‘์„ ๊ฒฝ์šฐ, ์ถฉ๋Œ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ค๋‹ˆ๋‹ค.
์ถฉ๋Œ ์‹œ ์ถฉ๋Œ ๊ฐ์ง€ํ•  ์˜ค๋ธŒ์ ํŠธ ๋ฐฐ์—ด์—์„œ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์‚ญ์ œ๋ฅผ ํ•ด๋„ ์ˆœ์„œ๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๊ฒŒ ๋ฐฐ์—ด์˜ ๋งจ ๋’ค์—์„œ๋ถ€ํ„ฐ ์ˆœํšŒํ•ฉ๋‹ˆ๋‹ค.

  // ... objects ๋ฐฐ์—ด ์„ ์–ธ ๋ฐ ์˜ค๋ธŒ์ ํŠธ ์ถ”๊ฐ€
  
  function Update(dt) {
    for(let i = objects.length - 1; i >= 0; i--) {
      // ํ”Œ๋ ˆ์ด์–ด ๊ธฐ์ค€์œผ๋กœ ํŠน์ • ์˜ค๋ธŒ์ ํŠธ๋“ค๊ณผ์˜ ๊ฑฐ๋ฆฌ ๊ณ„์‚ฐ
      if(PLAYER.position.distanceTo(objects[i].position) < 10) {
        // ๊ฑฐ๋ฆฌ๊ฐ€ 10 ๋ฏธ๋งŒ์ด๋ฉด ์ถฉ๋Œ ํŒ์ •์ด ๋˜์–ด, ํ•ด๋‹น ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์‚ญ์ œํ•˜๊ณ  ์ถฉ๋Œ ๊ฐ์ง€ํ•  ์˜ค๋ธŒ์ ํŠธ ๋ฐฐ์—ด์—์„œ๋„ ์‚ญ์ œ
        objects[i].kill();
        objects.splice(i, 1);
      }
    }
  }
 
  // ... 

๐Ÿ•น๏ธ ์ƒ˜ํ”Œ ๊ฒŒ์ž„ ์‚ดํŽด๋ณด๊ธฐ