여러분이 사용하고 계신 브라우저는 HTML5를 지원하지 않기 때문에 몇몇 요소가 제대로 보이도록 JScript를 사용하고 있습니다. 하지만 여러분의 브라우저 설정에서 스크립트 기능이 꺼져있으므로, 현재 페이지를 제대로 확인하시려면 스크립트 기능을 켜주셔야 합니다. HTML 게임 - 컨트롤러

HTML 게임 – 컨트롤러

1주전 작성

HTML Canvas 게임 컨트롤러

HTML Canvas 게임에서 사용자 입력을 처리하는 방법을 알아본다. 키보드, 마우스, 터치 등 다양한 입력 방식으로 게임을 제어하는 기법을 설명한다.

키보드 컨트롤 기본

키보드 입력을 사용하여 게임 오브젝트를 제어해보자.

<canvas id="myCanvas1" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<script>
var myGamePiece;
var myGameArea = {
canvas: document.getElementById("myCanvas1"),
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>

keydown과 keyup 이벤트 리스너를 사용하여 키보드 입력을 감지한다. 화살표 키(37, 38, 39, 40)를 사용하여 오브젝트를 움직인다.

다중 키 입력 처리

여러 키를 동시에 누를 수 있도록 개선해보자.

<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.keys = [];
this.interval = setInterval(updateGameArea, 20);
window.addEventListener('keydown', function (e) {
myGameArea.keys[e.keyCode] = true;
})
window.addEventListener('keyup', function (e) {
myGameArea.keys[e.keyCode] = 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.keys && myGameArea.keys[37]) {myGamePiece.speedX = -2; }
if (myGameArea.keys && myGameArea.keys[39]) {myGamePiece.speedX = 2; }
if (myGameArea.keys && myGameArea.keys[38]) {myGamePiece.speedY = -2; }
if (myGameArea.keys && myGameArea.keys[40]) {myGamePiece.speedY = 2; }
myGamePiece.newPos();
myGamePiece.update();
}
startGame();
</script>

keys 배열을 사용하여 여러 키의 상태를 동시에 추적한다. 이제 대각선 이동이 가능하다.

WASD 키 컨트롤

화살표 키 외에 WASD 키로도 조작할 수 있도록 추가해보자.

<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.keys = [];
this.interval = setInterval(updateGameArea, 20);
window.addEventListener('keydown', function (e) {
myGameArea.keys[e.keyCode] = true;
})
window.addEventListener('keyup', function (e) {
myGameArea.keys[e.keyCode] = 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;
if (this.x < 0) this.x = 0; if (this.x > myGameArea.canvas.width - this.width) this.x = myGameArea.canvas.width - this.width;
if (this.y < 0) this.y = 0; if (this.y > myGameArea.canvas.height - this.height) this.y = myGameArea.canvas.height - this.height;
}
}
function startGame() {
myGamePiece = new component(30, 30, "red", 225, 135);
myGameArea.start();
}
function updateGameArea() {
myGameArea.clear();
myGamePiece.speedX = 0;
myGamePiece.speedY = 0;
if (myGameArea.keys && (myGameArea.keys[37] || myGameArea.keys[65])) {myGamePiece.speedX = -3; }
if (myGameArea.keys && (myGameArea.keys[39] || myGameArea.keys[68])) {myGamePiece.speedX = 3; }
if (myGameArea.keys && (myGameArea.keys[38] || myGameArea.keys[87])) {myGamePiece.speedY = -3; }
if (myGameArea.keys && (myGameArea.keys[40] || myGameArea.keys[83])) {myGamePiece.speedY = 3; }
myGamePiece.newPos();
myGamePiece.update();
}
startGame();
</script>

WASD 키(W:87, A:65, S:83, D:68)를 화살표 키와 함께 사용할 수 있도록 했다. 또한 경계 검사를 추가하여 오브젝트가 캔버스를 벗어나지 않도록 했다.

마우스 컨트롤

마우스 클릭으로 오브젝트를 이동시켜보자.

<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);
this.canvas.addEventListener('mousedown', function (e) {
myGameArea.x = e.offsetX;
myGameArea.y = e.offsetY;
})
this.canvas.addEventListener('mouseup', function (e) {
myGameArea.x = false;
myGameArea.y = 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();
if (myGameArea.x && myGameArea.y) {
myGamePiece.x = myGameArea.x - myGamePiece.width / 2;
myGamePiece.y = myGameArea.y - myGamePiece.height / 2;
}
myGamePiece.update();
}
startGame();
</script>

mousedown 이벤트로 마우스 클릭 위치를 감지하고, offsetX와 offsetY를 사용하여 캔버스 내의 상대 좌표를 얻는다. 클릭한 위치로 오브젝트가 즉시 이동한다.

마우스 드래그

마우스를 드래그하여 오브젝트를 부드럽게 이동시켜보자.

<canvas id="myCanvas5" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<script>
var myGamePiece;
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);
this.canvas.addEventListener('mousedown', function (e) {
myGameArea.x = e.offsetX;
myGameArea.y = e.offsetY;
})
this.canvas.addEventListener('mouseup', function (e) {
myGameArea.x = false;
myGameArea.y = false;
})
this.canvas.addEventListener('mousemove', function (e) {
myGameArea.x = e.offsetX;
myGameArea.y = e.offsetY;
})
},
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();
if (myGameArea.x && myGameArea.y) {
myGamePiece.x = myGameArea.x - myGamePiece.width / 2;
myGamePiece.y = myGameArea.y - myGamePiece.height / 2;
}
myGamePiece.update();
}
startGame();
</script>

mousemove 이벤트를 추가하여 마우스를 드래그할 때도 오브젝트가 따라오도록 했다.

터치 컨트롤

모바일 기기의 터치 입력을 지원해보자.

<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);
this.canvas.addEventListener('mousedown', function (e) {
myGameArea.x = e.offsetX;
myGameArea.y = e.offsetY;
})
this.canvas.addEventListener('mouseup', function (e) {
myGameArea.x = false;
myGameArea.y = false;
})
this.canvas.addEventListener('mousemove', function (e) {
myGameArea.x = e.offsetX;
myGameArea.y = e.offsetY;
})
this.canvas.addEventListener('touchstart', function (e) {
myGameArea.x = e.touches[0].clientX - e.target.offsetLeft;
myGameArea.y = e.touches[0].clientY - e.target.offsetTop;
})
this.canvas.addEventListener('touchend', function (e) {
myGameArea.x = false;
myGameArea.y = false;
})
this.canvas.addEventListener('touchmove', function (e) {
myGameArea.x = e.touches[0].clientX - e.target.offsetLeft;
myGameArea.y = e.touches[0].clientY - e.target.offsetTop;
})
},
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();
if (myGameArea.x && myGameArea.y) {
myGamePiece.x = myGameArea.x - myGamePiece.width / 2;
myGamePiece.y = myGameArea.y - myGamePiece.height / 2;
}
myGamePiece.update();
}
startGame();
</script>

touchstart, touchend, touchmove 이벤트를 추가하여 터치 입력을 지원한다. clientX, clientY와 offsetLeft, offsetTop을 사용하여 정확한 터치 좌표를 계산한다.

가상 조이스틱

모바일 게임에서 자주 사용되는 가상 조이스틱을 구현해보자.

<canvas id="myCanvas7" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<script>
var myGamePiece;
var joystick = {
centerX: 60,
centerY: 210,
radius: 40,
knobX: 60,
knobY: 210,
knobRadius: 15,
active: false
};
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);
this.canvas.addEventListener('mousedown', function (e) {
var x = e.offsetX;
var y = e.offsetY;
var distance = Math.sqrt((x - joystick.centerX) * (x - joystick.centerX) + (y - joystick.centerY) * (y - joystick.centerY));
if (distance <= joystick.radius) { joystick.active = true; joystick.knobX = x; joystick.knobY = y; } }) this.canvas.addEventListener('mouseup', function (e) { joystick.active = false; joystick.knobX = joystick.centerX; joystick.knobY = joystick.centerY; }) this.canvas.addEventListener('mousemove', function (e) { if (joystick.active) { var x = e.offsetX; var y = e.offsetY; var distance = Math.sqrt((x - joystick.centerX) * (x - joystick.centerX) + (y - joystick.centerY) * (y - joystick.centerY)); if (distance <= joystick.radius) { joystick.knobX = x; joystick.knobY = y; } else { var angle = Math.atan2(y - joystick.centerY, x - joystick.centerX); joystick.knobX = joystick.centerX + Math.cos(angle) * joystick.radius; joystick.knobY = joystick.centerY + Math.sin(angle) * joystick.radius; } } }) }, 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; if (this.x < 0) this.x = 0; if (this.x > myGameArea.canvas.width - this.width) this.x = myGameArea.canvas.width - this.width;
if (this.y < 0) this.y = 0; if (this.y > myGameArea.canvas.height - this.height) this.y = myGameArea.canvas.height - this.height;
}
}
function drawJoystick() {
ctx = myGameArea.context;
ctx.strokeStyle = "black";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(joystick.centerX, joystick.centerY, joystick.radius, 0, 2 * Math.PI);
ctx.stroke();
ctx.fillStyle = joystick.active ? "lightblue" : "lightgray";
ctx.beginPath();
ctx.arc(joystick.knobX, joystick.knobY, joystick.knobRadius, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
}
function startGame() {
myGamePiece = new component(30, 30, "red", 225, 135);
myGameArea.start();
}
function updateGameArea() {
myGameArea.clear();
var deltaX = joystick.knobX - joystick.centerX;
var deltaY = joystick.knobY - joystick.centerY;
myGamePiece.speedX = deltaX / 10;
myGamePiece.speedY = deltaY / 10;
myGamePiece.newPos();
myGamePiece.update();
drawJoystick();
}
startGame();
</script>

가상 조이스틱은 원형 영역과 조작 노브로 구성된다. 노브의 위치에 따라 이동 방향과 속도가 결정된다.

키보드와 마우스 조합

키보드와 마우스를 함께 사용하는 게임 컨트롤을 구현해보자.

<canvas id="myCanvas8" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<script>
var myGamePiece;
var bullets = [];
var myGameArea = {
canvas: document.getElementById("myCanvas8"),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
this.keys = [];
this.interval = setInterval(updateGameArea, 20);
window.addEventListener('keydown', function (e) {
myGameArea.keys[e.keyCode] = true;
})
window.addEventListener('keyup', function (e) {
myGameArea.keys[e.keyCode] = false;
})
this.canvas.addEventListener('click', function (e) {
var mouseX = e.offsetX;
var mouseY = e.offsetY;
var playerCenterX = myGamePiece.x + myGamePiece.width / 2;
var playerCenterY = myGamePiece.y + myGamePiece.height / 2;
var angle = Math.atan2(mouseY - playerCenterY, mouseX - playerCenterX);
var speed = 5;
bullets.push({
x: playerCenterX,
y: playerCenterY,
speedX: Math.cos(angle) * speed,
speedY: Math.sin(angle) * speed,
width: 5,
height: 5
});
})
},
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;
if (this.x < 0) this.x = 0; if (this.x > myGameArea.canvas.width - this.width) this.x = myGameArea.canvas.width - this.width;
if (this.y < 0) this.y = 0; if (this.y > myGameArea.canvas.height - this.height) this.y = myGameArea.canvas.height - this.height;
}
}
function startGame() {
myGamePiece = new component(30, 30, "red", 225, 135);
myGameArea.start();
}
function updateGameArea() {
myGameArea.clear();
myGamePiece.speedX = 0;
myGamePiece.speedY = 0;
if (myGameArea.keys && myGameArea.keys[37]) {myGamePiece.speedX = -3; }
if (myGameArea.keys && myGameArea.keys[39]) {myGamePiece.speedX = 3; }
if (myGameArea.keys && myGameArea.keys[38]) {myGamePiece.speedY = -3; }
if (myGameArea.keys && myGameArea.keys[40]) {myGamePiece.speedY = 3; }
myGamePiece.newPos();
myGamePiece.update();
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
bullet.x += bullet.speedX;
bullet.y += bullet.speedY;
if (bullet.x < 0 || bullet.x > myGameArea.canvas.width || bullet.y < 0 || bullet.y > myGameArea.canvas.height) {
bullets.splice(i, 1);
} else {
myGameArea.context.fillStyle = "yellow";
myGameArea.context.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
}
}
}
startGame();
</script>

키보드로 플레이어를 이동시키고, 마우스 클릭으로 클릭한 방향으로 총알을 발사한다. Math.atan2()를 사용하여 발사 각도를 계산한다.

💡 게임 컨트롤러 개발 팁:
• 키보드 입력은 배열을 사용하여 여러 키의 동시 입력을 처리하자
• 마우스와 터치 이벤트를 모두 지원하면 다양한 기기에서 사용할 수 있다
• 가상 조이스틱은 모바일 게임에서 직관적인 조작감을 제공한다
• 입력 처리 시 경계 검사와 유효성 검증을 잊지 말자

게임패드 API 지원

웹 게임패드 API를 사용하여 컨트롤러 입력을 처리해보자.

<canvas id="myCanvas9" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<script>
var myGamePiece;
var gamepadIndex = null;
var myGameArea = {
canvas: document.getElementById("myCanvas9"),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
this.interval = setInterval(updateGameArea, 20);
window.addEventListener("gamepadconnected", function(e) {
gamepadIndex = e.gamepad.index;
console.log("게임패드 연결됨:", e.gamepad.id);
});
window.addEventListener("gamepaddisconnected", function(e) {
gamepadIndex = null;
console.log("게임패드 연결 해제됨");
});
},
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;
if (this.x < 0) this.x = 0; if (this.x > myGameArea.canvas.width - this.width) this.x = myGameArea.canvas.width - this.width;
if (this.y < 0) this.y = 0; if (this.y > myGameArea.canvas.height - this.height) this.y = myGameArea.canvas.height - this.height;
}
}
function handleGamepad() {
if (gamepadIndex !== null) {
var gamepad = navigator.getGamepads()[gamepadIndex];
if (gamepad) {
var leftStickX = gamepad.axes[0];
var leftStickY = gamepad.axes[1];
if (Math.abs(leftStickX) > 0.1) {
myGamePiece.speedX = leftStickX * 3;
} else {
myGamePiece.speedX = 0;
}
if (Math.abs(leftStickY) > 0.1) {
myGamePiece.speedY = leftStickY * 3;
} else {
myGamePiece.speedY = 0;
}
}
}
}
function startGame() {
myGamePiece = new component(30, 30, "red", 225, 135);
myGameArea.start();
}
function updateGameArea() {
myGameArea.clear();
handleGamepad();
myGamePiece.newPos();
myGamePiece.update();
}
startGame();
</script>

웹 게임패드 API를 사용하면 Xbox, PlayStation 등의 컨트롤러를 웹 게임에서 사용할 수 있다. axes 배열은 아날로그 스틱 값을, buttons 배열은 버튼 상태를 제공한다.

다양한 입력 방식을 지원하는 게임 컨트롤러 시스템을 구축하면 더 많은 사용자가 편리하게 게임을 즐길 수 있다. 각 입력 방식의 특성을 이해하고 적절히 조합하여 최적의 게임 경험을 제공하는 것이 중요하다.

참고
Mingg`s Diary
밍구
공부 목적 블로그