[오늘의 코딩 테스트]
[프로그래머스/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);
[하루 총평]
처음에 막혔던 부분이 해결되면서 쭉쭉 문제를 해결하고, 도전과제와 추가적인 구현사항을 구현하였다.
무사히 잘 프로젝트를 마무리 한 것 같아서 기쁘다!
'코딩 > TIL' 카테고리의 다른 글
[TIL] Socket.io 의 on 과 emit의 이해 (0) | 2024.06.26 |
---|---|
[TIL] 모의 면접 대비 공부 (0) | 2024.06.25 |
[TIL] 디펜스 게임 프로젝트 시작 (1) | 2024.06.18 |
[TIL/31일차] 개인과제 마무리 (1) | 2024.06.13 |
[TIL/30일차] 프로젝트 시작 (0) | 2024.06.13 |