배움 __IL/TIL 1기

TIL : 30번째- 230109 [1-2-월]

Mo_bi!e 2023. 1. 9. 18:18

I. INTRO : 지난시간 복습

입력감지 후 지령내리기

update() 점진적, draw()는 화면에 주사하는 역할을한다.

 

 

II. 게임만들기

1. 적기(enemy) 만들기

적기의 경우 자기스스로 해야한다. 다만 자기스스로 하는것이 힘들다.

스스로 움직임 VS 캔버스가 지령을 내려주기 무엇이 바람직하나?

두개 다 차례대로 해보자

 

(1) 움직이기

 keyDownHandler(e){

        // console.log("aaaa");

        console.log(`e.key : ${e.key}, e.char: ${e.char}`);
        
         switch(e.key){
            case "ArrowUp" :
                this.boy.move(1);
                break;
            case "ArrowRight" :
                this.boy.move(2);
                break;
            case "ArrowDown" :
                this.boy.move(3);
                break;
            case "ArrowLeft" :
                this.boy.move(4);
                break;

        }

    }
move(dir){
        // console.log("move 구분용");

        switch(dir){
            case 1: this.y -= 1; //북
                break;
            case 2: this.x += 1; //동
                break;
            case 3: this.y += 1; //남
                break;
            case 4: this.x -= 1; //서
                break;
        }
    }

이렇게 하면 누를 때 마다 캐릭터가 움직여진다

 

다만 이런경우 문제점이 캐릭터가 걷는 동작으로 움직이지 않는다.

 

(2) move()에서 update()로 움직임 적용

한편 키를 쭉 누를경우에 처음에 조금 움직였다가 어느정도 딜레이가 있은뒤 나머지 다 움직인다 

키 한번 누르고 어떤 때 다, 기다리게끔

 

move()에서 위치를 바꾸어주지 않는다.

결국 update()에서 적용되게끔해야한다.

class Boy{

    constructor(x , y){
    
    //dir 멤버 만들어주기
        //움직이는 방향에 대한 지역변수로
        this.dir = 0;
        }
}
update(){    
        //-----이동을 위한 코드------------------------

        switch(this.dir){
            case 1: this.y -= 1; //북
                break;
            case 2: this.x += 1; //동
                break;
            case 3: this.y += 1; //남
                break;
            case 4: this.x -= 1; //서
                break;
         
        }
}
move(dir){
        this.dir = dir;
        console.log(this.dir);
    }

그러나 멈추지 못하는 것이 문제이다.

 

update() 안에 x,y축에 대해서 움직여주는 switch 위치를 옮겨준다.

 

 

(3) onkeyup 지정해주기

키가 뺄때 움직임이 멈추게끔 해야한다 

그렇기 때문에 게임캔버스 생성자에 dom.onkeyup 의 프로퍼티에 콜백함수를 새로 만들어주어야한다.

 

    this.dom.onclick = this.clickHandler.bind(this);
     //bind는 호출할때가 아니라 위임할 때

    this.dom.onkeydown = this.keyDownHandler.bind(this);
    this.dom.onkeyup = this.keyUphandler.bind(this);

코드 가장아래에 이렇게 해주어야한다.

    keyUphandler(e){

        console.log(`eUP.key : ${e.key}, eUP.char: ${e.char}`);
        this.boy.move(0);

        switch(e.key){
            case "ArrowUp" :
                this.boy.stop(1);
                break;
            case "ArrowRight" :
                this.boy.stop(2);
                break;
            case "ArrowDown" :
                this.boy.stop(3);
                break;
            case "ArrowLeft" :
                this.boy.stop(4);
                break;

        }
    }

메소드도 추가해주기

그전 keyDownhandler와 내용은 비슷하다

멈추는것에 차이에 불과하기 때문이다.

 

결국 move()함수에 대해서 모두 없게끔 해준다.

 

(4) 두키 이상 동시에 누를 때 

그러나... 두개를 동시에 누르는 경우에 문제가 발생한다.

우리도 멈출 때에도 move()라고 하면안된다. 어떤 방향에 대해서 멈추라고 해야한다

 

stop()을 해주어야한다.

stop(dir){

    this.dir = dir;

    switch(this.dir){
        case 1: //북
        case 3:
            this.dir = 0; //남
            break;

        case 2: //동
        case 4: 
            this.dir = 0; //서
            break;
    }

}

하지만 이렇게 할 필요가 없다.

어차피 움직임을 결정하는것은 x,y축이 아니라 dir이다!

 

결국 dir에 값을 전달해주는 방법으로 하면된다

//아래 좀더 긴 내용 있음

stop(dir){

    this.dir = dir;

    switch(this.dir){
        case 1:                    //북
            this.moveUp = false;
            break;
        case 2:                    //동
            this.moveRight = false;
            break;
        case 3:
            this.moveDown = false; //남
            break;
        case 4: 
            this.moveLeft = false; //서
            break;
    }

}

 

(4) 멤버변수 생성 및 false 넣기?

왜 true false???? 아래 (5)에서 8방향으로 하기위한 조건문 발생을 위해서 이다.

class Boy{

    constructor(x , y){
    
        this.moveLeft = false;
        this.moveRight = false;
        this.moveUp = false;
        this.moveDown = false;
        
        }
        
     move(dir){
        // console.log("move 구분용");

        this.dir = dir;
        console.log(this.dir);

        switch(this.dir){
            case 1:                   //북
                this.moveUp = true;
                break;
            case 2:                   //동
                this.moveRight = true;
                break;
            case 3:
                this.moveDown = true; //남
                break;
            case 4: 
                this.moveLeft = true; //서
                break;
        }
    }
    
    stop(dir){

        this.dir = dir;

        switch(this.dir){
            case 1:                    //북
                this.moveUp = false;
                break;
            case 2:                    //동
                this.moveRight = false;
                break;
            case 3:
                this.moveDown = false; //남
                break;
            case 4: 
                this.moveLeft = false; //서
                break;
        }

    }
}

 

(5) 8방향 움직이기

4방향 뿐만 아니라 8방향으로 해보자 (대각선 걷기)

모든방향이 되기 위해서는 배타적이여서는 아니되고, 

 

결국 이런방식은 if - else 가 아니라 각각의 if문을 해주어야한다.

update(){    
        //-----이동을 위한 코드------------------------


    if(this.moveUp)
        this.y -=1;
    if(this.moveRight)
        this.x +=1;
    if(this.moveDown)
        this.y +=1;
    if(this.moveLeft)
        this.x -=1;
        
        
        // switch(this.dir){
        //     case 1: this.y -= 1; //북
        //         break;
        //     case 2: this.x += 1; //동
        //         break;
        //     case 3: this.y += 1; //남
        //         break;
        //     case 4: this.x -= 1; //서
        //         break;
        // }
        
        }

 

그러나 움직여도 걷는 모션이 없다.

(6)  움직일 때 걷게 하기

//제자리에서 도착하면 서있기
if(this.vx == 0 && this.vy == 0){
    this.ix = 1 ;

    //아래꺼 실행안되게하기 위해서
    return; 

}

벡터가 0일때 반환되어서 걷기가 멈춰있다.

 

여기다가 보다 큰 블럭으로 조건을 더 추가해준다.

//조건은 여기중에 단 하나라도 true이면 true이다.
//즉 눌린버튼이 아무것도 없으면 조건발생

if(!(this.moveLeft||this.moveRight||this.moveUp||this.moveDown||false))
//제자리에서 도착하면 서있기
    if(this.vx == 0 && this.vy == 0){
        this.ix = 1 ;

        //아래꺼 실행안되게하기 위해서
        return;

    }

이렇게 4방향 키중 하나도(아무것도) 안눌리면 

결국 ix = 1 이라는 서있는 자세가 나타난다

 

캐릭터방향전환 숙제☆

 

(7) 움직이는 속도 올리기

그렇다면 캐릭터가 더 빨리 움직이게 하려면 어떻게 해야하는가?

멤버변수speed를 만들고 3으로 넣어준다

    update(){    
        //-----이동을 위한 코드------------------------

        if(this.moveUp){
            this.y -=this.speed;

        }
        if(this.moveRight){
            this.x +=this.speed;

        }
        if(this.moveDown){
            this.y +=this.speed;

        }
        if(this.moveLeft){
            this.x -=this.speed;

        }}

3배로 빨라진다.

 

2. 캡슐화

<모든캡슐은 3가지를 가진다>

1. 속성 2. 메소드 3. 이벤트

 

(1) 은닉성(접근지정자)

ES6 이전에는 불가능했으나, 이제는 가능하다

 

예컨데 vx,vy를 숨기고싶다.

 

#vx로 넣어주면 private가된다

이제 vx의 모든 변수에 #을 다 붙어주어야한다.

결국 다른 클래스에서 #vx에 접근할수 없다.

 

(2) getter setter

Boy의 speed를 올리고싶을 때 직접 노출하기 보다는 게터세터로 해야한다

캡슐 깨지않기 위해서 getter setter을 사용한다.

class Boy{
    #speed;

	setSpeed(speed){
        this.#speed = speed;
    }

    getSpeed(){
        return this.#speed; 
    }
    
    }
class Gamecanvas{ //생성자 만들기

	this.boy = new Boy(100,100);
    this.boy.setSpeed(this.boy.getSpeed()+10);
    console.log(this.boy.getSpeed());
    
    }

그러면 접근이 가능하다. 그런데 이렇게 구현하기 번거롭다...

 

다른방법도 있다.

게터세터 사용한것인데, 속성이 밖으로 나온것처럼 하는 것이다.

set speed(val){
    this.#speed = val;
}

get speed(){
    return this.#speed; 
}
this.boy = new Boy(100,100);
    this.boy.speed++;
    console.log("speed"+ this.boy.speed);

 

 

(3)이벤트 

1) 우선 적기를 출현시키자

enemy 세팅하기

class Enemy{

    constructor(x,y){

        this.img = document.querySelector("#enemy");
        console.log(this.img);

        this.x = x || 350;
        this.y = y || 0;

        //48 64
        this.sw = 48;
        this.sh = 64;


    }

    draw(ctx){
        ctx.drawImage(this.img,
            this.x ,this.y)
            //위가 이미지, 아래가 캔버스 위치두기
    }


    update(){


    }
}

game-canvas에 몇가지 인스턴스 생성하고,  멤버변수나 호출함수를 더 추가해주어야함

 

II. ES6 Module

<모듈시스템>

매번 HTML도움받아서 import 받는 것이 바람직하지 않을 수 있음

스크립트는 스크립트인데 왜 HTML도움 받아야함?

 

JS는 모듈화 개념이 없으나, 추가가되었다

1. 스크립트가 스크립트 임포트해야하고 (HTML 없이)

2. 임포트 하기 에는 파일들이 전혀 영향을 주지않는,  그 안쪽이 다른파일에 아무런영향을 주지않는 고립화 되어야한다

 

1. 모듈을 동적으로 할수 있어야한다

 

2. 기존 JS파일의 모둘화

HTML을 가지고 import하는것은 코드를 나누는것에 불과하다

매번 하는것이 번거로움

3. 새로운 JS파일의 모듈화

script의 type에다가 module이라 쓰면 쓸 수있다.

 

    <!-- <script src="./items/boy.js"></script>
    <script src="./items/enemy.js"></script> 
    <script src="./items/backGround.js"></script>
    <script src="./panel/game-canvas.js"></script> -->
    
    <script type="module" src="./app.js"></script>
    
    <!-- <script src="./ex1-es6-var.js"></script> -->

이렇게 다 없앰

 

1)

그러고 app.js에 추가한다

import Gamecanvas from './panel/game-canvas.js';

window.addEventListener("load",function(){

그래도 바로 쓰는건 아니고 공개처리(export default)를 해야한다

export default Gamecanvas;

 

혹은 클래스 명에다가!

export default class Gamecanvas{ //생성자 만들기

    constructor(){

 

2)boy, background, Enemy 의 경우

import Boy from '../items/Boy.js';
import Background from '../items/backGround.js';
import Enemy from '../items/Enemy.js';

export default class Gamecanvas{ //생성자 만들기

그리고 각자의 파일(Boy.js / backGround.js / Enemy.js)export default(공개처리)를 설정해준다.

 

 

-앞으로는 이런식으로 모듈시스템을 쓴다.

 

 

4. Export 형식

(1) export default 함수의 명명 : 기본 노출 객체 대한 이름은 마음대로 정할 수 있다.

1)

<module1.js>

export default function test(){
    console.log("module test");
}

export function test1(){
    console.log("module test1")
}

export function test2(){
    console.log("module test2")
}

여기서 defaulttest 함수만 해당한다.

 

<module2.js>

function test(){
    console.log("module2 test");
}

 

<ex2-es6-module.js>

import aaaa,{test1, test2} from './module1.js';

aaaa();
test1();
test2();

모듈의 이름은 그냥 정하면된다

default의 경우 aaaa로 하면 되고

그외의 경우 {} 표시 내부에 따라서 그대로 하면 가능하다.

 

이 경우 default는 무조건 직접 설정해 주어야한다.

 

이제 남들이 만든것을 가져다 쓸 수있어야한다.

 

2)

import aaaa,{test1, test2} from './module1.js';
import bbb,{test1 as test3} from './module2.js';

aaaa();
test1();
test2();
bbb();
test3();
export default function test(){
    console.log("module2 test");
}

export function test1(){
    console.log("module test1")
}

이렇게 접근하는 경우 test1은 두개의 파일에서 이름이 동일하다

그렇기 때문에 내것처럼 이용해서 함수를 호출하는 경우 문제가 발생한다.

 

이 경우 import 에 as를 이용하면 이름문제를 해결이 가능하다.

 

 

3)

노출할수있는것이 변수 함수 객체 클래스 모두 가능하다

앞까지는 함수여서 함수의 이름을 바꿔서 쓴 느낌이다.

 

노출한것이 클래스

<클리스의 경우>

export class Exam{
    constructor(){
        this.kor = 2;
        this.eng = 3;
        this.math =4;
    }

}
import aaaa,{test1, test2, Exam} from './module1.js';

let exam1 = new Exam();
console.log(exam1);

 

 

<객체의 경우>

다른 파일에서 import 해서 쓸 경우에

 

전역객체를 만들고자 할 때 가능하다

다른파일로 있어도 객체는 각자 다른게 아니라 하나이다!

 

오로지 하나의 객체를 만들어야하고, 쓰게 만드는 경우 싱글통

누구도 새로 만들수없고, 누구도 하나만 쓸수있게끔

어디서나 접근할수 있게끔

 

 

결국 모듈은 내가 결정하냐 HTML이 결정하냐의 차이로 나누어진다.

 

 

-<마치며>-

다음시간 적기 관련 이벤트와 연결지어서 보고 (책임전가)

이제 배경움직이는것에 대해서 알아보자

 

 


1. 보충

1) set get 에 대한 이해

What (정의) : 접근자프로퍼티(__proto__ 처럼)로서 접근지정자가 private(은닉)인데, 클래스 필드값을 조작하기 위한 것

Why (존재이유) : 클래스의 필드에 접근하고자 이용이 된다.

How (방법) : 메소드 이름 get set 을 사용한다.

e.g. (예) :

 

2) module

What (정의) : application을 구성하는 개별적 요소이다.

Why (존재이유) : 코드를 재사용하고, 세부사항에 대해서 캡슐화하고 공개필요한 API만 노출하는 것

JS의 전역객체 공유 독자적인 스코프를 가지게 해서막을 수있음

 

How (방법) : 파일단위로 분리를 한다. 그리고 명시적으로 로드를 한다.

script 태그에 type = "module" 을 추가한다.

 

 

2. 회고 

 

1) 키보드로 움직이는 절차를 생각하자

 

[game-canvas : up/down-handler() ] =>

move(),stop()함수에 인수를 넣고 호출함

 

 

[Boy : move()/stop()] => 

호출을 받고나서 4방향에 대해서 boolean 값에 true / false 값을 넣어준다

 

[Boy : update()] 

4개의 조건문으로 캐릭터의 x,y(위치)값에 더해준다.

 

 

 

 

 

'배움 __IL > TIL 1기' 카테고리의 다른 글

TIL : 32번째- 230111 [1-2-수]  (0) 2023.01.11
TIL : 31번째- 230110 [1-2-화]  (0) 2023.01.10
TIL : 29번째- 230106 [1-1-금]  (0) 2023.01.06
TIL : 28번째- 230105 [1-1-목]  (0) 2023.01.05
TIL : 27번째- 230104 [1-1-수]  (0) 2023.01.04