Phaser Baduk Metaverse 프로젝트의 사용자 인터페이스와 사용자 경험 구현에 대해 설명합니다.
UI
(User Interface
)는 사용자가 프로그램과 상호작용하는 화면을 의미하고, UX
(User Experience
)는 사용자가 프로그램을 사용할 때 느끼는 전체적인 경험을 의미합니다.
UI/UX의 중요성:
바둑 게임에서의 UI/UX:
class BadukUI { constructor(scene) { this.scene = scene; this.board = null; this.stoneLayer = null; this.uiLayer = null; this.infoPanel = null; this.controlPanel = null; this.initUI(); } initUI() { // 바둑판 생성 this.createBoard(); // UI 레이어 생성 this.createUILayer(); // 정보 패널 생성 this.createInfoPanel(); // 컨트롤 패널 생성 this.createControlPanel(); } }
설명:
scene
- Phaser.js
의 게임 씬 객체board
- 바둑판 객체stoneLayer
- 돌들을 표시하는 레이어uiLayer
- UI 요소들을 표시하는 레이어infoPanel
- 게임 정보를 표시하는 패널controlPanel
- 버튼들을 표시하는 패널Phaser.js의 레이어 시스템:
createBoard() { // 19x19 격자 생성 const boardSize = 18 * 40; // 40px 간격 const startX = (800 - boardSize) / 2; const startY = (600 - boardSize) / 2; // 격자 그리기 const graphics = this.scene.add.graphics(); graphics.lineStyle(1, 0x8B4513, 1); for (let i = 0; i < 19; i++) { // 세로선 graphics.moveTo(startX + i * 40, startY); graphics.lineTo(startX + i * 40, startY + boardSize); // 가로선 graphics.moveTo(startX, startY + i * 40); graphics.lineTo(startX + boardSize, startY + i * 40); } graphics.strokePaths(); }
설명:
boardSize
- 바둑판의 전체 크기 (18 * 40 = 720px)startX, startY
- 바둑판의 시작 위치 (화면 중앙)graphics.lineStyle()
- 선의 스타일 설정 (두께, 색상)graphics.moveTo()
- 선의 시작점graphics.lineTo()
- 선의 끝점graphics.strokePaths()
- 그린 선들을 실제로 표시drawStarPoints(graphics, startX, startY) { const starPoints = [ [3, 3], [3, 9], [3, 15], [9, 3], [9, 9], [9, 15], [15, 3], [15, 9], [15, 15] ]; graphics.fillStyle(0x8B4513, 1); starPoints.forEach(([x, y]) => { graphics.fillCircle( startX + x * 40, startY + y * 40, 3 ); }); }
설명:
starPoints
- 바둑판의 별점 위치들 (3,3,9,9,15,15 등)graphics.fillStyle()
- 원의 색상 설정graphics.fillCircle()
- 원 그리기 (x, y, 반지름)createBoardBackground() { const boardSize = 18 * 40; const startX = (800 - boardSize) / 2; const startY = (600 - boardSize) / 2; // 바둑판 배경 (나무 색상) const background = this.scene.add.rectangle( startX + boardSize / 2, startY + boardSize / 2, boardSize + 20, boardSize + 20, 0xD2B48C ); background.setDepth(0); // 가장 뒤쪽에 배치 }
설명:
rectangle
- 사각형 생성 (x, y, width, height, color)setDepth(0)
- 레이어 순서 설정 (숫자가 작을수록 뒤쪽)createStone(x, y, color) { const stoneSize = 18; const startX = (800 - 18 * 40) / 2; const startY = (600 - 18 * 40) / 2; const stone = this.scene.add.circle( startX + x * 40, startY + y * 40, stoneSize, color === 'black' ? 0x000000 : 0xFFFFFF ); // 흰 돌에 테두리 추가 if (color === 'white') { stone.setStrokeStyle(1, 0x000000); } stone.setDepth(1); // 격자보다 앞쪽에 배치 return stone; }
설명:
circle
- 원 생성 (x, y, 반지름, 색상)setStrokeStyle()
- 테두리 스타일 설정setDepth(1)
- 격자보다 앞쪽에 배치addStoneClickHandler(stone, x, y) { stone.setInteractive(); stone.on('pointerdown', () => { // 돌 클릭 시 이벤트 처리 this.onStoneClick(x, y); }); // 호버 효과 stone.on('pointerover', () => { stone.setScale(1.1); }); stone.on('pointerout', () => { stone.setScale(1.0); }); }
설명:
setInteractive()
- 클릭 가능하게 설정on('pointerdown')
- 마우스 클릭 이벤트on('pointerover')
- 마우스 호버 이벤트setScale()
- 크기 조정 (1.1 = 10% 확대)createInfoPanel() { const panel = this.scene.add.rectangle( 650, 100, 120, 200, 0xF5F5F5 ); panel.setStrokeStyle(2, 0xCCCCCC); // 게임 정보 텍스트 this.gameInfoText = this.scene.add.text( 590, 20, '게임 정보', { fontSize: '16px', fill: '#333' } ); // 현재 플레이어 표시 this.currentPlayerText = this.scene.add.text( 590, 50, '현재: 흑', { fontSize: '14px', fill: '#000' } ); // 캡처된 돌 수 표시 this.capturedText = this.scene.add.text( 590, 80, '흑: 0개, 백: 0개', { fontSize: '12px', fill: '#666' } ); }
설명:
rectangle
- 패널 배경 생성setStrokeStyle()
- 테두리 설정add.text()
- 텍스트 추가 (x, y, 내용, 스타일)createControlPanel() { // 패스 버튼 this.passButton = this.scene.add.text( 590, 250, '패스', { fontSize: '16px', fill: '#333', backgroundColor: '#DDD' } ); this.passButton.setPadding(10, 5); this.passButton.setInteractive(); this.passButton.on('pointerdown', () => { this.onPass(); }); // 항복 버튼 this.surrenderButton = this.scene.add.text( 590, 300, '항복', { fontSize: '16px', fill: '#FFF', backgroundColor: '#D32F2F' } ); this.surrenderButton.setPadding(10, 5); this.surrenderButton.setInteractive(); this.surrenderButton.on('pointerdown', () => { this.onSurrender(); }); }
설명:
setPadding()
- 텍스트 주변 여백 설정setInteractive()
- 클릭 가능하게 설정on('pointerdown')
- 클릭 이벤트 처리handleResize() { const width = this.scene.game.config.width; const height = this.scene.game.config.height; // 화면 크기에 따라 UI 조정 if (width < 768) { // 모바일 레이아웃 this.adjustForMobile(); } else { // 데스크톱 레이아웃 this.adjustForDesktop(); } } adjustForMobile() { // 바둑판 크기 축소 this.board.setScale(0.8); // UI 패널 위치 조정 this.infoPanel.setPosition(300, 50); this.controlPanel.setPosition(300, 200); }
설명:
game.config.width/height
- 게임 화면 크기setScale()
- 크기 조정 (0.8 = 80% 크기)setPosition()
- 위치 변경setupTouchInterface() { // 터치 이벤트 설정 this.scene.input.on('pointerdown', (pointer) => { const x = Math.floor((pointer.x - this.boardStartX) / 40); const y = Math.floor((pointer.y - this.boardStartY) / 40); if (x >= 0 && x < 19 && y >= 0 && y < 19) { this.onBoardClick(x, y); } }); }
설명:
input.on('pointerdown')
- 터치/클릭 이벤트pointer.x/y
- 터치한 위치animateStonePlacement(x, y, color) { const stone = this.createStone(x, y, color); // 처음에는 작게 시작 stone.setScale(0.1); // 애니메이션으로 크기 조정 this.scene.tweens.add({ targets: stone, scaleX: 1, scaleY: 1, duration: 300, ease: 'Back.easeOut' }); }
설명:
setScale(0.1)
- 처음에는 10% 크기tweens.add()
- 애니메이션 추가duration: 300
- 300ms 동안 애니메이션ease: 'Back.easeOut
' - 탄성 효과animateStoneRemoval(stone) { this.scene.tweens.add({ targets: stone, scaleX: 0, scaleY: 0, alpha: 0, duration: 200, onComplete: () => { stone.destroy(); } }); }
설명:
alpha: 0
- 투명도 0으로 설정onComplete
- 애니메이션 완료 후 실행stone.destroy()
- 돌 객체 제거enableHighContrast() { // 고대비 색상 설정 this.boardBackground.setFillStyle(0x000000); this.gridLines.setStrokeStyle(1, 0xFFFFFF); // 텍스트 색상 조정 this.gameInfoText.setColor('#FFFFFF'); this.currentPlayerText.setColor('#FFFFFF'); }
설명:
setFillStyle()
- 배경색 설정setStrokeStyle()
- 선 색상 설정setColor()
- 텍스트 색상 설정setupKeyboardNavigation() { this.scene.input.keyboard.on('keydown', (event) => { switch(event.key) { case 'ArrowUp': this.moveSelection(0, -1); break; case 'ArrowDown': this.moveSelection(0, 1); break; case 'ArrowLeft': this.moveSelection(-1, 0); break; case 'ArrowRight': this.moveSelection(1, 0); break; case 'Enter': this.placeStone(); break; } }); }
설명:
input.keyboard.on('keydown')
- 키보드 이벤트HTML 파일:
<!DOCTYPE html> <html> <head> <title>바둑 게임</title> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/phaser.min.js"></script> </head> <body> <div id="game"></div> <script src="game.js"></script> </body> </html>
JavaScript 파일 (game.js
):
const config = { type: Phaser.AUTO, width: 800, height: 600, parent: 'game', scene: { create: create } }; const game = new Phaser.Game(config); function create() { // 바둑판 배경 const background = this.add.rectangle(400, 300, 720, 720, 0xD2B48C); // 격자 그리기 const graphics = this.add.graphics(); graphics.lineStyle(1, 0x8B4513, 1); for (let i = 0; i < 19; i++) { const x = 40 + i * 40; const y = 40 + i * 40; // 세로선 graphics.moveTo(x, 40); graphics.lineTo(x, 760); // 가로선 graphics.moveTo(40, y); graphics.lineTo(760, y); } graphics.strokePaths(); // 별점 그리기 const starPoints = [[3,3], [3,9], [3,15], [9,3], [9,9], [9,15], [15,3], [15,9], [15,15]]; graphics.fillStyle(0x8B4513, 1); starPoints.forEach(([x, y]) => { graphics.fillCircle(40 + x * 40, 40 + y * 40, 3); }); }
# 로컬 서버 실행 python -m http.server 8000 # 또는 npx http-server
테스트:
http://localhost:8000
접속
UI/UX
기본을 배웠다면 다음을 학습해보세요:
추천 학습 순서:
—
카테고리: 메타버스 | 관련 기술: Phaser.js, UI/UX, 반응형 디자인