Javascript: 게임 개발의 기초

Javascript: 게임 개발의 기초

1. 준비하기

CSS와 Javascript에 연결하기

HTML: 태그

canvas는 width와 height속성을 사용할 수 있는 요소로 드로잉 영역을 생성한다.

간단한 게임을 만들 때 게임이 작동하는 공간으로 사용된다.

HTML에 요소를 만들었으니 자바스크립트에도 연결해주어야 한다

var canvas = document.getElementById("canvas");

는 처음에 비어있는 도화지와 같다. 그림을 그릴 연필과 크레파스(도구)를 같이 불러와야 한다

.getContext('2d'); 메소드를 통해 2차원 랜더링의 함수(도구)에 접근할 수 있다

var ctx = canvas.getContext('2d');

캔버스의 크기를 정해준다

canvas.width=window.innerWidth - 100; canvas.height=window.innerHeight -100;

CSS는 각 크기를 100%로 설정해주어 다양한 환경에서 체크할 수도 있다(참고)

html{ width: 100%; height: 100%; } body{ width: 100%; height: 100%; } canvas{ width: 100%; height: 100%; }

*유튜버 Interactive Developer, 화면에 튀기는 공 만들기

2. 드로잉

Hitbox: 이미지를 넣기 전, 원과 네모 로 히트박스를 만들어 작동해본다

드로잉은 당연히 도구 를 이용해야한다

.getContext('2d')로 불러온 ctx를 이용한다

ctx.fillStyle = 'black'; //색깔은 검정 ctx.fillRect(20,20,100,100); //Rect = 사각형

.getContext의 함수 색 정하기 fillStyle = 'color'; name, rgb(), #HEX 사각형 그리기 테두리만 존재 .strokeRect(x,y,width,height) x,y = 좌표

width, height = 크기 채워진 사각형 .fillRect(x,y,width,height) 사각형 지우기 .clearRect(x,y,width,height) 패스로 그리기 패스 열기 .beginPath(); 형식

ctx.beginPath(); 패스 시작점 .moveTo(x, y); 연결하기 .lineTo(x,y); 채우기 .fill();

*Java의 경우 stroke → draw이다

등장 캐릭터 를 오브젝트로 생성한다

사각형을 만들어도 그것이 특정 object와 연결되어있지 않으면 소용없다

오브젝트 안, 좌표와 크기를 설정할 수 있다

draw()함수를 통해 오브젝트 내부 드로잉이 가능하다

객체.draw();로 출력할 수 있다

var cannon = { x : 10, y : 200, width : 50, height : 50, draw(){ ctx.fillStyle = 'green'; ctx.fillRect(this.x,this.y,this.width,this.height); } } cannon.draw(); //객체를 외부로 출력

장애물, 적 은 클래스로 만들어준다

Java의 클래스 생성과 같다. constructor(생성자)도 만들어서

초기값을 정해줄 수 있다(좌표 등)

클래스로 생성했기 때문에 클래스를 import 해주어야 호출할 수 있다(java개념)

class Wall(){ constructor(){ this.x = 300; this.y = 300; } draw(){ ctx.fillStyle = 'black'; ctx.fillRect(this.x,this.y,this.width,this.height); } } var wall = new Wall(); //import 시키기 wall.draw();

3. 애니메이션

애니메이션은 프레임 마다 계속 그려주는 것이다

객체를 이동시켜야 한다. 1초 안에 몇 프레임으로 움직이는가 설정해줘야한다

requestAnimationFrame()은 그것을 가능하게 한다

var animation; //나중에 중단을 위해 변수화한다 function FrameMove(){ //이 안에 호출된 함수를 다시 넣는다 animation = requestAnimationFrame(FrameMove) cannon.draw(); //cannon은 1초에 60번씩 생성된다 cannon.x ++; //cannon이 늘어나는 것을 확인할 수 있다 } FrameMove();

그러나 움직인다는 느낌이 들지 않는다. 계속 그림을 초기화 해줘야 한다

function FrameMove(){ ctx.clearRect(0,0,canvas.width,canvas.height); animation = requestAnimationFrame(FrameMove) cannon.draw(); cannon.x ++; } FrameMove();

타이머 를 활용해 방해물을 만들어보자

타이머 변수를 만들고 나머지 연산을 활용하여

특정 시간마다 오브젝트를 생성할 수 있다

timer기준으로 함수 내부에서 wall을 호출한다

배열명.push();를 통해 배열에 집어넣는다

그 배열을 forEach( (a)>={ } ) 반복문으로 다룬다

//타이머 변수를 외부에 설정 var timer = 0; //생성된 장애물들을 배열을 통해 관리한다 var walls = []; function FrameMove(){ ctx.clearRect(0,0,canvas.width,canvas.height); requestAnimationFrame(FrameMove); timer ++; if(timer % 150 === 0){ var wall = new Wall(); walls.push(wall); } walls.forEach((a)=>{ a.x--; //각 벽이 왼쪽으로 움직임 a.draw(); }) cannon.draw(); } FrameMove();

생성된 방해물은 특정조건을 만족할 때 배열에서 제거 한다

배열을 다루므로 walls에 접근하고 이미 forEach반복문으로 실행중이므로

forEach내부에서 조건문을 활용해 제거한다

제거하기 위해선 forEach에 두 개의 파라미터를 추가한다

var timer = 0; var walls = []; var animation; function FrameMove(){ ctx.clearRect(0,0,canvas.width,canvas.height); animation = requestAnimationFrame(FrameMove); timer ++; if(timer % 150 === 0){ var wall = new Wall(); walls.push(wall); } //forEach에 파라미터 추가 walls.forEach((a, i, o)=>{ if( a.x < 0 ){ o.splice(i, 1); //지우는 코드, splice 사용 } a.x--; a.draw(); }) cannon.draw(); } FrameMove();

4. 이벤트 연결

직접 움직이고 싶다면 역시 이벤트이다

canvas에서 사용하지만 canvas.addEventListener로 이벤트를 추가하지 않는다

window.addEventListener 로 이벤트를 걸어준다(혹은 document)

//먼저 캐논을 그려주고 cannon.draw(); //이벤트를 추가해준다 window.addEventListener('keydown',CannonMove); //좌, 우 방향키를 눌렀을 때 이벤트 function CannonMove(e){ if(e.key == 'ArrowLeft'){ ctx.clearRect(0,0,canvas.width,canvas.height); cannon.x -=5; cannon.draw(); } if(e.key == 'ArrowRight'){ ctx.clearRect(0,0,canvas.width,canvas.height); cannon.x +=5; cannon.draw(); } }

하지만 게임은 requestAnimationFrame이 실행되는 곳에서 보여져야한다

위와 같이 단순히 이벤트를 만들어봤자 한 프레임 구간에서만

캐논이 그려질 뿐이다. 아래는 단순 동작일 때의 연결이다

//window에서 이벤트추가 window.addEventListener('keydown',CannonMove); //draw부분을 빼고 값만 수정해준다 function CannonMove(e){ if(e.key == 'ArrowLeft'){ cannon.x -= 5; } if(e.key == 'ArrowRight'){ cannon.x += 5; } }

이벤트가 연속 동작일 때: 특정 변수를 매개체로 사용한다

예를 들면 점프같은 동작은 연속동작이다

하지만 requestAnimationFrame()가 사용되는 함수 안에서

이벤트의 사용을 반복하는 것은 좋지 않다

var jump = false; //window와 document모두 사용 가능하다 window.addEventListener('keypress', function(e){ if(e.code === 'KeyX'){ jump = true; } }) //requestAnimationFrame(); 을 사용하는 함수 내부 function FrameMove(){ ctx.clearRect(0,0,canvas.width,canvas.height); animation = requestAnimationFrame(FrameMove); timer ++; //jump변수가 true로 바뀔 때 y값이 움직인다 if(jump == true){ cannon.y --; } }

함수 안 타이머를 넣어 동작을 제어한다

위와 같이 만들면 무한히 점프한다

다시 돌아오게끔 만든다

//점프를 제어할 점프타이머를 만들었다 var jump_timer = 0; function FrameMove(){ ctx.clearRect(0,0,canvas.width,canvas.height); animation = requestAnimationFrame(FrameMove); timer ++; //점프가 켜지면, 점프타이머도 증가 if(jump == true){ cannon.y --; jump_timer ++; } //타이머가 50이상일 때 점프를 끄고 타이머 초기화 if(jump_timer>50){ jump=false; jump_timer=0; } //타이머에 상관없이 대포는 y 200까지 이동 if(jump == false){ if(cannon.y<=200){ cannon.y ++; } }

Collision check: 둘이 부딪힐 때 메소드 가 실행된다

부딪힌다는 것은 좌표가 겹친다 는 것이다

즉 좌표값의 차이를 계산해주어 함수에서 판별한다

function Collision_detection(cannon,wall){ var x_differ = wall.x - (cannon.x + cannon.width); var y_differ = (wall.y + wall.height) - cannon.y; if(x_differ<=0 && y_differ<=0){ ctx.clearRect(0,0,canvas.width,canvas.height); cancelAnimationFrame(animation); } } function FrameMove(){ ctx.clearRect(0,0,canvas.width,canvas.height); animation = requestAnimationFrame(FrameMove); timer ++; //중간 내용 생략// walls.forEach((a, i, o)=>{ if(a.x<0){ o.splice(i,1);} a.x--; //모든 장애물과의 충돌이므로 forEach내부에 넣는다 Collision_detection(cannon,a); a.draw(); }) cannon.draw(); }

*다양한 상황에서 충돌을 잡아낼 수 있도록 잘 변형한다

from http://devyoseph.tistory.com/17 by ccl(A) rewrite - 2021-09-27 03:01:27