====== ๐ŸŽจ UI/UX ๊ตฌํ˜„ ====== Phaser Baduk Metaverse ํ”„๋กœ์ ํŠธ์˜ ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค์™€ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ๊ตฌํ˜„์— ๋Œ€ํ•ด ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ---- ===== 1. UI/UX๋ž€ ๋ฌด์—‡์ธ๊ฐ€์š”? ===== ''UI''(''User Interface'')๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ํ”„๋กœ๊ทธ๋žจ๊ณผ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ํ™”๋ฉด์„ ์˜๋ฏธํ•˜๊ณ , ''UX''(''User Experience'')๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ํ”„๋กœ๊ทธ๋žจ์„ ์‚ฌ์šฉํ•  ๋•Œ ๋А๋ผ๋Š” ์ „์ฒด์ ์ธ ๊ฒฝํ—˜์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. **UI/UX์˜ ์ค‘์š”์„ฑ:** * ์‚ฌ์šฉ์ž๊ฐ€ ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์ธํ„ฐํŽ˜์ด์Šค. * ์ง๊ด€์ ์ธ ์กฐ์ž‘ ๋ฐฉ๋ฒ•. * ์ผ๊ด€๋œ ๋””์ž์ธ ์–ธ์–ด. * ์ ‘๊ทผ์„ฑ๊ณผ ์‚ฌ์šฉ์„ฑ ๊ณ ๋ ค. **๋ฐ”๋‘‘ ๊ฒŒ์ž„์—์„œ์˜ UI/UX:** * ๋ช…ํ™•ํ•œ ๋ฐ”๋‘‘ํŒ ํ‘œ์‹œ. * ๋Œ์„ ๋†“๋Š” ๋ฐฉ๋ฒ•์ด ์ง๊ด€์ . * ๊ฒŒ์ž„ ์ƒํƒœ ์ •๋ณด๊ฐ€ ๋ช…ํ™•. * ๋ชจ๋ฐ”์ผ๊ณผ ๋ฐ์Šคํฌํ†ฑ ๋ชจ๋‘ ์ง€์›. ---- ===== 2. ๋””์ž์ธ ์›์น™ ===== ==== 1) ์ง๊ด€์„ฑ ==== * ๋ฐ”๋‘‘ํŒ์˜ ์ „ํ†ต์ ์ธ 19x19 ๊ฒฉ์ž ๋ ˆ์ด์•„์›ƒ. * ๋ช…ํ™•ํ•œ ์ƒ‰์ƒ ๊ตฌ๋ถ„ (ํ‘๋ฐฑ ๋Œ). * ์ง๊ด€์ ์ธ ๋ฒ„ํŠผ ๋ฐฐ์น˜์™€ ์•„์ด์ฝ˜. * ์‚ฌ์šฉ์ž๊ฐ€ ํ•œ๋ˆˆ์— ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์ธํ„ฐํŽ˜์ด์Šค. ==== 2) ๋ฐ˜์‘ํ˜• ==== * ๋‹ค์–‘ํ•œ ํ™”๋ฉด ํฌ๊ธฐ์— ๋Œ€์‘. * ํ„ฐ์น˜ ์ธํ„ฐํŽ˜์ด์Šค ์ตœ์ ํ™”. * ํ‚ค๋ณด๋“œ ๋‹จ์ถ•ํ‚ค ์ง€์›. * ๋ชจ๋ฐ”์ผ๊ณผ ๋ฐ์Šคํฌํ†ฑ ๋ชจ๋‘ ์ตœ์ ํ™”. ==== 3) ์ ‘๊ทผ์„ฑ ==== * ๊ณ ๋Œ€๋น„ ์ƒ‰์ƒ ๋ชจ๋“œ. * ํ‚ค๋ณด๋“œ ๋„ค๋น„๊ฒŒ์ด์…˜. * ์Šคํฌ๋ฆฐ ๋ฆฌ๋” ์ง€์›. * ๋ชจ๋“  ์‚ฌ์šฉ์ž๊ฐ€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„. ---- ===== 3. UI ๊ตฌ์กฐ ์ดํ•ดํ•˜๊ธฐ ===== ==== 1) ๊ธฐ๋ณธ UI ํด๋ž˜์Šค ๊ตฌ์กฐ ==== 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'' - ๋ฒ„ํŠผ๋“ค์„ ํ‘œ์‹œํ•˜๋Š” ํŒจ๋„ ==== 2) ๋ ˆ์ด์–ด ๊ตฌ์กฐ ==== **Phaser.js์˜ ๋ ˆ์ด์–ด ์‹œ์Šคํ…œ:** * **๋ฐฐ๊ฒฝ ๋ ˆ์ด์–ด** - ๋ฐ”๋‘‘ํŒ ๊ฒฉ์ž * **๋Œ ๋ ˆ์ด์–ด** - ๋ฐ”๋‘‘๋Œ๋“ค * **UI ๋ ˆ์ด์–ด** - ๋ฒ„ํŠผ, ํ…์ŠคํŠธ, ํŒจ๋„ * **ํšจ๊ณผ ๋ ˆ์ด์–ด** - ์• ๋‹ˆ๋ฉ”์ด์…˜, ํšจ๊ณผ ---- ===== 4. ๋ฐ”๋‘‘ํŒ ๋ Œ๋”๋ง ===== ==== 1) ๊ธฐ๋ณธ ๊ฒฉ์ž ์ƒ์„ฑ ==== 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()'' - ๊ทธ๋ฆฐ ์„ ๋“ค์„ ์‹ค์ œ๋กœ ํ‘œ์‹œ ==== 2) ๋ณ„์  ์œ„์น˜ ํ‘œ์‹œ ==== 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, ๋ฐ˜์ง€๋ฆ„) ==== 3) ๋ฐ”๋‘‘ํŒ ๋ฐฐ๊ฒฝ ==== 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)'' - ๋ ˆ์ด์–ด ์ˆœ์„œ ์„ค์ • (์ˆซ์ž๊ฐ€ ์ž‘์„์ˆ˜๋ก ๋’ค์ชฝ) ---- ===== 5. ๋Œ ๋ Œ๋”๋ง ===== ==== 1) ๋Œ ์ƒ์„ฑ ํ•จ์ˆ˜ ==== 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)'' - ๊ฒฉ์ž๋ณด๋‹ค ์•ž์ชฝ์— ๋ฐฐ์น˜ ==== 2) ๋Œ ํด๋ฆญ ์ด๋ฒคํŠธ ==== 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% ํ™•๋Œ€) ---- ===== 6. UI ํŒจ๋„ ์ƒ์„ฑ ===== ==== 1) ์ •๋ณด ํŒจ๋„ ==== 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, ๋‚ด์šฉ, ์Šคํƒ€์ผ) ==== 2) ์ปจํŠธ๋กค ํŒจ๋„ ==== 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')'' - ํด๋ฆญ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ---- ===== 7. ๋ฐ˜์‘ํ˜• ๋””์ž์ธ ===== ==== 1) ํ™”๋ฉด ํฌ๊ธฐ ๊ฐ์ง€ ==== 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()'' - ์œ„์น˜ ๋ณ€๊ฒฝ ==== 2) ํ„ฐ์น˜ ์ธํ„ฐํŽ˜์ด์Šค ==== 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'' - ํ„ฐ์น˜ํ•œ ์œ„์น˜ * ์ขŒํ‘œ ๊ณ„์‚ฐ์œผ๋กœ ๋ฐ”๋‘‘ํŒ ์œ„์น˜ ํ™•์ธ ---- ===== 8. ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ ===== ==== 1) ๋Œ ๋†“๊ธฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ==== 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''' - ํƒ„์„ฑ ํšจ๊ณผ ==== 2) ๋Œ ์ œ๊ฑฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ==== 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()'' - ๋Œ ๊ฐ์ฒด ์ œ๊ฑฐ ---- ===== 9. ์ ‘๊ทผ์„ฑ ๊ธฐ๋Šฅ ===== ==== 1) ๊ณ ๋Œ€๋น„ ๋ชจ๋“œ ==== enableHighContrast() { // ๊ณ ๋Œ€๋น„ ์ƒ‰์ƒ ์„ค์ • this.boardBackground.setFillStyle(0x000000); this.gridLines.setStrokeStyle(1, 0xFFFFFF); // ํ…์ŠคํŠธ ์ƒ‰์ƒ ์กฐ์ • this.gameInfoText.setColor('#FFFFFF'); this.currentPlayerText.setColor('#FFFFFF'); } **์„ค๋ช…:** * ''setFillStyle()'' - ๋ฐฐ๊ฒฝ์ƒ‰ ์„ค์ • * ''setStrokeStyle()'' - ์„  ์ƒ‰์ƒ ์„ค์ • * ''setColor()'' - ํ…์ŠคํŠธ ์ƒ‰์ƒ ์„ค์ • ==== 2) ํ‚ค๋ณด๋“œ ๋„ค๋น„๊ฒŒ์ด์…˜ ==== 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')'' - ํ‚ค๋ณด๋“œ ์ด๋ฒคํŠธ * ๋ฐฉํ–ฅํ‚ค๋กœ ์„ ํƒ ์œ„์น˜ ์ด๋™ * Enter ํ‚ค๋กœ ๋Œ ๋†“๊ธฐ ---- ===== 10. ์‹ค์Šต ์˜ˆ์ œ ===== ==== 1) ๊ฐ„๋‹จํ•œ ๋ฐ”๋‘‘ํŒ UI ==== **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); }); } ==== 2) ์‹คํ–‰ ๋ฐฉ๋ฒ• ==== # ๋กœ์ปฌ ์„œ๋ฒ„ ์‹คํ–‰ python -m http.server 8000 # ๋˜๋Š” npx http-server **ํ…Œ์ŠคํŠธ:** * ๋ธŒ๋ผ์šฐ์ €์—์„œ ''http://localhost:8000'' ์ ‘์† * ๋ฐ”๋‘‘ํŒ์ด ํ‘œ์‹œ๋˜๋Š”์ง€ ํ™•์ธ ---- ===== 11. ๋‹ค์Œ ๋‹จ๊ณ„ ===== ''UI/UX'' ๊ธฐ๋ณธ์„ ๋ฐฐ์› ๋‹ค๋ฉด ๋‹ค์Œ์„ ํ•™์Šตํ•ด๋ณด์„ธ์š”: * **๊ณ ๊ธ‰ ์• ๋‹ˆ๋ฉ”์ด์…˜** - ๋ณต์žกํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ * **์‚ฌ์šด๋“œ ๋””์ž์ธ** - ํšจ๊ณผ์Œ๊ณผ ๋ฐฐ๊ฒฝ์Œ์•… * **๊ฒŒ์ž„ ์ƒํƒœ ๊ด€๋ฆฌ** - UI ์ƒํƒœ ๊ด€๋ฆฌ * **์‚ฌ์šฉ์ž ํ…Œ์ŠคํŠธ** - ์‹ค์ œ ์‚ฌ์šฉ์ž ํ”ผ๋“œ๋ฐฑ * **์„ฑ๋Šฅ ์ตœ์ ํ™”** - UI ๋ Œ๋”๋ง ์ตœ์ ํ™” **์ถ”์ฒœ ํ•™์Šต ์ˆœ์„œ:** - [[wiki:it:dream_of_enc:metaverse:phaser|๐ŸŽฎ Phaser.js ๊ฒŒ์ž„ ์—”์ง„]] - [[wiki:it:dream_of_enc:metaverse:game_logic|๐Ÿง  ๋ฐ”๋‘‘ ๊ฒŒ์ž„ ๋กœ์ง]] - [[wiki:it:dream_of_enc:metaverse:socketio|๐Ÿ”Œ Socket.IO ์‹ค์‹œ๊ฐ„ ํ†ต์‹ ]] --- **์นดํ…Œ๊ณ ๋ฆฌ:** [[wiki:it:dream_of_enc:metaverse:start|๋ฉ”ํƒ€๋ฒ„์Šค]] | **๊ด€๋ จ ๊ธฐ์ˆ :** Phaser.js, UI/UX, ๋ฐ˜์‘ํ˜• ๋””์ž์ธ