2024-10-02 CH-4 개인과제 (리얼 타임 과제) 스테이지 구분, 스테이지에 따른 점수 획득 구분 2

MOON·2024년 10월 2일
0

내일배움캠프 과제

목록 보기
15/36

오늘은 이제 필수 구현 기획중에서 스테이지 구분, 스테이지에 따른 점수 획득 구분 등을 구현해 보았습니다.

먼저 public아넹 Score.js파일을 보았습니다.

먼저 지속적으로 실행하는 update함수가 있었고 여기서 초당 점수를 획득을 하는 부분이 있었습니다.

update(deltaTime) {
 this.score += deltaTime * 0.001; // 초당 점수 획득 
 
 ...
 
 }

assets에서 stage.json파일을 확인해 보겠습니다. 기획에는 스테이지마다 초당 scorePerSecond에 맞게 점수를 획득하게 기획이 되어있습니다.

stage.json파일

{
  "name": "stage",
  "version": "1.0.0",
  "data": [
    { "id": 1000, "score": 0, "scorePerSecond": 1 },
    { "id": 1001, "score": 10, "scorePerSecond": 2 },
    { "id": 1002, "score": 30, "scorePerSecond": 4 },
    { "id": 1003, "score": 50, "scorePerSecond": 8 },
    { "id": 1004, "score": 80, "scorePerSecond": 16 },
    { "id": 1005, "score": 120, "scorePerSecond": 20 },
    { "id": 1006, "score": 200, "scorePerSecond": 30 }
  ]
}

그래서 일단 assets에서 stage.json파일의 값을 가져와서, 초당 점수를 획득하는 부분에서 scorePerSecond값을 곱해주어 해당 스테이지에 맞게 점수를 획득할 수 있게 만들어 주었습니다.

class Score {
  score = 0;
  scorePerSecond = 1;
  HIGH_SCORE_KEY = 'highScore'; // 로컬로 최종 점수 저장할 키
  stageChange = true;
  currentStageId = stageAssetData.data[0].id;
  targetStageId = this.currentStageId + 1; // 스테이지 데이터 테이블 구조가 1000, 1001,.... 이런식으로 구조가 되어있어 +1로 할당
  stage = 1;

  constructor(ctx, scaleRatio) {
    this.ctx = ctx;
    this.canvas = ctx.canvas;
    this.scaleRatio = scaleRatio;
  }

  update(deltaTime) {
    this.score += deltaTime * 0.001 * stageAssetData.data[this.stage - 1].scorePerSecond;
    if (
      stageAssetData.data[this.stage] &&
      Math.floor(this.score) >= stageAssetData.data[this.stage].score &&
      this.stageChange
    ) {
      this.stageChange = false;
      sendEvent(11, { currentStage: this.currentStageId, targetStage: this.targetStageId }); // 스테이지 이동
      // 여기서 클라이언트 현재 스테이지도 바뀌고 타켓스테이지도 변경
      this.currentStageId += 1;
      this.targetStageId += 1;
      this.stage++;
    }
    
    if (
      Math.floor(this.score) >= stageAssetData.data[this.stage - 1].score + 1 &&
      !this.stageChange
    ) {
      // stageChange 다시 초기화
      this.stageChange = true;
    }
  }

이렇게 동작이 잘되는 것을 확인한 후에 서버에서도 그럼 해당 점수가 제대로 점수를 획득하였는지 이상은 없는지 체크합니다. 그 후 서버에서도 해당 유저가 해당 스테이지에대한 정보를 추가해줍니다.

import { getGameAssets } from '../init/assets.js';
import { getStage, setStage } from '../models/stage.model.js';
import { scoreValidation } from '../utils/score.validation.js';

export const moveStageHandler = (userId, payload) => {
  // currentStage, targetStage
  // 유저의 현재 스테이지 정보 불러오기
  let currentStages = getStage(userId);
  if (!currentStages.length) {
    return { status: 'fail', message: 'No stages found for user' };
  }
  // 오름차순 -> 가장 큰 스테이지 ID를 확인 <- 유저의 현재 스테이지
  // 1번줄에서 작성한 것 처럼 유저가 계속해서 스테이지가 추가되면 가장 큰 스테이지 ID가 쌓이고 현재 유저의 스테이지일 테니깐 이런식으로 작성합니다.
  currentStages.sort((a, b) => a.id - b.id);
  const currentStage = currentStages[currentStages.length - 1];

  // 클라이언트 vs 서버 비교
  if (currentStage.id !== payload.currentStage) {
    return { status: 'fail', message: 'Current Stage mismatch' };
  }

  // targetStage 대한 검증 <- 게임에셋에 존재하는가? 넘어갈 스테이지 정보가 없다면
  const { stages } = getGameAssets();
  if (!stages.data.some((stage) => stage.id === payload.targetStage)) {
    return { status: 'fail', message: 'Target stage not found' };
  }

  // 점수 검증
  const serverTime = Date.now(); // 현재 타임스탬프
  if (!scoreValidation(serverTime, currentStages, payload.targetStage)) {
    return { status: 'fail', message: 'Invalid elapsed time' };
  }

  // 다음 스테이지 id 추가
  setStage(userId, payload.targetStage, serverTime); // 다음스테이지의 타임을 정해주었고
  return { status: 'success! next Stage' };
};

점수 검증

위에 코드 부분 중에서 해당 받은 값의 데이터를 이용하여 점수를 구하여 검증하는 과정을 가집니다.

// 점수 검증
  const serverTime = Date.now(); // 현재 타임스탬프
  if (!scoreValidation(serverTime, currentStages, payload.targetStage)) {
    return { status: 'fail', message: 'Invalid elapsed time' };
  }

scoreValidation() 함수 코드

import { getGameAssets } from '../init/assets.js';

export const scoreValidation = (serverTime, currentStages, targetStageId) => {
  const errorScope = 5; // 오차 범위
  const { stages } = getGameAssets();
  let totalScore = 0;

  // 점수검증에서 1스테이지 점수 와 2스테이지 점수 + 3스테이지 점수 등을 나누어 더해주었을때 오차 범위가 5인 약간 이러한 형태로 가야될것같다
  // 현재로는 일단 1스테이지 점수는 현재 플레이어의 총 스테이지만큼 반복문을 돌고 각 스테이지의 점수를 더해서 총 점수를 가져오고 그점수를 오차범위에 들어오는지 비교하여 확인하기
  for (let i = 0; i < currentStages.length; i++) {
    // 각 스테이지 구간에서 점수를 구한다.
    const stageEndTime =
      i === currentStages.length - 1 ? serverTime : currentStages[i + 1].timestamp; // 마지막 인덱스와 일치하면 즉 현재 스테이지면 현재 시간 serverTime을 할당 그외에는 바로 다음 스테이지 시작시간 즉 해당스테이지의 EndTime

    const elapsedTime = (stageEndTime - currentStages[i].timestamp) / 1000; // 각 스테이지 마다의 경과한 시간을 구합니다.
    // console.log('--------elapsedTime--------', elapsedTime);
    const stageScore = elapsedTime * stages.data[i].scorePerSecond; // 경과한 시간에 데이터 테이블의 초당 점수를 곱해줍니다.
    // console.log('-------stageScore---------', stageScore);
    totalScore += stageScore;
    // console.log('-------totalScore---------', totalScore);
  }
  const targetStage = stages.data.find((stage) => stage.id === targetStageId);
  const sfda = Math.abs(targetStage.score - totalScore);
  if (sfda > errorScope) return false;
  else return true;
};

일단 오늘은 여기까지 작성해 보았습니다.

오늘의 회고

와 사실 점수검증 부분에서 어떻게 각 스테이지의 점수를 획득 한 것을 가져와서 비교를 해야되나 머리가 하애지면서 다시 코드들의 흐름을 좀 보면서 조금씩 이해하면서 작성했습니다. 겨우 작성을 하기는 했지만 아직 갈길이 머네요 그래도 오늘도 화이팅!!

profile
안녕하세요

0개의 댓글