728x90

[오늘의 코딩 테스트]

https://ezez99.tistory.com/73

 

[프로그래머스/JS] 덧칠하기

[문제][풀이 코드]function solution(n, m, section) { var answer = 1; let drawsection = section[0]; for(let i = 1; i = m ){ drawsection = section[i]; answer++; } } return answer;}[해설]우선 drawsection이라는 변수를 만들어 첫번째로 색

ezez99.tistory.com

 

[오늘 한일]

● 필수 구현 과제 완성

-TowerHandler 기능 구현 완성

※InitTower

타워 핸들러의 InitTower와 BuyTower기능을 구현하였다.

InitTower의 경우 처음에 타워가 배치되었을 때를 가정하고 제작하였고, 초기 값인 3개까지 타워가 제작되고 해당 타워의 정보를 클라이언트에서 가져온다.

//초기 타워 세팅 이벤트
export const initTower = (userId, payload) => {
  //타워의 갯수가 기존 Initial tower의 갯수보다 많은지 체크
  if (numOfInitialTowers < payload.towersnum) {
    return { status: 'fail', message: 'The initial number of towers was exceeded.' };
  }
  const serverTime = Date.now(); // 현재 타임스탬프
  //타워의 데이터 저장
  setTower(userId, payload.towerId,1 ,payload.position, serverTime);
  return { status: 'success', message: '타워 배치 완료' };
};

※BuyTower

Tower를 구매할시 해당 타워가 생서오디고 서버에서 가지고 있는 userGold를 삭감 시키고, 해당 정보를 클라이언트에 보내는 역할을 한다.

export const buyTower = (userId, payload, socket) => {
  //타워의 가격 비교
  if (payload.userGold < payload.towerCost) {
    return { status: 'fail', message: 'There is little gold.' };
  }

  

  const serverTime = Date.now(); // 현재 타임스탬프

  const userGameState = getUserById(userId);

  userGameState.userGold -= userGameState.towerCost;

  // 업데이트된 게임 상태를 클라이언트에 전송
  socket.emit('updateGameState', {
    userGold: userGameState.userGold,
  });

  //타워의 데이터 저장
  setTower(userId, payload.towerId, 1 ,payload.position, serverTime);
  return { status: 'success' };
};

export const attackTower = (userId, payload) => {
  //payload로 입력받는 정보
  //타워 position , 몬스터 position, 타워의 사거리정보

  const towers = getTower(userId);

  //타워의 데이터 찾기 현재 때린 타워의 ID를 기반으로 저장된 타워를 찾는다.
  const tower = towers.find((data) => data.id === payload.towerId);

  //해당 Id의 타워가 존재하는지 체크
  if (!tower) {
    return { status: 'fail', message: 'There is No Tower' };
  }

  // 해당 위치의 타워가 존재하는지 체크
  if (tower.positon.x != payload.towerpos.x && tower.position.y != payload.towerpos.y) {
    return { status: 'fail', message: 'Position is Not Matching' };
  }

  const towerdistance = Math.sqrt(
    Math.pow(tower.towerpos.x - monster.x, 2) + Math.pow(tower.towerpos.y - monster.y, 2),
  );

  //타워의 사거리 체크
  if (payload.towerRange <= towerdistance) {
    return { status: 'fail', message: 'Tower range is not right' };
  }

  const serverTime = Date.now(); // 현재 타임스탬프

  setAttackTower(userId, payload.towerId, serverTime);

  return { status: 'success', message: 'towerattack' };
};

● 도전 구현 과제 완성

- 타워 환불 기능 구현 & 업그레이드 기능 구현

타워 환불 기능과 업그레이드 기능을 구현할 버튼을 각각 제작하고, 해당 버튼을 누르면 게임 중앙에 해당 기능이 활성화 되었다고 문자가 뜨게 하였다.

해당 문자가 뜬 뒤 타워를 누르면 각각 환불기능과 업그레이드 기능이 구현되도록 게임을 제작하였다.

//타워 클릭 이벤트
canvas.addEventListener('click', (event) => {
  const rect = canvas.getBoundingClientRect();
  const clickX = event.clientX - rect.left;
  const clickY = event.clientY - rect.top;
  const towerRangeX = 30;
  const towerRangeY = 30;

  for (let i = 0; i < towers.length; i++) {
    const tower = towers[i];

    const towerCenterX = tower.x + tower.width / 2;
    const towerCenterY = tower.y + tower.height / 2;

    const deltaX = Math.abs(towerCenterX - clickX);
    const deltaY = Math.abs(towerCenterY - clickY);

    if (deltaX <= towerRangeX && deltaY <= towerRangeY && isrefund) {
      sendEvent(8, { towerId: tower.towerId, towerpos: { x: tower.x, y: tower.y } });
      towers.splice(i, 1);
    } else if (deltaX <= towerRangeX && deltaY <= towerRangeY && isupgrade) {
      sendEvent(9, {
        towerId: tower.towerId,
        towerpos: { x: tower.x, y: tower.y },
        level: tower.level,
      });
    }
  }
});

UpgradeTower의 경우 해당 타워의 상태를 변경하는 UpdateTowerState함수에서 데이터를 관리하며, 해당 타워의 레벨이 변화할 경우 서버에서 클라이언트로 보내 클라이언트가 정보를 받아와 해당하는 타워의 ID의 레벨을 변경하여준다.

UpdateTowerState

const updateTowerState = (syncData) => {
  const towerdata = towers.find((data) => data.towerId === syncData.towerId);
  if (towerdata) {
    towerdata.level = syncData.towerLevel !== undefined ? syncData.towerLevel + 1 : 1;
  }
};

TowerHandler의 UpgradeTower부분

export const upgradeTower = (userId, payload, socket) => {
  const { towerData } = getGameAssets();
  const towers = getTower(userId);

  //타워의 데이터 찾기 현재 때린 타워의 ID를 기반으로 저장된 타워를 찾는다.
  const tower = towers.find((data) => data.id === payload.towerId);

  //해당 Id의 타워가 존재하는지 체크
  if (!tower) {
    return { status: 'fail', message: 'There is No Tower' };
  }

  // 해당 위치의 타워가 존재하는지 체크
  if (tower.position.x != payload.towerpos.x && tower.position.y != payload.towerpos.y) {
    return { status: 'fail', message: 'Position is Not Matching' };
  }

  const updateRate = towerData.data.find((tower) => tower.level === payload.level);
  const updateCost = towerData.data.find((tower) => tower.level + 1 === payload.level + 1);

  if (!updateCost) {
    return { status: 'fail', message: '없는 강화 정보값입니다.' };
  }

  const userGameState = getUserById(userId);

  if (userGameState.userGold < updateCost.upgradeCost) {
    return {
      status: 'fail',
      message: `강화 골드가 ${updateCost.upgradeCost - userGameState.userGold}만큼 부족합니다.`,
    };
  }

  const nowRate = Math.floor(Math.random() * 100 + 1);

  if (updateRate.upgradeRate <= nowRate) {
    return {
      status: 'fail',
      message: `강화가 실패하였습니다. 강화 확률 : ${updateRate.upgradeRate}`,
    };
  }

  userGameState.userGold -= updateCost.upgradeCost;
  const nowlevel = payload.level++;
  // 업데이트된 게임 상태를 클라이언트에 전송
  socket.emit('updateGameState', {
    userGold: userGameState.userGold,
  });

  socket.emit('updateTowerState', {
    towerId: payload.towerId,
    towerLevel: nowlevel,
  });

  tower.level = tower.level + 1;

  return {
    status: 'success',
    message: '타워 강화 성공!',
  };
};

● 추가 구현 사항 제작

- 사운드

게임에서 효과음 사운드가 들어가게 하였다.

효과음은 각각 골드몬스터가 죽었을 때, 몬스터가 죽었을 때, 타워가 때릴 때 3가지의 경우에 사운드가 난다.

const goldmonstersound = new Audio('sound/goldmonster.mp3');
const monsterdeadsound = new Audio('sound/monsterdead.mp3');
const towerattacksound = new Audio('sound/towerattack.mp3');

function playSound(sound) {
  sound.currentTime = 0;
  sound.play();
}

- 강화 확률

강화 확률에 따라 강화시 성공 실패 기능을 구현하였다.

강화 확률은 Json에서 관리하고 TowerHandler에서 강화 확률에 실패할 시 레벨이 오르지 않게 진행하였다.

const updateRate = towerData.data.find((tower) => tower.level === payload.level);
  const updateCost = towerData.data.find((tower) => tower.level + 1 === payload.level + 1);

  if (!updateCost) {
    return { status: 'fail', message: '없는 강화 정보값입니다.' };
  }

  const userGameState = getUserById(userId);

  if (userGameState.userGold < updateCost.upgradeCost) {
    return {
      status: 'fail',
      message: `강화 골드가 ${updateCost.upgradeCost - userGameState.userGold}만큼 부족합니다.`,
    };
  }

  const nowRate = Math.floor(Math.random() * 100 + 1);

  if (updateRate.upgradeRate <= nowRate) {
    return {
      status: 'fail',
      message: `강화가 실패하였습니다. 강화 확률 : ${updateRate.upgradeRate}`,
    };
  }

- 이펙트 변화

타워 레벨의 따른 빔 이펙트 색깔이 변하게 하였다. 해당 정보는 타워 draw 부분에서 관리하였다.

      if(this.level == 1){
      ctx.strokeStyle = 'skyblue';
      }else if(this.level == 2){
        ctx.strokeStyle = 'yellow';
      }else if(this.level == 3){
        ctx.strokeStyle = 'red';
      }else if(this.level == 4){
        ctx.strokeStyle = 'green';
      }else{
        ctx.strokeStyle = 'black';
      }

- 타워 공격력 레벨 표기

타워의 현재 공격력과 레벨을 타워 머리위에 표기하였다.

    ctx.font = '20px Arial';
    ctx.fillStyle = 'white';
    ctx.fillText(`(레벨 ${this.level})`, this.x + 5, this.y - 25);
    ctx.fillText(`(공격력 ${this.attackPower + 10 * (this.level - 1)})`, this.x - 7, this.y + 0);

[하루 총평]

처음에 막혔던 부분이 해결되면서 쭉쭉 문제를 해결하고, 도전과제와 추가적인 구현사항을 구현하였다.

무사히 잘 프로젝트를 마무리 한 것 같아서 기쁘다!