[Project_] RTG - Trouble Shooting

2024. 12. 18. 21:57·Project_xxx

Real Time Game 트러블 슈팅

WebSocket 통신을 사용한 게임 구현하기

 

● Chrome 브라우저에서 네트워크 연결 에러 페이지에서 등장하는 이스터 에그 게임

T-Rex Dinosaur Game

 

● 구현 해야하는 것

 - WebSocket을 사용하여 다음 스테이지로 넘어가야 한다.

 - 현재 상태 : 하드 코딩으로 넘어가고 있는 상태

스테이지를 넘어가는 update 함수

 

처음에는...

● 처음에는 assets을 모아둔 폴더에서 직접적으로 가져오려고 했다. 그랬더니...

GET http://localhost:3001/assets/stage.json net::ERR_ABORTED 404 (Not Found)

위와 같은 에러가 발생했다.

→ 왜 그런가 했더니

app.js의 express.static 상태

 

- 현재 서버에서는 'public' 이라는 디렉토리 안에 있는 파일들만 외부에서 사용되도록 정적처리 되어 있다.

- 'assets' 디렉토리는 'public' 에서는 접근 허용이 불가함으로 stage.json을 자체적으로 가져와서 사용할 수는 없다.

현재 내 로컬의 디렉토리 설정

 

그럼 어떻게 해야하는가?

● 다시 생각해보자!

  - 현재 WebSocket 통신에 대한 개념으로 게임을 구현하려고 하고 있다!

  - 구현하려는 코드에는 현재 클라이언트 상에서 플레이하는 유저의 점수 정보와 스테이지 점수를
    서버에서 보내주는 데이터와 비교하여 맞는지 비교해야한다.

  

 1) 클라이언트에서 userId를 판별해야한다

// public/Score.js
initUser() {
    const socket = io(); // Socket.io 연결

    // 서버로부터 userId 수신
    socket.on('connection', (data) => {
      this.userId = data.uuid; // 서버에서 받은 userId 저장
      console.log('Initialized user with ID:', this.userId);
    });
  }

- 서버에서 userId를 uuid 형태로 저장

- Socket 통신을 사용하여 'connection'으로 정의한 곳에서 데이터를 가져온다

// src/handlers/register.handler.js
const registerHandler = (io) => {
  io.on('connection', (socket) => {
    // 접속을 했을 때 이벤트
    console.log('A user connected:', socket.id);

    // UUID 생성
    const userUUID = uuidv4();

    if (!userUUID) {
      console.error('Failed to generate userUUID');
      return;
    }

    // 스테이지 초기화 및 유저 추가
    createStage(userUUID);
    console.log('Generated userUUID: ', userUUID);
    addUser({ uuid: userUUID, socketId: socket.id });

 

2) userId를 가져왔으니 currentStage와 targetStage에 대한 데이터를 stage.json의 내용과 비교해야한다

// public/Score.js
// score에 대한 데이터
 async getTargetScore(stageId) {
    return new Promise((resolve, reject) => {
      this.socket.emit('request-game-assets', (response) => {
        if (response.status === 'success') {
          const stage = response.data.stages?.data?.find((s) => s.id === stageId);
          if (!stage) {
            console.error(`Stage with ID ${stageId} not found`);
            resolve(null); // 해당 스테이지가 없으면 null 반환
          } else {
            resolve(stage.score); // 스테이지의 목표 점수 반환
          }
        } else {
          console.error('Failed to fetch target score:', response.message);
          reject(null);
        }
      });
    });
  }
  
  // stage에 대한 데이터
  async getStageData(stageId) {
    return new Promise((resolve, reject) => {
      this.socket.emit('request-game-assets', (response) => {
        if (response.status === 'success') {
          const stage = response.data.stages?.data?.find((s) => s.id === stageId);
          if (!stage) {
            console.error(`Stage with ID ${stageId} not found`);
            resolve(null); // 해당 스테이지가 없으면 null 반환
          } else {
            resolve(stage); // 스테이지 데이터 반환
          }
        } else {
          console.error('Failed to fetch stage data:', response.message);
          reject(null);
        }
      });
    });
  }

- 'public' 에서 Socket 통신을 통해 'request-game-assets'에 대한 데이터를 요청한다.

// src/handlers/register.handler.js
// 클라이언트가 game assets 요청 시 처리
    socket.on('request-game-assets', (callback) => {
      try {
        const assets = getGameAssets(); // gameAssets 가져오기
        callback({ status: 'success', data: assets });
      } catch (error) {
        console.error('Failed to retrieve game assets:', error.message);
        callback({ status: 'fail', message: 'Failed to retrieve game assets' });
      }
    });

- 'request-game-assets'(으)로 가져올 데이터를 서버에서 불러온 후 클라이언트에게 데이터를 넘겨준다.

 

3) 필요한 데이터들을 전부 가져왔으니 점수에 따라 스테이지를 변경!

const stageData = await this.getStageData(this.currentStageId); // 현재 스테이지 데이터 가져오기

      if (!stageData) {
        console.error('Stage data is null. Cannot proceed.');
        return;
      }

      const { score: targetScore, scorePerSecond } = stageData;

      // 점수 증가 (스테이지별 scorePerSecond를 반영)
      this.score += deltaTime * 0.001 * scorePerSecond;

      // 목표 점수에 도달하고 stageChange 플래그가 true일 때 다음 스테이지로 이동
      if (this.score >= targetScore && this.stageChange) {
        console.log('Condition met: Moving to next stage');
        this.stageChange = false; // 플래그 비활성화

        sendEvent(
          11,
          { currentStage: this.currentStageId, targetStage: this.currentStageId + 1 },
          (response) => {
            if (response.status === 'success') {
              this.currentStageId += 1; // 다음 스테이지로 이동
            } else {
              console.error('Failed to move to the next stage:', response.message);
            }
            this.stageChange = true; // 다시 요청 가능하도록 플래그 초기화
          },
        );
      }

- sendEvent를 통해 11으로 정의한 handlerMapping을 통해 stage가 넘어가는 handler에 클라이언트의 데이터 제공

- 제공된 데이터를 토대로 currentStage를 비교해서 다음 스테이지로 넘어가는 stage.handler 함수 실행

 

막혔던 부분

● response 호출

// public/Socket.js
const sendEvent = (handlerId, payload) => {
  socket.emit(
    'event',
    {
      userId,
      clientVersion: CLIENT_VERSION,
      handlerId,
      payload,
    },

- response으로 status : 'success'를 받아 스테이지가 넘어갔는지 판별을 하려고 했지만 response가 불러와지지 않았다.

 

● 왜 그런가?

 - 위의 getStageData, getTargetScore, update에서 모두 response를 사용하여 서버에서 보내준 데이터를 사용하고 있는데

   public/Socket.js에서 서버와 통신을 담당하는 코드에서는 정작 response를 불러오는 코드가 존재하지 않았다.

 

어떻게 수정했는가?

const sendEvent = (handlerId, payload, callback) => {
  socket.emit(
    'event',
    {
      userId,
      clientVersion: CLIENT_VERSION,
      handlerId,
      payload,
    },
    (response) => {
      if (callback) callback(response); // 클라이언트 콜백 호출
    },
  );
};

- callback 함수를 사용하여 해결

- socket.emit으로 'evnet'로 정의된 곳에 데이터를 전송하고, 서버가 요청을 처리한 후
  'response'를 클라이언트로 반환

 

어떻게든 돌아간다!

● 하드 코딩, http 호출 등 여러가지 방법을 사용해보았고 지금 현재 공부하고 적용해야할 것은
   결국 Socket 통신을 사용해서 서버와 클라이언트 간에 통신이 이루어져야 한다.

 

● 처음에 http 사용 방식과 비슷하다고 잘못 이해하고 있었고 이는 결국 여러가지를 테스트해야하는
   삽질을 하게 되었다.

 

● Socket 통신으로 구현하니 실시간으로 통신이 가능하여 최고 점수 비교 등 실시간에 필요한 기능들을
   구현할 수 있을 것이다.

'Project_xxx' 카테고리의 다른 글

[Project_] TD Trouble Shooting - 1  (1) 2024.12.30
[Project_] RTG - Trouble Shooting 2  (1) 2024.12.19
[Project_] FO - 2 (Trouble Shooting)  (1) 2024.12.06
[Project_] FO - 1  (1) 2024.12.03
[Project_] Item-Simulator [Trouble Shooting]  (0) 2024.11.28
'Project_xxx' 카테고리의 다른 글
  • [Project_] TD Trouble Shooting - 1
  • [Project_] RTG - Trouble Shooting 2
  • [Project_] FO - 2 (Trouble Shooting)
  • [Project_] FO - 1
wakelight23
wakelight23
했던 공부를 기록하는 곳
  • wakelight23
    wakelight23 공부기록
    wakelight23
  • 전체
    오늘
    어제
    • 분류 전체보기 (110)
      • Programming Language (26)
        • Error Note (1)
        • HTML, CSS (3)
        • Javascript 공부기록 (5)
        • SQL 문제풀이 (4)
      • Develop Diary (34)
        • Interview (20)
        • TheFourFs (13)
      • Project_xxx (18)
      • IT (32)
        • DB (0)
        • Hardware (3)
        • Network (10)
        • Server (11)
        • OS (4)
        • 게임수학 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    Til
    Wil
    today i learnd
    Today I learned
    KPT회고
    today i leraned
    today i lerned
    weekly i learned
  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
wakelight23
[Project_] RTG - Trouble Shooting
상단으로

티스토리툴바