내일배움캠프 Node.js 트랙 14일차
1. ZEP에서 이루어진 로그라이크 게임 개발 프로젝트
(1) 지난 시간 복기
지난 시간엔 fs와 json을 이용해 업적 기능을 구현해보았다. json은 Javascript Object Notation의 약자로, 텍스트 기반의 데이터 교환 형식이며, fsp를 통해 데이터를 읽고 쓸 수 있었다.
// JSON 파일을 로드하는 함수
let loadJson = async (filePath) => {
try {
const data = await fsp.readFile(filePath, 'utf8');
return JSON.parse(data);
} catch (err) {
console.error('파일 읽기 오류:', err);
return null; // 파일을 읽지 못했을 경우 null 반환
}
};
// 업적 완료하기
let unlockAchievement = async (filePath, index) => {
try {
let jsonData = await loadJson(filePath);
// 업적 수정하기 (예: 첫 번째 업적의 isUnlocked를 true로 변경)
if (jsonData && jsonData.achievements && jsonData.achievements.length > 0) {
jsonData.achievements[index].isUnlocked = true; // 업적을 unlocked로 변경
}
// 수정된 데이터를 다시 JSON 문자열로 변환
const updatedData = JSON.stringify(jsonData, null, 2);
// 파일에 다시 쓰기
await fsp.writeFile(filePath, updatedData, 'utf8');
} catch (err) {
console.error('파일 수정 오류:', err);
}
};
// 완료한 업적 불러오기
async function getAchievements() {
let jsonData;
if (!jsonData) {
jsonData = await loadJson('./data.json'); // 업적 데이터 파일 경로
}
if (jsonData && jsonData.achievements) {
jsonData.achievements.forEach((achievement) => {
if (achievement.isUnlocked) {
console.log(
chalk.hex('#E8B86D')(`====================|**⁂**|====================
업적: ${achievement.name}
설명: ${achievement.description}
===============================================`),
);
}
});
} else {
console.log('업적 데이터가 없습니다.');
}
}
이를 바탕으로 세이브 기능을 구현하기로 결정했다. 진행상황이 자동으로 저장되는 기능은 많은 로그라이크 게임들이 지원하고 있기 때문에 장르적인 측면에서도 문제 없을 듯하다.
업적 기능과 저장 기능의 차이가 있다면 업적 기능은 내가 작성한 json형식의 파일에서 isUnlocked라는 불리언 프로퍼티를 true로 고쳐주기만 했지만, 저장 기능에서는 인게임 데이터 자체를 json형식으로 내보내야 한다. 특히 player 객체는 통째로 저장하는 방식을 사용하고자 한다. 따라서 아래와 같이 필요한 함수들의 기능을 정리해 보았다.
(1) 게임 시작 함수의 매개변수인 player객체와 uiStyle문자열을 json 형식으로 저장한다.
(2) json 파일을 읽어오고 파싱을 거쳐서 세이브 파일의 내용을 표현해야 한다.
이를 바탕으로 코드를 작성하였다.
import fsp from 'fs/promises'; // fs/promises에서 fs를 가져옵니다.
// fs 모듈이란?
// Node.js의 기본 파일 시스템 모듈. 파일 읽고 쓰기, 콜백 함수 데이터 전달 받기, 디렉토리 작업 등 다양한 기능을 제공한다. fsp는 바로 이 fs의 프로미스(promises) 버전이다. fs를 비동기식으로 처리해준다.
// JSON 파일을 로드하는 함수
let loadJson = async (filePath) => {
try {
// filePath 경로에 있는 파일을 UTF-8 인코딩 방식으로 읽어오고
const data = await fsp.readFile(filePath, 'utf8');
// data 문자열을 JavaScript 객체로 파싱한 후 반환한다.
return JSON.parse(data);
} catch (err) {
console.error('파일 읽기 오류:', err);
return null; // 파일을 읽지 못했을 경우 null 반환
}
};
// ...업적 기능 생략...
let loadSaveFile = async () => {
try {
const jsonData = await loadJson('./savedGame.json'); // 세이브 데이터 파일 경로
if (jsonData && jsonData.save) {
// player가 문자열이면 JSON.parse()를 사용하고, 그렇지 않으면 그대로 사용
let difficultyInfo =
jsonData.save.player.difficulty === 1
? 'NORMAL'
: jsonData.save.player.difficulty === 1.2
? 'HARD'
: 'HELL';
console.log(colors.elite('저장 파일'));
console.log(
colors.green2(`
| Stage: ${jsonData.save.player.stage} | ${jsonData.save.player.blessing} | ${difficultyInfo} |`),
);
console.log(`${chalk.hex('#15B392').bold(`| 플레이어 | ${jsonData.save.player.name} | 방어도${Math.round(jsonData.save.player.defense)} | 유대감${Math.round(jsonData.save.player.bondingIndex)} | \n`)}
`);
return jsonData;
} else {
console.log('세이브 데이터가 없습니다.');
}
} catch (err) {
console.error('로딩 오류:', err);
}
};
async function saveAndExit(filePath, player, uiStyle) {
try {
// JavaScript 객체로 파싱해온 데이터를 jsonData라는 변수에 할당
let jsonData = (await loadJson(filePath)) || { save: {} };
// 인게임 데이터를 저장
jsonData.save.player = player;
jsonData.save.uiStyle = uiStyle;
// 다시 json 형식으로 변환
const updatedData = JSON.stringify(jsonData, null, 2);
// 비동기 파일 쓰기
await fsp.writeFile(filePath, updatedData, 'utf8');
console.log('데이터가 성공적으로 저장되었습니다.');
} catch (err) {
console.error('파일 저장 오류:', err);
}
}
export { loadJson, getAchievements, unlockAchievement, saveAndExit, loadSaveFile };
함수 사용 예시
await saveAndExit('./savedGame.js', player, uiStyle);
'개발일지 > TIL(Today I Learned)' 카테고리의 다른 글
2024-11-18 (2) | 2024.11.18 |
---|---|
2024-11-15 (1) | 2024.11.15 |
2024-11-13 (2) | 2024.11.13 |
2024-11-12 (1) | 2024.11.12 |
2024-11-11 (5) | 2024.11.11 |
댓글