클래스 (class)
함수 생성자 constructor
// let brick = {
// left: 0, right:0, top:0, bottom:0,
// column: 0, row: 0,
// }
// 벽돌 left, top, right, bottom, col, row, position + 움직이는 기능
// function Brick.prototype() {
// static movingAction = function () { this.left++; console.log('내가 움직이고 있어') }
// }
//1
// function Brick /* implement Brick.prototype */ (left, top, right, bottom) {
// this.left = left,
// this.top = top,
// this.right = right,
// this.bottom = bottom
// // this.movingAction = function () { console.log('내가 움직이고 있어') }
// }
// // prototype이란 실제 우리가 객체를 만들때 사용되는기는 하지만 1대1로 매칭 되지 않고 구조를 잡는 단계에서만 사용되는 데이터를 얘기한다.
// Brick.prototype.movingAction = function () { this.left++; console.log('내가 움직이고 있어') } // 함수를 20개를 만들지 않고 한개를 만들어서 다른 내용들을 공유 할 수 있다.
// for(let i = 0; i < 20; i++) // movingAction이 20개가 만들어진다. 값을 바꾸는 동작을 연산만 해주면되는데 실질적으로 바끼는 데이터는
// {
// let tempBrick = new Brick(0, 0, 10, 10);
// tempBrick.movingAction();
// }
// 1
// class로 객체의 설계도를 만든다.
// 명사로 지칭되는 객체를 설계한다. ex : 자동차, 책, 몬스터, 사람....등 constructor안에 (속성과 기능)으로 표현한다.
class Brick {
constructor(left, top, right, bottom, color) {
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
this.isAlive = true;
this.color = color;
}
draw() {
if (this.isAlive)
{
context.rect(this.left, this.top, brickWidth, brickHeight);
context.fillStyle = this.color;
context.fill();
}
}
}
// constructor(left, top, right, bottom) { // 이부분을 쓰지 않기 위해 Brick을 상속 해준다. extends Brick
// this.left = left;
// this.top = top;
// this.right = right;
// this.bottom = bottom;
// }
// class MovingBrick extends Brick{
// movingAction () {
// this.left++;
// // console.log('내가 움직이고 있어');
// }
// }
const canvas = document.getElementById('myCanvas'); //아이디를 가지고 특정 태그에 접근을 한다.
const context = canvas.getContext('2d');
canvas.style.backgroundColor = "aqua";
//게임 진행 관련
let deadBricksCount = 0;
// 벽돌 관련
const brickWidth = 50; // 간격은 10
const brickHeight = 25; // 간격 5
const brickColumn = 4; // 열
const brickRow = 5; // 핼
let bricks ; // 벽돌전체
let brickCount = brickColumn * brickRow;
// let bricks = [];
// let brickCount = brickColumn * brickRo w;
// 장애물 관련
const wallWidth = 50;
const wallHeight = 20;
let wallPosX = canvas.width / 2 - 25;
let wallPosY = canvas.height / 2 ;
let wallMoveDirX = -1;
let wallMoveSpeed = 1;
let wall = { // position
left:200, right:250, top:200, bottom:220, //함수안에서 변수들을 바꿔줘야한다.
};
// 볼 관련
const arcRadius = 20;
let arcPosX = 200;
// let arcPosY = canvas.height / 2 + 150;
let arcPosY = 360;
let arcMoveDirX = -1; // 음수로 줘야 정방향
let arcMoveDirY = -1; // 음수로 줘야 정방향 // -1을 주면 Y가 위로 움직인다.
let arcMoveSpeed = 3;
let ball = {
left:0, right:0, top:0, bottom:0,
};
// let brick = {
// left:0 , right:0, top:0, bottom:0,
// column:0, row:0, // 몇번째 열,행에 있는 것을 표현 할 수 있는 index
// }
// 패들 관련
const barWidth = 200;
const barHeight = 20;
let barPosX = canvas.width / 2 - barWidth / 2;
let barPosY = canvas.height - barHeight;
let barMoveSpeed = 20;
// 가운데가 0.0
let paddle = {
left:0, right:0, top:0, bottom:0,
};
let getStart = true;
// 키처리 함수 추가
document.addEventListener('keydown', keyDownEventHandler); // 이벤트가 잇을때 해당함수를 호출해준다.
document.addEventListener('keyup', keyUpEventHandler);
// 함수 모음
function keyDownEventHandler(e)
{
if (e.key== " " && getStart){
setInterval(update, 10);
getStart = false
}
if (e.key == 'ArrowRight')
{
// 바를 오른쪽으로 이동
if (barPosX + barWidth < canvas.width)
{
barPosX += barMoveSpeed;
if (getStart){
arcPosX += barMoveSpeed;
}
//1
}
}
else if (e.key == 'ArrowLeft')
{
// 바를 왼쪽으로 이동
if (barPosX > 0)
{
barPosX -= barMoveSpeed;
if (getStart) {
arcPosX -= barMoveSpeed;
}
//1
}
}
//2 코드가 많이 중복되면 함수를 빼던가 연산량이 많지않으면 한번에 써주는 것이 좋다.
paddle.left = barPosX;
paddle.right = barPosX + barWidth;
paddle.top = barPosY;
paddle.bottom = barPosY + barWidth;
}
function keyUpEventHandler()
{
}
async function gameWin (timeout)
{
if(brickCount == 0)
{
setTimeout(() => {
window.location.reload(true);
alert('gamewin');
}, timeout);
}
}
function gameOver ()
{
if(arcPosY > 370)
{
window.location.reload(true);
alert('gameover');
}
}
function update()
{
// 데이터 수정 (도형의 위치 이동)
if (arcPosX - arcRadius < 0) //arcPosX - 50 원의 좌측끝
{
arcMoveDirX = 1;
}
else if (arcPosX + arcRadius > canvas.width) // arcPosX + 50 원의 우측 끝
{
arcMoveDirX = -1;
}
if (arcPosY - arcRadius < 0)
{
arcMoveDirY = 1;
}
else if (arcPosY + arcRadius > canvas.width)
{
arcMoveDirY = -1;
}
// 벽돌 움직이기
if (wallPosX + 50 > canvas.width)
{
wallMoveDirX = -1;
}
else if (wallPosX < 0){
wallMoveDirX = 1;
}
arcPosX += arcMoveDirX * arcMoveSpeed;
arcPosY += arcMoveDirY * arcMoveSpeed;
wallPosX += wallMoveDirX * wallMoveSpeed;
ball.left = arcPosX - (arcRadius); // 위치가 계속 바낄때마다 바낀다.
ball.right = arcPosX + (arcRadius); // 위치가 계속 바낄때마다 바낀다.
ball.top = arcPosY - (arcRadius); // 위치가 계속 바낄때마다 바낀다.
ball.bottom = arcPosY + (arcRadius); // 위치가 계속 바낄때마다 바낀다.
// 벽돌이 움직이기 위한
wall.left = wallPosX ; // 시작지점 0.0
wall.right = wallPosX + 50 ;
// wall.top = wallPosY;
// wall.bottom = wallPosY - 20;
// 충돌확인
if (isCollisionRectToRect(ball, paddle))
{
console.log('a');
arcMoveDirY = -1;
arcPosY = paddle.top - arcRadius;
// arcPosY = paddle + bricks;
}
if(isCollisionRectToRect(ball, wall)) {
// || arcPosX < wall.left) {
if (arcPosX > wall.right ) {
arcMoveDirX *= -1 ;
arcPosX = wall.right + arcRadius;
}
else if (arcPosX < wall.left) {
arcMoveDirX *= -1 ;
arcPosX = wall.left - arcRadius;
}
else if (arcPosY < wall.top) {
arcMoveDirY *= -1 ;
arcPosY = wall.top - arcRadius;
}
else if (arcPosY < wall.bottom) {
arcMoveDirY *= -1 ;
arcPosY = wall.bottom + arcRadius;
}
// else arcMoveDirY *= - 1;
}
for (let i = 0; i < brickRow; i ++)
{
for(let j = 0; j < brickColumn; j++)
{
if (bricks[i][j].isAlive && isCollisionRectToRect(ball, bricks[i][j]))
{
// 벽돌을 안보이게 하던지, 위치를 바꾸던지, ball의 방향 변경
console.log('i : ', i, 'j', j); //
bricks[i][j].isAlive = false; // 중복해서 찍히지 않게 해준다.
arcMoveDirY = -arcMoveDirY;
brickCount--;
// break;
}
}
// gameOver();
gameWin(3000);
}
}
function checkToWin()
{
// 1. bricks배열에 있는 정보로 처리
// let bricks = [];
// bricks[0] = [];
// bricks[1] = [];
// bricks[2] = [];
// bricks 안의 요소를 가져오면 bricks는 배열이다.
let flatBricks = bricks.flat();
// console.log(flatBricks);
//
// let deadBricks = bricks.flat().filter(brick => brick.isAlive == false); // map쓰는 것과 비슷하다. 조건을 확인해서 해당 조건에 만족하는 것들로 다시 선언해준다.
// if(deadBricks.length == brickRow * brickColumn)
// {
// // 게임 클리어
// alert("게임클리어");
// }
// 2. 카운트를 세는 변수를 만들어서 처리
// if (deadBricksCount == brickRow * brickColumn)
// {
// // 게임 클리어
// }
}
//충돌 bricks[0][0] bricks[0][1]
function isCollisionRectToRect(rectA, rectB)
{
// a의 왼쪽과 b의 오른쪽
// a의 오른쪾 b의 왼쪽
// a의 아래쪽 b의 위쪽
// a의 위쪽과 b의 아래쪽
if (rectA.left > rectB.right ||
rectA.right < rectB.left ||
rectA.top > rectB.bottom ||
rectA.bottom < rectB.top )
{
return false;
}
return true;
}
// bricks 와 ball이 충돌 했을때 없어지게 해야한다. + 2중 충돌 한번 충돌시 색상변경
// function crushBricks(rectA, rectB)
// {
// }
function draw()
{
// 화면 클리어
context.clearRect(0, 0, canvas.width, canvas.height);
drawCanvas();
// 다른 도형 그리기
drawRect();
drawArc();
drawBricks();
drawWall();
}
function drawCanvas() {
context.beginPath();
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = '#CA9B89';
context.fill();
context.closePath();
}
function drawArc() // 컴파일 순간에 내용물이 정해지고 호출된 시점에 사용이 된다.
{ //함수 하나에 clear move close 3가지가 들어가있다.
context.beginPath();
context.arc(arcPosX, arcPosY, arcRadius, 0, 2 * Math.PI);
context.fillStyle = 'blue';
context.fill();
context.closePath();
}
function drawWall()
{
context.beginPath();
context.rect(wallPosX, wallPosY, wallWidth, wallHeight);
context.fillStyle = 'black';
context.fill();
context.closePath();
}
function drawRect()
{
context.beginPath();
// context.rect(canvas.width / 2, canvas.height / 2, 100, 100); // 가운데 좌표
context.rect(barPosX, barPosY, barWidth, barHeight); //
context.fillStyle = 'red';
context.fill();
context.closePath();
}
function setBricks()
{
bricks = [];
for(let i = 0; i < brickRow; i++) // 가로 5 줄
{
bricks[i] = [];
for(let j = 0; j < brickColumn; j++) // 세로 4줄
{
// TODO : right : left + 50 해보기
// bricks[i][j] = { //2차원 배열안의 요소에 접근을 해야 left,right,top,bottom,
// left:55 + j * (brickWidth + 30), // 시작위치(왼쪽에서 얼마나 뛰어서 시작할건가) + j * (벽돌위치 + 각각의 간격)
// right:55 + j * (brickWidth + 30) + 50,
// // right:this.left + 50, // this.left는 left가 값으로 변경되기 전인지 후인지 알수 없다.
// top:30 + i * (brickHeight + 10),
// bottom:30 + i * (brickHeight + 10) + 25,
// column:i, row:j,
// isAlive:true
// };
bricks[i][j] = new Brick( 55 + j * (brickWidth + 30), // left
30 + i * (brickHeight + 10), // top
55 + j * (brickWidth + 30) + 50, // right
30 + i * (brickHeight + 10) + 25, // bottom
'green'
);
}
}
}
// 위에는 데이터 값만 지정을 해준 것이고 그 데이터 값을 그려줘야한다. drawBricks
function drawBricks()
{
context.beginPath();
for(let i = 0; i < brickRow; i++)
{
for(let j = 0; j < brickColumn; j++)
{
bricks[i][j].draw();
}
}
context.closePath();
}
setBricks();
setInterval(draw, 10);