[Javascript] setInterval을 활용해 캐릭터 자동회전 구현하기

예리에르·2022년 11월 12일
4

JavaScript

목록 보기
6/7
post-thumbnail

개발 동기

현재 회사에서 프로젝트간 텀이 있어 팀원끼리 함께 게더타운 같은 게임을 만들고 있다. 이번에 맡은 역할은 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);

위와 같은 형식으로 사용하였다.

2개의 setInterval

캐릭터를 그리는 메인 interval 과 캐릭터의 방향을 결정하는 함수를 실행하는 interval

2개를 사용했다.

private registerTimer = () => {
        this.timer = setInterval(this.onDraw, 1000 / 30);
    };

private registerCharacterTimer = () => {
        this.characterTimer = setInterval(this.getDirection, 1000);

	}

용도에 따라 함수가 실행하는 시간을 다르게 준것을 볼 수 있다.

1. registerTimer

canvas에 그림을 그리는 메인 역할을 수행하는 onDraw함수가 실행된다.
onDraw에는 2개의 메인 함수가 실행된다.

  1. 방향에 중요한 역활을 하는 count를 관리하는 함수
  2. 그 count를 참고해 실제로 그림을 그리는 함수
	@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 역할이다.

2.registerCharacterTimer

그림의 그리는 함수 실행속도 보다 느리게 실행하면서 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를 다루는 능력을 향상 했던것 같다.

profile
비전공 프론트엔드 개발자의 개발일기😈 ✍️

0개의 댓글