본문 바로가기

Front-End: Web/JavaScript

[코딩애플] js part 3-12. class로 만들어보는 2D 게임

반응형

class로 만들어보는 간단한 2D 게임 (배웠으면 써먹어야하니까)

자바스크립트만으로도 간단한 게임을 만들 수 있다.

  • HTMl, CSS, JS만으로 만든 게임 중 해볼만한 명작: candy box, Synergism

우리는 크롬 공룡 게임을 만들어 보자.

게임 개발 원리와 구현 방법

  1. 화면에 네모, 원 그릴 수 있어야 함 (네모 위에 공룡, 선인장 등 그림을 입힘)

-> <canvas>태그를 이용하면 자바스크립트로 간단하게 네모를 그릴 수 있다.

  1. 프레임마다 코드실행 가능해야 (애니메이션을 위해)

예로 들어서 공룡이 방향키를 누르면 60px 이동해야 한다. 근데 순간이동하면 안되겠지. 서서히 60px로 이동시켜야 한다. 그러면 1초에 60번 정도 +1px 해주면 된다. 그래서 1초에 60번 움직이도록 자바스크립트 코드를 짤 줄 알아야 한다. 브라우저 기본 함수가 있는데 그걸 쓰면 된다.

  1. collision check 할 수 있어야

충돌 시 처리를 해줘야 한다. 예로 들면 움직이다가 적과 충돌하면 내 체력이 -1 깎인다던지. 별건 앙니고 중학교 수학 정도만 할 줄 알면 된다.

네모 그리는 법

🕹️index.html

<body>
    <canvas id="canvas"></canvas>
    <script src="main.js"></script>
</body>

🕹️main.js - canvas 생성하는 기본 코드

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

canvas.width = window.innerWidth - 100;
canvas.height = window.innerHeight - 100;

공룡 만들기

ctx.fillStyle = "green";
ctx.fillRect(10, 10, 100, 100); // 위에서 10, 오른쪽에서 10 위치에서 100x100 사이즈의 네모 그리기

근데 이렇게 먼저 하지 말고, **등장 캐릭터의 속성부터 object 자료에 정리해두면 편리**하다.

var dino = {
  // 공룡 등장 좌표
  x: 10,
  y: 200,
  // 공룡 폭과 높이
  width: 50,
  height: 50,
  // 공룡 그리는 함수
  draw() {
    ctx.fillStyle = "green";
    ctx.fillRect(this.x, this.y, this.width, this.height);
  },
};
dino.draw(); // 공룡 그리기 완료!

장애물 만들기

장애물들은 각각 width, height가 다른 특성을 가지고 있다. 비슷한 object가 많이 필요할 것 같으니 class로 만들어서 객체 생성 기계를 만들자.

class Cactus {
  constructor() {
    this.x = 500;
    this.y = 200;
    this.width = 50;
    this.height = 50;
  }
  draw() {
    ctx.fillStyle = "red";
    ctx.fillRect(this.x, this.y, this.weight, this.height);
  }
}
var cactus = new Cactus();
cactus.draw();

애니메이션 만들기

만약 100px만큼 이동하게 하려면, dino의 x값을 수정하면 된다.

dino.x = 100;

근데 이건 순간이동이지 애니메이션이 아니다. 애니메이션화하려면 1초에 60번 x++해줘야 한다.

// 1초에 60번 해당 코드를 실행해야 함
dino.x += 1;

(게임 개발을 본격적으로하려면 개임 개발 라이브러리를 쓰는게 나음) 근데 우린 쌩으로 개발해볼거다.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);
  dino.x++; // 1초에 dino를 1px씩 움직이고
  dino.draw(); // 이를 그림
}

executeByFrame();
  • **requestAnimationFrame**(callback) : 기본 자바스크립트 함수. **1초에 60번 실행**함. 프레임마다 실행할거를 콜백함수 안에 넣는다.

1초에 60번 실행되어서 화면에 네모가 우측으로 커진다. 우측으로 커지는 게 아니라 실은 dino가 이동하면서 그리고 있는거다.

근데 왜 잔상이 남지? 잔상을 안남게하려면 그리기 전에 캔버스를 지우면 된다.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height); // ⬅️

  dino.x++;
  dino.draw();
}

장애물도 애니메이션 생성하기

근데 크롬 게임은 공룡이 움직이는게 아니라, 장애물이 공룡쪽으로 다가간다. 그래서 공룡을 굳이 움직이게 할 필요는 없다.

장애물을 생성해보자.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  var cactus = new Cactus(); // ⬅️
  cactus.draw(); // ⬅️
  dino.draw();
}

장애물은 2~3초에 하나씩 나와야 한다. 그래서 2~3초에 한 번 이걸 실행하도록 하자.

**게임 세상에선 항상 '초'단위로 움직이는 게 아니라 '프레임'단위로 움직인다.**

타이머를 생성하자. 1초에 60 프레임으로 진행이 된다, 하면 타이머에 조건문을 건다.

var timer = 0;

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactus.draw();
  }

  dino.draw();
}

장애물 여러개 관리하기

하지만 장애물은 하나가 아니라 여러개 존재한다. 그럼 장애물을 만들 때마다 array에 담아서 보관하면 관리하기 편하다.

var timer = 0;
var cactuses = [];

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }
    
  cactuses.forEach((a) => {
    a.x--; // 1초에 60px 왼쪽으로 이동
    a.draw();
  });
  dino.draw();
}

(정리) 120프레임마다 {장애물} 예쁘게 생성하고 array에 집어넣음. 그리고 마지막에 array에 있던거 다 draw()해줌.

Recap

  1. 네모 그리기
  • 그릴 캐릭터의 정보 미리 object 자료로 정리하면 편함
  1. 코드를 1초에 60번 실행하면 애니메이션 만들 수 있음 (requestAnimationFrame)
  • 120프레임마다 장애물도 소환해서 array에 보관했음

화면에서 없어져서 필요없어진 장애물 제거하기

cactuses.forEach((a) => {
    // x 좌표가 0미만이면 제거하기
    a.draw();
});
cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
        o.splice(i, 1);
    }
    a.x--;
    a.draw();
});
  • 첫 번째 파라미터(a) : currentValue. 처리할 현재 요소 (=요소 값)
  • 두 번째 파라미터(i) : index. 처리할 현재 요소의 인덱스 (=요소 인덱스)
  • 세 번째 파라미터(o) : array. forEach()를 호출한 배열 (=**순회 중인 배열**)

스페이스바 누르면 점프 -> eventListener

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    }
    a.x--;
    a.draw();
  });

  if (jumping === true) { // ⬅️
    dino.y--;
  }
  dino.draw();
}

executeByFrame();

var jumping = false;
document.addEventListener("keydown", function (e) {
  if (e.code === "Space") {
    jumping = true;
  }
});

근데 이렇게 짜면 공룡이 무한히 하늘로 승천한다. y를 무한히 가는 게 아니라, 제한을 둬야한다. 100프레임 지나면 dino.y-- 점프를 그만하고 ++해준다던지.

jump 타이머를 만들자.

var timer = 0;
var cactuses = [];
var jumpTimer = 0; // ⬅️

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    }
    a.x--;
    a.draw();
  });

  if (jumping === true) {
    dino.y--;
    jumpTimer++; // ⬅️
  }
  if (jumpTimer > 100) { // ⬅️
    jumping = false;
  }
  dino.draw();
}

공룡을 다시 내려오도록 하자.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    }
    a.x--;
    a.draw();
  });

  if (jumping === true) {
    dino.y--;
    jumpTimer++;
  } else { // ⬅️
    if (jumpTimer > 0) {
      dino.y++;
      jumpTimer--;
    }
  }
  if (jumpTimer > 100) {
    jumping = false;
  }
  dino.draw();
}

collision detection

  1. 충돌체크하기 (충돌하면 뭔가 일어나야 함)

공룡과 장애물의 충돌을 어떻게 감지할 수 있을까?

![](.\images\05.png)

장애물의 x좌표 - 공룡의 x좌표 < 0 면 충돌이 일어났다고 볼 수 있다.

![](.\images\06.png)

근데 이건 x축이 만났을 때만 가정한거고, y축에서 만날 수도 있을거다.

![](.\images\07.png)

그래서 x축과 y축이 모두 만났을 때 '충돌했다'고 볼 수 있다.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    } else {
      a.x--;

      isCollapse(dino, a); // ⬅️ 충돌 체크는 여기서 해야

      a.draw();
    }
  });

  if (jumping === true) {
    dino.y--;
    jumpTimer++;
  } else {
    if (jumpTimer > 0) {
      dino.y++;
      jumpTimer--;
    }
  }
  if (jumpTimer > 100) {
    jumping = false;
  }
  dino.draw();
}

executeByFrame();

// 충돌 확인
function isCollapse(dino, cactus) {
  var diffX = cactus.x - (dino.x + dino.width); 
  var diffY = cactus.y - (dino.y + dino.height);
}

var jumping = false;
document.addEventListener("keydown", function (e) {
  if (e.code === "Space") {
    jumping = true;
  }
});

cactus의 x좌표 - 공룡의 x좌표를 구해야하는데, 공룡의 x좌표는 (dino.x + dino.width)다.

![](.\images\08.png)

y의 경우에도 공룡의 y좌표는 (dino.y + dino.height)다.

충돌시 게임 중단

var animation;

function executeByFrame() {
  animation = requestAnimationFrame(executeByFrame);
  ...
}
  

function isCollapse(dino, cactus) {
  var diffX = cactus.x - (dino.x + dino.width);
  var diffY = cactus.y - (dino.y + dino.height);
  
  if (diffX < 0 && diffY < 0) { // 충돌 - 게임 중단
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    cancelAnimationFrame(animation);
  }
}
  • **cancelAnimationFrame** : 애니메이션 중단

네모 대신 이미지 넣기

  1. 장애물

장애물을 그려보자. 먼저 이미지를 가져온다.

var img1 = new Image();
img1.src = "cactus.png";

그리고 장애물을 그리는 함수를

draw() {
	ctx.fillStyle = "red";
	ctx.fillRect(this.x, this.y, this.weight, this.height);
}

아래 코드로 변경한다.

draw() {
    ctx.drawImage(img1, this.x, this.y);
}
  1. 공룡
var img2 = new Image();
img2.src = "dinosaur.png";

var dino = {
  // 공룡 등장 좌표
  x: 10,
  y: 200,
  // 공룡 폭과 높이
  width: 50,
  height: 50,
  // 공룡 그리는 함수
  draw() {
    ctx.drawImage(img2, this.x, this.y, this.width, this.height); // ⬅️
  },
};
dino.draw(); // 공룡 그리기 완료!

결과

![](.\images\09.png)

🕹️전체 코드 (main.js)

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

canvas.width = window.innerWidth - 100;
canvas.height = window.innerHeight - 100;

var img2 = new Image();
img2.src = "dinosaur.png";

var dino = {
  // 공룡 등장 좌표
  x: 10,
  y: 200,
  // 공룡 폭과 높이
  width: 50,
  height: 50,
  // 공룡 그리는 함수
  draw() {
    ctx.drawImage(img2, this.x, this.y, this.width, this.height);
  },
};
dino.draw(); // 공룡 그리기 완료!

var img1 = new Image();
img1.src = "cactus.png";

class Cactus {
  constructor() {
    this.x = 600;
    this.y = 200;
    this.width = 50;
    this.height = 50;
  }
  draw() {
    ctx.drawImage(img1, this.x, this.y);
  }
}

var timer = 0;
var cactuses = [];
var jumpTimer = 0;
var animation;

function executeByFrame() {
  animation = requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 200 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  timer++;

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    }
    a.x--;

    isCollapse(dino, a); // 충돌 체크는 여기서 해야

    a.draw();
  });

  if (jumping === true) {
    dino.y--;
    jumpTimer++;
  } else if (jumping === false) {
    if (dino.y < 200) {
      jumpTimer = 0;
      dino.y++;
    }
  }

  if (jumpTimer > 100) {
    jumping = false;
  }
  dino.draw();
}

executeByFrame();

// 충돌 확인
function isCollapse(dino, cactus) {
  var diffX = cactus.x - (dino.x + dino.width);
  var diffY = cactus.y - (dino.y + dino.height);
  // 충돌 - 게임 중단
  if (diffX < 0 && diffY < 0) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    cancelAnimationFrame(animation);
  }
}

var jumping = false;
document.addEventListener("keydown", function (e) {
  if (e.code === "Space") {
    jumping = true;
  }
});

 

class로 만들어보는 간단한 2D 게임 (배웠으면 써먹어야하니까)

자바스크립트만으로도 간단한 게임을 만들 수 있다.

  • HTMl, CSS, JS만으로 만든 게임 중 해볼만한 명작: candy box, Synergism

우리는 크롬 공룡 게임을 만들어 보자.

게임 개발 원리와 구현 방법

  1. 화면에 네모, 원 그릴 수 있어야 함 (네모 위에 공룡, 선인장 등 그림을 입힘)

-> <canvas>태그를 이용하면 자바스크립트로 간단하게 네모를 그릴 수 있다.

  1. 프레임마다 코드실행 가능해야 (애니메이션을 위해)

예로 들어서 공룡이 방향키를 누르면 60px 이동해야 한다. 근데 순간이동하면 안되겠지. 서서히 60px로 이동시켜야 한다. 그러면 1초에 60번 정도 +1px 해주면 된다. 그래서 1초에 60번 움직이도록 자바스크립트 코드를 짤 줄 알아야 한다. 브라우저 기본 함수가 있는데 그걸 쓰면 된다.

  1. collision check 할 수 있어야

충돌 시 처리를 해줘야 한다. 예로 들면 움직이다가 적과 충돌하면 내 체력이 -1 깎인다던지. 별건 앙니고 중학교 수학 정도만 할 줄 알면 된다.

네모 그리는 법

🕹️index.html

<body>
    <canvas id="canvas"></canvas>
    <script src="main.js"></script>
</body>

🕹️main.js - canvas 생성하는 기본 코드

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

canvas.width = window.innerWidth - 100;
canvas.height = window.innerHeight - 100;

공룡 만들기

ctx.fillStyle = "green";
ctx.fillRect(10, 10, 100, 100); // 위에서 10, 오른쪽에서 10 위치에서 100x100 사이즈의 네모 그리기

근데 이렇게 먼저 하지 말고, **등장 캐릭터의 속성부터 object 자료에 정리해두면 편리**하다.

var dino = {
  // 공룡 등장 좌표
  x: 10,
  y: 200,
  // 공룡 폭과 높이
  width: 50,
  height: 50,
  // 공룡 그리는 함수
  draw() {
    ctx.fillStyle = "green";
    ctx.fillRect(this.x, this.y, this.width, this.height);
  },
};
dino.draw(); // 공룡 그리기 완료!

장애물 만들기

장애물들은 각각 width, height가 다른 특성을 가지고 있다. 비슷한 object가 많이 필요할 것 같으니 class로 만들어서 객체 생성 기계를 만들자.

class Cactus {
  constructor() {
    this.x = 500;
    this.y = 200;
    this.width = 50;
    this.height = 50;
  }
  draw() {
    ctx.fillStyle = "red";
    ctx.fillRect(this.x, this.y, this.weight, this.height);
  }
}
var cactus = new Cactus();
cactus.draw();

애니메이션 만들기

만약 100px만큼 이동하게 하려면, dino의 x값을 수정하면 된다.

dino.x = 100;

근데 이건 순간이동이지 애니메이션이 아니다. 애니메이션화하려면 1초에 60번 x++해줘야 한다.

// 1초에 60번 해당 코드를 실행해야 함
dino.x += 1;

(게임 개발을 본격적으로하려면 개임 개발 라이브러리를 쓰는게 나음) 근데 우린 쌩으로 개발해볼거다.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);
  dino.x++; // 1초에 dino를 1px씩 움직이고
  dino.draw(); // 이를 그림
}

executeByFrame();
  • **requestAnimationFrame**(callback) : 기본 자바스크립트 함수. **1초에 60번 실행**함. 프레임마다 실행할거를 콜백함수 안에 넣는다.

1초에 60번 실행되어서 화면에 네모가 우측으로 커진다. 우측으로 커지는 게 아니라 실은 dino가 이동하면서 그리고 있는거다.

근데 왜 잔상이 남지? 잔상을 안남게하려면 그리기 전에 캔버스를 지우면 된다.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height); // ⬅️

  dino.x++;
  dino.draw();
}

장애물도 애니메이션 생성하기

근데 크롬 게임은 공룡이 움직이는게 아니라, 장애물이 공룡쪽으로 다가간다. 그래서 공룡을 굳이 움직이게 할 필요는 없다.

장애물을 생성해보자.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  var cactus = new Cactus(); // ⬅️
  cactus.draw(); // ⬅️
  dino.draw();
}

장애물은 2~3초에 하나씩 나와야 한다. 그래서 2~3초에 한 번 이걸 실행하도록 하자.

**게임 세상에선 항상 '초'단위로 움직이는 게 아니라 '프레임'단위로 움직인다.**

타이머를 생성하자. 1초에 60 프레임으로 진행이 된다, 하면 타이머에 조건문을 건다.

var timer = 0;

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactus.draw();
  }

  dino.draw();
}

장애물 여러개 관리하기

하지만 장애물은 하나가 아니라 여러개 존재한다. 그럼 장애물을 만들 때마다 array에 담아서 보관하면 관리하기 편하다.

var timer = 0;
var cactuses = [];

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }
    
  cactuses.forEach((a) => {
    a.x--; // 1초에 60px 왼쪽으로 이동
    a.draw();
  });
  dino.draw();
}

(정리) 120프레임마다 {장애물} 예쁘게 생성하고 array에 집어넣음. 그리고 마지막에 array에 있던거 다 draw()해줌.

Recap

  1. 네모 그리기
  • 그릴 캐릭터의 정보 미리 object 자료로 정리하면 편함
  1. 코드를 1초에 60번 실행하면 애니메이션 만들 수 있음 (requestAnimationFrame)
  • 120프레임마다 장애물도 소환해서 array에 보관했음

화면에서 없어져서 필요없어진 장애물 제거하기

cactuses.forEach((a) => {
    // x 좌표가 0미만이면 제거하기
    a.draw();
});
cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
        o.splice(i, 1);
    }
    a.x--;
    a.draw();
});
  • 첫 번째 파라미터(a) : currentValue. 처리할 현재 요소 (=요소 값)
  • 두 번째 파라미터(i) : index. 처리할 현재 요소의 인덱스 (=요소 인덱스)
  • 세 번째 파라미터(o) : array. forEach()를 호출한 배열 (=**순회 중인 배열**)

스페이스바 누르면 점프 -> eventListener

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    }
    a.x--;
    a.draw();
  });

  if (jumping === true) { // ⬅️
    dino.y--;
  }
  dino.draw();
}

executeByFrame();

var jumping = false;
document.addEventListener("keydown", function (e) {
  if (e.code === "Space") {
    jumping = true;
  }
});

근데 이렇게 짜면 공룡이 무한히 하늘로 승천한다. y를 무한히 가는 게 아니라, 제한을 둬야한다. 100프레임 지나면 dino.y-- 점프를 그만하고 ++해준다던지.

jump 타이머를 만들자.

var timer = 0;
var cactuses = [];
var jumpTimer = 0; // ⬅️

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    }
    a.x--;
    a.draw();
  });

  if (jumping === true) {
    dino.y--;
    jumpTimer++; // ⬅️
  }
  if (jumpTimer > 100) { // ⬅️
    jumping = false;
  }
  dino.draw();
}

공룡을 다시 내려오도록 하자.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    }
    a.x--;
    a.draw();
  });

  if (jumping === true) {
    dino.y--;
    jumpTimer++;
  } else { // ⬅️
    if (jumpTimer > 0) {
      dino.y++;
      jumpTimer--;
    }
  }
  if (jumpTimer > 100) {
    jumping = false;
  }
  dino.draw();
}

collision detection

  1. 충돌체크하기 (충돌하면 뭔가 일어나야 함)

공룡과 장애물의 충돌을 어떻게 감지할 수 있을까?

![](.\images\05.png)

장애물의 x좌표 - 공룡의 x좌표 < 0 면 충돌이 일어났다고 볼 수 있다.

![](.\images\06.png)

근데 이건 x축이 만났을 때만 가정한거고, y축에서 만날 수도 있을거다.

![](.\images\07.png)

그래서 x축과 y축이 모두 만났을 때 '충돌했다'고 볼 수 있다.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    } else {
      a.x--;

      isCollapse(dino, a); // ⬅️ 충돌 체크는 여기서 해야

      a.draw();
    }
  });

  if (jumping === true) {
    dino.y--;
    jumpTimer++;
  } else {
    if (jumpTimer > 0) {
      dino.y++;
      jumpTimer--;
    }
  }
  if (jumpTimer > 100) {
    jumping = false;
  }
  dino.draw();
}

executeByFrame();

// 충돌 확인
function isCollapse(dino, cactus) {
  var diffX = cactus.x - (dino.x + dino.width); 
  var diffY = cactus.y - (dino.y + dino.height);
}

var jumping = false;
document.addEventListener("keydown", function (e) {
  if (e.code === "Space") {
    jumping = true;
  }
});

cactus의 x좌표 - 공룡의 x좌표를 구해야하는데, 공룡의 x좌표는 (dino.x + dino.width)다.

![](.\images\08.png)

y의 경우에도 공룡의 y좌표는 (dino.y + dino.height)다.

충돌시 게임 중단

var animation;

function executeByFrame() {
  animation = requestAnimationFrame(executeByFrame);
  ...
}
  

function isCollapse(dino, cactus) {
  var diffX = cactus.x - (dino.x + dino.width);
  var diffY = cactus.y - (dino.y + dino.height);
  
  if (diffX < 0 && diffY < 0) { // 충돌 - 게임 중단
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    cancelAnimationFrame(animation);
  }
}
  • **cancelAnimationFrame** : 애니메이션 중단

네모 대신 이미지 넣기

  1. 장애물

장애물을 그려보자. 먼저 이미지를 가져온다.

var img1 = new Image();
img1.src = "cactus.png";

그리고 장애물을 그리는 함수를

draw() {
	ctx.fillStyle = "red";
	ctx.fillRect(this.x, this.y, this.weight, this.height);
}

아래 코드로 변경한다.

draw() {
    ctx.drawImage(img1, this.x, this.y);
}
  1. 공룡
var img2 = new Image();
img2.src = "dinosaur.png";

var dino = {
  // 공룡 등장 좌표
  x: 10,
  y: 200,
  // 공룡 폭과 높이
  width: 50,
  height: 50,
  // 공룡 그리는 함수
  draw() {
    ctx.drawImage(img2, this.x, this.y, this.width, this.height); // ⬅️
  },
};
dino.draw(); // 공룡 그리기 완료!

결과

![](.\images\09.png)

🕹️전체 코드 (main.js)

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

canvas.width = window.innerWidth - 100;
canvas.height = window.innerHeight - 100;

var img2 = new Image();
img2.src = "dinosaur.png";

var dino = {
  // 공룡 등장 좌표
  x: 10,
  y: 200,
  // 공룡 폭과 높이
  width: 50,
  height: 50,
  // 공룡 그리는 함수
  draw() {
    ctx.drawImage(img2, this.x, this.y, this.width, this.height);
  },
};
dino.draw(); // 공룡 그리기 완료!

var img1 = new Image();
img1.src = "cactus.png";

class Cactus {
  constructor() {
    this.x = 600;
    this.y = 200;
    this.width = 50;
    this.height = 50;
  }
  draw() {
    ctx.drawImage(img1, this.x, this.y);
  }
}

var timer = 0;
var cactuses = [];
var jumpTimer = 0;
var animation;

function executeByFrame() {
  animation = requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 200 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  timer++;

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    }
    a.x--;

    isCollapse(dino, a); // 충돌 체크는 여기서 해야

    a.draw();
  });

  if (jumping === true) {
    dino.y--;
    jumpTimer++;
  } else if (jumping === false) {
    if (dino.y < 200) {
      jumpTimer = 0;
      dino.y++;
    }
  }

  if (jumpTimer > 100) {
    jumping = false;
  }
  dino.draw();
}

executeByFrame();

// 충돌 확인
function isCollapse(dino, cactus) {
  var diffX = cactus.x - (dino.x + dino.width);
  var diffY = cactus.y - (dino.y + dino.height);
  // 충돌 - 게임 중단
  if (diffX < 0 && diffY < 0) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    cancelAnimationFrame(animation);
  }
}

var jumping = false;
document.addEventListener("keydown", function (e) {
  if (e.code === "Space") {
    jumping = true;
  }
});

class로 만들어보는 간단한 2D 게임 (배웠으면 써먹어야하니까)

자바스크립트만으로도 간단한 게임을 만들 수 있다.

  • HTMl, CSS, JS만으로 만든 게임 중 해볼만한 명작: candy box, Synergism

우리는 크롬 공룡 게임을 만들어 보자.

게임 개발 원리와 구현 방법

  1. 화면에 네모, 원 그릴 수 있어야 함 (네모 위에 공룡, 선인장 등 그림을 입힘)

-> <canvas>태그를 이용하면 자바스크립트로 간단하게 네모를 그릴 수 있다.

  1. 프레임마다 코드실행 가능해야 (애니메이션을 위해)

예로 들어서 공룡이 방향키를 누르면 60px 이동해야 한다. 근데 순간이동하면 안되겠지. 서서히 60px로 이동시켜야 한다. 그러면 1초에 60번 정도 +1px 해주면 된다. 그래서 1초에 60번 움직이도록 자바스크립트 코드를 짤 줄 알아야 한다. 브라우저 기본 함수가 있는데 그걸 쓰면 된다.

  1. collision check 할 수 있어야

충돌 시 처리를 해줘야 한다. 예로 들면 움직이다가 적과 충돌하면 내 체력이 -1 깎인다던지. 별건 앙니고 중학교 수학 정도만 할 줄 알면 된다.

네모 그리는 법

🕹️index.html

<body>
    <canvas id="canvas"></canvas>
    <script src="main.js"></script>
</body>

🕹️main.js - canvas 생성하는 기본 코드

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

canvas.width = window.innerWidth - 100;
canvas.height = window.innerHeight - 100;

공룡 만들기

ctx.fillStyle = "green";
ctx.fillRect(10, 10, 100, 100); // 위에서 10, 오른쪽에서 10 위치에서 100x100 사이즈의 네모 그리기

근데 이렇게 먼저 하지 말고, **등장 캐릭터의 속성부터 object 자료에 정리해두면 편리**하다.

var dino = {
  // 공룡 등장 좌표
  x: 10,
  y: 200,
  // 공룡 폭과 높이
  width: 50,
  height: 50,
  // 공룡 그리는 함수
  draw() {
    ctx.fillStyle = "green";
    ctx.fillRect(this.x, this.y, this.width, this.height);
  },
};
dino.draw(); // 공룡 그리기 완료!

장애물 만들기

장애물들은 각각 width, height가 다른 특성을 가지고 있다. 비슷한 object가 많이 필요할 것 같으니 class로 만들어서 객체 생성 기계를 만들자.

class Cactus {
  constructor() {
    this.x = 500;
    this.y = 200;
    this.width = 50;
    this.height = 50;
  }
  draw() {
    ctx.fillStyle = "red";
    ctx.fillRect(this.x, this.y, this.weight, this.height);
  }
}
var cactus = new Cactus();
cactus.draw();

애니메이션 만들기

만약 100px만큼 이동하게 하려면, dino의 x값을 수정하면 된다.

dino.x = 100;

근데 이건 순간이동이지 애니메이션이 아니다. 애니메이션화하려면 1초에 60번 x++해줘야 한다.

// 1초에 60번 해당 코드를 실행해야 함
dino.x += 1;

(게임 개발을 본격적으로하려면 개임 개발 라이브러리를 쓰는게 나음) 근데 우린 쌩으로 개발해볼거다.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);
  dino.x++; // 1초에 dino를 1px씩 움직이고
  dino.draw(); // 이를 그림
}

executeByFrame();
  • **requestAnimationFrame**(callback) : 기본 자바스크립트 함수. **1초에 60번 실행**함. 프레임마다 실행할거를 콜백함수 안에 넣는다.

1초에 60번 실행되어서 화면에 네모가 우측으로 커진다. 우측으로 커지는 게 아니라 실은 dino가 이동하면서 그리고 있는거다.

근데 왜 잔상이 남지? 잔상을 안남게하려면 그리기 전에 캔버스를 지우면 된다.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height); // ⬅️

  dino.x++;
  dino.draw();
}

장애물도 애니메이션 생성하기

근데 크롬 게임은 공룡이 움직이는게 아니라, 장애물이 공룡쪽으로 다가간다. 그래서 공룡을 굳이 움직이게 할 필요는 없다.

장애물을 생성해보자.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  var cactus = new Cactus(); // ⬅️
  cactus.draw(); // ⬅️
  dino.draw();
}

장애물은 2~3초에 하나씩 나와야 한다. 그래서 2~3초에 한 번 이걸 실행하도록 하자.

**게임 세상에선 항상 '초'단위로 움직이는 게 아니라 '프레임'단위로 움직인다.**

타이머를 생성하자. 1초에 60 프레임으로 진행이 된다, 하면 타이머에 조건문을 건다.

var timer = 0;

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactus.draw();
  }

  dino.draw();
}

장애물 여러개 관리하기

하지만 장애물은 하나가 아니라 여러개 존재한다. 그럼 장애물을 만들 때마다 array에 담아서 보관하면 관리하기 편하다.

var timer = 0;
var cactuses = [];

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }
    
  cactuses.forEach((a) => {
    a.x--; // 1초에 60px 왼쪽으로 이동
    a.draw();
  });
  dino.draw();
}

(정리) 120프레임마다 {장애물} 예쁘게 생성하고 array에 집어넣음. 그리고 마지막에 array에 있던거 다 draw()해줌.

Recap

  1. 네모 그리기
  • 그릴 캐릭터의 정보 미리 object 자료로 정리하면 편함
  1. 코드를 1초에 60번 실행하면 애니메이션 만들 수 있음 (requestAnimationFrame)
  • 120프레임마다 장애물도 소환해서 array에 보관했음

화면에서 없어져서 필요없어진 장애물 제거하기

cactuses.forEach((a) => {
    // x 좌표가 0미만이면 제거하기
    a.draw();
});
cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
        o.splice(i, 1);
    }
    a.x--;
    a.draw();
});
  • 첫 번째 파라미터(a) : currentValue. 처리할 현재 요소 (=요소 값)
  • 두 번째 파라미터(i) : index. 처리할 현재 요소의 인덱스 (=요소 인덱스)
  • 세 번째 파라미터(o) : array. forEach()를 호출한 배열 (=**순회 중인 배열**)

스페이스바 누르면 점프 -> eventListener

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    }
    a.x--;
    a.draw();
  });

  if (jumping === true) { // ⬅️
    dino.y--;
  }
  dino.draw();
}

executeByFrame();

var jumping = false;
document.addEventListener("keydown", function (e) {
  if (e.code === "Space") {
    jumping = true;
  }
});

근데 이렇게 짜면 공룡이 무한히 하늘로 승천한다. y를 무한히 가는 게 아니라, 제한을 둬야한다. 100프레임 지나면 dino.y-- 점프를 그만하고 ++해준다던지.

jump 타이머를 만들자.

var timer = 0;
var cactuses = [];
var jumpTimer = 0; // ⬅️

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    }
    a.x--;
    a.draw();
  });

  if (jumping === true) {
    dino.y--;
    jumpTimer++; // ⬅️
  }
  if (jumpTimer > 100) { // ⬅️
    jumping = false;
  }
  dino.draw();
}

공룡을 다시 내려오도록 하자.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    }
    a.x--;
    a.draw();
  });

  if (jumping === true) {
    dino.y--;
    jumpTimer++;
  } else { // ⬅️
    if (jumpTimer > 0) {
      dino.y++;
      jumpTimer--;
    }
  }
  if (jumpTimer > 100) {
    jumping = false;
  }
  dino.draw();
}

collision detection

  1. 충돌체크하기 (충돌하면 뭔가 일어나야 함)

공룡과 장애물의 충돌을 어떻게 감지할 수 있을까?

![](.\images\05.png)

장애물의 x좌표 - 공룡의 x좌표 < 0 면 충돌이 일어났다고 볼 수 있다.

![](.\images\06.png)

근데 이건 x축이 만났을 때만 가정한거고, y축에서 만날 수도 있을거다.

![](.\images\07.png)

그래서 x축과 y축이 모두 만났을 때 '충돌했다'고 볼 수 있다.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    } else {
      a.x--;

      isCollapse(dino, a); // ⬅️ 충돌 체크는 여기서 해야

      a.draw();
    }
  });

  if (jumping === true) {
    dino.y--;
    jumpTimer++;
  } else {
    if (jumpTimer > 0) {
      dino.y++;
      jumpTimer--;
    }
  }
  if (jumpTimer > 100) {
    jumping = false;
  }
  dino.draw();
}

executeByFrame();

// 충돌 확인
function isCollapse(dino, cactus) {
  var diffX = cactus.x - (dino.x + dino.width); 
  var diffY = cactus.y - (dino.y + dino.height);
}

var jumping = false;
document.addEventListener("keydown", function (e) {
  if (e.code === "Space") {
    jumping = true;
  }
});

cactus의 x좌표 - 공룡의 x좌표를 구해야하는데, 공룡의 x좌표는 (dino.x + dino.width)다.

![](.\images\08.png)

y의 경우에도 공룡의 y좌표는 (dino.y + dino.height)다.

충돌시 게임 중단

var animation;

function executeByFrame() {
  animation = requestAnimationFrame(executeByFrame);
  ...
}
  

function isCollapse(dino, cactus) {
  var diffX = cactus.x - (dino.x + dino.width);
  var diffY = cactus.y - (dino.y + dino.height);
  
  if (diffX < 0 && diffY < 0) { // 충돌 - 게임 중단
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    cancelAnimationFrame(animation);
  }
}
  • **cancelAnimationFrame** : 애니메이션 중단

네모 대신 이미지 넣기

  1. 장애물

장애물을 그려보자. 먼저 이미지를 가져온다.

var img1 = new Image();
img1.src = "cactus.png";

그리고 장애물을 그리는 함수를

draw() {
	ctx.fillStyle = "red";
	ctx.fillRect(this.x, this.y, this.weight, this.height);
}

아래 코드로 변경한다.

draw() {
    ctx.drawImage(img1, this.x, this.y);
}
  1. 공룡
var img2 = new Image();
img2.src = "dinosaur.png";

var dino = {
  // 공룡 등장 좌표
  x: 10,
  y: 200,
  // 공룡 폭과 높이
  width: 50,
  height: 50,
  // 공룡 그리는 함수
  draw() {
    ctx.drawImage(img2, this.x, this.y, this.width, this.height); // ⬅️
  },
};
dino.draw(); // 공룡 그리기 완료!

결과

![](.\images\09.png)

🕹️전체 코드 (main.js)

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

canvas.width = window.innerWidth - 100;
canvas.height = window.innerHeight - 100;

var img2 = new Image();
img2.src = "dinosaur.png";

var dino = {
  // 공룡 등장 좌표
  x: 10,
  y: 200,
  // 공룡 폭과 높이
  width: 50,
  height: 50,
  // 공룡 그리는 함수
  draw() {
    ctx.drawImage(img2, this.x, this.y, this.width, this.height);
  },
};
dino.draw(); // 공룡 그리기 완료!

var img1 = new Image();
img1.src = "cactus.png";

class Cactus {
  constructor() {
    this.x = 600;
    this.y = 200;
    this.width = 50;
    this.height = 50;
  }
  draw() {
    ctx.drawImage(img1, this.x, this.y);
  }
}

var timer = 0;
var cactuses = [];
var jumpTimer = 0;
var animation;

function executeByFrame() {
  animation = requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 200 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  timer++;

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    }
    a.x--;

    isCollapse(dino, a); // 충돌 체크는 여기서 해야

    a.draw();
  });

  if (jumping === true) {
    dino.y--;
    jumpTimer++;
  } else if (jumping === false) {
    if (dino.y < 200) {
      jumpTimer = 0;
      dino.y++;
    }
  }

  if (jumpTimer > 100) {
    jumping = false;
  }
  dino.draw();
}

executeByFrame();

// 충돌 확인
function isCollapse(dino, cactus) {
  var diffX = cactus.x - (dino.x + dino.width);
  var diffY = cactus.y - (dino.y + dino.height);
  // 충돌 - 게임 중단
  if (diffX < 0 && diffY < 0) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    cancelAnimationFrame(animation);
  }
}

var jumping = false;
document.addEventListener("keydown", function (e) {
  if (e.code === "Space") {
    jumping = true;
  }
});

class로 만들어보는 간단한 2D 게임 (배웠으면 써먹어야하니까)

자바스크립트만으로도 간단한 게임을 만들 수 있다.

  • HTMl, CSS, JS만으로 만든 게임 중 해볼만한 명작: candy box, Synergism

우리는 크롬 공룡 게임을 만들어 보자.

게임 개발 원리와 구현 방법

  1. 화면에 네모, 원 그릴 수 있어야 함 (네모 위에 공룡, 선인장 등 그림을 입힘)

-> <canvas>태그를 이용하면 자바스크립트로 간단하게 네모를 그릴 수 있다.

  1. 프레임마다 코드실행 가능해야 (애니메이션을 위해)

예로 들어서 공룡이 방향키를 누르면 60px 이동해야 한다. 근데 순간이동하면 안되겠지. 서서히 60px로 이동시켜야 한다. 그러면 1초에 60번 정도 +1px 해주면 된다. 그래서 1초에 60번 움직이도록 자바스크립트 코드를 짤 줄 알아야 한다. 브라우저 기본 함수가 있는데 그걸 쓰면 된다.

  1. collision check 할 수 있어야

충돌 시 처리를 해줘야 한다. 예로 들면 움직이다가 적과 충돌하면 내 체력이 -1 깎인다던지. 별건 앙니고 중학교 수학 정도만 할 줄 알면 된다.

네모 그리는 법

🕹️index.html

<body>
    <canvas id="canvas"></canvas>
    <script src="main.js"></script>
</body>

🕹️main.js - canvas 생성하는 기본 코드

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

canvas.width = window.innerWidth - 100;
canvas.height = window.innerHeight - 100;

공룡 만들기

ctx.fillStyle = "green";
ctx.fillRect(10, 10, 100, 100); // 위에서 10, 오른쪽에서 10 위치에서 100x100 사이즈의 네모 그리기

근데 이렇게 먼저 하지 말고, **등장 캐릭터의 속성부터 object 자료에 정리해두면 편리**하다.

var dino = {
  // 공룡 등장 좌표
  x: 10,
  y: 200,
  // 공룡 폭과 높이
  width: 50,
  height: 50,
  // 공룡 그리는 함수
  draw() {
    ctx.fillStyle = "green";
    ctx.fillRect(this.x, this.y, this.width, this.height);
  },
};
dino.draw(); // 공룡 그리기 완료!

장애물 만들기

장애물들은 각각 width, height가 다른 특성을 가지고 있다. 비슷한 object가 많이 필요할 것 같으니 class로 만들어서 객체 생성 기계를 만들자.

class Cactus {
  constructor() {
    this.x = 500;
    this.y = 200;
    this.width = 50;
    this.height = 50;
  }
  draw() {
    ctx.fillStyle = "red";
    ctx.fillRect(this.x, this.y, this.weight, this.height);
  }
}
var cactus = new Cactus();
cactus.draw();

애니메이션 만들기

만약 100px만큼 이동하게 하려면, dino의 x값을 수정하면 된다.

dino.x = 100;

근데 이건 순간이동이지 애니메이션이 아니다. 애니메이션화하려면 1초에 60번 x++해줘야 한다.

// 1초에 60번 해당 코드를 실행해야 함
dino.x += 1;

(게임 개발을 본격적으로하려면 개임 개발 라이브러리를 쓰는게 나음) 근데 우린 쌩으로 개발해볼거다.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);
  dino.x++; // 1초에 dino를 1px씩 움직이고
  dino.draw(); // 이를 그림
}

executeByFrame();
  • **requestAnimationFrame**(callback) : 기본 자바스크립트 함수. **1초에 60번 실행**함. 프레임마다 실행할거를 콜백함수 안에 넣는다.

1초에 60번 실행되어서 화면에 네모가 우측으로 커진다. 우측으로 커지는 게 아니라 실은 dino가 이동하면서 그리고 있는거다.

근데 왜 잔상이 남지? 잔상을 안남게하려면 그리기 전에 캔버스를 지우면 된다.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height); // ⬅️

  dino.x++;
  dino.draw();
}

장애물도 애니메이션 생성하기

근데 크롬 게임은 공룡이 움직이는게 아니라, 장애물이 공룡쪽으로 다가간다. 그래서 공룡을 굳이 움직이게 할 필요는 없다.

장애물을 생성해보자.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  var cactus = new Cactus(); // ⬅️
  cactus.draw(); // ⬅️
  dino.draw();
}

장애물은 2~3초에 하나씩 나와야 한다. 그래서 2~3초에 한 번 이걸 실행하도록 하자.

**게임 세상에선 항상 '초'단위로 움직이는 게 아니라 '프레임'단위로 움직인다.**

타이머를 생성하자. 1초에 60 프레임으로 진행이 된다, 하면 타이머에 조건문을 건다.

var timer = 0;

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactus.draw();
  }

  dino.draw();
}

장애물 여러개 관리하기

하지만 장애물은 하나가 아니라 여러개 존재한다. 그럼 장애물을 만들 때마다 array에 담아서 보관하면 관리하기 편하다.

var timer = 0;
var cactuses = [];

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }
    
  cactuses.forEach((a) => {
    a.x--; // 1초에 60px 왼쪽으로 이동
    a.draw();
  });
  dino.draw();
}

(정리) 120프레임마다 {장애물} 예쁘게 생성하고 array에 집어넣음. 그리고 마지막에 array에 있던거 다 draw()해줌.

Recap

  1. 네모 그리기
  • 그릴 캐릭터의 정보 미리 object 자료로 정리하면 편함
  1. 코드를 1초에 60번 실행하면 애니메이션 만들 수 있음 (requestAnimationFrame)
  • 120프레임마다 장애물도 소환해서 array에 보관했음

화면에서 없어져서 필요없어진 장애물 제거하기

cactuses.forEach((a) => {
    // x 좌표가 0미만이면 제거하기
    a.draw();
});
cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
        o.splice(i, 1);
    }
    a.x--;
    a.draw();
});
  • 첫 번째 파라미터(a) : currentValue. 처리할 현재 요소 (=요소 값)
  • 두 번째 파라미터(i) : index. 처리할 현재 요소의 인덱스 (=요소 인덱스)
  • 세 번째 파라미터(o) : array. forEach()를 호출한 배열 (=**순회 중인 배열**)

스페이스바 누르면 점프 -> eventListener

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    }
    a.x--;
    a.draw();
  });

  if (jumping === true) { // ⬅️
    dino.y--;
  }
  dino.draw();
}

executeByFrame();

var jumping = false;
document.addEventListener("keydown", function (e) {
  if (e.code === "Space") {
    jumping = true;
  }
});

근데 이렇게 짜면 공룡이 무한히 하늘로 승천한다. y를 무한히 가는 게 아니라, 제한을 둬야한다. 100프레임 지나면 dino.y-- 점프를 그만하고 ++해준다던지.

jump 타이머를 만들자.

var timer = 0;
var cactuses = [];
var jumpTimer = 0; // ⬅️

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    }
    a.x--;
    a.draw();
  });

  if (jumping === true) {
    dino.y--;
    jumpTimer++; // ⬅️
  }
  if (jumpTimer > 100) { // ⬅️
    jumping = false;
  }
  dino.draw();
}

공룡을 다시 내려오도록 하자.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    }
    a.x--;
    a.draw();
  });

  if (jumping === true) {
    dino.y--;
    jumpTimer++;
  } else { // ⬅️
    if (jumpTimer > 0) {
      dino.y++;
      jumpTimer--;
    }
  }
  if (jumpTimer > 100) {
    jumping = false;
  }
  dino.draw();
}

collision detection

  1. 충돌체크하기 (충돌하면 뭔가 일어나야 함)

공룡과 장애물의 충돌을 어떻게 감지할 수 있을까?

![](.\images\05.png)

장애물의 x좌표 - 공룡의 x좌표 < 0 면 충돌이 일어났다고 볼 수 있다.

![](.\images\06.png)

근데 이건 x축이 만났을 때만 가정한거고, y축에서 만날 수도 있을거다.

![](.\images\07.png)

그래서 x축과 y축이 모두 만났을 때 '충돌했다'고 볼 수 있다.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    } else {
      a.x--;

      isCollapse(dino, a); // ⬅️ 충돌 체크는 여기서 해야

      a.draw();
    }
  });

  if (jumping === true) {
    dino.y--;
    jumpTimer++;
  } else {
    if (jumpTimer > 0) {
      dino.y++;
      jumpTimer--;
    }
  }
  if (jumpTimer > 100) {
    jumping = false;
  }
  dino.draw();
}

executeByFrame();

// 충돌 확인
function isCollapse(dino, cactus) {
  var diffX = cactus.x - (dino.x + dino.width); 
  var diffY = cactus.y - (dino.y + dino.height);
}

var jumping = false;
document.addEventListener("keydown", function (e) {
  if (e.code === "Space") {
    jumping = true;
  }
});

cactus의 x좌표 - 공룡의 x좌표를 구해야하는데, 공룡의 x좌표는 (dino.x + dino.width)다.

![](.\images\08.png)

y의 경우에도 공룡의 y좌표는 (dino.y + dino.height)다.

충돌시 게임 중단

var animation;

function executeByFrame() {
  animation = requestAnimationFrame(executeByFrame);
  ...
}
  

function isCollapse(dino, cactus) {
  var diffX = cactus.x - (dino.x + dino.width);
  var diffY = cactus.y - (dino.y + dino.height);
  
  if (diffX < 0 && diffY < 0) { // 충돌 - 게임 중단
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    cancelAnimationFrame(animation);
  }
}
  • **cancelAnimationFrame** : 애니메이션 중단

네모 대신 이미지 넣기

  1. 장애물

장애물을 그려보자. 먼저 이미지를 가져온다.

var img1 = new Image();
img1.src = "cactus.png";

그리고 장애물을 그리는 함수를

draw() {
	ctx.fillStyle = "red";
	ctx.fillRect(this.x, this.y, this.weight, this.height);
}

아래 코드로 변경한다.

draw() {
    ctx.drawImage(img1, this.x, this.y);
}
  1. 공룡
var img2 = new Image();
img2.src = "dinosaur.png";

var dino = {
  // 공룡 등장 좌표
  x: 10,
  y: 200,
  // 공룡 폭과 높이
  width: 50,
  height: 50,
  // 공룡 그리는 함수
  draw() {
    ctx.drawImage(img2, this.x, this.y, this.width, this.height); // ⬅️
  },
};
dino.draw(); // 공룡 그리기 완료!

결과

![](.\images\09.png)

🕹️전체 코드 (main.js)

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

canvas.width = window.innerWidth - 100;
canvas.height = window.innerHeight - 100;

var img2 = new Image();
img2.src = "dinosaur.png";

var dino = {
  // 공룡 등장 좌표
  x: 10,
  y: 200,
  // 공룡 폭과 높이
  width: 50,
  height: 50,
  // 공룡 그리는 함수
  draw() {
    ctx.drawImage(img2, this.x, this.y, this.width, this.height);
  },
};
dino.draw(); // 공룡 그리기 완료!

var img1 = new Image();
img1.src = "cactus.png";

class Cactus {
  constructor() {
    this.x = 600;
    this.y = 200;
    this.width = 50;
    this.height = 50;
  }
  draw() {
    ctx.drawImage(img1, this.x, this.y);
  }
}

var timer = 0;
var cactuses = [];
var jumpTimer = 0;
var animation;

function executeByFrame() {
  animation = requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 200 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  timer++;

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    }
    a.x--;

    isCollapse(dino, a); // 충돌 체크는 여기서 해야

    a.draw();
  });

  if (jumping === true) {
    dino.y--;
    jumpTimer++;
  } else if (jumping === false) {
    if (dino.y < 200) {
      jumpTimer = 0;
      dino.y++;
    }
  }

  if (jumpTimer > 100) {
    jumping = false;
  }
  dino.draw();
}

executeByFrame();

// 충돌 확인
function isCollapse(dino, cactus) {
  var diffX = cactus.x - (dino.x + dino.width);
  var diffY = cactus.y - (dino.y + dino.height);
  // 충돌 - 게임 중단
  if (diffX < 0 && diffY < 0) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    cancelAnimationFrame(animation);
  }
}

var jumping = false;
document.addEventListener("keydown", function (e) {
  if (e.code === "Space") {
    jumping = true;
  }
});

class로 만들어보는 간단한 2D 게임 (배웠으면 써먹어야하니까)

자바스크립트만으로도 간단한 게임을 만들 수 있다.

  • HTMl, CSS, JS만으로 만든 게임 중 해볼만한 명작: candy box, Synergism

우리는 크롬 공룡 게임을 만들어 보자.

게임 개발 원리와 구현 방법

  1. 화면에 네모, 원 그릴 수 있어야 함 (네모 위에 공룡, 선인장 등 그림을 입힘)

-> <canvas>태그를 이용하면 자바스크립트로 간단하게 네모를 그릴 수 있다.

  1. 프레임마다 코드실행 가능해야 (애니메이션을 위해)

예로 들어서 공룡이 방향키를 누르면 60px 이동해야 한다. 근데 순간이동하면 안되겠지. 서서히 60px로 이동시켜야 한다. 그러면 1초에 60번 정도 +1px 해주면 된다. 그래서 1초에 60번 움직이도록 자바스크립트 코드를 짤 줄 알아야 한다. 브라우저 기본 함수가 있는데 그걸 쓰면 된다.

  1. collision check 할 수 있어야

충돌 시 처리를 해줘야 한다. 예로 들면 움직이다가 적과 충돌하면 내 체력이 -1 깎인다던지. 별건 앙니고 중학교 수학 정도만 할 줄 알면 된다.

네모 그리는 법

🕹️index.html

<body>
    <canvas id="canvas"></canvas>
    <script src="main.js"></script>
</body>

🕹️main.js - canvas 생성하는 기본 코드

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

canvas.width = window.innerWidth - 100;
canvas.height = window.innerHeight - 100;

공룡 만들기

ctx.fillStyle = "green";
ctx.fillRect(10, 10, 100, 100); // 위에서 10, 오른쪽에서 10 위치에서 100x100 사이즈의 네모 그리기

근데 이렇게 먼저 하지 말고, **등장 캐릭터의 속성부터 object 자료에 정리해두면 편리**하다.

var dino = {
  // 공룡 등장 좌표
  x: 10,
  y: 200,
  // 공룡 폭과 높이
  width: 50,
  height: 50,
  // 공룡 그리는 함수
  draw() {
    ctx.fillStyle = "green";
    ctx.fillRect(this.x, this.y, this.width, this.height);
  },
};
dino.draw(); // 공룡 그리기 완료!

장애물 만들기

장애물들은 각각 width, height가 다른 특성을 가지고 있다. 비슷한 object가 많이 필요할 것 같으니 class로 만들어서 객체 생성 기계를 만들자.

class Cactus {
  constructor() {
    this.x = 500;
    this.y = 200;
    this.width = 50;
    this.height = 50;
  }
  draw() {
    ctx.fillStyle = "red";
    ctx.fillRect(this.x, this.y, this.weight, this.height);
  }
}
var cactus = new Cactus();
cactus.draw();

애니메이션 만들기

만약 100px만큼 이동하게 하려면, dino의 x값을 수정하면 된다.

dino.x = 100;

근데 이건 순간이동이지 애니메이션이 아니다. 애니메이션화하려면 1초에 60번 x++해줘야 한다.

// 1초에 60번 해당 코드를 실행해야 함
dino.x += 1;

(게임 개발을 본격적으로하려면 개임 개발 라이브러리를 쓰는게 나음) 근데 우린 쌩으로 개발해볼거다.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);
  dino.x++; // 1초에 dino를 1px씩 움직이고
  dino.draw(); // 이를 그림
}

executeByFrame();
  • **requestAnimationFrame**(callback) : 기본 자바스크립트 함수. **1초에 60번 실행**함. 프레임마다 실행할거를 콜백함수 안에 넣는다.

1초에 60번 실행되어서 화면에 네모가 우측으로 커진다. 우측으로 커지는 게 아니라 실은 dino가 이동하면서 그리고 있는거다.

근데 왜 잔상이 남지? 잔상을 안남게하려면 그리기 전에 캔버스를 지우면 된다.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height); // ⬅️

  dino.x++;
  dino.draw();
}

장애물도 애니메이션 생성하기

근데 크롬 게임은 공룡이 움직이는게 아니라, 장애물이 공룡쪽으로 다가간다. 그래서 공룡을 굳이 움직이게 할 필요는 없다.

장애물을 생성해보자.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  var cactus = new Cactus(); // ⬅️
  cactus.draw(); // ⬅️
  dino.draw();
}

장애물은 2~3초에 하나씩 나와야 한다. 그래서 2~3초에 한 번 이걸 실행하도록 하자.

**게임 세상에선 항상 '초'단위로 움직이는 게 아니라 '프레임'단위로 움직인다.**

타이머를 생성하자. 1초에 60 프레임으로 진행이 된다, 하면 타이머에 조건문을 건다.

var timer = 0;

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactus.draw();
  }

  dino.draw();
}

장애물 여러개 관리하기

하지만 장애물은 하나가 아니라 여러개 존재한다. 그럼 장애물을 만들 때마다 array에 담아서 보관하면 관리하기 편하다.

var timer = 0;
var cactuses = [];

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }
    
  cactuses.forEach((a) => {
    a.x--; // 1초에 60px 왼쪽으로 이동
    a.draw();
  });
  dino.draw();
}

(정리) 120프레임마다 {장애물} 예쁘게 생성하고 array에 집어넣음. 그리고 마지막에 array에 있던거 다 draw()해줌.

Recap

  1. 네모 그리기
  • 그릴 캐릭터의 정보 미리 object 자료로 정리하면 편함
  1. 코드를 1초에 60번 실행하면 애니메이션 만들 수 있음 (requestAnimationFrame)
  • 120프레임마다 장애물도 소환해서 array에 보관했음

화면에서 없어져서 필요없어진 장애물 제거하기

cactuses.forEach((a) => {
    // x 좌표가 0미만이면 제거하기
    a.draw();
});
cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
        o.splice(i, 1);
    }
    a.x--;
    a.draw();
});
  • 첫 번째 파라미터(a) : currentValue. 처리할 현재 요소 (=요소 값)
  • 두 번째 파라미터(i) : index. 처리할 현재 요소의 인덱스 (=요소 인덱스)
  • 세 번째 파라미터(o) : array. forEach()를 호출한 배열 (=**순회 중인 배열**)

스페이스바 누르면 점프 -> eventListener

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    }
    a.x--;
    a.draw();
  });

  if (jumping === true) { // ⬅️
    dino.y--;
  }
  dino.draw();
}

executeByFrame();

var jumping = false;
document.addEventListener("keydown", function (e) {
  if (e.code === "Space") {
    jumping = true;
  }
});

근데 이렇게 짜면 공룡이 무한히 하늘로 승천한다. y를 무한히 가는 게 아니라, 제한을 둬야한다. 100프레임 지나면 dino.y-- 점프를 그만하고 ++해준다던지.

jump 타이머를 만들자.

var timer = 0;
var cactuses = [];
var jumpTimer = 0; // ⬅️

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    }
    a.x--;
    a.draw();
  });

  if (jumping === true) {
    dino.y--;
    jumpTimer++; // ⬅️
  }
  if (jumpTimer > 100) { // ⬅️
    jumping = false;
  }
  dino.draw();
}

공룡을 다시 내려오도록 하자.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    }
    a.x--;
    a.draw();
  });

  if (jumping === true) {
    dino.y--;
    jumpTimer++;
  } else { // ⬅️
    if (jumpTimer > 0) {
      dino.y++;
      jumpTimer--;
    }
  }
  if (jumpTimer > 100) {
    jumping = false;
  }
  dino.draw();
}

collision detection

  1. 충돌체크하기 (충돌하면 뭔가 일어나야 함)

공룡과 장애물의 충돌을 어떻게 감지할 수 있을까?

![](.\images\05.png)

장애물의 x좌표 - 공룡의 x좌표 < 0 면 충돌이 일어났다고 볼 수 있다.

![](.\images\06.png)

근데 이건 x축이 만났을 때만 가정한거고, y축에서 만날 수도 있을거다.

![](.\images\07.png)

그래서 x축과 y축이 모두 만났을 때 '충돌했다'고 볼 수 있다.

function executeByFrame() {
  requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 120 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    } else {
      a.x--;

      isCollapse(dino, a); // ⬅️ 충돌 체크는 여기서 해야

      a.draw();
    }
  });

  if (jumping === true) {
    dino.y--;
    jumpTimer++;
  } else {
    if (jumpTimer > 0) {
      dino.y++;
      jumpTimer--;
    }
  }
  if (jumpTimer > 100) {
    jumping = false;
  }
  dino.draw();
}

executeByFrame();

// 충돌 확인
function isCollapse(dino, cactus) {
  var diffX = cactus.x - (dino.x + dino.width); 
  var diffY = cactus.y - (dino.y + dino.height);
}

var jumping = false;
document.addEventListener("keydown", function (e) {
  if (e.code === "Space") {
    jumping = true;
  }
});

cactus의 x좌표 - 공룡의 x좌표를 구해야하는데, 공룡의 x좌표는 (dino.x + dino.width)다.

![](.\images\08.png)

y의 경우에도 공룡의 y좌표는 (dino.y + dino.height)다.

충돌시 게임 중단

var animation;

function executeByFrame() {
  animation = requestAnimationFrame(executeByFrame);
  ...
}
  

function isCollapse(dino, cactus) {
  var diffX = cactus.x - (dino.x + dino.width);
  var diffY = cactus.y - (dino.y + dino.height);
  
  if (diffX < 0 && diffY < 0) { // 충돌 - 게임 중단
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    cancelAnimationFrame(animation);
  }
}
  • **cancelAnimationFrame** : 애니메이션 중단

네모 대신 이미지 넣기

  1. 장애물

장애물을 그려보자. 먼저 이미지를 가져온다.

var img1 = new Image();
img1.src = "cactus.png";

그리고 장애물을 그리는 함수를

draw() {
	ctx.fillStyle = "red";
	ctx.fillRect(this.x, this.y, this.weight, this.height);
}

아래 코드로 변경한다.

draw() {
    ctx.drawImage(img1, this.x, this.y);
}
  1. 공룡
var img2 = new Image();
img2.src = "dinosaur.png";

var dino = {
  // 공룡 등장 좌표
  x: 10,
  y: 200,
  // 공룡 폭과 높이
  width: 50,
  height: 50,
  // 공룡 그리는 함수
  draw() {
    ctx.drawImage(img2, this.x, this.y, this.width, this.height); // ⬅️
  },
};
dino.draw(); // 공룡 그리기 완료!

결과

![](.\images\09.png)

🕹️전체 코드 (main.js)

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

canvas.width = window.innerWidth - 100;
canvas.height = window.innerHeight - 100;

var img2 = new Image();
img2.src = "dinosaur.png";

var dino = {
  // 공룡 등장 좌표
  x: 10,
  y: 200,
  // 공룡 폭과 높이
  width: 50,
  height: 50,
  // 공룡 그리는 함수
  draw() {
    ctx.drawImage(img2, this.x, this.y, this.width, this.height);
  },
};
dino.draw(); // 공룡 그리기 완료!

var img1 = new Image();
img1.src = "cactus.png";

class Cactus {
  constructor() {
    this.x = 600;
    this.y = 200;
    this.width = 50;
    this.height = 50;
  }
  draw() {
    ctx.drawImage(img1, this.x, this.y);
  }
}

var timer = 0;
var cactuses = [];
var jumpTimer = 0;
var animation;

function executeByFrame() {
  animation = requestAnimationFrame(executeByFrame);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  if (timer % 200 === 0) {
    var cactus = new Cactus();
    cactuses.push(cactus);
  }

  timer++;

  cactuses.forEach((a, i, o) => {
    if (a.x < 0) {
      o.splice(i, 1);
    }
    a.x--;

    isCollapse(dino, a); // 충돌 체크는 여기서 해야

    a.draw();
  });

  if (jumping === true) {
    dino.y--;
    jumpTimer++;
  } else if (jumping === false) {
    if (dino.y < 200) {
      jumpTimer = 0;
      dino.y++;
    }
  }

  if (jumpTimer > 100) {
    jumping = false;
  }
  dino.draw();
}

executeByFrame();

// 충돌 확인
function isCollapse(dino, cactus) {
  var diffX = cactus.x - (dino.x + dino.width);
  var diffY = cactus.y - (dino.y + dino.height);
  // 충돌 - 게임 중단
  if (diffX < 0 && diffY < 0) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    cancelAnimationFrame(animation);
  }
}

var jumping = false;
document.addEventListener("keydown", function (e) {
  if (e.code === "Space") {
    jumping = true;
  }
});
반응형