1. Canvas JS 공부
1) Canvas 기본 개념 공부
- main.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./app.js"></script>
<!-- 문서가 다 읽혀지고 읽어지는 방법 : html, js에서 해결 가능(document.addEventListener 이용) -->
</head>
<body>
<!-- 주의사항 : css를 키우면 배율이 커진다. 그래서 속성을 키워야 한다. 즉, canvas를 키워야 한다.
보통의 경우 속성을 키우고 css로 줄이는 경우는 이미지가 깨지는것을 방지해준다. -->
<canvas class="game-canvas" width="500" height="400"></canvas>
</body>
</html>
- app.js
window.addEventListener("load", function(){
// 이것을 붙여줘야지 canvas 자동완성 한다.
/** @type {CanvasRenderingContext2D} */
var canvas = document.querySelector(".game-canvas");
// css에서 style을 부여할 때, 이름을 명시해줄때, id명을 주거나 태그를 줄 수 있다.
var ctx = canvas.getContext('2d'); // 처음에 왜 안 읽혀졌지?
// ctx.fillRect(10,10,50,50); // 사각형 색깔 채우기
// ctx.strokeStyle(20,10,150,200); // 사각형 틀 그리기
ctx.drawImage(img,100,100);
});
2) 이미지 표시하기
window.addEventListener("load", function(){
// 이것을 붙여줘야지 canvas의 메서드가 자동완성 한다.
/** @type {CanvasRenderingContext2D} */
var canvas = document.querySelector(".game-canvas");
// document.querySelector는 css에서 style을 부여할 때, 이름을 명시해줄때, id명을 주거나 태그를 줄 수 있다.
var ctx = canvas.getContext('2d'); // 처음에 왜 안 읽혀졌지?
//var img = document.createElement('image'); // 이런 방식도 가능하다!!
var img = new Image(); // image 객체 만들기
img.src = './image/boy.jpg'; // image를 해당 경로로 가져오기
img.addEventListener("load", function(){
ctx.drawImage(img,300,300); // 바로 실행하기!
});
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./app.js"></script>
</head>
<body>
<!-- 주의사항 : css를 키우면 배율이 커진다. 그래서 속성을 키워야 한다. 즉, canvas를 키워야 한다. -->
<canvas class="game-canvas" width="500" height="400"></canvas>
</body>
</html>
- 현재 이 상태는 일부분이 잘리므로 전체이미지를 일부분만 따서 가져와야한다. 따라서, image 자르는 API를 배우자!!
3) 이미지 크롭하여 이미지의 일부분만 표시하기
window.addEventListener("load", function(){
// 이것을 붙여줘야지 canvas의 메서드가 자동완성 한다.
/** @type {CanvasRenderingContext2D} */
var canvas = document.querySelector(".game-canvas");
// css에서 style을 부여할 때, 이름을 명시해줄때, id명을 주거나 태그를 줄 수 있다.
var ctx = canvas.getContext('2d'); // 처음에 왜 안 읽혀졌지?
//ctx.fillRect(10,10,50,50); // 사각형 색깔 채우기
//ctx.strokeStyle(20,10,150,200); // 사각형 틀 그리기
//var img = document.createElement('image'); // image 객체만드는 방식은 이런 방식도 가능하다!!
var img = new Image(); // image 객체 1개 만들면 메모리에 image 객체가 만들어진다.
img.src = './image/boy.jpg'; // image 속성을 해당 경로에 넣어주기
img.addEventListener("load", function(){ // 캔버스에 그릴려면, load가 다 끝난 다음에 그린다.
ctx.drawImage(img,200,200); // 그래서, 바로 그리는 것이 아니라 이 image가 load가 되는 것을 기다렸다가 끝나면 그린다.
ctx.drawImage(img,50,50,70,120); // load가 끝나면, 실행될 이벤트 핸들러 함수를 만들어 주자!
ctx.drawImage(img,100,100,200,200,0,0,100,100); // 소스 이미지로서 전체 이미지를 크롭할 때 사용
}); // (앞 4개 숫자는 원본 소스를 크롭하는 구간, 뒤 4개 숫자는 어디에 이미지를 출력할지 위치와 크기를 각각 표시)
// **그렇기 때문에 게임 시작할 때, 처음에 이미지를 모두 load시키고 시작한다.
});
4) 애니메이션 효과로 점을 이동시켜보기
5) game Test
-main.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
canvas { background: #eee;}
</style>
<title>Document</title>
<script src="./keyboardtest.js"></script>
</head>
<body>
<canvas class="myCanvas" width="480" height="320"></canvas>
</body>
</html>
-keyboardtest.js
window.addEventListener("load", function(){
/** @type {CanvasRenderingContext2D} */
var canvas = document.querySelector(".myCanvas"); // .이 굉장히 중요하다!!(이것 때문에 에러발생!!)
var ctx = canvas.getContext("2d");
var x = canvas.width/2; // 파란색 사각형이 움직이도록 해주는 값 기준과 조정
var y = canvas.height-30;
var dx = 2;
var dy = -2;
var paddleHeight = 20; // 이동하는 물체 크기
var paddleWidth = 20;
var paddleX = (canvas.width-paddleWidth)/2; // 이미지 보정하여 파란색 사각형이 키보드로 이동할 때, 사용
var paddleY = (canvas.height-paddleHeight)/2;
var rightPressed = false; // 키보드 입력 초기 값
var leftPressed = false;
var upPressed = false;
var downPressed = false;
var itemRowCount = 2; // 아이템의 배열 index
var itemColumnCount = 2;
var itemWidth = 15; // 아이템 크기
var itemHeight = 15;
var itemPadding = 250; // 아이템들 사이의 간격
var itemOffsetTop = 20; // 아이템들의 중간 좌표 조정
var itemOffsetLeft = 20; // 아이템들의 중간 좌표 조정
var items = []; // 아이템을 배열로 선언
var exitHeight = 15;
var exitWidth = 15;
var exit = {x:0, y:0, status: 1}; // Exit 문 status 설정과 가로 세로 길이의 초기 설정
for (let c = 0; c < itemColumnCount; c++) { // 아이템에 status 초기 설정(나중에 충돌 컨트롤을 위해서)
items[c] = [];
for (let r = 0; r < itemRowCount; r++) {
items[c][r] = { x: 0, y: 0, status: 1 };
}
}
document.addEventListener("keydown", keyDownHandler, false); // 이벤트 위임
document.addEventListener("keyup", keyUpHandler, false);
function keyDownHandler(e) { // 키코드로 방향키 누를 때, 조건 설정
if(e.key == "Right" || e.key == "ArrowRight") {
rightPressed = true;
}
else if(e.key == "Left" || e.key == "ArrowLeft") {
leftPressed = true;
}
else if(e.key == "Up" || e.key == "ArrowUp") {
upPressed = true;
//console.log(upPressed);
}
else if(e.key == "Down" || e.key == "ArrowDown") {
downPressed = true;
//console.log(downPressed);
}
}
function keyUpHandler(e) { // 키보드 안 누를 때, 조건 설정
if(e.key == "Right" || e.key == "ArrowRight") {
rightPressed = false;
}
else if(e.key == "Left" || e.key == "ArrowLeft") {
leftPressed = false;
}
else if(e.key == "Up" || e.key == "ArrowUp") {
upPressed = false;
//console.log(upPressed);
}
else if(e.key == "Down" || e.key == "ArrowDown") {
downPressed = false;
//console.log(downPressed);
}
}
function drawPaddle() {
ctx.beginPath(); // beginPath는 매우 중요하다!!!(안쓰면 동기화가 안되어서 한붓 그리기처럼 된다.)
ctx.rect(paddleX, paddleY, paddleWidth, paddleHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function drawItem() {
// ctx.beginPath(); // beginPath는 매우 중요하다!!!(안쓰면 동기화가 안되어서 한붓 그리기처럼 된다.)
// ctx.arc (50, 50, 10, 0, Math.PI * 2, true); // 1) 일일이 노가다로 아이템 그리기(원)
// ctx.stroke();
// ctx.fillStyle = "brown";
// ctx.fill();
// ctx.closePath();
for (var c = 0; c < itemColumnCount; c++) { // 2) 배열로 아이템 그리기 방법(사각형)
for (var r = 0; r < itemRowCount; r++) {
if (items[c][r].status == 1) {
var itemX = (c * (itemWidth + itemPadding) + itemOffsetLeft); // 배열 인덱스마다 넘어가면서 item 그리기
var itemY = (r * (itemHeight + itemPadding) + itemOffsetTop);
items[c][r].x = itemX; // 나중에 충돌 검색을 위해서 사용함.
items[c][r].y = itemY;
ctx.beginPath();
ctx.rect(itemX, itemY, itemWidth, itemHeight);
ctx.fillStyle = "red";
ctx.fill();
ctx.closePath();
//console.log(items[c][r].status);
}
}
}
}
function drawExitDoor() { // Exit 그리기!(status를 연결시켜서 그려야 한다.)
if(exit.status == 1) {
var exitX = 150;
var exitY = 280;
exit.x = exitX; // 나중에 Exit문과 충돌 검사용
exit.y = exitY;
ctx.beginPath();
ctx.rect(exitX, exitY, exitHeight, exitWidth);
ctx.fillStyle = "green";
ctx.fill();
ctx.closePath();
}
}
function drawInventory() { // 인벤토리 그리기
ctx.beginPath();
ctx.rect(canvas.width-150, 0, 150, canvas.height);
ctx.fillStyle = "black";
ctx.fill();
ctx.closePath();
}
function detectItem(){ // 아이템 줍기 코드
for (var c = 0; c < itemColumnCount; c++) {
for (var r = 0; r < itemRowCount; r++) {
var i = items[c][r];
var count = 0;
if (i.status == 1) {
//console.log(i.y);
// 범위 나중에 다시 고치기..
if (i.x < paddleX && paddleX < i.x + itemWidth && i.y < paddleY && paddleY < i.y + itemHeight) {
i.status = 0; // 범위(item 좌표로부터 사용자 키보드 거리까지 비교하여 충돌 검색)
console.log("collision");
ctx.clearRect(i.x, i.y, itemWidth, itemHeight); // 충돌되면 item 주운 것이므로 item 삭제
ctx.beginPath(); // 아이템 먹으면, 인벤토리에 그 아이템을 채우기!!(이거 버그 발생)
ctx.rect(canvas.width-150, 100*count, 25, 25);
ctx.fillStyle = "red";
ctx.fill();
ctx.closePath();
}
count++;
}
}
}
}
function detectExitDoor() { // 열쇠 4개 먹으면 탈출 성공!!
if((items[0][0].status == 0 && items[0][1].status == 0 && items[1][0].status == 0 && items[1][1].status == 0) && exit.status == 1 ){
if (exit.x < paddleX && paddleX < exit.x + exitHeight && exit.y < paddleY && paddleY < exit.y + exitWidth) {
exit.status = 0;
ctx.clearRect(exit.x, exit.y, exitHeight, exitWidth);
alert("Exit Room!!");
}
}
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawPaddle();
drawInventory();
drawItem();
drawExitDoor();
detectItem();
detectExitDoor();
if(rightPressed) { // 키보드를 누를 때마다 박스의 위치를 변경해준다.
paddleX += 2;
// if (paddleX + paddleWidth > canvas.width){ // 예전 코드
// paddleX = canvas.width - paddleWidth;
// }
if(paddleX + paddleWidth > canvas.width-150){ // 인벤토리 추가 후 키보드를 누를 때, 인벤토리 고정을 위한 코드
paddleX = canvas.width-150 - paddleWidth;
}
}
else if(leftPressed) {
paddleX -= 2;
if (paddleX < 0){ // 키보드를 누를 때, canvas 화면 고정을 위한 코드
paddleX = 0;
}
}
else if(upPressed) {
paddleY -= 2;
if (paddleY < 0){ // 키보드를 누를 때, canvas 화면 고정을 위한 코드
paddleY = 0;
}
}
else if(downPressed) {
paddleY += 2;
if (paddleY + paddleHeight > canvas.height){ // 인벤토리 추가 후 키보드를 누를 때, 인벤토리 고정을 위한 코드
paddleY = canvas.height - paddleHeight;
}
}
x += dx;
y += dy; // 파란색 사각형이 움직일 때, 미세 값으로 프레임 조정
}
setInterval(draw, 10);
});