-
지뢰찾기를 만들어보자 #9 - 사용자 인터페이스 변경 2/4기술 관련/etc 2022. 10. 10. 11:08
지난번 글에서 지뢰찾기 게임의 사용자 인터페이스를 변경 했었다. https://mc500.tistory.com/675 이번 글에서는 이어서 점수와 깃발 갯수를 표시하는 부분을 변경해 보도록 하겠다.
3. 점수 및 깃발 수 표시 변경
Windows 지뢰 찾기에는 이 정보를 표시할 때 빨간색 숫자로 표시되었었다. 상태에 따라 표시가 되는 곳을 7개의 구역으로 나누어져 있기에 7 segment 라고 부르며 전광판이나 전자 회로에서 간단한 숫자를 표시할 때 사용된다.
앞서 해왔던 방법으로 위의 이미지를 타일로 만들어 표시하는게 가장 쉬운 방법이다. 하지만 이번에는 HTML5 Canvas의 기능을 이용하여 표현해 보고자 한다.
7 segement의 각 부분에 번호를 지정하고 이를 활용하고자 한다. 번호는 사실 프로그래밍 하는 사람 마음이므로 편하게 지정 할 수 있다. 이 지뢰찾기에서는 각 부분에 다음과 같은 숫자를 지정했고 아래 지정된 숫자 또는 기호에 대한 code table을 구현 했다.
const CONST_7SEGMENT_CODES = { '0':[ true, true, true, true, true, true, false], '1':[false, false, true, true, false, false, false], '2':[false, true, true, false, true, true, true], '3':[false, true, true, true, true, false, true], '4':[ true, false, true, true, false, false, true], '5':[ true, true, false, true, true, false, true], '6':[ true, true, false, true, true, true, true], '7':[ true, true, true, true, false, false, false], '8':[ true, true, true, true, true, true, true], '9':[ true, true, true, true, true, false, true], '-':[false, false, false, false, false, false, true], }
코드 테이블이 준비되었다면, 헤더 영역의 특정 위치에 segment 모듈 하나를 그리는 method를 만들어 보자draw7Segment(x, y, number) { let fillStyleOn = "rgba(256, 0, 0, 1)" let fillStyleOff = "rgba(103, 50, 36, 1)" let ctx = this.header.ctx x = x || 0 y = y || 0 let segment = CONST_7SEGMENT_CODES[number] || CONST_7SEGMENT_CODES['-'] // Background ctx.beginPath() ctx.fillStyle = "rgba(36, 50, 36, 1)" ctx.rect(x, y, 18, 29) ctx.fill() ctx.closePath() // TODO: draw each segments }
여기에 각각 segment들을 그리는 코드를 추가한다.
더보기// 0. left-top ctx.beginPath() if (segment[0]) { ctx.fillStyle = fillStyleOn } else { ctx.fillStyle = fillStyleOff } ctx.strokeStyle = ctx.fillStyle ctx.moveTo(x+2, y+2) ctx.lineTo(x+2, y+13) ctx.lineTo(x+4, y+11) ctx.lineTo(x+4, y+1) ctx.lineTo(x+2, y+2) ctx.fill() ctx.stroke() ctx.closePath() // 1. top ctx.beginPath() if (segment[1]) { ctx.fillStyle = fillStyleOn } else { ctx.fillStyle = fillStyleOff } ctx.rect(x+5, y+1, 8, 3) ctx.fill() ctx.closePath() // 2. right-top ctx.beginPath() if (segment[2]) { ctx.fillStyle = fillStyleOn } else { ctx.fillStyle = fillStyleOff } ctx.strokeStyle = ctx.fillStyle ctx.moveTo(x+14, y+1) ctx.lineTo(x+14, y+11) ctx.lineTo(x+16, y+13) ctx.lineTo(x+16, y+2) ctx.lineTo(x+14, y+1) ctx.fill() ctx.stroke() ctx.closePath() // 3. right-bottom ctx.beginPath() if (segment[3]) { ctx.fillStyle = fillStyleOn } else { ctx.fillStyle = fillStyleOff } ctx.strokeStyle = ctx.fillStyle ctx.moveTo(x+14, y+17) ctx.lineTo(x+14, y+27) ctx.lineTo(x+15, y+27) ctx.lineTo(x+16, y+26) ctx.lineTo(x+16, y+15) ctx.lineTo(x+14, y+17) ctx.fill() ctx.stroke() ctx.closePath() // 4. bottom ctx.beginPath() if (segment[4]) { ctx.fillStyle = fillStyleOn } else { ctx.fillStyle = fillStyleOff } ctx.rect(x+5, y+25, 8, 3) ctx.fill() ctx.closePath() // 5. right-bottom ctx.beginPath() if (segment[5]) { ctx.fillStyle = fillStyleOn } else { ctx.fillStyle = fillStyleOff } ctx.strokeStyle = ctx.fillStyle ctx.moveTo(x+2, y+15) ctx.lineTo(x+2, y+26) ctx.lineTo(x+3, y+27) ctx.lineTo(x+4, y+27) ctx.lineTo(x+4, y+17) ctx.lineTo(x+2, y+15) ctx.fill() ctx.stroke() ctx.closePath() // 6. center ctx.beginPath() if (segment[6]) { ctx.fillStyle = fillStyleOn } else { ctx.fillStyle = fillStyleOff } ctx.strokeStyle = ctx.fillStyle ctx.moveTo(x+5, y+13) ctx.lineTo(x+4, y+14) ctx.lineTo(x+5, y+15) ctx.lineTo(x+13, y+15) ctx.lineTo(x+14, y+14) ctx.lineTo(x+13, y+13) ctx.lineTo(x+5, y+13) ctx.fill() ctx.stroke() ctx.closePath()
이렇게 만들어진 draw7Segement()를 이용하여 게임 점수를 표시하는 method를 만든다. 7segment는 각각의 자릿수를 의미하므로 100의 자리, 10의 자리, 1의 자리에 대한 번호를 얻고 이를 array로 담아 연속된 형태로 호출한다.
drawElapsedTime(seconds) { let digits = [ if (seconds > 99) { let n = seconds - seconds%100 seconds = seconds-n digits.push(n/100) } else { digits.push(0) } if (number > 9) { let n = seconds - seconds%10 seconds = seconds-n digits.push(n/10) } else { digits.push(0) } digits.push(seconds) for (let i in digits) { this.draw7Segment(i*18, 0, digits[i]) } },
이렇게 만들어진 method는 게임 각각 초기화 코드인 initGames()와 elapsted_time을 변경하는 method곳에서 호출한다.
this.initBoxes() this.refreshAll() this.drawFace() this.drawElapsedTime(this.elapsed_time)
let diff = (new Date().getTime()) - self.start_time self.elapsed_time = Math.round(diff/1000) self.drawElapsedTime(self.elapsed_time)
스마일 아이콘 왼쪽에 아래 표기된 시간과 동일하게 나타나는 것을 볼 수 있다.
남은 깃발 개수도 표시할 drawLeftFlags() method도 drawElapsedTime()과 크게 다르지 않으므로, 자릿 수 구분을 위한 로직을 분리하여 buildDigits()로 만들고 이를 이용하도록 한다.buildDigits(number) { let digits = [] if (number > 99) { let n = number - number%100 number = number-n digits.push(n/100) } else { digits.push(0) } if (number > 9) { let n = number - number%10 number = number-n digits.push(n/10) } else { digits.push(0) } digits.push(number) return digits }, drawElapsedTime(seconds) { let digits = this.buildDigits(seconds) for (let i in digits) { this.draw7Segment(i*18, 0, digits[i]) } }, drawLeftFlags(flags) { let digits = this.buildDigits(flags) for (let i in digits) { this.draw7Segment(206+i*18, 0, digits[i]) } },
drawLeftFlags()는 initGames()와 깃발 수를 변경하는 flagup()에서 호출한다.
this.initBoxes() this.refreshAll() this.drawFace() this.drawElapsedTime(this.elapsed_time) this.drawLeftFlags(this.mines)
this.drawLeftFlags(this.mines - this.flags)
그리고, 마지막으로 기존에 Text로 표시되던 코드를 화면에 표시되지 않도록 변경하면
<div style="display:none;"> <span>{{elapsed_time}} sec(s), {{mines - flags}} left</span> </div>
4. 999초 시간 제한
게임 진행 시간 표시는 999초 (대략 16분) 까지 표시된다. 만약 그 이상인 경우 게임은 자동으로 종료되도록 코드를 추가한다.self.drawElapsedTime(self.elapsed_time) if (self.elapsed_time > 999) { self.finishGame() }
5. 깃발 수를 지뢰 갯수로 제한
현재는 깃발수가 무제한으로 되어 있는데 남은 깃발의 개수를 표시하는 부분이 음수가 되므로 이에 대한 제한을 둔다.
flagup() 에서 깃발을 다음과 같이 추가할 때 지뢰의 갯수와 비교를 하여 음수가 되지 않도록 한다.if (this.flags == this.mines) { return }
4. 게임 실패 시 모든 상자 공개
finishGame()에 revealBoxes()를 추가하여 게임에서 실패 했을 때 그대로 결과만 보여주는 것이 아닌 가려져 있던 모든 상자를 공개하여 상태를 확인 할 수 있도록 한다.this.start_time = 0 this.gameover = true if (win) { this.highscores[this.level] = record this.drawFace(CONST_FACE.win) } else { this.revealBoxes() this.drawFace(CONST_FACE.lose) }
'기술 관련 > etc' 카테고리의 다른 글
지뢰찾기를 만들어보자 #11 - 사용자 인터페이스 변경 4/4 (0) 2022.10.29 지뢰찾기를 만들어보자 #10 - 사용자 인터페이스 변경 3/4 (0) 2022.10.22 지뢰찾기를 만들어보자 #8 - 사용자 인터페이스 변경 1/4 (0) 2022.10.09 지뢰찾기를 만들어보자 #7 - 게임 스코어 구현 (1) 2022.10.05 지뢰찾기를 만들어보자 #6 - 게임 난이도 설정 (0) 2022.10.02