recursal/MDN huggingface dataset:
득점
==

* « 이전 (en-US)
* 다음 » (en-US)

이것은 Gamedev Phaser tutorial (en-US)의 16단계중 11번째 과정입니다. 이 과정의 소스코드를 얻고자 한다면 이 강좌를 수강한 후 Gamedev-Phaser-Content-Kit/demos/lesson11.html를 통해 얻으세요.

점수를 얻는 방식의 게임은 좀더 재미있을 것 입니다.— 당신 또는, 친구의 최고 기록을 갈아치울수도 있습니다. 이 글에서는 우리의 게임에 득점 시스템을 추가해 보려고 합니다.

우리는 점수를 저장해두기 위해 여러 변수를 사용하고, 점수를 화면에 출력하기 위해 내부의 `text()` 메서드를 사용합니다.

새로운 변수들
-------

이전 정의되었던 변수들 바로 다음에 다음과 같이 새로운 변수 두개를 추가해 주세요:


```js
// …
let scoreText;
let score = 0;
```

게임 화면에 점수가 표시 되도록 해보자
---------------------

`지금 create()` 함수 뒤에 다음과 같은 코드를 추가해봅시다:


```js
scoreText = game.add.text(5, 5, "Points: 0", {
  font: "18px Arial",
  fill: "#0095DD",
});
```


`text()` 메소드는 4개의 변수를 가질수 있습니다:

* x와 y 좌표값 자리에 글자를 그려냅니다.
* 실제 글자가 나타날 것입니다.
* 폰트도 글자와 함께 나타날 것입니다.

마지막 변수는 CSS와 매우 비슷해 보입니다. 우리의 점수는 파란색에, 18픽셀 크기이며, Arial 폰트를 사용하여 나타날 것입니다.

블록이 무너지면 점수를 갱신합니다
------------------

우리는 공이 블록을 칠때마다 점수를 증가시킵니다 그리고 공이 블록을 칠때마다 동시에 점수판을 갱신하여 화면에 현재 점수를 나타낼 것입니다. 이건 `setText()매소드를 써서 해결 할수 있습니다` — `ballHitBrick()` 함수 아래로 다음과 같은 새로운 코드 두 줄을 추가하세요:


```js
function ballHitBrick(ball, brick) {
  brick.kill();
  score += 10;
  scoreText.setText(`Points: ${score}`);
}
```


됐습니다, — `index.html` 을 다시 새로고침 해주고 공이 블록을 칠때마다 점수가 갱신되는지 확인해줍니다.

코드를 비교해보세요
----------

이번 강좌에서 배운 득점 시스템이 어떻게 작동하는지 이해하고자 한다면, 다음 아래를 이용해 시연해보세요.:

다음 단계
-----

우리는 이제 득점 시스템을 게임에 추가했습니다. 그러나 게임에서 이길 수 없다면, 게임하면서 득점하는게 무슨 소용일까요? 그래서 승리 상태를 추가해보려고 합니다. 게임에서 이기게 하고 싶다면 다음으로 넘어갑시다: win the game (en-US).

* « 이전 (en-US)
* 다음 » (en-US)
개발 마무리
======

* « 이전

Gamedev Canvas tutorial의 **10개의 레슨 중 마지막 단계**입니다. 이 수업을 마친 후 비교하여 살펴볼 소스코드는 Gamedev-Canvas-workshop/lesson10.html에서 찾아볼 수 있습니다.

우리가 쓰는 모든 게임에 항상 개선해야 할 것이 있습니다. 예를 들자면, 우리는 플레이어에게 한 개보다 많은 라이프를 제공할 수 있습니다. 플레이어들은 한 번 또는 두 번의 실수를 만들어도 여전히 게임을 끝낼 수 있습니다. 우리는 또한 우리의 코드 렌더링을 개선할 수 있습니다.

플레이어에게 생명을 부여하기
---------------

생명을 구현하는 방법은 아주 간단합니다. 먼저 변수를 선언했던 곳과 같은 장소에서 변수를 추가하여 생명 수를 저장합니다.


```js
let lives = 3;
```


남은 생명 수를 표시하는 방법은 점수 카운터가 그려지는 것과 같은 형태입니다. `drawScore()`함수 아래에 다음 기능을 추가하세요.


```js
function drawLives() {
  ctx.font = "16px Arial";
  ctx.fillStyle = "#0095DD";
  ctx.fillText(`Lives: ${lives}`, canvas.width - 65, 20);
}
```


지금까지 바닥에 닿으면 바로 게임을 끝내는 형식이었습니다. 이제 우리는 더 이상 게임을 이용할 수 없게 될 때까지 남은 생명 수를 줄일 것입니다. 또한 플레이어가 게임 오버되면 다시 생명과 블럭들을 리셋하여 공과 패들 위치를 재설정할 수 있습니다. 그렇게 하기 위해 `draw()` 함수 내에서 다음 세 줄을 다음 블럭의 코드로 대체합시다.


```js
alert("GAME OVER");
document.location.reload();
clearInterval(interval); // Needed for Chrome to end game
```


이를 통해 아래와 같이 약간 복잡한 논리를 추가할 수 있습니다:


```js
lives--;
if (!lives) {
  alert("GAME OVER");
  document.location.reload();
  clearInterval(interval); // Needed for Chrome to end game
} else {
  x = canvas.width / 2;
  y = canvas.height - 30;
  dx = 2;
  dy = -2;
  paddleX = (canvas.width - paddleWidth) / 2;
}
```


자, 이제 공이 화면의 맨 아래 가장자리에 맞으면 생명(`lives`)을 하나 뺍니다. 생명이 없다면 게임이 끝나고 아직 남아있다면 공의 위치와 패들의 위치가 재설정 됩니다.

### 남은 생명 표시 렌더링하기

이제 `draw()`함수 내에서 `drawLives()`에 생명 수를 추가하고 `drawLives()` 아래에 생명 수를 추가해야 합니다.


```js
drawLives();
```

requestAnimationFrame()을 사용하여 랜더링 개선하기
--------------------------------------

이제 게임 메커니즘과 연결이 되지 않지만, 그것이 렌더링되는 방식에 대해 연구해 봅시다. `requestAnimationFrame`은 우리가 현재 `setInterval()` (en-US)을 사용하여 구현한 고정된 프레임레이트(fixed framerate)보다 더 낫게 게임을 렌더링합니다. 다음 줄을,


```js
const interval = setInterval(draw, 10);
```


이렇게 대체하세요.


```js
draw();
```


다음 각 인스턴스를 제거합니다:


```js
clearInterval(interval); // Needed for Chrome to end game
```


그 다음, `draw()`함수의 맨 아래 (닫는 '`}`'바로 전에) 다음 줄을 추가하여 `draw()`함수가 반복적으로 자신을 호출하게 합니다:


```js
requestAnimationFrame(draw);
```


이 `draw()` 함수는 현재 `requestAnimationFrame()` 루프 내에서 반복적으로 실행되고 있지만, 고정된 10ms 프레임 대신 프레임의 제어권을 브라우저에 다시 부여 합니다. 이는 프레임과 적절하게 일치하고 필요할 때만 모양을 만들 것입니다. 이것은 이전의 `setInterval()` 방법보다 더 효율적이고 부드럽게 애니메이션 루프를 만듭니다.

코드 비교하기
-------

그것이 이 레슨의 전부입니다. 게임의 최종 버전이 준비되어 있고 시작할 준비가 되어 있습니다!

**참고:** 생명의 수와 공이 패들에서 튕겨나가는 각도를 변경해 보세요.

게임 오버 - 마무리!
------------

축하합니다! 당신은 모든 수업을 완수하였습니다! 이쯤 되면 이제 캔버스의 조작 기초와 간단한 2D 게임 구현 방법을 알게 되었을 것입니다. 이제 몇몇 프레임워크를 배우고 게임 개발을 계속하기에 좋은 시간입니다. 이 시리즈의 2D breakout game using Phaser 또는 Cyber Orb built in Phaser (en-US) 튜토리얼을 확인해 볼 수 있습니다. Games section on MDN의 섹션에서 더 많은 지식과 영감을 얻어보세요.

당신은 또한 이 튜토리얼 시리즈의 메인 페이지로 돌아갈 수 있습니다. 즐겁게 코딩하세요!

* « 이전
벽돌 만들기
======

* « 이전
* 다음 »

이번 단계는 Gamedev Canvas tutorial (en-US)의 여섯 번째 학습입니다. Gamedev-Canvas-workshop/lesson6.html에서 이번 학습의 소스코드를 확인할 수 있습니다.

게임플레이 원리를 수정한 후에, 우리는 게임에서 패배할 수 있게 되었습니다. 이것은 실제 게임에 보다 가까운 느낌이기 때문에 훌륭합니다. 하지만 벽과 패들에 공이 튀기는 것 말고 할 수 있는 것이 없기 때문에 금방 지루해집니다. 벽돌깨기 게임에서 진정으로 필요로 한 것은 공으로 파괴할 벽돌입니다. 이 것이 지금 우리가 만들 것입니다!

벽돌에 대한 변수 설정하기
--------------

이번 학습의 모든 목표는 벽돌들을 위한 코드를 2차원 배열로 동작하는 반복문을 통해 제공하는 것입니다. 그러나 먼저 우리는 가로, 세로, 행, 열 등 벽돌에 대한 값을 정의할 몇몇 변수들을 설정해야 합니다. 지난 학습에서 작성한 코드에 아래 코드를 추가해봅시다.


```js
const brickRowCount = 3;
const brickColumnCount = 5;
const brickWidth = 75;
const brickHeight = 20;
const brickPadding = 10;
const brickOffsetTop = 30;
const brickOffsetLeft = 30;
```


우리는 벽돌 배열 행과 열의 수, 그것들의 가로, 세로길이, 각 벽돌이 서로 닿지 않을 정도의 간격과 벽돌이 캔버스의 모서리에 닿지 않게 할 오프셋 변수들을 정의했습니다.

우리는 2차원 배열에 벽돌을 담았습니다. 배열은 열 `c`, 행 `r`, 그리고 배열의 각 객체엔 화면에 벽돌을 그릴 위치를 나타낼 `x`, `y` 위치를 가지고 있습니다. 위에서 변수를 선언한 코드 뒤에 아래 코드를 추가해봅시다.


```js
const bricks = [];
for (let c = 0; c < brickColumnCount; c++) {
  bricks[c] = [];
  for (let r = 0; r < brickRowCount; r++) {
    bricks[c][r] = { x: 0, y: 0 };
  }
}
```


위 코드는 행과 열 수만큼 반복되면서 새로운 벽돌을 만듭니다. 각 벽돌 객체는 이후에 충돌감지에 사용됩니다.

벽돌을 그리는 방법
----------

이제 배열안의 모든 벽돌을 반복해서 화면에 그려줄 함수를 만들어봅시다. 코드는 아래와 같습니다.


```js
function drawBricks() {
  for (let c = 0; c < brickColumnCount; c++) {
    for (let r = 0; r < brickRowCount; r++) {
      bricks[c][r].x = 0;
      bricks[c][r].y = 0;
      ctx.beginPath();
      ctx.rect(0, 0, brickWidth, brickHeight);
      ctx.fillStyle = "#0095DD";
      ctx.fill();
      ctx.closePath();
    }
  }
}
```


다시 행, 열 반복을 통해 각 벽돌의 `x`, `y` 값을 설정하고, 캔버스에 `brickWidth` \* `brickHeight` 크기의 벽돌들을 그립니다. 문제는 모든 벽돌들이 좌표 (0, 0) 위치해있다는 것입니다. 우리는 약간의 연산을 통해 각 벽돌의 `x`, `y` 값을 계산해야 합니다.


```js
const brickX = c \* (brickWidth + brickPadding) + brickOffsetLeft;
const brickY = r \* (brickHeight + brickPadding) + brickOffsetTop;
```


`brickX`는 `brickWidth` + `brickPadding`에 `c`를 곱하고, `brickOffsetLeft`를 더한 값입니다. `brickY`는 변수 `r`, `brickHeight`, `brickOffsetTop` 변수를 사용한다는 것을 제외하곤 동일합니다. 이제 모든 벽돌들을 올바른 위치에, 알맞은 간격을 두고, 캔버스 모서리로부터 오프셋 값만큼의 거리를 둔 상태로 그릴수 있게되었습니다.

`brickX`와 `brickY` 값을 (0, 0) 대신에 좌표 값으로 할당한 후에 `drawBricks` 함수의 마지막 버전은 아래와 같을 것입니다. 이 코드는 `drawpaddle` 함수 아래에 추가해봅시다.


```js
function drawBricks() {
  for (let c = 0; c < brickColumnCount; c++) {
    for (let r = 0; r < brickRowCount; r++) {
      const brickX = c \* (brickWidth + brickPadding) + brickOffsetLeft;
      const brickY = r \* (brickHeight + brickPadding) + brickOffsetTop;
      bricks[c][r].x = brickX;
      bricks[c][r].y = brickY;
      ctx.beginPath();
      ctx.rect(brickX, brickY, brickWidth, brickHeight);
      ctx.fillStyle = "#0095DD";
      ctx.fill();
      ctx.closePath();
    }
  }
}
```

실제 벽돌을 그리기
----------

이 학습에서 마지막으로 할 일은 `drawBrick`함수를 호출하는 코드를 `draw`함수 어딘가에, 되도록이면 시작하는 부분에, 캔버스를 초기화하는 부분과 공을 그리는 사이에 추가하는 것입니다. 아래 코드를 `drawBall()` 코드 위에 추가해봅시다.


```js
drawBricks();
```

코드 비교해보기
--------

이 부분에서 게임은 조금 더 흥미로워졌습니다.

**참고:** 연습하기: 행과 열의 수를 바꿔서 벽돌의 수를 변경해보거나, 위치를 변경해봅시다.

다음 단계
-----

이제 우리에겐 벽돌이 있습니다! 하지만 공은 벽돌들과의 반응이 없습니다. 우리는 다음 단계에서 이 문제에 대해 다룰 것입니다: 충돌 감지

* « 이전
* 다음 »
점수 추가와 승패 판정 방법
===============

* « 이전
* 다음 »

이번 단계는 Gamedev Canvas tutorial의 8번째 단계입니다. Gamedev-Canvas-workshop/lesson8.html에서 이번 단계의 소스 코드를 확인할 수 있습니다.

벽돌 깨기 기능은 잘 작동합니다. 하지만 더 나은 게임이 되기 위해서, 유저가 벽돌을 깰 때마다 점수를 얻고, 그 점수를 기록하는 점수 기능을 만들 수 있습니다.

점수 계산하기
-------

게임 플레이 도중 점수를 볼 수 있다면, 친구들에게 자랑할 수 있을 것입니다. 그러기 위해서는 점수를 기록할 변수가 필요합니다. 작성중인 JS파일의 변수 영역의 하단에, 아래 코드를 추가합시다.


```js
var score = 0;
```


점수 화면을 만들고, 업데이트를 하기 위해서 `drawScore()` 함수가 필요합니다. 아래 코드를 `collisionDetection()` 함수 아래에 추가합시다.


```js
function drawScore() {
  ctx.font = "16px Arial";
  ctx.fillStyle = "#0095DD";
  ctx.fillText("Score: " + score, 8, 20);
}
```


텍스트를 캔버스 안에 그리는 것은 도형을 그리는 작업과 비슷합니다. 폰트를 정의하는 것은 CSS에서의 작업과 같습니다. — `font()` (en-US) 함수에서 크기와 타입을 설정할 수 있습니다. 그런 다음 `fillStyle()` (en-US) 함수를 사용하여 글꼴의 색상을 설정하고 `fillText()` (en-US) 함수를 이용해서 캔버스에 배치될 실제 텍스트와 배치 위치를 설정하는 데 사용합니다. 첫 번째 매개 변수는 텍스트 자체입니다. 위의 코드는 현재 점수를 나타내며 마지막 두 매개 변수는 텍스트가 캔버스에 배치될 좌표입니다.

벽돌이 깨질 때마다 점수 변수의 값을 증가시키기 위해서 `collisionDetection()` 함수에 강조 표시된 코드를 입력하여 수정합시다.


```js
function collisionDetection() {
  for (var c = 0; c < brickColumnCount; c++) {
    for (var r = 0; r < brickRowCount; r++) {
      var b = bricks[c][r];
      if (b.status == 1) {
        if (
          x > b.x &&
          x < b.x + brickWidth &&
          y > b.y &&
          y < b.y + brickHeight
        ) {
          dy = -dy;
          b.status = 0;
          score++;
        }
      }
    }
  }
}
```


`draw()` 함수 안에서 위 함수를 호출하면 호출 될 때마다 다시 그리기 때문에 점수 변동을 확인할 수 있습니다. `draw()` 아래 코드를 `drawPaddle()` 아래에서 호출합니다.


```js
drawScore();
```

모든 벽돌이 파괴되었을 때 승리 메시지 표시
------------------------

점수가 오르는 것은 좋지만, 평생 점수가 올라가진 않을 것입니다. 모든 벽돌의 파괴가 결국 게임의 주된 목적이기 때문에 모든 점수를 얻는다면 승리 메시지를 표시해야 합니다. `collisionDetection()`함수에 강조된 코드를 붙여넣읍시다.


```js
function collisionDetection() {
  for (var c = 0; c < brickColumnCount; c++) {
    for (var r = 0; r < brickRowCount; r++) {
      var b = bricks[c][r];
      if (b.status == 1) {
        if (
          x > b.x &&
          x < b.x + brickWidth &&
          y > b.y &&
          y < b.y + brickHeight
        ) {
          dy = -dy;
          b.status = 0;
          score++;
          if (score == brickRowCount \* brickColumnCount) {
            alert("YOU WIN, CONGRATULATIONS!");
            document.location.reload();
          }
        }
      }
    }
  }
}
```


결국 사용자는 모든 벽돌을 파괴한 순간, 실제로 게임에서 승리할 수 있습니다. 이는 게임과 관련하여 굉장히 중요합니다. `document.location.reload()`기능은 페이지를 다시 로드하고 경고 버튼이 클릭되면 게임을 다시 시작합니다.

코드 비교
-----

최종 코드는 아래와 같습니다. 지금까지 작성한 코드와 비교해 봅시다.

**참고:** **추가 학습**: 벽돌을 깰 때마다 얻는 점수를 늘리고, 게임 클리어 시 최종 점수를 경고창에 표시해 봅시다.

다음 단계
-----

게임은 현재 꽤 멋지게 보입니다. 다음 단계에서는, Mouse controls (마우스로 패들 조종하는 방법)을 통해서 게임을 즐길 수 있습니다.

* « 이전
* 다음 »
마우스로 패들 조종하기
============

* « 이전
* 다음 »

이 레슨은 Gamedev Canvas tutorial의 10개 중 **9번째** 단계입니다. 당신은 Gamedev-Canvas-workshop/lesson9.html에서 완성된 소스코드를 찾을 수 있습니다.

게임 자체는 사실상 완성되었으므로, 이를 다듬는 작업을 해봅시다. 우리는 이미 키보드로 조종하는 코드를 추가했지만 마우스 조종하는 코드를 쉽게 추가할 수 있습니다.

마우스 이동 감지하기
-----------

마우스 움직임을 감지하는 것은 키 누름을 감지하는 것보다 더욱 쉽습니다. 우리에게 필요한 모든 것은 `mousemove` (en-US) 이벤트에 대한 리스너입니다. 이 `keyup event` 바로 아래에 이 행을 추가해보세요.


```js
document.addEventListener("mousemove", mouseMoveHandler, false);
```

마우스 움직임에 따라 패들 이동하기
-------------------

포인터 좌표에 따라 패들 위치를 업데이트할 수 있습니다. 다음의 핸들러 함수가 정확히 이것을 할 것입니다. 추가한 이전 줄 아래에 다음 함수를 추가해보세요.


```js
function mouseMoveHandler(e) {
  const relativeX = e.clientX - canvas.offsetLeft;
  if (relativeX > 0 && relativeX < canvas.width) {
    paddleX = relativeX - paddleWidth / 2;
  }
}
```


이 함수에서 우리는 첫번째로 `relativeX` 값을 계산하는데, 이는 뷰포트 (`e.clientX`) 안에 있는 가로 마우스 위치에 캔버스의 좌측 끝과 뷰포트 (`canvas.offsetLeft`) 의 좌측 끝 사이의 거리를 뺀 값과 같습니다. 이는 캔버스의 좌측 끝과 마우스 포인터 사이의 거리와 같습니다. 상대적인 X 포인터 위치가 0보다 크고 캔버스 폭보다 적으면 포인터가 경계 내에 있게 되고, `paddleX`의 위치(패들 왼쪽 가장자리에 배치되어 있습니다)는 패들 폭의 반을 뺀 `relativeX`의 값으로 설정되므로 이동은 패들의 중앙과 관련될 것입니다.

이제 패들은 마우스 커서의 움직임을 따르겠지만, 움직임을 캔버스 크기로 제한하고 있기 때문에 어느 쪽에서도 완전히 사라지지 않을 것입니다.

코드 비교하기
-------

지금까지 해왔던 코드를 한번 비교해보세요.

**참고:** 패들이 캔버스 양쪽 가장자리에서 반만 잘려나가 보일 것입니다. 패들 이동 제한 경계를 조정해 보세요.

다음 단계
-----

이제 우리는 완전한 게임을 할 수 있게 되었습니다. 우리는 몇 가지 작은 코드만으로 이 레슨을 마치게 될 것 입니다! — 마지막.

* « 이전
* 다음 »
게임 오버
=====

* « 이전
* 다음 »

이것은 Gamedev Canvas tutorial (en-US) 의 다섯 번째 학습입니다. 이 학습을 통해 완성된 코드는 Gamedev-Canvas-workshop/lesson5.html 에서 확인할 수 있습니다.

공이 벽에서 튕겨져 나오는 것과 패들을 움직이는 것을 보는건 재밌지만, 더 진전이 필요합니다. 게임에서 질 수 있다면 좋겠습니다. 벽돌 깨기 게임에서 패배하는 구조는 꽤 단순합니다. 만약 패들이 공을 놓친다면, 공은 캔버스의 아래 면에 닿을 것이고, 그대로 게임은 끝납니다.

게임 오버 기능 적용하기
-------------

'게임 오버' 기능을 만들어봅시다. 아래에 세 번째 학습에서 작성한, 벽에서 공을 반사시키는 코드의 일부가 있습니다.


```js
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
  dx = -dx;
}

if (y + dy > canvas.height - ballRadius || y + dy < ballRadius) {
  dy = -dy;
}
```


사면 모두에서 공을 튕겨내지 말고 왼쪽, 위쪽, 오른쪽, 세 면에만 적용해봅시다. 아래쪽 면에 닿는 순간 게임은 끝납니다. 우리는 공이 밑면에 충돌하는 순간 "게임 오버" 상태로 바뀌게 하기 위해 두 번째 `if` 블록을 수정할 것입니다. 우선 경고 메시지를 보여주고 페이지를 리로딩해서 게임을 다시 시작하게 할 것입니다. 두번째 `if` 블록을 아래와 같이 수정해봅시다.


```js
if (y + dy < ballRadius) {
  dy = -dy;
} else if (y + dy > canvas.height - ballRadius) {
  alert("GAME OVER");
  document.location.reload();
}
```

Paddle은 공을 튕겨내야지
----------------

이번 학습에서 할 마지막 일은 공과 패들 사이의 충돌 감지같은, 공을 게임 화면으로 되돌아가게 튕겨내는 기능을 만드는 것입니다. 가장 쉬운 방법은 공의 중심이 패들의 내부에 있는지 확인하는 것이다. 위에서 수정한 코드를 약간 고쳐봅시다.


```js
if (y + dy < ballRadius) {
  dy = -dy;
} else if (y + dy > canvas.height - ballRadius) {
  if (x > paddleX && x < paddleX + paddleWidth) {
    dy = -dy;
  } else {
    alert("GAME OVER");
    document.location.reload();
  }
}
```


공이 캔버스의 밑면에 닿는 순간, 공이 패들의 안쪽에 있는지 확인해야 합니다. 만약 그렇다면, 우리가 기대하는대로 공은 튕겨져야 합니다. 그게 아니라면, 게임의 전과 같이 끝나야 합니다.

코드 비교하기
-------

여기 완성된 코드가 있으니 작성한 코드와 비교해봅시다.

**참고:** **연습**: 공이 패들에 닿았을 때 공의 속도를 빠르게 만들어봅시다.

다음 단계
-----

지금까지 아주 잘 하고 있습니다. 게임은 플레이 할 가치가 생겼고 이젠 게임이 끝날 수도 있습니다. 하지만 뭔가가 빠졌습니다. 여섯 번째 학습 — 벽돌 영역을 만들어보기 (en-US) — 로 이동해서 몇 개의 벽돌을 만들어 봅시다.

* « 이전
* 다음 »
공을 벽에 튕기기
=========

* « 이전
* 다음 »

이 글은 Gamedev Canvas 튜토리얼 (en-US)에 포함된 10단계 중 3단계 글입니다. 이 글을 다 읽고 난 뒤 완성된 소스코드는 Gamedev-Canvas-workshop/lesson3.html에서 확인해볼 수 있습니다.

공이 움직이는 걸 보니 기쁩니다! 하지만 기쁨을 즐길 틈도 없이 공이 스크린 밖으로 사라져버리네요. 이 문제를 해결하기 위해 공이 캔버스의 4군데 모서리에 다달았을 때 튕겨나올 수 있도록 간단한 충돌 감지 기능(더 자세한 내용은 다음 글 (en-US)에서 다시 설명할 예정입니다)을 구현해야 합니다.

간단한 충돌 감지
---------

충돌을 감지하기 위해서는 공이 벽에 닿았는지를 확인하고 그에 따라 움직이는 방향을 수정해야 합니다.

보다 계산을 쉽게 하기 위하여 `ballRadius` 변수를 만든 뒤 원의 반지름 값을 대입하여 계산하는데 사용합니다. 아래의 코드를 기존의 변수들 아래에 삽입하세요:


```js
const ballRadius = 10;
```


이제`drawBall()` func기능 안에 볼을 그리는 코드를 아래와 같이 수정하세요:


```js
ctx.arc(x, y, ballRadius, 0, Math.PI \* 2);
```

### 위 아래 방향으로 튕기기

캔버스에는 총 4개의 모서리 즉, 4개의 벽이 있습니다. 일단 상단의 벽에 집중해 보겠습니다. 공을 그리는 매 프레임마다 우리는 볼이 상단 모서리에 닿았는지 확인해야합니다 — 닿았다면 볼이 움직이는 방향을 반대로 바꾸어 캔버스 안에 여전히 공이 보이도록 만들어주어야 합니다. 캔버스 내 위치 구조는 좌상단으로 부터 시작하는 것을 잊지마세요:


```js
if (y + dy < 0) {
  dy = -dy;
}
```


만약 공의 위치에서 `y`값이 0보다 작은 경우 음/양수를 반대로 바꾸어주어 y 축의 방향을 바꾸어 줍니다. 공이 매 프레임마다 2픽셀만큼 움직이고 있었다면, 이제는 매 프레임마다 2픽셀만큼 "아래 방향으로" 이동할 것입니다.

위 코드는 상단 모서리를 튕기도록 해주기 때문에 이번엔 하단 모서리를 튕기도록 해보겠습니다:


```js
if (y + dy > canvas.height) {
  dy = -dy;
}
```


공의 위치에서 y값이 캔버스의 높이보다 큰 경우(좌상단으로부터 y값을 측정하기 때문에 상단모서리에서의 y값은 0, 하단모서리에서의 y값은 480, 즉 캔버서의 높이값임을 잊지마세요) y축 움직임의 반대 방향으로 튕겨냅니다.

위의 두가지 코드를 하나로 합칠 수 있습니다:


```js
if (y + dy > canvas.height || y + dy < 0) {
  dy = -dy;
}
```


둘 중 하나의 조건이라도 `true`를 만족한다면, 공의 방향은 반대로 바뀝니다.

### 좌우로 튕겨내기

우리는 방금 상, 하단 모서리를 인식했으므로 이번엔 좌우 모서리를 생각해봅시다. 거의 같은 문제이므로 우리는 `y` 대신 `x`값을 대입하여 그대로 반복해주기만 하면 됩니다:


```js
if (x + dx > canvas.width || x + dx < 0) {
  dx = -dx;
}

if (y + dy > canvas.height || y + dy < 0) {
  dy = -dy;
}
```


이 후 위 코드를 draw() 함수블럭이 끝나는 중괄호 바로 전에 삽입합니다.

### 공이 여전히 벽 밖으로 사라져요!

코드를 테스트해보세요. — 아마 캔버스 모서리에서 튕겨나오는 볼이 인상적이지 않나요? 하지만 또 다른 문제가 생겼습니다. — 공이 벽을 튕겨나와 방향이 바뀌기 전에 살짝 벽 안으로 숨어버리네요:

![skyblue ball disappearing into the top of the white wall.](/ko/docs/Games/Tutorials/2D_Breakout_game_pure_JavaScript/Bounce_off_the_walls/ball-in-wall.png)

이 문제는 우리가 충돌을 감지할 때 그 기준을 공의 원점에 두고 계산했지만, 원의 둘레를 기준으로 계산을 해야 하기 때문입니다. 벽에 공이 절반쯤 지난 뒤가 아니라 닿자마자 튕겨나와야 합니다. 이에 맞게 코드를 조금 수정해봅시다. 여러분이 삽입한 코드를 아래와 같이 수정해보세요:


```js
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
  dx = -dx;
}
if (y + dy > canvas.height - ballRadius || y + dy < ballRadius) {
  dy = -dy;
}
```


공의 원점과 벽 사이의 거리가 공의 반지름과 같아졌을 때 공의 움직임이 바뀌도록 만들었습니다. 이제야 볼이 정확히 튕겨나오고 있네요.

여러분의 코드를 비교해보세요
---------------

자, 다시 여러분이 작성한 코드를 완성된 코드와 비교해보고 게임을 실행해보세요:

**참고:** 공이 벽에 부딪힐 때마다 공의 색을 무작위로 변화시켜보세요.

다음 단계
-----

이제 우리는 공이 게임판 안에서 어디서 어떻게 움직이고 있는지 파악했습니다. 네번째 챕터에서는 조작 가능한 배드를 구현해볼 것입니다. — 패드와 키보드 조작 (en-US).

* « 이전
* 다음 »
프로그레시브 웹 앱
==========

프로그레시브 웹 앱은 최신 웹 API와 전통적인 점진적 개선 전략을 함께 사용해 크로스 플랫폼 웹 어플리케이션을 생성합니다. 이러한 앱은 어디서든 동작하며 네이티브 앱에서의 사용자 경험 이점과 동일한 몇 가지 기능들을 제공합니다. 이 문서에서는 이에 대해 여러분이 알아야 할 모든 것들에 대해 알려드립니다.

PWA 이점
------

PWA는 발견 가능, 설치 가능, 연결 가능해야 하고, 네트워크에 독립적이어야 하고, 점진적이고 재참여가 가능해야하며 반응형이고 안전해야 합니다. 이러한 것들이 무엇을 의미하는지는 프로그레시브 웹 앱 이점 (en-US) 문서를 읽어보시기 바랍니다. PWA를 구현하는 방법은 아래 섹션에 나열된 가이드를 참조하십시오.

핵심 PWA 가이드
----------

다음 가이드는 간단한 예제를 살펴보고 모든 조각이 어떻게 동작하는지 보여줌으로써 PWA 구현을 위해 해야하는 것들이 무엇인지 보여줍니다.

1. 프로그레시브 웹 앱 소개 (en-US)
2. 프로그레시브 웹 앱 구조 (en-US)
3. Service worker를 사용해 PWA를 오프라인에서 동작하게 만들기 (en-US)
4. PWA를 설치가능하게 만드는 방법 (en-US)
5. 알림과 푸시를 사용해 PWA를 재참여(re-engageable)가능하게 만드는 방법 (en-US)
기술 가이드
------

* 클라이언트 사이드 저장소 (en-US) — 웹 저장소, IndexedDB, service worker를 언제 어떻게 사용하는지 보여주는 상세 가이드
* Service worker 사용 (en-US) — Service Worker API에 대한 더 자세한 가이드.
* IndexedDB 사용 — IndexedDB의 근본에 대한 자세한 설명.
* Web Storage API 사용 (en-US) — 간단하게 만들어진 Web storage API.
* Application Shell 구조를 사용한 웹 앱 즉시 로딩 — App Shell 코딩 패턴을 사용해 빠르게 로딩되는 앱을 생성하는 가이드.
* Push API 사용 (en-US) — Web Push API의 기초에 대해 배우기.
* Notifications API 사용 — 웹 알림 요약.
* 반응형 디자인 블록 구축 (en-US) — 반응형 디자인 기초, 최신 앱 레이아웃 필수 주제 배우기.
* 모바일 우선 (en-US) — 반응형 어플리케이션 레이아웃을 생성할 때, 보통 기본적으로 모바일 레이아웃을 생성하고, 더 넓은 상단 레이아웃을 구축하는 것이 좋습니다.
* 홈 화면에 추가 가이드 (en-US) — 앱을 홈 화면에 추가하여 얻을 수 있는 이점에 대해 배우기.
도구
--

* localForage — 아주 간단하게 클라이언트 사이드 데이터 저장소를 만들 수 있는 훌륭하고 가벼운 JavaScript 라이브러리. 기본적으로 IndexedDB를 사용하고 필요한 경우 Web SQL/Web Storage를 사용합니다.
* ServiceWorkerWare — 손쉬운 Service Worker 개발을 위한 Express 같은 마이크로프레임워크.
* oghliner — 템플릿이자 GitHub 페이지에 오프라인 웹 앱을 배포하기 위한 도구.
* sw-precache — 특정 리소스를 프리캐싱(미리 캐싱;precache)하는 service worker 코드를 생성하는 노드 모듈.
* workbox — 더 나은 캐싱 전략과 쉬운 프리캐싱을 제공하는 sw-precache의 정신적 지주.
* upup — 사이트가 사용자들을 위해 항상 존재하도록 해주는 작은 스크립트.
* The service worker cookbook — 오프라인 앱 및 그 이상의 다양한 앱 구현 방법을 보여주는 훌륭한 service worker/push 가이드 시리즈.
함께 보기
-----

* Progressive Web Apps landing page and check list on web.dev
* Progressive Web Apps: Everything You Need To Know
* Progressive Web Apps: Escaping Tabs Without Losing Our Soul
* Instant Loading Web Apps with An Application Shell Architecture
* Workbox — set of libraries to power a "production-ready" service worker for your PWA
* Lighthouse — web-app auditing tool that includes PWA-auditing features
* localForage — `localStorage`-like *async* storage, to improve your PWA's offline experience
XPath
=====

XPath는 XML Path Language를 나타내고 XML 문서의 다른 부분을 주소 지정하는(가리키는) 유연한 방법을 제공하는 비XML 문법을 씁니다. 이와 동시에, 또한 패턴과 일치하는지 알아보기 위해 문서 안에 주소 지정된 노드를 검사하는데도 쓰일 수 있습니다.

XPath는 주로 XSLT에서 쓰입니다. 하지만 또한 getElementById 메소드, childNodes 특성들에 의존하는 대신 HTML과 XUL 같은 다른 XML에 기반을 둔 언어 문서를 DOM을 통해 항해하는 더 나은 강력한 방법으로도 쓰입니다. 다음은 모호합니다: 이는 확장기능 안에서 특히 쓸모 있습니다, 특히 overlays를 고려하면.

XPath는 XML 문서의 계층 구조를 항해하기 위해 경로 표기법(URL의 경우와 같이)을 씁니다. URI와 XML 속성값에 쓰일 수 있도록 비XML 문법을 씁니다.

문서
--

Introduction to using XPath in JavaScript

XPath의 비XSLT 사용을 기술합니다.

XPath:Axes

XPath 명세에 정의된 축의 정의와 목록. 축은 노드 사이의 관계를 기술하는데 씁니다.

XPath:Functions

기본 XPath 함수와 XPath에 더해진 XSLT 고유의 추가분 설명과 나열.

Transforming XML with XSLT

XSLT는 변환하고 싶은 XML 문서 내의 코드 조각(segment)을 주소지정하기 위해 XPath를 씁니다.

XPath snippets (en-US)

These are JavaScript utility functions, that can be used in your own code, based on DOM Level 3 XPath APIs.

What is XSLT?

XSLT와 XPath에 관한 이 광범위한 소개는 두 기술 사이에 우선순위가 없다 보고 읽는 이를 배경, 문맥, 구조, 개념, 입문 용어로 안내합니다.

Tools
-----

XPath tester

온라인 XPath 작성기/디버거

Related Topics
--------------

* XSLT, XML, DOM
* Comparison of CSS Selectors and XPath (en-US)
XSLT
====

**Extensible Stylesheet Language Transformations** (**XSLT**)는 전문 처리 소프트웨어와 함께 XML 문서 변환에 쓰이는 XML에 기반을 둔 언어입니다. 비록 그 처리가 "변환"으로 되어 있더라도, 원 문서는 바뀌지 않습니다. 오히려, 새 XML 문서는 기존 문서의 컨텐트에 바탕을 두고 만듭니다. 그러므로, 새 문서를 표준 XML 문법이나 다른 형식(HTML이나 순수 텍스트 같은)으로 처리기가 결과를 직렬(출력)할 수도 있습니다. XSLT는 다른 XML 스키마 사이에 데이터를 바꾸거나 XML 데이터를 웹 페이지나 PDF 문서로 바꾸는데 가장 자주 쓰입니다.

Documentation
-------------

XSLT Element Reference

Reference.

Transforming XML with XSLT

XSLT는 스타일시트 만든 이가 그러길 바란다면 컨텐트를 모조리 다시 정리하고 다른 형식으로 변환하는 것을 포함하는 컨텐트 조작(manipulating)과 정렬이라는 중요한 두 방식으로 본래 XML 문서를 변환하게 합니다.

Using the Mozilla JavaScript interface to XSL Transformations

이 문서는 Mozilla 1.2의 JavaScript 인터페이스와 XSLT 처리 엔진까지 설명합니다.

Specifying parameters using processing instructions (en-US)

Firefox allows stylesheet parameters to be specified when using the `<?xml-stylesheet?>` processing instruction. This is done using the `<?xslt-param?>` PI described in this document.

XSLT Tutorial

이 W3Schools 입문서는 읽는 이에게 XML 문서를 XHTML같은 다른 형식으로 변환하기 위해 어떻게 XSLT를 사용하는지를 가르칩니다.

What is XSLT?

XSLT와 XPath에 대한 이 방대한 해설은 두 기술에 대한 지식이 없다고 가정하고, 배경, 문맥, 구조, 개념, 입문 용어를 통해 읽는 이를 이끕니다.

Common XSLT Errors (en-US)

This article lists some common problems using XSLT in Firefox.

Related Topics
--------------

* XML, XPath
XML: 확장가능한 마크업 언어
=================

**확장가능한 마크업 언어**는 문서 객체 모델의 엄격한 직렬화입니다.

XML 소개XML(확장 가능한 마크업 언어)은 HTML과 유사하지만, 미리 정의된 태그가 없는 마크업 언어입니다. 대신 필요에 맞게 특별히 설계된 태그를 직접 정의합니다. 이는 데이터를 저장, 검색, 공유할 수 있는 형식으로 저장할 수 있는 강력한 방법입니다. 가장 중요한 것은 XML의 기본 형식이 표준화되어 있기 때문에, 로컬 또는 인터넷을 통해 시스템이나 플랫폼 간 XML을 공유하거나 전송하는 경우에도, 표준화된 XML 구문으로 덕분에 수신자가 데이터를 파싱 할 수 있다는 점입니다.

XML 파싱 및 직렬화웹 상에서 XML을 파싱하고 직렬화할 때 사용할 수 있는 객체는 다음과 같습니다.
웹 개발자 안내서
=========

MDN 문서에는 많은 가이드가 있습니다. 이 문서는 추가 사용 예시를 추가하거나 API 또는 기능을 사용하는 방법을 가르치는 것을 목표로 합니다. 이 페이지는 가장 인기 있는 자료로 연결됩니다.

HTML
----

HTML로 웹 구조화하기

 HTML 학습 영역은 처음부터 HTML을 배우는 데 도움이 되는 자습서를
 제공합니다.


HTML 기본

 이 글은 HTML에 대한 기본적인 이해를 제공합니다. 이 가이드를 따른 후 HTML
 학습 영역에서 자료를 더 탐색할 수 있습니다.


CSS
---

CSS를 이용한 HTML 스타일링 익히기

전체 CSS 자습서는 텍스트 스타일 지정, 레이아웃 만들기 등 첫걸음을 뗄 수 있도록 도와줄 것입니다.

CSS 레이아웃 가이드 (en-US)

 MDN 전반에 걸쳐 CSS Layout에 대한 많은 가이드가 있으며, 이 페이지는
 그것들을 모두 모았습니다.


CSS 애니메이션 사용하기

 CSS 애니메이션은 사용하면 CSS 스타일 구성에서 다른 CSS 스타일 구성으로의
 전환을 애니메이션으로 만들 수 있습니다. 이 가이드는 애니메이션 속성을
 시작하는 데에 도움이 될 것입니다.


JavaScript
----------

JavaScript 학습영역

당신이 완전한 초보자이든 기술을 다시 배우고 싶든, 이 문서가 출발점이 될 수 있습니다.

AJAX (en-US)

 AJAX는 웹 애플리케이션이 전체 브라우저 페이지를 다시 로드하지 않고도
 사용자 인터페이스에 빠르고 점진적으로 업데이트할 수 있도록 하는 기술
 그룹들을 정의하는 용어입니다. 이렇게 하면 애플리케이션이 사용자 작업에 더빠르게 응답할 수 있습니다.


Media
-----

웹 상 그래픽

 모던 웹사이트와 애플리케이션들은 종종 다양하고 정교한 그래픽을
 제공해야합니다.


오디오 및 비디오 전달 (en-US)

 우리는 '정적' 미디어 파일에서 적응형 라이브 스트림에 이르기까지 다양한
 방법으로 웹에서 오디오와 비디오를 전달할 수 있습니다. 이 문서는 웹 기반
 미디어의 다양한 전달 메커니즘과 인기 있는 브라우저와의 호환성을 탐색하기
 위한 출발점으로 작성되었습니다.


오디오 및 비디오 조작 (en-US)

 웹의 장점은 기술을 결합하여 새로운 형태를 만들 수 있다는 것입니다.
 브라우저에 기본 오디오 및 비디오가 있다는 것은 이러한 데이터 스트림을
 사용하여 `<canvas>` 와,
 WebGL또는
 Web Audio API 와 같은
 기술과 함께 오디오 및 비디오를 직접 수정할 수 있음을 의미합니다. 예시로
 오디오에 reverb/compression 효과 추가 또는 grayscale/sepia 필터를 비디오에
 사용하는 것이 있습니다. 이 문서에서는 수행해야 할 작업을 설명하는 참고서를
 제공합니다.


APIs
----

FormData 객체 사용 (en-US)

 이 `FormData` 객체를 사용하면
 key/value 값을 컴파일하여 `XMLHttpRequest` 로 보낼 수 있습니다. 주로 양식
 데이터를 보내기 위한 것이지만, 양식과 독립적으로 데이터를 전송하는 데
 사용할 수 있습니다. `submit()`은 양식의 인코딩 유형이 "multipart/form-data"
 로 설정된 경우 폼의 메서드가 데이터를 전송하기 위해 사용하는 것과 동일한
 형식입니다.


프로그레시브 웹 앱

 프로그레시브 웹 앱(PWA)은 최신 웹 API와 전통적인 점진적 개선 전략을 함께
 사용해 크로스 플랫폼 웹 애플리케이션을 생성합니다. 이러한 앱은 어디서든
 동작하며 네이티브 앱에서의 사용자 경험 이점과 동일한 몇 가지 기능들을
 제공합니다. 이 문서에서는 이에 대해 여러분이 알아야 할 모든 것들에 대해
 알려드립니다.


XML 파싱 및 직렬화

 웹 플랫폼은 각각 장단점이 있는 다양한 XML 구문을 분석하고 직렬화하는
 방법을 제공합니다.


Performance
-----------

최적화 및 성능 (en-US)

최신 웹 앱과 사이트를 구축할 때, 콘텐츠를 빠르고 효율적으로 만드는 것이 중요합니다. 이를 통해 강력한 데스크탑 시스템과 약한 핸드헬드 장치 모두에서 효과적으로 수행할 수 있습니다.

Mobile web development
----------------------

모바일 웹 개발 (en-US)

이 문서에서는 모바일 장치에서 잘 작동하는 웹 사이트를 디자인하는 데 필요한 몇 가지 주요 기술에 대한 개요를 제공합니다.

Fonts
-----

Variable fonts guide (en-US)

variable fonts를 설계 시 사용하는 방법을 알아보십시오.

웹 오픈 글꼴 형식(WOFF) (en-US)

WOFF(Web Open Font Format)는 웹에서 누구나 무료로 사용할 수 있는 글꼴 파일 형식입니다.

User interface development
--------------------------

User input and controls (en-US)

모던 웹에서 사용자 입력은 단순히 마우스와 키보드로만 이뤄지는 것이 아닙니다. 예를 들어 터치스크린을 생각해 보십시오. 이 문서에서는 기본 기술에 대한 자세한 정보가 필요한 사람을 위해 FAQ, 실제 사례 및 추가 정보 링크와 함께 개방형 웹 앱에서 사용자 입력을 관리하고 제어를 구현하기 위한 권장 사항을 제공합니다.