
HTML 게임 Canvas
HTML Canvas를 사용하여 게임을 만드는 방법을 알아본다. 이 튜토리얼에서는 Canvas 기반 게임 개발의 기본 개념과 구조를 설명한다.
게임 영역 만들기
먼저 게임이 진행될 기본 캔버스 영역을 만들어보자.
<canvas id="myCanvas" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<script>
var myGamePiece;
var myGameArea = {
canvas: document.getElementById("myCanvas"),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[0]);
this.interval = setInterval(updateGameArea, 20);
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function startGame() {
myGameArea.start();
}
function updateGameArea() {
myGameArea.clear();
}
startGame();
</script>
이 코드는 게임의 기본 구조를 만든다. myGameArea 객체는 캔버스를 관리하고, updateGameArea() 함수는 게임 루프를 담당한다.
게임 컴포넌트 추가
게임에 첫 번째 오브젝트(빨간 사각형)를 추가해보자.
<canvas id="myCanvas2" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<script>
var myGamePiece;
var myGameArea = {
canvas: document.getElementById("myCanvas2"),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
this.interval = setInterval(updateGameArea, 20);
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function component(width, height, color, x, y) {
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.update = function() {
ctx = myGameArea.context;
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
function startGame() {
myGamePiece = new component(30, 30, "red", 10, 120);
myGameArea.start();
}
function updateGameArea() {
myGameArea.clear();
myGamePiece.update();
}
startGame();
</script>
component 생성자 함수는 게임 오브젝트를 만든다. 각 컴포넌트는 크기, 색상, 위치 정보를 가지고 있으며, update() 메서드로 자신을 그린다.
오브젝트 움직이기
게임 오브젝트에 움직임을 추가해보자.
<canvas id="myCanvas3" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<script>
var myGamePiece;
var myGameArea = {
canvas: document.getElementById("myCanvas3"),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
this.interval = setInterval(updateGameArea, 20);
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function component(width, height, color, x, y) {
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.update = function() {
ctx = myGameArea.context;
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
this.newPos = function() {
this.x += this.speedX;
this.y += this.speedY;
}
}
function startGame() {
myGamePiece = new component(30, 30, "red", 10, 120);
myGameArea.start();
}
function updateGameArea() {
myGameArea.clear();
myGamePiece.newPos();
myGamePiece.update();
}
startGame();
</script>
newPos() 메서드는 오브젝트의 위치를 속도에 따라 업데이트한다. 현재는 속도가 0이므로 움직이지 않는다.
속도 설정하여 움직이기
오브젝트에 실제 속도를 설정하여 움직이게 만들어보자.
<canvas id="myCanvas4" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<script>
var myGamePiece;
var myGameArea = {
canvas: document.getElementById("myCanvas4"),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
this.interval = setInterval(updateGameArea, 20);
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function component(width, height, color, x, y) {
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.update = function() {
ctx = myGameArea.context;
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
this.newPos = function() {
this.x += this.speedX;
this.y += this.speedY;
}
}
function startGame() {
myGamePiece = new component(30, 30, "red", 10, 120);
myGamePiece.speedX = 1;
myGamePiece.speedY = -1;
myGameArea.start();
}
function updateGameArea() {
myGameArea.clear();
myGamePiece.newPos();
myGamePiece.update();
}
startGame();
</script>
이제 빨간 사각형이 오른쪽 위 방향으로 움직인다. speedX는 수평 속도, speedY는 수직 속도를 나타낸다.
여러 오브젝트 추가
게임에 여러 개의 오브젝트를 추가해보자.
<canvas id="myCanvas5" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<script>
var myGamePiece;
var myObstacles = [];
var myGameArea = {
canvas: document.getElementById("myCanvas5"),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
this.interval = setInterval(updateGameArea, 20);
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function component(width, height, color, x, y) {
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.update = function() {
ctx = myGameArea.context;
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
this.newPos = function() {
this.x += this.speedX;
this.y += this.speedY;
}
}
function startGame() {
myGamePiece = new component(30, 30, "red", 10, 120);
myObstacles.push(new component(10, 200, "green", 300, 120));
myObstacles.push(new component(10, 200, "green", 350, 120));
myObstacles.push(new component(10, 200, "green", 400, 120));
myGameArea.start();
}
function updateGameArea() {
var x, height, gap, minHeight, maxHeight, minGap, maxGap;
for (i = 0; i < myObstacles.length; i += 1) {
myObstacles[i].speedX = -1;
myObstacles[i].newPos();
myObstacles[i].update();
}
myGameArea.clear();
myGamePiece.newPos();
myGamePiece.update();
for (i = 0; i < myObstacles.length; i += 1) {
myObstacles[i].update();
}
}
startGame();
</script>[/code]
이 예제에서는 녹색 장애물들을 추가했다. 장애물들은 왼쪽으로 움직여서 스크롤링 효과를 만든다.
키보드 컨트롤 추가
키보드 입력을 통해 게임 오브젝트를 제어해보자.
[code lang="markup"]<canvas id="myCanvas6" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<script>
var myGamePiece;
var myGameArea = {
canvas: document.getElementById("myCanvas6"),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
this.interval = setInterval(updateGameArea, 20);
window.addEventListener('keydown', function (e) {
myGameArea.key = e.keyCode;
})
window.addEventListener('keyup', function (e) {
myGameArea.key = false;
})
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function component(width, height, color, x, y) {
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.update = function() {
ctx = myGameArea.context;
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
this.newPos = function() {
this.x += this.speedX;
this.y += this.speedY;
}
}
function startGame() {
myGamePiece = new component(30, 30, "red", 10, 120);
myGameArea.start();
}
function updateGameArea() {
myGameArea.clear();
myGamePiece.speedX = 0;
myGamePiece.speedY = 0;
if (myGameArea.key && myGameArea.key == 37) {myGamePiece.speedX = -1; }
if (myGameArea.key && myGameArea.key == 39) {myGamePiece.speedX = 1; }
if (myGameArea.key && myGameArea.key == 38) {myGamePiece.speedY = -1; }
if (myGameArea.key && myGameArea.key == 40) {myGamePiece.speedY = 1; }
myGamePiece.newPos();
myGamePiece.update();
}
startGame();
</script>
이제 화살표 키를 사용하여 빨간 사각형을 움직일 수 있다.
- 37: 왼쪽 화살표
- 38: 위쪽 화살표
- 39: 오른쪽 화살표
- 40: 아래쪽 화살표
충돌 감지 추가
게임 오브젝트 간의 충돌을 감지하는 기능을 추가해보자.
<canvas id="myCanvas7" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<script>
var myGamePiece;
var myObstacle;
var myGameArea = {
canvas: document.getElementById("myCanvas7"),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
this.interval = setInterval(updateGameArea, 20);
window.addEventListener('keydown', function (e) {
myGameArea.key = e.keyCode;
})
window.addEventListener('keyup', function (e) {
myGameArea.key = false;
})
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
},
stop: function() {
clearInterval(this.interval);
}
}
function component(width, height, color, x, y) {
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.update = function() {
ctx = myGameArea.context;
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
this.newPos = function() {
this.x += this.speedX;
this.y += this.speedY;
}
this.crashWith = function(otherobj) {
var myleft = this.x;
var myright = this.x + (this.width);
var mytop = this.y;
var mybottom = this.y + (this.height);
var otherleft = otherobj.x;
var otherright = otherobj.x + (otherobj.width);
var othertop = otherobj.y;
var otherbottom = otherobj.y + (otherobj.height);
var crash = true;
if ((mybottom < othertop) || (mytop > otherbottom) || (myright < otherleft) || (myleft > otherright)) {
crash = false;
}
return crash;
}
}
function startGame() {
myGamePiece = new component(30, 30, "red", 10, 120);
myObstacle = new component(10, 200, "green", 300, 120);
myGameArea.start();
}
function updateGameArea() {
if (myGamePiece.crashWith(myObstacle)) {
myGameArea.stop();
} else {
myGameArea.clear();
myObstacle.speedX = -1;
myObstacle.newPos();
myObstacle.update();
myGamePiece.speedX = 0;
myGamePiece.speedY = 0;
if (myGameArea.key && myGameArea.key == 37) {myGamePiece.speedX = -1; }
if (myGameArea.key && myGameArea.key == 39) {myGamePiece.speedX = 1; }
if (myGameArea.key && myGameArea.key == 38) {myGamePiece.speedY = -1; }
if (myGameArea.key && myGameArea.key == 40) {myGamePiece.speedY = 1; }
myGamePiece.newPos();
myGamePiece.update();
}
}
startGame();
</script>
crashWith() 메서드는 두 오브젝트가 겹치는지 확인한다. 충돌이 감지되면 게임이 중지된다.
점수 시스템 추가
게임에 점수 표시 기능을 추가해보자.
<canvas id="myCanvas8" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<script>
var myGamePiece;
var myObstacle;
var myScore;
var myGameArea = {
canvas: document.getElementById("myCanvas8"),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
this.frameNo = 0;
this.interval = setInterval(updateGameArea, 20);
window.addEventListener('keydown', function (e) {
myGameArea.key = e.keyCode;
})
window.addEventListener('keyup', function (e) {
myGameArea.key = false;
})
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
},
stop: function() {
clearInterval(this.interval);
}
}
function component(width, height, color, x, y, type) {
this.type = type;
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.update = function() {
ctx = myGameArea.context;
if (this.type == "text") {
ctx.font = this.width + " " + this.height;
ctx.fillStyle = color;
ctx.fillText(this.text, this.x, this.y);
} else {
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
this.newPos = function() {
this.x += this.speedX;
this.y += this.speedY;
}
this.crashWith = function(otherobj) {
var myleft = this.x;
var myright = this.x + (this.width);
var mytop = this.y;
var mybottom = this.y + (this.height);
var otherleft = otherobj.x;
var otherright = otherobj.x + (otherobj.width);
var othertop = otherobj.y;
var otherbottom = otherobj.y + (otherobj.height);
var crash = true;
if ((mybottom < othertop) || (mytop > otherbottom) || (myright < otherleft) || (myleft > otherright)) {
crash = false;
}
return crash;
}
}
function startGame() {
myGamePiece = new component(30, 30, "red", 10, 120);
myObstacle = new component(10, 200, "green", 300, 120);
myScore = new component("30px", "Consolas", "black", 280, 40, "text");
myGameArea.start();
}
function updateGameArea() {
if (myGamePiece.crashWith(myObstacle)) {
myGameArea.stop();
} else {
myGameArea.clear();
myGameArea.frameNo += 1;
myObstacle.speedX = -1;
myObstacle.newPos();
myObstacle.update();
myGamePiece.speedX = 0;
myGamePiece.speedY = 0;
if (myGameArea.key && myGameArea.key == 37) {myGamePiece.speedX = -1; }
if (myGameArea.key && myGameArea.key == 39) {myGamePiece.speedX = 1; }
if (myGameArea.key && myGameArea.key == 38) {myGamePiece.speedY = -1; }
if (myGameArea.key && myGameArea.key == 40) {myGamePiece.speedY = 1; }
myGamePiece.newPos();
myGamePiece.update();
myScore.text = "점수: " + myGameArea.frameNo;
myScore.update();
}
}
startGame();
</script>
이제 게임에 점수가 표시된다. frameNo를 사용하여 시간에 따라 점수가 증가한다.
💡 Canvas 게임 개발 팁:
• 게임 루프는 일정한 간격으로 실행되어야 한다. setInterval()을 사용하여 프레임 레이트를 제어하자
• 모든 게임 오브젝트는 update()와 newPos() 메서드를 가져야 한다
• 충돌 감지는 사각형의 경계를 비교하여 구현할 수 있다
• 성능을 위해 불필요한 계산을 피하고 효율적인 렌더링을 고려해야 한다
게임 재시작 기능
게임이 끝났을 때 재시작할 수 있는 기능을 추가해보자.
<canvas id="myCanvas9" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<button onclick="restartGame()">게임 재시작</button>
<script>
var myGamePiece;
var myObstacle;
var myScore;
var gameRunning = false;
var myGameArea = {
canvas: document.getElementById("myCanvas9"),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
this.frameNo = 0;
this.interval = setInterval(updateGameArea, 20);
window.addEventListener('keydown', function (e) {
myGameArea.key = e.keyCode;
})
window.addEventListener('keyup', function (e) {
myGameArea.key = false;
})
gameRunning = true;
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
},
stop: function() {
clearInterval(this.interval);
gameRunning = false;
}
}
function component(width, height, color, x, y, type) {
this.type = type;
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.update = function() {
ctx = myGameArea.context;
if (this.type == "text") {
ctx.font = this.width + " " + this.height;
ctx.fillStyle = color;
ctx.fillText(this.text, this.x, this.y);
} else {
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
this.newPos = function() {
this.x += this.speedX;
this.y += this.speedY;
}
this.crashWith = function(otherobj) {
var myleft = this.x;
var myright = this.x + (this.width);
var mytop = this.y;
var mybottom = this.y + (this.height);
var otherleft = otherobj.x;
var otherright = otherobj.x + (otherobj.width);
var othertop = otherobj.y;
var otherbottom = otherobj.y + (otherobj.height);
var crash = true;
if ((mybottom < othertop) || (mytop > otherbottom) || (myright < otherleft) || (myleft > otherright)) {
crash = false;
}
return crash;
}
}
function startGame() {
myGamePiece = new component(30, 30, "red", 10, 120);
myObstacle = new component(10, 200, "green", 300, 120);
myScore = new component("30px", "Consolas", "black", 280, 40, "text");
myGameArea.start();
}
function updateGameArea() {
if (myGamePiece.crashWith(myObstacle)) {
myGameArea.stop();
} else {
myGameArea.clear();
myGameArea.frameNo += 1;
myObstacle.speedX = -1;
myObstacle.newPos();
myObstacle.update();
myGamePiece.speedX = 0;
myGamePiece.speedY = 0;
if (myGameArea.key && myGameArea.key == 37) {myGamePiece.speedX = -1; }
if (myGameArea.key && myGameArea.key == 39) {myGamePiece.speedX = 1; }
if (myGameArea.key && myGameArea.key == 38) {myGamePiece.speedY = -1; }
if (myGameArea.key && myGameArea.key == 40) {myGamePiece.speedY = 1; }
myGamePiece.newPos();
myGamePiece.update();
myScore.text = "점수: " + myGameArea.frameNo;
myScore.update();
}
}
function restartGame() {
if (!gameRunning) {
startGame();
}
}
startGame();
</script>
이제 게임이 끝난 후 "게임 재시작" 버튼을 클릭하여 새로운 게임을 시작할 수 있다.
HTML5 Canvas를 사용한 게임 개발은 웹에서 인터랙티브한 콘텐츠를 만드는 강력한 방법이다. 이 기본 구조를 바탕으로 더 복잡한 게임 메커니즘, 그래픽 효과, 사운드 등을 추가하여 완전한 게임을 만들 수 있다.