Javascript
setInterval()
개념
setInterval() 함수는 특정 동작(함수)를 특정 시간마다 실행시켜준다. setInterval(함수, ms 단위 초) 형태로 사용한다.
예시
function createGhost() {
const ghostDiv = document.createElement("div");
ghostDiv.className = "ghost";
ghostDiv.style.left = `${Math.floor(Math.random() * 756)}px`;
document.querySelector("#bg").appendChild(ghostDiv);
setInterval(moveGhost, 1000);
}
setInterval(createGhost, 5000);
setInterval()이 실행할 함수를 만들어두고, setInterval()의 첫번째 인자로 넣어준다.
참고
https://developer.mozilla.org/en-US/docs/Web/API/setInterval
DOM으로 CSS 속성 변경하기
개념
document.querySelector() 등으로 HTML 요소를 선택한 다음, document.querySelector().style.propertyName = 값;으로 css 속성을 수정할 수 있다. 이때 주의할 점은 변경하는 property의 값이 px 등의 단위를 필요로 할 때, css에서 작성할 때와 달리 값의 숫자와 "px"이라는 문자를 따옴표나 백틱 등을 사용해서 표기해줘야 한다는 점이다.
예시
ghostDiv.style.left = `${Math.floor(Math.random() * 756)}px`;
divHero.style.left = myHero.left + "px";
Class로 해볼까
오답노트 1
function createGhost() {
const ghostDiv = document.createElement("div");
let ghostTop = 0;
ghostDiv.className = "ghost";
ghostDiv.style.left = `${Math.floor(Math.random() * 756)}px`;
ghostDiv.style.top = ghostTop + "px";
document.querySelector("#bg").appendChild(ghostDiv);
function moveGhost() {
ghostTop = ghostTop + 10;
ghostDiv.style.top = ghostTop + "px";
}
setInterval(moveGhost, 1000);
console.log(ghostDiv.style.top);
}
setInterval(createGhost, 5000);
- 속성값 변경 문제 : 유령이 아래로 움직이게 ghostDiv.style.top 속성을 변경하려고 하니, 숫자와 "px"이라는 문자열이 결합된 값이라 그런지 숫자 부분을 증가시켜서 다시 값을 부여하기가 곤란했다. 현재 ghostDiv.style.top 값을 받아와 일정 값을 더해서 돌려주려고 했는데 console.log()로 top 속성의 값이 확인되지도 않고... 그래서 hero 캐릭터의 left, bottom 값을 변수(클래스의 field)로 저장해놓았던 것처럼 createGhost() 함수 안에 변수를 만들었다.
- 스코프 문제 : 어찌저찌해서 내려오기는 하는데 생성된 유령에 대한 변수가 모두 creatGhost() 함수 안에 선언되어있다보니, top 속성의 값을 저장한 변수(ghostTop)을 받아와서 변경하고 적용하기 위해 moveGhost() 함수도 createGhost() 함수 안에 써야 작동한다. 그래서 함수가 분리되지 않아 있어 각각이 무슨 역할을 하는지, 코드가 어떻게 작성하는지 보기 불편하다. 내가 봐도 이런 코드 작성법은 적합하지 않은 것 같다.
오답노트 2
class Enemy {
constructor() {
this.left = Math.floor(Math.random() * 756);
this.top = 0;
}
create() {
const ghostDiv = document.createElement("div");
ghostDiv.className = "ghost";
ghostDiv.style.left = this.left + "px";
ghostDiv.style.top = this.top + "px";
document.querySelector("#bg").appendChild(ghostDiv);
}
move() {
}
}
function createGhost() {
const newGhost = new Enemy();
newGhost.create();
}
setInterval(createGhost, 5000);
- 유령 element에 대한 클래스를 만들어, 인스턴스에 메서드를 실행하여 element를 생성하고 이동시킬 수 있게 했다. 유령의 left(랜덤), top 속성 값을 인스턴스마다 저장해두어 괜히 스코프 꼬이게 변수를 만들 필요가 없어졌다.
- 클래스가 필요했던 이유는, 각 유령마다 서로 다른 left, top 속성 값을 갖기 때문에 각자 따로 값을 저장해두었다가 변경시켜야 하기 때문이다.
오답노트 3
class Enemy {
constructor() {
this.left = Math.floor(Math.random() * 756);
this.top = 0;
}
create() {
const ghostDiv = document.createElement("div");
ghostDiv.className = "ghost";
ghostDiv.style.left = this.left + "px";
ghostDiv.style.top = this.top + "px";
document.querySelector("#bg").appendChild(ghostDiv);
}
move() {
this.top += 10;
ghostDiv.style.top = this.top + "px";
}
}
function createGhost() {
const newGhost = new Enemy();
newGhost.create();
setInterval(newGhost.move, 1000);
}
setInterval(createGhost, 5000);
- move() 메서드를 콜백함수로 하는 setInterval()을 createGhost() 안에 만들었다. move() 메서드에서 인스턴스에 저장된 top 값을 증가시킨 뒤, 해당 유령 element의 top 속성에 실제로 적용했다.
- reference error : move() 메서드 안에 ghostDiv.style.top을 썼는데 ghostDiv는 create() 메서드에서 선언한 변수이다. 따라서 move() 메서드에서는 당연히 쓸 수 없다. 심지어 create() 메서드를 먼저 실행해놓지 않는다면, ghostDiv가 지목하고 있는 요소 자체가 존재하지도 않을 것이다.
- 에러를 보고 무슨 뜻인지(위 내용) 깨닫기까지 한참 걸렸다...
오답노트 4
class Enemy {
constructor() {
this.left = Math.floor(Math.random() * 756);
this.top = 0;
}
move() {
if (this.top < 536) {
this.top += 10;
}
}
}
function createGhost() {
const newGhost = new Enemy();
const ghostDiv = document.createElement("div");
ghostDiv.className = "ghost";
document.querySelector("#bg").appendChild(ghostDiv);
ghostDiv.style.left = newGhost.left + "px";
ghostDiv.style.top = newGhost.top + "px";
setInterval(newGhost.move(), 1000);
}
setInterval(createGhost, 5000);
- 다른 메서드에서 이어서 쓸 수 없다는 걸 깨달았으니 클래스의 create() 메서드를 삭제하고, 인스턴스가 각 유령 element의 left, top 값을 저장해두고 업데이트하는 기능만 하게 만들었다. 즉, 유령을 생성할 때마다 그 element의 left, top 속성을 저장하고 메서드를 실행하여 이동시킬 수 있게 했다.
- 문제 : move() 메서드로 변경한 top 값을 실제 유령 element의 style.top에 전달할 방법을 못 찾겠다. 1초마다 유령을 이동시킬 setInterval()는 생성된 유령 element 정보(변수)를 가져오기 위해 createGhost() 함수 안에서 실행하려고 했다. 그런데 setInterval()의 콜백함수에 다음과 같은 내용의 코드를 주욱 쓸 수 없었다.
newGhost.move(); ghostDiv.style.top = newGhost.top + "px";
- 결국 클래스 사용을 포기했다!
해결
function createGhost() {
const ghostDiv = document.createElement("div");
ghostDiv.className = "ghost";
ghostDiv.style.left = `${Math.floor(Math.random() * 756)}px`;
ghostDiv.style.top = 0;
document.querySelector("#bg").appendChild(ghostDiv);
}
function moveGhost() {
const ghostsArr = document.getElementsByClassName("ghost");
const ghostsLeng = ghostsArr.length;
if (ghostsLeng > 0) {
for (let i = 0; i < ghostsLeng; i++) {
const ghost = ghostsArr[i];
let ghostTop = parseInt(ghost.style.top);
if (ghostTop < 536) {
ghostTop += 10;
ghost.style.top = ghostTop + "px";
} else {
// 바닥 끝까지 내려오면 게임끝
}
}
}
}
setInterval(createGhost, 5000);
setInterval(moveGhost, 1000);
아예 접근을 달리하여, 생성된 모든 .ghost div에 적용되는 함수를 1초마다 반복실행하기로 했다. 덕분에 setInterval() 함수를 createGhost()에서 꺼낼 수 있었다.
- "ghost" 클래스를 가진 모든 요소들을 가져와 배열(정확히는 HTML Collection)로 받는다.
- 반복문의 실행 횟수가 될 배열 길이를 구한다.
- 반복문으로 배열의 모든 요소에 접근하여 top 속성 값을 증가시킨다.
- for (ghost in ghostsArr)로 각 요소들을 가져오려고 했더니, ghostArr 형식이 HTML Collection라서 그런지 ghost 값이 태그가 아니라 0으로 나왔다. 그래서 그냥 인덱스 번호로 각 요소들을 가져왔다.
- 요소의 top 속성 값은 10px일 때 "px"까지 포함되어있기 때문에 parseInt()를 사용하여 숫자만 받아왔다.
- top 값이 맵의 높이를 초과하면 아래로 뚫고 나갈 수 있기 때문에 범위를 536(맵 height 600 - 유령 height 54)로 해두었다.
남은 과제는 캐릭터와 유령이 닿으면 유령이 터지게 하는 것, 바닥까지 내려왔을 때 게임이 끝나게 하는 것.
'TIL' 카테고리의 다른 글
[TIL-116] 위코드 15일차: 위스타그램 - 로그인 페이지 (0) | 2022.02.07 |
---|---|
[TIL-115] 위코드 14일차: enemyRain 게임 - 유령 잡기 (0) | 2022.02.06 |
[TIL-113] 위코드 12일차: enemyRain 게임 - 배경 이미지와 캐릭터 이동 (0) | 2022.02.04 |
[TIL-112] Javascript 클래스 (0) | 2022.02.03 |
[TIL-111] 노마드코더 그림판(JS) 클론코딩 #2.4~#2.7 (완강) (0) | 2022.02.02 |