[[분류:프로그래밍 언어]] ==개요== HTML은 세계 최고의 프로그래밍 언어이다! 개발자 도구로 각종 웹사이트를 들여다보면 그 코드가 HTML로 되어 있는 것을 볼 수 있을 것이다. 프로그래밍을 배우고 싶은 사람은 한번 HTML을 배워보도록 하자. ==HTML 파일을 만드는 법== Visual Studio Code 같은 전문 프로그래밍 도구도 필요 없을 정도로 쉽다. 'gpt야 만들어줘'를 통해 3분이면 HTML 코드를 얻을 수 있다! ==이것을 이용한 작품== ===대항해 시대([[손현준(교사)|손현준]])=== [[손현준(교사)]]이 자신의 블로그 '번데기의 최후'에 올려 놓은 항해 게임이다. 코드는 아래와 같다. {{{#!folding 전체 코드 <!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>🌋 화산 피하기 게임 🚗</title> <style> /* 기본 스타일 및 레이아웃 */ body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f0f0f0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; flex-direction: column; } #game-container { position: relative; width: 300px; height: 500px; background-color: #556b2f; border: 5px solid #000; overflow: hidden; touch-action: none; } /* 플레이어 스타일 */ #player { position: absolute; width: 40px; height: 40px; font-size: 35px; text-align: center; line-height: 40px; bottom: 10px; transform: rotate(90deg); transition: transform 0.1s linear; z-index: 10; } /* 장애물 공통 스타일 */ .obstacle { position: absolute; box-sizing: border-box; } /* 화산 스타일 */ .volcano-spot { border-radius: 50%; opacity: 0; transition: opacity 0.2s linear; } .warning { background-color: sienna; opacity: 0.6; box-shadow: 0 0 8px rgba(160, 82, 45, 0.7); } .erupted { background-color: red; box-shadow: 0 0 15px 8px #ff4500; transform: scale(1.1); opacity: 1; } /* 검은 벽 장애물 스타일 */ .black-wall { background-color: #2c2c2c; box-shadow: 0 0 5px rgba(0, 0, 0, 0.7); z-index: 5; } /* UI 요소 스타일 */ .ui-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.8); color: white; display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; z-index: 20; } #game-title { position: absolute; top: 10px; width: 100%; text-align: center; color: white; font-size: 24px; font-weight: bold; text-shadow: 2px 2px 4px #000; z-index: 15; } #info-board { position: absolute; top: 5px; right: 5px; color: white; font-size: 14px; font-weight: bold; z-index: 15; } #game-over { display: none; } #start-screen { display: flex; } .game-button { padding: 10px 20px; font-size: 20px; cursor: pointer; margin-top: 30px; border: none; border-radius: 5px; background-color: #4CAF50; color: white; } </style> </head> <body> <div id="game-container"> <div id="game-title">🌋 화산 피하기 🚗</div> <div id="info-board"> 거리: <span id="distance-display">0</span>m </div> <div id="player">🚗</div> <div id="start-screen" class="ui-overlay"> <h1>화산 피하기</h1> <p>화산 폭발과 벽을 피해<br>최대한 멀리 도망가세요!</p> <button id="start-button" class="game-button">게임 시작</button> </div> <div id="game-over" class="ui-overlay"> <h1>💥 게임 오버! 💥</h1> 최종 점수: <span id="final-score">0</span><br> 이동 거리: <span id="final-distance">0</span>m<br> <button id="restart-button" class="game-button">다시 시작</button> </div> </div> <script> // DOM 요소 const gameContainer = document.getElementById('game-container'); const player = document.getElementById('player'); const distanceDisplay = document.getElementById('distance-display'); const gameOverScreen = document.getElementById('game-over'); const finalScoreSpan = document.getElementById('final-score'); const finalDistanceSpan = document.getElementById('final-distance'); const restartButton = document.getElementById('restart-button'); const startScreen = document.getElementById('start-screen'); const startButton = document.getElementById('start-button'); // 게임 설정 const CONTAINER_WIDTH = gameContainer.clientWidth; const CONTAINER_HEIGHT = gameContainer.clientHeight; const PLAYER_SIZE = 40; const PLAYER_SPEED = 4; const BASE_SCROLL_SPEED = 3.0; // 게임 상태 변수 let isGameRunning = false; let score = 0, distance = 0; let playerX, playerY, playerAngle, playerScaleX; let keysPressed = {}; let lastVolcanoTime = 0, lastWallTime = 0; let animationFrameId; function initGame() { isGameRunning = true; score = 0; distance = 0; playerX = CONTAINER_WIDTH / 2 - PLAYER_SIZE / 2; playerY = CONTAINER_HEIGHT - PLAYER_SIZE - 10; playerAngle = 90; playerScaleX = 1; distanceDisplay.textContent = '0'; startScreen.style.display = 'none'; gameOverScreen.style.display = 'none'; document.querySelectorAll('.obstacle').forEach(el => el.remove()); player.style.left = `${playerX}px`; player.style.top = `${playerY}px`; player.style.transform = `rotate(90deg) scaleX(1)`; player.style.visibility = 'visible'; lastVolcanoTime = performance.now(); lastWallTime = performance.now(); if(animationFrameId) cancelAnimationFrame(animationFrameId); gameLoop(); } function updatePlayer() { let dx = 0, dy = 0; if (keysPressed['ArrowLeft'] || keysPressed['a']) dx -= PLAYER_SPEED; if (keysPressed['ArrowRight'] || keysPressed['d']) dx += PLAYER_SPEED; if (keysPressed['ArrowUp'] || keysPressed['w']) dy -= PLAYER_SPEED; if (keysPressed['ArrowDown'] || keysPressed['s']) dy += PLAYER_SPEED; let nextX = playerX + dx; let nextY = playerY + dy; document.querySelectorAll('.black-wall').forEach(wall => { const wallRect = wall.getBoundingClientRect(); const containerRect = gameContainer.getBoundingClientRect(); const wallGameRect = { left: wallRect.left - containerRect.left, top: wallRect.top - containerRect.top, right: wallRect.right - containerRect.left, bottom: wallRect.bottom - containerRect.top }; const hPlayerRect = { left: nextX, top: playerY, right: nextX + PLAYER_SIZE, bottom: playerY + PLAYER_SIZE }; if (hPlayerRect.right > wallGameRect.left && hPlayerRect.left < wallGameRect.right && hPlayerRect.bottom > wallGameRect.top && hPlayerRect.top < wallGameRect.bottom) { nextX = playerX; } const vPlayerRect = { left: playerX, top: nextY, right: playerX + PLAYER_SIZE, bottom: nextY + PLAYER_SIZE }; if (vPlayerRect.right > wallGameRect.left && vPlayerRect.left < wallGameRect.right && vPlayerRect.bottom > wallGameRect.top && vPlayerRect.top < wallGameRect.bottom) { nextY = playerY; } }); playerX = Math.max(0, Math.min(CONTAINER_WIDTH - PLAYER_SIZE, nextX)); playerY = Math.max(0, Math.min(CONTAINER_HEIGHT - PLAYER_SIZE, nextY)); if (dx !== 0 || dy !== 0) { if (dy < 0) playerAngle = dx === 0 ? 90 : (dx < 0 ? 45 : 135); else if (dy > 0) playerAngle = dx === 0 ? -90 : (dx < 0 ? -45 : -135); else playerAngle = 0; if (dx < 0) playerScaleX = 1; else if (dx > 0) playerScaleX = -1; } player.style.left = `${playerX}px`; player.style.top = `${playerY}px`; player.style.transform = `rotate(${playerAngle}deg) scaleX(${playerScaleX})`; } function isOverlapping(newRect) { let overlapping = false; document.querySelectorAll('.obstacle').forEach(obstacle => { const existingRect = obstacle.getBoundingClientRect(); const containerRect = gameContainer.getBoundingClientRect(); const existingGameRect = { left: existingRect.left - containerRect.left, top: existingRect.top - containerRect.top, right: existingRect.right - containerRect.left, bottom: existingRect.bottom - containerRect.top }; const padding = 10; if (newRect.right + padding > existingGameRect.left && newRect.left - padding < existingGameRect.right && newRect.bottom + padding > existingGameRect.top && newRect.top - padding < existingGameRect.bottom) { overlapping = true; } }); return overlapping; } function createVolcano(timestamp, difficultyLevel) { const interval = Math.max(500, 1600 - difficultyLevel * 150); if (timestamp - lastVolcanoTime < interval) return; lastVolcanoTime = timestamp; let newRect, w, h, x, y; for (let i = 0; i < 10; i++) { const minSize = Math.min(50, 30 + difficultyLevel * 4); const maxSize = Math.min(90, 60 + difficultyLevel * 6); const size = Math.random() * (maxSize - minSize) + minSize; w = size; h = size; if (Math.random() < 0.5) h = Math.random() * (maxSize - minSize) + minSize; x = Math.random() * (CONTAINER_WIDTH - w); y = -h; newRect = { left: x, top: y, right: x + w, bottom: y + h }; if (!isOverlapping(newRect)) break; if (i === 9) return; } const volcano = document.createElement('div'); volcano.className = 'obstacle volcano-spot'; volcano.style.width = `${w}px`; volcano.style.height = `${h}px`; volcano.style.left = `${x}px`; volcano.style.top = `${y}px`; gameContainer.appendChild(volcano); setTimeout(() => volcano.classList.add('warning'), 600); setTimeout(() => { volcano.classList.remove('warning'); volcano.classList.add('erupted'); }, 800); setTimeout(() => volcano.remove(), 3000); } function createBlackWall(timestamp, difficultyLevel) { const interval = Math.max(1200, 2800 - difficultyLevel * 200); if (timestamp - lastWallTime < interval) return; lastWallTime = timestamp; let newRect, width, angle, x, y; for (let i = 0; i < 10; i++) { const minWidth = Math.min(PLAYER_SIZE * 1.5, PLAYER_SIZE + difficultyLevel * 5); const maxWidth = Math.min(PLAYER_SIZE * 2.5, PLAYER_SIZE * 2 + difficultyLevel * 8); const WALL_HEIGHT = 20; width = Math.random() * (maxWidth - minWidth) + minWidth; angle = Math.random() * 180 - 90; x = Math.random() * (CONTAINER_WIDTH - width); y = -WALL_HEIGHT; newRect = { left: x, top: y, right: x + width, bottom: y + WALL_HEIGHT }; if (!isOverlapping(newRect)) break; if (i === 9) return; } const wall = document.createElement('div'); wall.className = 'obstacle black-wall'; wall.style.width = `${width}px`; wall.style.height = `20px`; wall.style.left = `${x}px`; wall.style.top = `${y}px`; wall.style.transform = `rotate(${angle}deg)`; gameContainer.appendChild(wall); setTimeout(() => wall.remove(), 5000); } function updateObstacles(currentScrollSpeed) { document.querySelectorAll('.obstacle').forEach(obstacle => { let top = parseFloat(obstacle.style.top) || 0; obstacle.style.top = `${top + currentScrollSpeed}px`; if (obstacle.classList.contains('erupted')) { checkVolcanoCollision(obstacle); } if (obstacle.classList.contains('black-wall')) { const playerRect = { left: playerX, top: playerY, right: playerX + PLAYER_SIZE, bottom: playerY + PLAYER_SIZE }; const wallRect = obstacle.getBoundingClientRect(); const containerRect = gameContainer.getBoundingClientRect(); const wallGameRect = { left: wallRect.left - containerRect.left, top: wallRect.top - containerRect.top, right: wallRect.right - containerRect.left, bottom: wallRect.bottom - containerRect.top }; if (playerRect.right > wallGameRect.left && playerRect.left < wallGameRect.right && playerRect.bottom > wallGameRect.top && playerRect.top < wallGameRect.bottom) { // ⭐ [수정됨] 벽이 플레이어를 밀어낼 때, 즉시 화면에 반영 playerY = wallGameRect.bottom; player.style.top = `${playerY}px`; } } }); } function checkVolcanoCollision(volcano) { if (!isGameRunning) return; const playerRect = player.getBoundingClientRect(); const volcanoRect = volcano.getBoundingClientRect(); if (playerRect.right > volcanoRect.left && playerRect.left < volcanoRect.right && playerRect.bottom > volcanoRect.top && playerRect.top < volcanoRect.bottom) { endGame(); } } function endGame() { if (!isGameRunning) return; isGameRunning = false; cancelAnimationFrame(animationFrameId); player.style.visibility = 'hidden'; finalScoreSpan.textContent = Math.floor(score); finalDistanceSpan.textContent = Math.floor(distance); gameOverScreen.style.display = 'flex'; } function gameLoop(timestamp) { if (!isGameRunning) return; const difficultyLevel = Math.floor(distance / 200); const currentScrollSpeed = BASE_SCROLL_SPEED + difficultyLevel * 0.25; updatePlayer(); createVolcano(timestamp, difficultyLevel); createBlackWall(timestamp, difficultyLevel); updateObstacles(currentScrollSpeed); score += currentScrollSpeed * 0.5; distance += currentScrollSpeed * 0.1; distanceDisplay.textContent = `${Math.floor(distance)}`; if (playerY >= CONTAINER_HEIGHT - PLAYER_SIZE) { endGame(); } animationFrameId = requestAnimationFrame(gameLoop); } // 이벤트 리스너 document.addEventListener('keydown', (e) => { keysPressed[e.key] = true; }); document.addEventListener('keyup', (e) => { keysPressed[e.key] = false; }); startButton.addEventListener('click', initGame); restartButton.addEventListener('click', initGame); </script> </body> </html> }}}