소개
지난 봄 (2010년) HTML5 및 관련 기술에 대한 지원이 급증하는 데 관심을 갖게 되었습니다. 당시 친구와 저는 프로그래밍 및 개발 기술을 연마하고 서로 주고받은 게임 아이디어를 구현하기 위해 2주간의 게임 개발 대회에서 서로 경쟁하고 있었습니다. 그래서 자연스럽게 HTML5 요소를 경쟁 출품작에 통합하여 작동 방식을 더 잘 이해하고 이전 HTML 사양을 사용하여 거의 불가능했던 작업을 할 수 있도록 했습니다.
HTML5의 여러 새로운 기능 중 캔버스 태그에 대한 지원이 늘어나면서 JavaScript를 사용하여 양방향 아트를 구현할 수 있는 흥미로운 기회가 생겼습니다. 이를 통해 현재 Entanglement라고 하는 퍼즐 게임을 구현해 보았습니다. Catan Settlers 타일의 뒷면을 사용하여 프로토타입을 이미 만들었으므로 이를 청사진으로 삼아 웹 플레이를 위해 HTML5 캔버스에 육각형 타일을 만드는 데 필요한 세 가지 필수 요소가 있습니다. 육각형 그리기, 경로 그리기, 타일 회전입니다. 다음은 이러한 각 기능을 현재 형식으로 구현한 방법을 자세히 설명합니다.
육각형 그리기
Entanglement의 원래 버전에서는 여러 캔버스 그리기 메서드를 사용하여 육각형을 그렸지만 현재 게임 형식에서는 drawImage()
를 사용하여 스프라이트 시트에서 클립된 텍스처를 그립니다.

이미지를 하나의 파일로 결합하여 이 경우 10개가 아닌 하나의 서버 요청만 실행됩니다. 선택한 육각형을 캔버스에 그리려면 먼저 캔버스, 컨텍스트, 이미지라는 도구를 모아야 합니다.
캔버스를 만들려면 HTML 문서에 다음과 같이 캔버스 태그만 있으면 됩니다.
<canvas id="myCanvas"></canvas>
스크립트로 가져올 수 있도록 ID를 지정합니다.
var cvs = document.getElementById('myCanvas');
두 번째로, 그리기를 시작할 수 있도록 캔버스의 2D 컨텍스트를 가져와야 합니다.
var ctx = cvs.getContext('2d');
마지막으로 이미지가 필요합니다. 웹페이지와 동일한 폴더에 있는 'tiles.png'라는 이름의 이미지인 경우 다음과 같이 가져올 수 있습니다.
var img = new Image();
img.src = 'tiles.png';
이제 세 가지 구성요소가 있으므로 ctx.drawImage()를 사용하여 스프라이트 시트에서 원하는 단일 육각형을 캔버스에 그릴 수 있습니다.
ctx.drawImage(img, sourceX, sourceY, sourceWidth, sourceHeight,
destinationX, destinationY, destinationWidth, destinationHeight);
이 경우 맨 윗줄에서 왼쪽에서 네 번째인 육각형을 사용합니다. 또한 원본과 동일한 크기를 유지하면서 왼쪽 상단의 캔버스에 그립니다. 육각형의 너비가 400픽셀이고 높이가 346픽셀이라고 가정하면 전체적으로 다음과 같이 표시됩니다.
var cvs = document.getElementById('myCanvas');
var ctx = cvs.getContext('2d');
var img = new Image();
img.src = 'tiles.png';
var sourceX = 1200;
var sourceY = 0;
var sourceWidth = 400;
var sourceHeight = 346;
var destinationX = 0;
var destinationY = 0;
var destinationWidth = 400;
var destinationHeight = 346;
ctx.drawImage(img, sourceX, sourceY, sourceWidth, sourceHeight,
destinationX, destinationY, destinationWidth, destinationHeight);
이미지의 일부를 캔버스에 복사한 결과 다음과 같이 표시됩니다.

그리기 경로
이제 캔버스에 육각형을 그렸으므로 그 위에 몇 개의 선을 그려 보겠습니다. 먼저 육각형 타일과 관련된 몇 가지 도형을 살펴보겠습니다. 한쪽에 두 개의 선 끝이 있어야 하며, 각 선 끝은 각 가장자리의 끝에서 1/4 지점과 가장자리의 1/2 지점에서 서로 떨어져 있어야 합니다.

또한 멋진 곡선을 원하므로 약간의 시행착오를 거쳐 각 엔드포인트의 가장자리에서 직각선을 그리면 육각형의 지정된 각도 주위에서 각 엔드포인트 쌍의 교차점이 지정된 엔드포인트의 멋진 베이즈 커널 제어점을 만든다는 것을 발견했습니다.

이제 엔드포인트와 제어점을 모두 캔버스 이미지에 해당하는 데카르트 평면에 매핑했으므로 코드로 돌아갈 준비가 되었습니다. 간단하게 하기 위해 한 줄로 시작합니다. 먼저 왼쪽 상단 엔드포인트에서 오른쪽 하단 엔드포인트로 경로를 그립니다. 이전의 육각형 이미지가 400x346이므로 상단 엔드포인트는 가로 150픽셀, 아래 0픽셀이 됩니다. 즉, (150, 0)입니다. 제어 지점은 (150, 86)입니다. 하단 가장자리 엔드포인트는 (250, 346)이고 제어 포인트는 (250, 260)입니다.

이제 좌표가 있으므로 그리기를 시작할 준비가 되었습니다. ctx.beginPath()로 새로 시작하고 다음을 사용하여 첫 번째 엔드포인트로 이동합니다.
ctx.moveTo(pointX1,pointY1);
그런 다음 다음과 같이 ctx.bezierCurveTo()를 사용하여 선 자체를 그릴 수 있습니다.
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
선에 멋진 테두리를 만들려면 매번 다른 너비와 색상을 사용하여 이 경로를 두 번 획 처리합니다. 색상은 ctx.strokeStyle 속성을 사용하여 설정되고 너비는 ctx.lineWidth를 사용하여 설정됩니다. 첫 번째 선을 그리면 다음과 같이 표시됩니다.
var pointX1 = 150;
var pointY1 = 0;
var controlX1 = 150;
var controlY1 = 86;
var controlX2 = 250;
var controlY2 = 260;
var pointX2 = 250;
var pointY2 = 346;
ctx.beginPath();
ctx.moveTo(pointX1, pointY1);
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.lineWidth = 10;
ctx.strokeStyle = '#786c44';
ctx.stroke();
이제 첫 번째 선이 구불구불하게 지나가는 육각형 타일이 생겼습니다.

다른 10개의 엔드포인트와 해당하는 베지어 곡선 제어점의 좌표를 입력하면 위 단계를 반복하여 다음과 같은 카드를 만들 수 있습니다.

캔버스 회전
타일이 있으면 게임에서 다양한 경로를 사용할 수 있도록 타일을 회전할 수 있어야 합니다. 캔버스를 사용하여 이를 수행하려면 ctx.translate()
및 ctx.rotate()
를 사용합니다. 타일이 중심을 중심으로 회전하도록 하려면 먼저 캔버스 참조점을 육각형 타일의 중앙으로 이동해야 합니다.
이를 위해 다음을 사용합니다.
ctx.translate(originX, originY);
여기서 originX는 육각형 타일 너비의 절반이고 originY는 높이의 절반이므로 다음과 같이 됩니다.
var originX = 200;
var originY = 173;
ctx.translate(originX, originY);
이제 새 중심점을 사용하여 카드를 회전할 수 있습니다. 육각형은 6개의 면이 있으므로 Math.PI의 배수를 3으로 나눈 값으로 회전해야 합니다. 간단하게 다음을 사용하여 시계 방향으로 한 번만 회전해 보겠습니다.
ctx.rotate(Math.PI / 3);
하지만 육각형과 선이 이전 (0, 0) 좌표를 원점으로 사용하고 있으므로 회전을 완료한 후에는 그리기 전에 다시 변환해야 합니다. 이제 총 3개의 값이 있습니다.
var originX = 200;
var originY = 173;
ctx.translate(originX, originY);
ctx.rotate(Math.PI / 3);
ctx.translate(-originX, -originY);
렌더링 코드 앞에 위의 변환 및 회전을 배치하면 이제 회전된 타일을 렌더링합니다.

요약
위에서 이미지 렌더링, 베지어 곡선 그리기, 캔버스 회전 등 캔버스 태그를 사용하여 HTML5에서 제공하는 몇 가지 기능을 강조 표시했습니다. Entanglement에 HTML5 캔버스 태그와 JavaScript 그리기 도구를 사용해 보니 즐거운 경험이었습니다. 이 개방적이고 신흥 기술로 다른 사람들이 만드는 다양한 새로운 애플리케이션과 게임을 기대합니다.
코드 참조
위에 제공된 모든 코드 예시는 아래에 참조로 통합되어 있습니다.
var cvs = document.getElementById('myCanvas');
var ctx = cvs.getContext('2d');
var img = new Image();
img.src = 'tiles.png';
var originX = 200;
var originY = 173;
ctx.translate(originX, originY);
ctx.rotate(Math.PI / 3);
ctx.translate(-originX, -originY);
var sourceX = 1200;
var sourceY = 0;
var sourceWidth = 400;
var sourceHeight = 346;
var destinationX = 0;
var destinationY = 0;
var destinationWidth = 400;
var destinationHeight = 346;
ctx.drawImage(img, sourceX, sourceY, sourceWidth, sourceHeight,
destinationX, destinationY, destinationWidth, destinationHeight);
ctx.beginPath();
var pointX1 = 150;
var pointY1 = 0;
var controlX1 = 150;
var controlY1 = 86;
var controlX2 = 250;
var controlY2 = 260;
var pointX2 = 250;
var pointY2 = 346;
ctx.moveTo(pointX1, pointY1);
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.lineWidth = 10;
ctx.strokeStyle = '#786c44';
ctx.stroke();
ctx.beginPath();
pointX1 = 250;
pointY1 = 0;
controlX1 = 250;
controlY1 = 86;
controlX2 = 150;
controlY2 = 86;
pointX2 = 75;
pointY2 = 43;
ctx.moveTo(pointX1, pointY1);
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.lineWidth = 10;
ctx.strokeStyle = '#786c44';
ctx.stroke();
ctx.beginPath();
pointX1 = 150;
pointY1 = 346;
controlX1 = 150;
controlY1 = 260;
controlX2 = 300;
controlY2 = 173;
pointX2 = 375;
pointY2 = 213;
ctx.moveTo(pointX1, pointY1);
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.lineWidth = 10;
ctx.strokeStyle = '#786c44';
ctx.stroke();
ctx.beginPath();
pointX1 = 325;
pointY1 = 43;
controlX1 = 250;
controlY1 = 86;
controlX2 = 300;
controlY2 = 173;
pointX2 = 375;
pointY2 = 130;
ctx.moveTo(pointX1, pointY1);
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.lineWidth = 10;
ctx.strokeStyle = '#786c44';
ctx.stroke();
ctx.beginPath();
pointX1 = 25;
pointY1 = 130;
controlX1 = 100;
controlY1 = 173;
controlX2 = 100;
controlY2 = 173;
pointX2 = 25;
pointY2 = 213;
ctx.moveTo(pointX1, pointY1);
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.lineWidth = 10;
ctx.strokeStyle = '#786c44';
ctx.stroke();
ctx.beginPath();
pointX1 = 325;
pointY1 = 303;
controlX1 = 250;
controlY1 = 260;
controlX2 = 150;
controlY2 = 260;
pointX2 = 75;
pointY2 = 303;
ctx.moveTo(pointX1, pointY1);
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.lineWidth = 10;
ctx.strokeStyle = '#786c44';
ctx.stroke();