간이 블랙홀 시뮬레이터
제작 동기
💡
1학년 1학기때 만들었던 c언어를 이용한 공 시뮬레이션을 웹으로 만들어서 더 높은 퀄리티로 완성시켜보고싶어 만들게 되었습니다
프로젝트 설명
💡
사용자는 블랙홀의 반지름과 점을 생성할 위치를 입력받고, 입력된 값을 가지고 블랙홀과 점을 생성합니다. 점이 블랙홀로 빨려들어가는 과정을 볼수 있는 시뮬레이터입니다.
소스코드
HTML
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Blackhole Simulator</title> <link rel="stylesheet" href="style.css"> </head> <body> <div id="background"></div> <div id="titleScreen"> <h1 id="titleText">BLACKHOLE SIMULATOR</h1> </div> <div id="flashEffect"></div> <div id="controls"> <label>블랙홀 반지름: <input type="number" id="radiusInput" value="50"></label> <label>점의 X 위치: <input type="number" id="pointXInput" value="300"></label> <label>점의 Y 위치: <input type="number" id="pointYInput" value="200"></label> <label>회전 속도: <input type="range" id="rotationSpeedInput" min="0.001" max="0.05" step="0.001" value="0.01"></label> <button onclick="startSimulation()">시뮬레이션 시작</button> </div> <canvas id="simulation"></canvas> <script src="script.js"></script> </body> </html>
CSS
@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@700&display=swap'); body { margin: 0; overflow: hidden; background-color: black; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; } #background { position: absolute; width: 100%; height: 100%; filter: brightness(0.6); } #titleScreen { position: absolute; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; opacity: 1; transition: opacity 2s ease-out; } #titleText { font-family: 'Cinzel', serif; color: white; font-size: 50px; opacity: 0; transform: scale(0.5); animation: fadeInScaleUp 3s ease-out forwards; } @keyframes fadeInScaleUp { 0% { opacity: 0; transform: scale(0.5); } 50% { opacity: 1; transform: scale(1.5); } 100% { opacity: 1; transform: scale(1); } } #flashEffect { position: absolute; width: 100%; height: 100%; background: white; opacity: 0; transition: opacity 0.2s ease-out; } #controls { position: absolute; top: 20px; z-index: 10; background: rgba(255, 255, 255, 0.1); padding: 10px; border-radius: 10px; display: none; flex-direction: column; color: white; } canvas { display: none; }
Javascript
캔버스 설정
const canvas = document.getElementById("simulation"); const ctx = canvas.getContext("2d"); canvas.width = window.innerWidth; canvas.height = window.innerHeight; //그림을 그릴 캔버스를 선언 //ctx를 브러쉬로 설정 //width와 height 캔버스를 화면 크기만큼 할당
초기변수
let centerX = canvas.width / 2; let centerY = canvas.height / 2; let blackRadius; let pointX, pointY, distance, angle; let speed = 3; const minSpeed = 0.1; let roSpeed; //centerX,centerY으로 블랙홀 위치를 화면 가운데로 설정 // blackradius로 블랙홀 반지름 설정 //pointX,Y는 점의 시작 위치 //angle은 점이 도는 각도 //speed는 점이 블랙홀로 빨려들어가는 속도 //rospeed는 점이 도는 속도
오프닝 애니메이션
settimeout(() => { document.getElementById("titleScreen").style.opacity = "0"; settimeout(() => { document.getElementById("titleScreen").style.display = "none"; document.getElementById("controls").style.display = "flex"; }, 2000); }, 3000); //3초 후 스크린의 투명도를 0으로 설정 //2초후 스크린을 사라지게함 //이후 설정창을 띄움
시뮬레이션 함수
function startSimul() { document.getElementById("controls").style.display = "none"; document.getElementById("gameOverScreen").style.opacity = "0"; document.getElementById("flashEffect").style.opacity = "0"; canvas.style.display = "block"; blackHoleRadius = parseInt(document.getElementById("radiusInput").value); pointX = parseInt(document.getElementById("pointXInput").value); pointY = parseInt(document.getElementById("pointYInput").value); rotationSpeed = parseFloat(document.getElementById("rotationSpeedInput").value); distance = Math.sqrt((pointX - centerX) ** 2 + (pointY - centerY) ** 2); angle = Math.atan2(centerY - pointY, centerX - pointX); animate(); } //시뮬레이션 시작을 누르면 설정창 숨김 //게임 오버 메세지와 반짝이는 효과를 숨김 //캔버스를 화면에 보이게함 //사용자의 입력값으로 블랙홀의 크기와 점의 속도, 회전 속도를 설정 //distance로 블랙홀과 점 사이의 거리를 계산 //angle로 점과 블랙홀 사이의 각도를 계산 //애니메이션 시작
애니메이션 함수
function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.save(); ctx.shadowBlur = 30; ctx.shadowColor = "rgba(255, 255, 255, 0.5)"; ctx.beginPath(); ctx.arc(centerX, centerY, blackHoleRadius, 0, Math.PI * 2); ctx.fillStyle = "black"; ctx.fill(); ctx.restore(); if (distance > blackRadius) { speed = Math.max(minSpeed, speed * 0.99); distance *= 0.98; angle += rotationSpeed; pointX = centerX + distance * Math.cos(angle); pointY = centerY + distance * Math.sin(angle); } else { document.getElementById("flashEffect").style.opacity = "1"; setTimeout(() => { document.getElementById("flashEffect").style.opacity = "0"; setTimeout(() => document.getElementById("controls").style.display = "flex", 2000); }, 200); return; } ctx.beginPath(); ctx.arc(pointX, pointY, 5, 0, Math.PI * 2); ctx.fillStyle = "white"; ctx.fill(); requestAnimationFrame(animate); } //캔버스를 지움 //블랙홀을 검은 원으로 그림 //빛나는 효과 추가 //거리가 블랙홀보다 클때 계속이동 //속도를 점점 느려지게함 //점과 블랙홀 사이를 0.98배씩 줄임 //점이 회전하며 빨려들어감 //점이 블랙홀에 닿으면 화면이 흰색으로 번쩍거림 //2초후에 설정창을 다시 보이게함 //후에 받은 값으로 흰색 점을 그림 //requestAnimationFrame으로 애니메이션을 반복
시연영상
실행영상
느낀점!💫
💡
원래 1학년 1학기때 c언어 프로젝트로 구현하고자 했던 모습이 딱 이 모습이었다. 내 마음대로 속도도 조절할수 있고 블랙홀에 먹힐때 빛이 번쩍 하는것도 구현하고 싶었는데 잘 되었다. c언어 프로젝트는 visual studio로 했는데 계산기능도 지원을 잘 안해주고 오류도 엄청 자주나서 되게 힘들었었다. 근데 html,css,js로 하니까 복잡할거 없이 그냥 함수 기능이 딱딱 있어서 너무 좋았다 그냥 찾아서 사용하기만 하면 되었다. 레버같은것도 문서를 찾아보거나 ai한테 예시로 하나 만들어보라고 하고 그걸 따서 사용하였는데, 콘솔창으로 하는 c언어보다 백배 천만배 더 나았던거같다. 결론: 프론트엔드 언어나 파이썬을 하자 정 안되면 c언어보다는 c++을 하ㅈㅏ.. 아무튼 예전에 만들었었던 프로젝트를 발전시킬수 있는 좋은 경험이었던거같다.
작성:UNIFOX 유창대