Project/01 Cannon Game

Cannon Game_day4 (2021.09.29)

devyoseph 2021. 9. 30. 16:56
해결해야할 사항들
에너지를 키다운으로 축적해서 발포, 대포의 색이 붉어진다
부서진 벽의 가루가 중력에 의해 떨어진다
공의 무게가 크면 벽을 부술 수도 있다
대포의 몸체와 발사부를 구분한다
대포의 움직임일 때 애니메이션 효과가 있다
공의 움직임이 멈추거나 대포가 자신의 포탄에 맞으면 생명이 감소한다

 

스테이지 벽돌

스테이지: 벽돌의 위치가 저장되어 있는 곳이다
벽돌의 위치를 크롬창을 띄우면서 좌표값을 하나하나 입력하는 것은
너무 비효율적이었다. '쉽게 스테이지를 만들어갈 수는 없을까?'를 고민했고
자바스크립트 게임들의 코딩을 조사하면서 해답을 얻게 되었다.
키워드는 2차배열 반복문이었다
this.walls =[
            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
        ];
        this.walls_hor_X=[
            [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        ];
        this.walls_hor_Y=[
            [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        ];
Stage constructor(생성자) 내부에 내가 최대한 배치하고 싶은 벽돌들을 1이라는 숫자를 사용해 나타낸다
또한 resize시 어떤 디바이스 화면 크기에도 맞게 만들고 싶었다
벽돌들의 크기를 전체화면 크기의 일정비율 크기로 조정하고 그에 맞는 배치가 필요했다
위 2차배열의 경우 24행 6열이다. 그래서 먼저 전체 너비의 24분의 1만큼 벽돌의 너비를 설정했다
//this.wallsXCount = 24;
//this.wallsYCount = 6;
this.wallsWidth_horizontal = stageWidth/24;
언제든지 벽돌의 개수를 변경하기 위해 위처럼 다시 변수화했지만 배열을 이용하는 부분이 있어서 지금은 사용하지 않는 변수가 되었다.
1) walls[ ]에 원하는 벽돌 구성을 만들고
2) 반복문으로 walls_hor_X와 walls_hr_Y에 각각의 좌표를 넣어준다
this.y = stageHeight/20;
        for(let i = 0; i < this.walls.length; i++){
            this.x=0; this.y += this.wallsHeight_horizontal;
            for(let j = 0; j < this.walls[i].length; j++){
                this.walls_hor_X[i][j]=this.x;
                this.walls_hor_Y[i][j]=this.y;
                
                //좌표 증감
                this.x += this.wallsWidth_horizontal;              
            }
        }
x는 배열의 행이 바뀔 때마다 초기화해주는 것을 명심해야한다
생성된 x, y좌표를 각 배열에 넣는다. 2차배열의 경우 [i][j]로 넣어준다
이후 if문을 통해 스테이지를 클리어하면 다음 스테이지로 넘어가도록 만들 것이다

 

벽돌: 스테이지의 위치 정보를 받아 직사각형의 형태로 존재한다
벽돌에게 필요한 것 기능은 다음과 같았다
1) 공을 만나면 공의 속도 방향을 바꾸고
2) 자기 자신은 사라진다
먼저 벽돌의 draw의 너비와 높이는 위 설정한 스테이지 너비의 비율로 설정해야했다
또한 위 2차배열에서 0과 1을 구분해 벽돌을 생성할지 말지 정해줘야하기 때문에
this.type을 통해 2차배열인 walls의 값을 가져오고 if문을 활용했다
draw(ctx, balls, brick_touch){
            this.brickCollision(balls, brick_touch);
            if(this.type ==1){
                ctx.fillStyle = 'red';
                ctx.strokeRect(this.x, this.y, this.brick_width, this.brick_height);
            }
        }
벽돌을 만들 때 인스턴스 간 연결이 가장 중요했다. 그리고 다음과 같은 사고가 필요했다.
1) 반복문을 통해 좌표가 들어있는 각각의 벽돌을 만들어준다(인스턴스화)
2) 각각의 인스턴스는 이미 좌표값을 내부에 가지고 있기에 벽돌을 배열에 무작위로 집어넣어도 상관없다
3) animation 내부에 스테이지를 로드할 필요는 없다
  i) 외부에서 벽돌 인스턴스를 배열에 집어넣고
 ii) requestAnimationFrame 안에서 벽돌 배열에 있는 벽돌을 계속 보여주면 된다
iii) 즉, animation 내부에서 배열을 forEach를 사용해 나타낸다(for, while)
//메인 모듈의 constructor에서 스테이지 로드
class App{
    constructor(){
if(onGame === false){
            for(let i = 0; i < this.walls.length; i++){
                for(let j = 0; j < this.walls[i].length; j++){
                    var brick = new Brick(this.walls[i][j],this.walls_hor_X[i][j],this.walls_hor_Y[i][j],
                                this.stageWidth,this.stageHeight, balls, brick_touch);
                    bricks.push(brick);
                }
            }   
            onGame = true;}
	}

//animate() 내부: constructor에서 만든 배열을 draw한다
animate(t){
        window.requestAnimationFrame(this.animate.bind(this));
      
        //스테이지: forEach사용
        bricks.forEach((brick_each,i , o)=>{
            brick_each.draw(this.ctx, balls, brick_touch)
            if(brick_each.type == 0){
                o.splice(i,1);
            }
        })
 }
 
 }
4) 인스턴스 배열과 인스턴스 배열끼리의 충돌은 다음과 같이 해결한다(A와 B)
  i) A의 클래스로 직접 들어가 draw를 통해 B의 인스턴스 배열 전체를 가져온다
 ii) 메소드를 만들고 메소드 안에서 반복문을 통해 B의 값들을 꺼내어 대조한다
iii) 메소드 내부에서 A와  B의 값을 직접 수정하면서 충돌을 구현한다
iv) 충돌 설정시 주의점은 미리 기준 좌표를 통해 충돌 경계선 좌표를 생성해주고 시작한다(계산이 복잡하다)

 

중력, 속도감소

공에는 지구와 동일하거나 작은 중력이 작용한다
포탄의 y속도는 draw메소드 내에서 프레임마다 중력가속도의 40분의 1만큼 감소한다
draw(ctx, stageWidth, stageHeight){
        //중력가속도
        this.vy += this.g/40;
}
벽에는 마찰력이 작용한다(부분해소)
에너지를 통해 마찰력에 의한 에너지 감소와 그에 따른 속도 감소는 설명하기 어려웠다.
대신 벽에 부딫히면 속도의 방향에 -1의 곱을 주면서 동시에 속도가 줄어들게 메소드를 변경했다
bounceWindow(stageWidth,stageHeight){
        const minX = this.radius;
        const maxX = stageWidth-this.radius;
        const minY = this.radius;
        const maxY = stageHeight-this.radius;

        //벽에 부딪힐 때 속도 감소
        if(this.x <= minX || this.x >= maxX){
            this.vx *= -0.90;
            this.vy *= 0.87;
            this.x += this.vx;
        //바닥
        } else if(this.y >= maxY){
            this.vy *= -0.75;
            this.vx *= 0.7; 
            this.y += this.vy;
        //천장
        } else if(this.y <= minY){
            this.vy *= -0.97;
            this.vx *= 0.9; 
            this.y += this.vy;
        }
    }

 

 

대포

대포가 이동할 수 있다
app.js에 document.addEventListener를 만들고
cannonMove 함수를 만들어 canvas에서 대포가 움직이도록 했다
대포는 각도를 조절할 수 있다
angle의 초기값을 Math.PI/4(45도)로 두고
addEventListener를 통해 위 방향키와 아래 방향키를 누르면
각각 PI/180만큼 angle변수가 증감하게 만들었다
증감된 angle을 ball 인스턴스에 넣었고
초기 속도 speed를 x축과 y축으로 분해하여
speed*cos(angle) speed*sin(angle)로 포탄의 방향을 설정했다
포탄이 발사된다
addEventListener를 통해 'Space'를 누르면 포탄이 발사된다
발사되는 위치는 대포의 위치를 기준하며 포탄 자바스크립트 내부에 벽을 만나면
속도의 값을 -1을 곱해 튕겨나오게 만들었다. 
하나를 만들 때는 상관 없지만 여러개를 원할 때마다 꺼내쓰고 싶으면 배열을 활용한다
배열은 push를 사용해야 하며 주의할 점은 다음과 같다
1) animation 외부에서 포탄의 인스턴스를 정의하고 animation 내부로 가져오게 되면 두가지 문제점이 발생한다
하나는 계속해서 생성할 수 없게 되고(이미 외부에서 생성한 인스턴스) 기존 정해진 속도에서 가속도가 붙게 된다
2) animation 내부, if문 안에서 인스턴스 변수를 만들고 바로 배열에 집어넣어준다