달토끼 점프 게임 실습교안
달토끼 점프 게임 실습교안
소개
이 실습에서는 HTML, CSS, JavaScript를 이용하여 간단한 달토끼 점프 게임을 만들어 보겠습니다. 초등학생들이 직접 따라할 수 있도록 단계별로 안내합니다.
준비물
인터넷이 연결된 컴퓨터
텍스트 에디터 (예: Visual Studio Code, Sublime Text 등)
실습 단계
1단계: HTML 구조 작성
먼저 HTML 파일을 생성하고, 게임에 필요한 기본 구조를 작성합니다.
<!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, html {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
font-family: Arial, sans-serif;
}
#gameCanvas {
background-color: #000033; /* 밤하늘을 표현하기 위한 진한 파란색 */
}
#gameStats {
position: absolute;
top: 10px;
left: 10px;
color: white;
font-size: 20px;
}
#startScreen, #gameOverScreen {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
color: white;
font-size: 24px;
background-color: rgba(0, 0, 0, 0.7);
padding: 20px;
border-radius: 10px;
}
button {
font-size: 20px;
padding: 10px 20px;
margin-top: 20px;
cursor: pointer;
}
</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<div id="gameStats">점수: <span id="score">0</span></div>
<div id="startScreen">
<h1>달토끼 점프 게임</h1>
<p>장애물을 넘고 별을 모으세요!</p>
<button id="startButton">게임 시작</button>
</div>
<div id="gameOverScreen" style="display: none;">
<h1>게임 오버</h1>
<p>당신의 점수: <span id="finalScore"></span></p>
<button id="restartButton">다시 시작</button>
</div>
</body>
</html>
2단계: 게임 로직 구현
게임의 핵심 로직을 JavaScript로 작성합니다.
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreElement = document.getElementById('score');
const startScreen = document.getElementById('startScreen');
const gameOverScreen = document.getElementById('gameOverScreen');
const startButton = document.getElementById('startButton');
const restartButton = document.getElementById('restartButton');
const finalScoreElement = document.getElementById('finalScore');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let score = 0;
let gameState = 'start'; // 'start', 'playing', 'gameOver'
// SVG 이미지 정의
const bunnySvg = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80">
<style>rect { stroke: #000; stroke-width: 0.5; }</style>
<rect x="30" y="30" width="20" height="30" fill="#FFF"/>
<rect x="30" y="10" width="20" height="20" fill="#FFF"/>
<rect x="25" y="5" width="10" height="15" fill="#FFF"/>
<rect x="45" y="5" width="10" height="15" fill="#FFF"/>
<rect x="35" y="15" width="5" height="5" fill="#000"/>
<rect x="45" y="15" width="5" height="5" fill="#000"/>
<rect x="40" y="25" width="5" height="5" fill="#FFC0CB"/>
<rect x="25" y="55" width="10" height="5" fill="#FFF"/>
<rect x="45" y="55" width="10" height="5" fill="#FFF"/>
</svg>`;
const shellSvg = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 40">
<style>rect { stroke: #000; stroke-width: 0.2; }</style>
<g transform="translate(0,0)">
<rect x="2" y="18" width="4" height="4" fill="#FFA07A"/>
<rect x="6" y="14" width="4" height="8" fill="#FFA07A"/>
<rect x="10" y="10" width="4" height="12" fill="#FFA07A"/>
<rect x="14" y="6" width="4" height="16" fill="#FFA07A"/>
<rect x="18" y="10" width="4" height="12" fill="#FFA07A"/>
<rect x="22" y="14" width="4" height="8" fill="#FFA07A"/>
<rect x="26" y="18" width="4" height="4" fill="#FFA07A"/>
<rect x="18" y="22" width="8" height="4" fill="#FF8C69"/>
</g>
</svg>`;
const cloudSvg = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 80">
<style>rect { stroke: #E6E6FA; stroke-width: 0.2; }</style>
<g transform="translate(0,0)">
<rect x="10" y="20" width="10" height="10" fill="#FFFFFF" opacity="0.7"/>
<rect x="20" y="10" width="20" height="20" fill="#FFFFFF" opacity="0.7"/>
<rect x="40" y="20" width="10" height="10" fill="#FFFFFF" opacity="0.7"/>
</g>
</svg>`;
const starSvg = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
<polygon points="25,5 30,20 45,20 35,30 40,45 25,35 10,45 15,30 5,20 20,20" fill="#FFD700"/>
</svg>`;
// SVG를 이미지로 변환
function createImageFromSvg(svgString) {
const svgBlob = new Blob([svgString], {type: 'image/svg+xml'});
const svgUrl = URL.createObjectURL(svgBlob);
const img = new Image();
img.src = svgUrl;
return img;
}
const bunnyImg = createImageFromSvg(bunnySvg);
const shellImg = createImageFromSvg(shellSvg);
const cloudImg = createImageFromSvg(cloudSvg);
const starImg = createImageFromSvg(starSvg);
// 게임 객체
const bunny = {
x: 100,
y: canvas.height - 150,
width: 120,
height: 120,
speed: 5,
jumpStrength: 20,
yVelocity: 0,
isJumping: false
};
const obstacles = [];
const clouds = [];
const stars = [];
// 구름 초기화
for (let i = 0; i < 5; i++) {
clouds.push({
x: Math.random() * canvas.width,
y: Math.random() * (canvas.height / 2),
speed: 0.5 + Math.random(),
size: 80 + Math.random() * 80
});
}
// 게임 루프
function gameLoop() {
if (gameState === 'playing') {
update();
draw();
}
requestAnimationFrame(gameLoop);
}
function update() {
// 달토끼 업데이트
if (bunny.isJumping) {
bunny.yVelocity += 0.8; // 중력
bunny.y += bunny.yVelocity;
if (bunny.y > canvas.height - 150) {
bunny.y = canvas.height - 150;
bunny.isJumping = false;
bunny.yVelocity = 0;
}
}
// 장애물 업데이트
if (Math.random() < 0.02) {
obstacles.push({
x: canvas.width,
y: canvas.height - 90,
width: 60,
height: 60
});
}
obstacles.forEach((obstacle, index) => {
obstacle.x -= 5;
if (obstacle.x + obstacle.width < 0) {
obstacles.splice(index, 1);
score++;
scoreElement.textContent = score;
}
// 충돌 감지
if (
bunny.x < obstacle.x + obstacle.width &&
bunny.x + bunny.width > obstacle.x &&
bunny.y < obstacle.y + obstacle.height &&
bunny.y + bunny.height > obstacle.y
) {
gameOver();
}
});
// 구름 업데이트
clouds.forEach(cloud => {
cloud.x -= cloud.speed;
if (cloud.x + cloud.size < 0) {
cloud.x = canvas.width;
cloud.y = Math.random() * (canvas.height / 2);
cloud.size = 80 + Math.random() * 80;
}
});
// 별 업데이트
if (Math.random() < 0.02) {
stars.push({
x: canvas.width,
y: Math.random() * (canvas.height - 100),
size: 30 + Math.random() * 20
});
}
stars.forEach((star, index) => {
star.x -= 3;
if (star.x + star.size < 0) {
stars.splice(index, 1);
}
// 달토끼와의 충돌 감지
if (
bunny.x < star.x + star.size &&
bunny.x + bunny.width > star.x &&
bunny.y < star.y + star.size &&
bunny.y + bunny.height > star.y
) {
stars.splice(index, 1);
score += 5;
scoreElement.textContent = score;
}
});
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 별 배경 그리기
ctx.fillStyle = 'white';
for (let i = 0; i < 100; i++) {
ctx.beginPath();
ctx.arc(Math.random() * canvas.width, Math.random() * canvas.height, 1, 0, Math.PI * 2);
ctx.fill();
}
// 달 그리기
ctx.fillStyle = '#FFFACD';
ctx.beginPath();
ctx.arc(canvas.width - 100, 100, 50, 0, Math.PI * 2);
ctx.fill();
// 구름 그리기
clouds.forEach(cloud => {
ctx.drawImage(cloudImg, cloud.x, cloud.y, cloud.size, cloud.size / 2);
});
// 별 그리기
stars.forEach(star => {
ctx.drawImage(starImg, star.x, star.y, star.size, star.size);
});
// 달토끼 그리기
ctx.drawImage(bunnyImg, bunny.x, bunny.y, bunny.width, bunny.height);
// 장애물 그리기
obstacles.forEach(obstacle => {
ctx.drawImage(shellImg, obstacle.x, obstacle.y, obstacle.width, obstacle.height);
});
}
// 이벤트 리스너
document.addEventListener('keydown', (event) => {
if (event.code === 'Space' && !bunny.isJumping && gameState === 'playing') {
bunny.isJumping = true;
bunny.yVelocity = -bunny.jumpStrength;
}
});
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
bunny.y = canvas.height - 150;
});
startButton.addEventListener('click', startGame);
restartButton.addEventListener('click', startGame);
function startGame() {
gameState = 'playing';
score = 0;
scoreElement.textContent = '0';
obstacles.length = 0;
stars.length = 0;
bunny.y = canvas.height - 150;
bunny.isJumping = false;
bunny.yVelocity = 0;
startScreen.style.display = 'none';
gameOverScreen.style.display = 'none';
}
function gameOver() {
gameState = 'gameOver';
finalScoreElement.textContent = score;
gameOverScreen.style.display = 'block';
}
// 게임 루프 시작
gameLoop();
</script>
전체 코드
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Enhanced 달토끼 (Moon Rabbit) Jumping Game</title>
<style>
body, html {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
font-family: Arial, sans-serif;
}
#gameCanvas {
background-color: #000033; /* Dark blue for night sky */
}
#gameStats {
position: absolute;
top: 10px;
left: 10px;
color: white;
font-size: 20px;
}
#startScreen, #gameOverScreen {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
color: white;
font-size: 24px;
background-color: rgba(0, 0, 0, 0.7);
padding: 20px;
border-radius: 10px;
}
button {
font-size: 20px;
padding: 10px 20px;
margin-top: 20px;
cursor: pointer;
}
</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<div id="gameStats">Score: <span id="score">0</span></div>
<div id="startScreen">
<h1>달토끼 (Moon Rabbit)</h1>
<p>Jump over obstacles and collect stars!</p>
<button id="startButton">Start Game</button>
</div>
<div id="gameOverScreen" style="display: none;">
<h1>Game Over</h1>
<p>Your score: <span id="finalScore"></span></p>
<button id="restartButton">Play Again</button>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreElement = document.getElementById('score');
const startScreen = document.getElementById('startScreen');
const gameOverScreen = document.getElementById('gameOverScreen');
const startButton = document.getElementById('startButton');
const restartButton = document.getElementById('restartButton');
const finalScoreElement = document.getElementById('finalScore');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let score = 0;
let gameState = 'start'; // 'start', 'playing', 'gameOver'
// SVG definitions
const bunnySvg = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80">
<style>rect { stroke: #000; stroke-width: 0.5; }</style>
<rect x="30" y="30" width="20" height="30" fill="#FFF"/>
<rect x="30" y="10" width="20" height="20" fill="#FFF"/>
<rect x="25" y="5" width="10" height="15" fill="#FFF"/>
<rect x="45" y="5" width="10" height="15" fill="#FFF"/>
<rect x="35" y="15" width="5" height="5" fill="#000"/>
<rect x="45" y="15" width="5" height="5" fill="#000"/>
<rect x="40" y="25" width="5" height="5" fill="#FFC0CB"/>
<rect x="25" y="55" width="10" height="5" fill="#FFF"/>
<rect x="45" y="55" width="10" height="5" fill="#FFF"/>
</svg>`;
const shellsSvg = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 40">
<style>rect { stroke: #000; stroke-width: 0.2; }</style>
<g transform="translate(0,0)">
<rect x="2" y="18" width="4" height="4" fill="#FFA07A"/>
<rect x="6" y="14" width="4" height="8" fill="#FFA07A"/>
<rect x="10" y="10" width="4" height="12" fill="#FFA07A"/>
<rect x="14" y="6" width="4" height="16" fill="#FFA07A"/>
<rect x="18" y="10" width="4" height="12" fill="#FFA07A"/>
<rect x="22" y="14" width="4" height="8" fill="#FFA07A"/>
<rect x="26" y="18" width="4" height="4" fill="#FFA07A"/>
<rect x="18" y="22" width="8" height="4" fill="#FF8C69"/>
</g>
</svg>`;
const cloudsSvg = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 80">
<style>rect { stroke: #E6E6FA; stroke-width: 0.2; }</style>
<g transform="translate(0,0)">
<rect x="10" y="20" width="10" height="10" fill="#FFFFFF" opacity="0.7"/>
<rect x="20" y="10" width="20" height="20" fill="#FFFFFF" opacity="0.7"/>
<rect x="40" y="20" width="10" height="10" fill="#FFFFFF" opacity="0.7"/>
</g>
</svg>`;
const starSvg = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
<polygon points="25,5 30,20 45,20 35,30 40,45 25,35 10,45 15,30 5,20 20,20" fill="#FFD700"/>
</svg>`;
// Create image objects
function createImageFromSvg(svgString) {
const svgBlob = new Blob([svgString], {type: 'image/svg+xml'});
const svgUrl = URL.createObjectURL(svgBlob);
const img = new Image();
img.src = svgUrl;
return img;
}
const bunnyImg = createImageFromSvg(bunnySvg);
const shellImg = createImageFromSvg(shellsSvg);
const cloudImg = createImageFromSvg(cloudsSvg);
const starImg = createImageFromSvg(starSvg);
// Game objects
const bunny = {
x: 100,
y: canvas.height - 150,
width: 120,
height: 120,
speed: 5,
jumpStrength: 20,
yVelocity: 0,
isJumping: false
};
const obstacles = [];
const clouds = [];
const stars = [];
// Initialize clouds
for (let i = 0; i < 5; i++) {
clouds.push({
x: Math.random() * canvas.width,
y: Math.random() * (canvas.height / 2),
speed: 0.5 + Math.random(),
size: 80 + Math.random() * 80
});
}
// Game loop
function gameLoop() {
if (gameState === 'playing') {
update();
draw();
}
requestAnimationFrame(gameLoop);
}
function update() {
// Update bunny
if (bunny.isJumping) {
bunny.yVelocity += 0.8; // Gravity
bunny.y += bunny.yVelocity;
if (bunny.y > canvas.height - 150) {
bunny.y = canvas.height - 150;
bunny.isJumping = false;
bunny.yVelocity = 0;
}
}
// Update obstacles
if (Math.random() < 0.02) {
obstacles.push({
x: canvas.width,
y: canvas.height - 90,
width: 60,
height: 60
});
}
obstacles.forEach((obstacle, index) => {
obstacle.x -= 5;
if (obstacle.x + obstacle.width < 0) {
obstacles.splice(index, 1);
score++;
scoreElement.textContent = score;
}
// Collision detection
if (
bunny.x < obstacle.x + obstacle.width &&
bunny.x + bunny.width > obstacle.x &&
bunny.y < obstacle.y + obstacle.height &&
bunny.y + bunny.height > obstacle.y
) {
gameOver();
}
});
// Update clouds
clouds.forEach(cloud => {
cloud.x -= cloud.speed;
if (cloud.x + cloud.size < 0) {
cloud.x = canvas.width;
cloud.y = Math.random() * (canvas.height / 2);
cloud.size = 80 + Math.random() * 80;
}
});
// Update stars
if (Math.random() < 0.02) {
stars.push({
x: canvas.width,
y: Math.random() * (canvas.height - 100),
size: 30 + Math.random() * 20
});
}
stars.forEach((star, index) => {
star.x -= 3;
if (star.x + star.size < 0) {
stars.splice(index, 1);
}
// Collision detection with bunny
if (
bunny.x < star.x + star.size &&
bunny.x + bunny.width > star.x &&
bunny.y < star.y + star.size &&
bunny.y + bunny.height > star.y
) {
stars.splice(index, 1);
score += 5;
scoreElement.textContent = score;
}
});
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw stars background
ctx.fillStyle = 'white';
for (let i = 0; i < 100; i++) {
ctx.beginPath();
ctx.arc(Math.random() * canvas.width, Math.random() * canvas.height, 1, 0, Math.PI * 2);
ctx.fill();
}
// Draw moon
ctx.fillStyle = '#FFFACD';
ctx.beginPath();
ctx.arc(canvas.width - 100, 100, 50, 0, Math.PI * 2);
ctx.fill();
// Draw clouds
clouds.forEach(cloud => {
ctx.drawImage(cloudImg, cloud.x, cloud.y, cloud.size, cloud.size / 2);
});
// Draw collectible stars
stars.forEach(star => {
ctx.drawImage(starImg, star.x, star.y, star.size, star.size);
});
// Draw bunny
ctx.drawImage(bunnyImg, bunny.x, bunny.y, bunny.width, bunny.height);
// Draw obstacles
obstacles.forEach(obstacle => {
ctx.drawImage(shellImg, obstacle.x, obstacle.y, obstacle.width, obstacle.height);
});
}
// Event listeners
document.addEventListener('keydown', (event) => {
if (event.code === 'Space' && !bunny.isJumping && gameState === 'playing') {
bunny.isJumping = true;
bunny.yVelocity = -bunny.jumpStrength;
}
});
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
bunny.y = canvas.height - 150;
});
startButton.addEventListener('click', startGame);
restartButton.addEventListener('click', startGame);
function startGame() {
gameState = 'playing';
score = 0;
scoreElement.textContent = '0';
obstacles.length = 0;
stars.length = 0;
bunny.y = canvas.height - 150;
bunny.isJumping = false;
bunny.yVelocity = 0;
startScreen.style.display = 'none';
gameOverScreen.style.display = 'none';
}
function gameOver() {
gameState = 'gameOver';
finalScoreElement.textContent = score;
gameOverScreen.style.display = 'block';
}
// Start the game loop
gameLoop();
</script>
</body>
</html>
공유하기
조회수 : 1011