현재 회사에서 프로젝트간 텀이 있어 팀원끼리 함께 게더타운 같은 게임을 만들고 있다. 이번에 맡은 역할은 Node를 활용해 monogo 연결하여 was 작업을 맡아서 진행했다. 사용자, 캐릭터, 맵 등 다양한 스키마를 설계하고 api를 만드는 경험을 하였다. 자세한 이야기는 차주 프로젝트 1차 마무리하면서 자세하게 정리할 것이다.
진짜 동기를 이야기하면 맡은 작업 중 클라이언트가 직접 스킨을 커스텀 할 수 있는데 해당 api를 연결하면서 front 작업도 동시에 진행하였다. 그때 필요한 것이 사용자가 선택한 스킨을 미리 보여줘야했다. 팀장님의 추가 요구사항으로 해당 게임의 스킨은 한면만 있는 것이 아니라 앞,뒤,왼쪽,오른쪽 모습이 다 존재하기 때문에 클라이언트가 바로 이해할 수 있게 자동회전 해달라는 것이었다.
12개의 캐릭터의 움직임을 나타낸 한장의 이미지가 있다. 이 이미지를 나눠서 잘라서 한 움직이는 것 처럼 나타내면 되는 것이다.
그래서 방향에 따라 이미지를 어떻게 나눠야 하는지 고민을 했다.
x축 y축을 기준으로 생각했을때 4방향의 모습은 y축을 기준으로 한 행씩 다르게 나타나고 있었기 때문에 y축을 특징으로 잡아 코드를 작성하면 되겠다라는 방향을 잡았다.
기존에 이미지를 불러올 때는 간편하게 <img src=""/>
코드를 사용해서 보여주는 형식으로 사용을 했다. 하지만 정적인 한장의 이미지를 전체 보여주는것이 아니라 받은 이미지를 가공해야했기 때문에 다른 방법을 이미지를 로드해야했다.
document.createElement('img')
사용그래서 img
태그를 document.createElement() 를 사용해서 불러오고 해당 태그의 속성을 이용하기로 했다.
@action
private loadResource = (path:string) => {
const img = document.createElement('img');
img.src = path;
img.onload = (e:any) => {
this.characterImage = e.target;
this.init();
};
img.onerror = () => {
console.log("스킨 로드 실패", path);
};
}
이미지 리소스를 로드하는 함수를 만들어 컴포넌트가 monunt 되거나 update 될 때 실행하게 하였다.
characterImage
에 담은 이미지는 이미지가 필요한 코드를 작성할 때
const {characterImage:image} = this;
...
ctx.drawImage(image, 0, index, WIDTH, HEIGHT, 0, 0, WIDTH, HEIGHT);
위와 같은 형식으로 사용하였다.
캐릭터를 그리는 메인 interval 과 캐릭터의 방향을 결정하는 함수를 실행하는 interval
2개를 사용했다.
private registerTimer = () => {
this.timer = setInterval(this.onDraw, 1000 / 30);
};
private registerCharacterTimer = () => {
this.characterTimer = setInterval(this.getDirection, 1000);
}
용도에 따라 함수가 실행하는 시간을 다르게 준것을 볼 수 있다.
canvas에 그림을 그리는 메인 역할을 수행하는 onDraw함수가 실행된다.
onDraw에는 2개의 메인 함수가 실행된다.
@action
private calculateCount = () => {
this.cnt++
if (this.cnt > 20) {
this.cnt = 0;
}
}
if (cnt < 5) {
ctx.clearRect(0, 0, WIDTH, HEIGHT);
ctx.drawImage(image, WIDTH, index, WIDTH, HEIGHT, 0, 0, WIDTH, HEIGHT);
} else if (cnt >5 && cnt<10){
ctx.clearRect(0, 0, WIDTH, HEIGHT);
ctx.drawImage(image, 0, index, WIDTH, HEIGHT, 0, 0, WIDTH, HEIGHT);
} else if (cnt >10 && cnt<15){
ctx.clearRect(0, 0, WIDTH, HEIGHT);
ctx.drawImage(image, WIDTH*2, index, WIDTH, HEIGHT, 0, 0, WIDTH, HEIGHT);
} else if (cnt >15 && cnt<20){
ctx.clearRect(0, 0, WIDTH, HEIGHT);
ctx.drawImage(image, 0, index, WIDTH, HEIGHT, 0, 0, WIDTH, HEIGHT);
}
20을 기준으로 count를 변경하고 그 변화에 맞춰 움직임에 프레임에 맞는 함수를 이미지를 그린다. 그림을 보면 정지,왼발,오른발 이렇게 3가지의 프레임이 보이는데 그림의 순서를
정지-왼발 움직임 - 정지 - 오른발 움직임
여기서 봐야하는 점은 하단의 index
라고 변수이다. 이 변수가 위 이미지의 y 역할이다.
그림의 그리는 함수 실행속도 보다 느리게 실행하면서 y축을 이동을 조절하였다.
@action
private getDirection = () => {
this.xIndex += 1;
if (this.xIndex > 7) {
this.xIndex = 0;
}
this.directionIndex = this.props.isStop?DIRECTION_Y[0]: DIRECTION_Y[this.xIndex];
}
개발을 통해 이미지를 직접올리고 불러오면서 img 태그와 이미지에 대한 개념을 깊이 할 수 있었다. interval 함수를 활용해서 요구사항을 완료하면서 canvas를 다루는 능력을 향상 했던것 같다.