반응형

https://youtu.be/qkTtmgCjHhM

코딩애플님의 간단한 javaScript 게임개발 예제를 따라 작성해보았다.

게임개발 2까지 진도를 나가서, 이중점프 안되게 뭐 다 코드를 작성했다.

드래그도 안되게 했고, 스페이스바를 눌렀을 때 홈페이지 내부에서 동작하지 않도록 처리도 했다.

 

let $canvas = document.getElementById('canvas');
let $h1 = document.querySelector('h1');
let ctx = $canvas.getContext('2d');
let $pScore = document.getElementById('score');
let $pHScore = document.getElementById('Hscore');

// 드래그 방지
document.onselectstart= () => {return false};
// // space바로 page down 방지 (keyborad 입력 금지)
// document.onkeydown= () => {return false};

$canvas.width = window.innerWidth - 100;
$canvas.height = window.innerHeight - 100;
$h1.style.display = 'none';
$h1.style.position = 'absolute';
// $pHScore.style.float = "right";
// $pScore.style.float = "right";

let line = {
    draw(){
        ctx.moveTo(-20,450);
        ctx.lineTo(2000,450);
        ctx.lineWidth = 4;
        ctx.stroke();
    }
}

let me = {
    x : 40,
    y : 400,
    width : 50,
    height : 50,
    
    draw(){
        ctx.fillStyle = 'rgb(0,200,0)';
        ctx.fillRect(this.x, this.y, this.width, this.height);
    }
}

let jumpNow = false;
let jumping = false;
let standing = false;
let jumpTimer = 0;
let jumpSpeed = 3;
// 나의 점프

//
let jumpKey = ['Space', 'ArrowUp', 'KeyW'];
let standKey = ['ArrowDown', 'KeyS']
document.addEventListener("keydown", (e) => {
    if(jumpKey.includes(e.code)){
        if(jumpNow === false){
            jumping = true;
            standing = false;
        } 

        if(game == false){
            game = true;
            location.reload();
        }
    }
    
    else if(standKey.includes(e.code)){
        standing = true;
     
    }
}) 

class Enemy {
    constructor(){
        this.x = 1600;
        this.y = 400;
        this.width = 50;
        this.height = 50;
    }
    draw(){
        ctx.fillStyle = 'rgb(200,0,0)';
        ctx.fillRect(this.x, this.y, this.width, this.height);
    }
}

let $input = document.querySelector('input');

let score = 0;
if(localStorage.getItem('highScore') === null){
    localStorage.setItem('highScore', 0);
}
let highScore = localStorage.getItem('highScore');

let game = true;
let timer = 0;
let enemies = [];

// 빈도 수
const hard = 140;
const normal = 240;
const difficulty = [hard, normal];
const choice = 0;

// 속도

// 충돌 여부
function isCrush(me, enemy){
    let xDif;
    let yDif = enemy.y - (me.y + me.height);
    
    if(enemy.x > 40){
        xDif = enemy.x - (me.x + me.width);
    }
    
    else{
        xDif = me.x - (enemy.x + enemy.width);
    }
    
    
    if(xDif < 0 && yDif <0){
        cancelAnimationFrame(animation);
        ctx.clearRect(0,0, $canvas.width, $canvas.height);

        game = false;
        // $h1.style.position = 'position:relative';
        // $h1.style.display = 'block';
        
        //최고기록 갱신
        if(score > highScore){
            highScore = score;
            localStorage.setItem('highScore', highScore);
        }  
    }
}

function excuteEveryFrame(){
    animation = requestAnimationFrame(excuteEveryFrame);
    timer++;
    score++;
    
    ctx.clearRect(0,0, $canvas.width, $canvas.height);

    // 적 생성
    if(timer % difficulty[1] == 0){
        let enemy = new Enemy();
        enemies.push(enemy);
    }

    // 적 소환 및 제거
    enemies.forEach((e, i, o) =>{
        if(e.x < -40) {
            o.splice(i, 1);
        }

        isCrush(me, e);

        e.draw();
        e.x -= 3;
    })

    if(jumping === true && jumpNow === false){
        me.y-= (jumpSpeed);
        jumpTimer++;
        
    }

    if(jumpTimer > 40 || standing == true){
        jumping = false;
        jumpTimer = 0;
        jumpNow = true;
    }

    if(jumping === false){
        if(me.y <= 400){
            me.y+= (jumpSpeed+0.3);

            if(standing === true){
                me.y += (jumpSpeed + 1);
            }
            // 오차 방지 및 중복 점프 방지
            if(me.y > 398){ 
                me.y = 400;
                jumpNow = false;
            }
        }
    
    }

    // 선, 자신 생성
    me.draw();
    line.draw();
    $pScore.innerHTML = 'score : ' + score;
    $pHScore.innerHTML = 'highscore : ' + highScore;

    // 최고기록 실시간 갱신
    if(score > highScore){
        $pHScore.innerHTML = 'highscore : ' + score;
    }

}

excuteEveryFrame();

 

코드해석은 추후 수정하도록 하겠다!

궁금한 부분은 질문 바란다.

반응형
반응형

함수 중복 제거하기

document.querySelector('#num-0').addEventListener('click', onClickNumber('0'));
document.querySelector('#num-1').addEventListener('click', onClickNumber('1'));
document.querySelector('#num-2').addEventListener('click', onClickNumber('2'));
document.querySelector('#num-3').addEventListener('click', onClickNumber('3'));
document.querySelector('#num-4').addEventListener('click', onClickNumber('4'));
document.querySelector('#num-5').addEventListener('click', onClickNumber('5'));
document.querySelector('#num-6').addEventListener('click', onClickNumber('6'));
document.querySelector('#num-7').addEventListener('click', onClickNumber('7'));
document.querySelector('#num-8').addEventListener('click', onClickNumber('8'));
document.querySelector('#num-9').addEventListener('click', onClickNumber('9'));

위와 같은 수식이 있다고 했을 때, 같은 로직이 반복되니 onClickNumber()이라는 함수로 묶어 중복을 제거했다.

const onCLickNumber = (number) => {
	if(operater) {
		numTwo += number;
	} else {
		numOne += number;
	}
	$result.value += number;
	}

위와 같은 수식이라면 에러가 나게된다. 왜? addEventListener에서 사용되는 콜백함수이므로 return이 무엇인지 살펴보아야 하는데, onCLickNumber()의 return값은 undefined이므로 결국 아래와 같아진다.

addEventListener('click', undefined);

그렇다는 건 이때 return에 함수를 주기위해서 고차함수를 사용해야하는데 다음과 같이 고쳐주면 되겠다.

const onCLickNumber = (number) => () => {
	if(operater) {
		numTwo += number;
	} else {
		numOne += number;
	}
	$result.value += number;
	}
// 당연히 인자가 앞에 와야겠지? 생각 잘하자.

간단하다.

+) 코드 정리 방법 - if문

return을 해버리면, 함수 자체가 끝나게 되므로, if else문을 사용하지 않고도 else의 효과를 낼 수 있다.

 

배열 - forEach, map, fill 알아보기

Array(9) // 배열의 길이가 9인 배열 생성 이건 javascript에서만 가능해용 이거 함수네..
Array(9).fill(0) // (9) [0, 0, 0, 0, 0, 0, 0, 0, 0]

fill은 보면 알겠지만 채워준다.

const answer = [5, 6, 3, 2];
const value = '4652'
let strike = 0;
let ball = 0;

answer.forEach((element, index) => {
	const answerIndex = value.indexOf(element);	
	if(answerIndex === index) strike++;
}} 
console.log(strike+"개 맞췄습니다")
//return 2개 맞췄습니다.

forEach는 for문 도는 거 처럼 행동 닉값 함 ㅇㅇ, element와 index를 순서로 매개변수를 받을 수 있다. 배열의 처음부터 끝까지 순회하며, 콜백함수 형식으로 사용해야 한다.

const array = [5, 6, 3, 2];
const result = array.map((element, index) => {
	return element * 2;
})
//return undefined, result (4) [10, 12, 6, 4]

map함수도 마찬가지로 element와 index를 순서로 매개변수를 받고, 배열의 처음부터 끝까지 순회하며 콜백함수 형식으로 사용해야하는 것은 같지만, return을 주어 그 값을 배열에 mapping 할 수 있다.

const array = Array(9).fill(1).map((el,idx) => idx +1)
array.splice(2,3) // index가 2인곳 부터 3개 빼온다.
//return (3) [3, 4, 5], array (6) [1, 2, 6, 7, 8, 9]
let a = array.slice(4)
//return (2) 8, 9
let b = array.slice(4,5)
//return 8, slice(startIndex, EndIndex-1) 

이런식으로 응용할 수 있겠다.

Array 함수를 통해 길이가 9이고, 1~9까지의 값을 가지는 array를 선언했다.

splice 함수를 통해 index가 2인곳 부터 3개를 반환했고

slice 함수를 통해 a에 인덱스가 4인곳 부터 따로 배열을 저장했으며

slice 함수를 통해 b에 인덱스가 4인곳 부터 5-1인곳 까지 배열을 저장했다.

 

조그마한 정리

  1. 배열 정렬은 그냥 검색해라.
  2. setTimeout( 콜백함수, 시간(밀리초))
  3. 스코프 - 범위 : var은 함수 스코프, let은 블록 스코프

 

이벤트 관리 EventListener

태그.addEventListener('click', 고차함수())); // 마우스 버튼 클릭 시 함수 행동
태그.removeEventlistener('click',고차함수())); // 변수 사용하는 것이 더 좋다
/*
mouseover 마우스를 태그 위에 올리면 발생
mouseout 마우스가 태그 바깥으로 벗어나면 발생
mousedown 마우스버튼을 누르고 떼기 전, 태그를 드래그할 떄 사용
mouseup 드래그한 태그를 드랍할 떄 사용
focus 태그에 포커스가 갔을 때 발생
blur 태그가 포커스에서 벗어났을 떄 발생
keypress 키를 누르는 순간 시작 누르고 있는 동안 계속 발생
keydown 키를 누를 때 발생
key up 키를 뗄 때 발생
load 페이지 상 모든 요소(파일)의 다운로드가 완료되었을 때 발생
resize 브라우저 창 크기 조절 시 발생
scroll 스크롤바 드래그 혹은 키보드(up, down)를 사용하거나 휠을 사용해서 웹페이지를 스크롤 할 때 발생
페이지에 스크롤바 없으면 이벤트 발생 안함.
unload 링크 클릭하여 다른페이지 이동 혹은 브라우저 탭 닫을 떄 브라우저 창을 닫을 때 이벤트 발생
change 상태가 변경되었을 때 발생
*/

위의 식은 잘못되었다. 왜냐면 함수와 함수간 비교는 객체와 객체간 비교로 볼 수 있는데, 이 상황에서 객체간 비교는 항상 false가 나온다. 객체의 호출은 재생성과 똑같다고 생각하면 되기에 그런데, 변수는 조금 다르다. 그렇기에 함수의 값을 변수에 담아 비교해보도록 하자. 그러면 같다고 할 수 있으니께롱

니 변수에 저장~

 

반응형

+ Recent posts