달릴때의 이미지를 구성하기 위해 먼저 가만히 있을때와 달릴때의 상태를 구분해야 한다 이는 이후 만들 점프 등 다양한 동작에도 해당한다.
class Fighter extends Sprite {
...
offset = { x: 0, y: 0 },
}) {
super({
...
});
...
this.sprites = sprites;
for (const sprite in sprites) {
sprites[sprite].image = new Image();
sprites[sprite].image.src = sprites[sprite].imageSrc;
}
console.log(this.sprites);
}
const player = new Fighter({
...
sprites: {
idle: {
imageSrc: "./img/samuraiMack/Idle.png",
framesMax: 8,
},
run: {
imageSrc: "./img/samuraiMack/Run.png",
framesMax: 8,
image: new Image(),
},
},
});
동작을 구분할 sprites를 만들어주고 player에 sprite로 각 상태를 정해주어 clg로 출력해보았다.
Idle과 run 둘 다 잘 받아온다.
이제 a를 눌렀을 때 이미지를 run이미지로 바꿔주도록 하면
//player movement
if (keys.a.pressed && player.lastKey === "a") {
player.velocity.x = -4;
player.image = player.sprites.run.image;
} else if (keys.d.pressed && player.lastKey === "d") {
player.velocity.x = 4;
player.image = player.sprites.run.image;
}
달리는 애니메이션이 나온다.
그런데 키를 떼어도 계속 달리는 애니메이션만 나오는데
//player movement
player.image = player.sprites.idle.image;
if (keys.a.pressed && player.lastKey === "a") {
...
기본 동작으로 Idle을 설정해주면 된다.
다음으로 점프 모션을 만들자 sprites에 jump를 추가하고
sprites: {
...
jump: {
imageSrc: "./img/samuraiMack/Jump.png",
framesMax: 2,
},
},
체공중일때만 점프모션이 나오도록 하기 위해 조건문으로 이미지를 바꿔주도록 하자
//player movement
...
if (player.velocity.y < 0) {
player.image = player.sprites.jump.image;
player.framesMax = player.sprites.jump.framesMax;
}
갑자기 분신술을 하는데 framesMax의 값이 잘못 전달되고있어서 그렇다.
Fighter class에 switchSprite메소드를 만들어 각각의 frameMax를 관리하자.
코드를 입력하세요
switch (sprite) {
case "idle":
if (this.image !== this.sprites.idle.image) {
this.image = this.sprites.idle.image;
this.framesMax = this.sprites.idle.framesMax;
}
break;
case "run":
if (this.image !== this.sprites.run.image) {
this.image = this.sprites.run.image;
this.framesMax = this.sprites.run.framesMax;
}
break;
case "jump":
if (this.image !== this.sprites.jump.image) {
this.image = this.sprites.jump.image;
this.framesMax = this.sprites.jump.framesMax;
}
break;
default:
break;
}
}
//player movement
player.switchSprite("idle");
if (keys.a.pressed && player.lastKey === "a") {
player.velocity.x = -4;
player.switchSprite("run");
} else if (keys.d.pressed && player.lastKey === "d") {
player.velocity.x = 4;
player.switchSprite("run");
}
if (player.velocity.y < 0) {
player.switchSprite("jump");
}
이전보다 낫긴 하지만 점프할때 사라지는 현상이 있다.
달리기는 8프레임인데 점프는 2프레임이라 그런데 각각의 case마다 framesCurrent를 초기화해주면 된다.
switchSprite(sprite) {
switch (sprite) {
case "idle":
if (this.image !== this.sprites.idle.image) {
this.image = this.sprites.idle.image;
this.framesMax = this.sprites.idle.framesMax;
this.framesCurrent = 0;
}
break;
case "run":
if (this.image !== this.sprites.run.image) {
this.image = this.sprites.run.image;
this.framesMax = this.sprites.run.framesMax;
this.framesCurrent = 0;
}
break;
case "jump":
if (this.image !== this.sprites.jump.image) {
this.image = this.sprites.jump.image;
this.framesMax = this.sprites.jump.framesMax;
this.framesCurrent = 0;
}
break;
default:
break;
}
좌우로 움직일때에도 모션이 이상한데
기본 동작인 player.switchSprite("idle")를 전역으로 두지 않고 else로 빼주면 해결된다.
//player movement
if (keys.a.pressed && player.lastKey === "a") {
player.velocity.x = -4;
player.switchSprite("run");
} else if (keys.d.pressed && player.lastKey === "d") {
player.velocity.x = 4;
player.switchSprite("run");
} else {
player.switchSprite("idle");
}
if (player.velocity.y < 0) {
player.switchSprite("jump");
}
이제 떨어질때의 애니메이션을 적용하자 앞의 점프와 거의 유사한 과정이다.
sprites: {
...
fall: {
imageSrc: "./img/samuraiMack/Fall.png",
framesMax: 2,
},
},
});
switchSprite(sprite) {
switch (sprite) {
...
case "fall":
if (this.image !== this.sprites.fall.image) {
this.image = this.sprites.fall.image;
this.framesMax = this.sprites.fall.framesMax;
this.framesCurrent = 0;
}
break;
default:
break;
}
}
//player movement
...
if (player.velocity.y < 0) {
player.switchSprite("jump");
} else if (player.velocity.y > 0) {
player.switchSprite("fall");
}
이제 공격 모션을 적용시켜보자.
이것 역시 이전과 유사한데
sprites: {
...
attack1: {
imageSrc: "./img/samuraiMack/Attack1.png",
framesMax: 6,
},
},
switchSprite(sprite) {
switch (sprite) {
...
case "attack1":
if (this.image !== this.sprites.attack1.image) {
this.image = this.sprites.attack1.image;
this.framesMax = this.sprites.attack1.framesMax;
this.framesCurrent = 0;
}
break;
default:
break;
}
}
sprites와 case를 추가하고 이벤트 리스너를 보면 스페이스바를 누를때 attack 메소드를 호출하고 있다.
window.addEventListener("keydown", (event) => {
//player key
switch (event.key) {
...
case " ":
player.attack();
break;
attack 메소드 내에서 switchSprite를 호출해주면
attack() {
this.switchSprite("attack1");
this.isAttacking = true;
setTimeout(() => {
this.isAttacking = false;
}, 100);
}
뭔가 움찔움찔하기만 하고 공격하지 않는다.
아까 기본값으로 Idle을 설정해 주었기 때문에 attack이 실행되자마자 idle로 돌아가기 때문이다.
attack상태일 때는 이를 무시하도록 하면
switchSprite(sprite) {
if (this.image === this.sprites.attack1.image) return;
switch (sprite) {
...
스페이스바를 누르지 않았는데도 계속 공격하는데 현재 동작중인 프레임을 공격 프레임의 -1만큼 일떄 라는 조건을 추가하면
switchSprite(sprite) {
if (
this.image === this.sprites.attack1.image &&
this.framesCurrent < this.sprites.attack1.framesMax - 1
)
return;
한번만 공격하고 다시 아래 switch문으로 넘어가면서 해결된다.
다음 포스팅에선 잠시 숨겨두었던 enemy를 다시 다뤄보자