js实现重力感应迷宫球滚动小游戏代码
代码语言:html
所属分类:游戏
代码描述:js实现重力感应迷宫球滚动小游戏代码
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <style> body { /* https://coolors.co/f06449-ede6e3-7d82b8-36382e-613f75 */ --background-color: #ede6e3; --wall-color: #36382e; --joystick-color: #210124; --joystick-head-color: #f06449; --ball-color: #f06449; --end-color: #7d82b8; --text-color: #210124; font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; background-color: var(--background-color); } html, body { height: 100%; margin: 0; } #center { display: flex; align-items: center; justify-content: center; height: 100%; } #game { display: grid; grid-template-columns: auto 150px; grid-template-rows: 1fr auto 1fr; gap: 30px; perspective: 600px; } #maze { position: relative; grid-row: 1 / -1; grid-column: 1; width: 350px; height: 315px; display: flex; justify-content: center; align-items: center; } #end { width: 65px; height: 65px; border: 5px dashed var(--end-color); border-radius: 50%; } #joystick { position: relative; background-color: var(--joystick-color); border-radius: 50%; width: 50px; height: 50px; display: flex; align-items: center; justify-content: center; margin: 10px 50px; grid-row: 2; } #joystick-head { position: relative; background-color: var(--joystick-head-color); border-radius: 50%; width: 20px; height: 20px; cursor: grab; animation-name: glow; animation-duration: 0.6s; animation-iteration-count: infinite; animation-direction: alternate; animation-timing-function: ease-in-out; animation-delay: 4s; } @keyframes glow { 0% { transform: scale(1); } 100% { transform: scale(1.2); } } .joystick-arrow:nth-of-type(1) { position: absolute; bottom: 55px; width: 0; height: 0; border-left: 10px solid transparent; border-right: 10px solid transparent; border-bottom: 10px solid var(--joystick-color); } .joystick-arrow:nth-of-type(2) { position: absolute; top: 55px; width: 0; height: 0; border-left: 10px solid transparent; border-right: 10px solid transparent; border-top: 10px solid var(--joystick-color); } .joystick-arrow:nth-of-type(3) { position: absolute; left: 55px; width: 0; height: 0; border-top: 10px solid transparent; border-bottom: 10px solid transparent; border-left: 10px solid var(--joystick-color); } .joystick-arrow:nth-of-type(4) { position: absolute; right: 55px; width: 0; height: 0; border-top: 10px solid transparent; border-bottom: 10px solid transparent; border-right: 10px solid var(--joystick-color); } #note { grid-row: 3; grid-column: 2; text-align: center; font-size: 0.8em; color: var(--text-color); transition: opacity 2s; } a:visited { color: inherit; } .ball { position: absolute; margin-top: -5px; margin-left: -5px; border-radius: 50%; background-color: var(--ball-color); width: 10px; height: 10px; } .wall { position: absolute; background-color: var(--wall-color); transform-origin: top center; margin-left: -5px; } .wall::before, .wall::after { display: block; content: ""; width: 10px; height: 10px; background-color: inherit; border-radius: 50%; position: absolute; } .wall::before { top: -5px; } .wall::after { bottom: -5px; } .black-hole { position: absolute; margin-top: -9px; margin-left: -9px; border-radius: 50%; background-color: black; width: 18px; height: 18px; } #youtube, #youtube-card { display: none; } @media (min-height: 425px) { /** Youtube logo by https://codepen.io/alvaromontoro */ #youtube { z-index: 2; display: block; width: 100px; height: 70px; position: absolute; bottom: 20px; right: 20px; background: red; border-radius: 50% / 11%; transform: scale(0.8); transition: transform 0.5s; } #youtube:hover, #youtube:focus { transform: scale(0.9); } #youtube::before { content: ""; display: block; position: absolute; top: 7.5%; left: -6%; width: 112%; height: 85%; background: red; border-radius: 9% / 50%; } #youtube::after { content: ""; display: block; position: absolute; top: 20px; left: 40px; width: 45px; height: 30px; border: 15px solid transparent; box-sizing: border-box; border-left: 30px solid white; } #youtube span { font-size: 0; position: absolute; width: 0; height: 0; overflow: hidden; } #youtube:hover + #youtube-card { display: block; position: absolute; bottom: 12px; right: 10px; padding: 25px 130px 25px 25px; width: 300px; background-color: white; } } </style> </head> <body translate="no" > <div id="center"> <div id="game"> <div id="maze"> <div id="end"></div> </div> <div id="joystick"> <div class="joystick-arrow"></div> <div class="joystick-arrow"></div> <div class="joystick-arrow"></div> <div class="joystick-arrow"></div> <div id="joystick-head"></div> </div> <div id="note"> Click the joystick to start! <p>Move every ball to the center. Ready for hard mode? Press H</p> </div> </div> </div> <script> /* If you want to know how this game works, you can find a source code walkthrough video here: https://youtu.be/bTk6dcAckuI Follow me on twitter for more: https://twitter.com/HunorBorbely */ Math.minmax = (value, limit) => { return Math.max(Math.min(value, limit), -limit); }; const distance2D = (p1, p2) => { return Math.sqrt((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2); }; // Angle between the two points const getAngle = (p1, p2) => { let angle = Math.atan((p2.y - p1.y) / (p2.x - p1.x)); if (p2.x - p1.x < 0) angle += Math.PI; return angle; }; // The closest a ball and a wall cap can be const closestItCanBe = (cap, ball) => { let angle = getAngle(cap, ball); const deltaX = Math.cos(angle) * (wallW / 2 + ballSize / 2); const deltaY = Math.sin(angle) * (wallW / 2 + ballSize / 2); return { x: cap.x + deltaX, y: cap.y + deltaY }; }; // Roll the ball around the wall cap const rollAroundCap = (cap, ball) => { // The direction the ball can't move any further because the wall holds it back let impactAngle = getAngle(ball, cap); // The direction the ball wants to move based on it's velocity let heading = getAngle( { x: 0, y: 0 }, { x: ball.velocityX, y: ball.velocityY }); // The angle between the impact direction and the ball's desired direction // The smaller this angle is, the bigger the impact // The closer it is to 90 degrees the smoother it gets (at 90 there would be no collision) let impactHeadingAngle = impactAngle - heading; // Velocity distance if not hit would have occurred const velocityMagnitude = distance2D( { x: 0, y: 0 }, { x: ball.velocityX, y: ball.velocityY }); // Velocity component diagonal to the impact const velocityMagnitudeDiagonalToTheImpact = Math.sin(impactHeadingAngle) * velocityMagnitude; // How far should the ball be from the wall cap const closestDistance = wallW / 2 + ballSize / 2; const rotationAngle = Math.atan( velocityMagnitudeDiagonalToTheImpact / closestDistance); const deltaFromCap = { x: Math.cos(impactAngle + Math.PI - rotationAngle) * closestDistance, y: Math.sin(impactAngle + Math.PI - rotationAngle) * closestDistance }; const x = ball.x; const y = ball.y; const velocityX = ball.x - (cap.x + deltaFromCap.x); const velocityY = ball.y - (cap.y + deltaFromCap.y); const nextX = x + velocityX; const nextY = y + velocityY; return { x, y, velocityX, velocityY, nextX, nextY }; }; // Decreases the absolute value of a number but keeps it's sign, doesn't go below abs 0 const slow = (number, difference) => { if (Math.abs(number) <= difference) return 0; if (number > difference) return number - difference; return number + difference; }; const mazeElement = document.getElementById("maze"); const joystickHeadElement = document.getElementById("joystick-head"); const noteElement = document.getElementById("note"); // Note element for instructions and game won, game failed texts let hardMode = false; let previousTimestamp; let gameInProgress; let mouseStartX; let mouseStartY; let accelerationX; let accelerationY; let frictionX; let frictionY; const pathW = 25; // Path width const wallW = 10; // Wall width const ballSize = 10; // Width and height of the ball const holeSize = 18; const debugMode = false; let balls = []; let ballElements = []; let holeElements = []; resetGame(); // Draw balls for the first time balls.forEach(({ x, y }) => { const ball = document.createElement("div"); ball.setAttribute("class", "ball"); ball.style.cssText = `left: ${x}px; top: ${y}px; `; mazeElement.appendChild(ball); ballElements.push(ball); }); // Wall metadata const walls = [ // Border { column: 0, row: 0, horizontal: true, length: 10 }, { column: 0, row: 0, horizontal: false, length: 9 }, { column: 0, row: 9, horizontal: true, length: 10 }, { column: 10, row: 0, horizontal: false, length: 9 }, // Horizontal lines starting in 1st column { column: 0, row: 6, horizontal: true, length: 1 }, { column: 0, row: 8, horizontal: true, length: 1 }, // Horizontal lines starting in 2nd column { column: 1, row: 1, horizontal: true, length: 2 }, { column: 1, row: 7, horizontal: true, length: 1 }, // Horizontal lines starting in 3rd column { column: 2, row: 2, horizontal: true, length: 2 }, { column: 2, row: 4, horizontal: true, length: 1 }, { column: 2, row: 5, horizontal: true, length: 1 }, { column: 2, row: 6, horizontal: true, length: 1 }, // Horizontal lines starting in 4th column { column: 3, row: 3, horizontal: true, length: 1 }, { column: 3, row: 8, horizontal: true, length: 3 }, // Horizontal lines starting in 5th column { column: 4, row: 6, horizontal: true, length: 1 }, // Horizontal lines starting in 6th column { column: 5, row: 2, horizontal: true, length: 2 }, { column: 5, row: 7, horizontal: true, length: 1 }, // Horizontal lines starting in 7th column { column: 6, row: 1, horizontal: true, length: 1 }, { column: 6, row: 6, horizontal: true, length: 2 }, // Horizontal lines starting in 8th column { column: 7, row: 3, horizontal: true, length: 2 }, { column: 7, row: 7, horizontal: true, length: 2 }, // Horizontal lines starting in 9th column { column: 8, row: 1, horizontal: true, length: 1 }, { column: 8, row: 2, horizontal: true, length: 1 }, { column: 8, row: 3, horizontal: true, length: 1 }, { column: 8, row: 4, horizontal: true, length: 2 }, { column: 8, row: 8, horizontal: true, length: 2 }, // Vertical lines after the 1st column { column: 1, row: 1, horizontal: false, length: 2 }, { column: 1, row: 4, horizontal: false, length: 2 }, // Vertical lines after the 2nd column { column: 2, row: 2, horizontal: false, length: 2 }, { column: 2, row: 5, horizontal: false, length: 1 }, { column: 2, row: 7, horizontal: false, length: 2 }, // Vertical lines after the 3rd column { column: 3, row: 0, horizontal: false, length: 1 }, { column: 3, row: 4, horizontal: false, length: 1 }, { column: 3, row: 6, horizontal: false, length: 2 }, // Vertical lines after the 4th column { column: 4, row: 1, horizontal: false, length: 2 }, { column: 4, row: 6, horizontal: false, length: 1 }, // Vertical lines after the 5th column { column: 5, row: 0, horizontal: false, length: 2 }, { column: 5, row: 6, horizontal: false, length: 1 }, { column: 5, row: 8, horizontal: false, length: 1 }, // Vertical lines after the 6th column { column: 6, row: 4, horizontal: false, length: 1 }, { column: 6, row: 6, horizontal: false, length: 1 }, // Vertical lines after the 7th column { column: 7, row: 1, horizontal: false, length: 4 }, { column: 7, row: 7, horizontal: false, length: 2 }, // Vertical lines after the 8th column { column: 8, row: 2, horizontal: false, length: 1 }, { column: 8, row: 4, horizontal: false, length: 2 }, // Vertical lines after the 9th column { column: 9, row: 1, horizontal: false, length: 1 }, { column: 9, row: 5, horizontal: false, length: 2 }]. map(wall => ({ x: wall.column * (pathW + wallW), y: wall.row * (pathW + wallW), horizontal: wall.horizontal, length: wall.length * (pathW + wallW) })); // Draw walls walls.forEach(({ x, y, horizontal, length }) => { const wall = document.createElement("div"); wall.setAttribute("class", "wall"); wall.style.cssText = ` left: ${x}px; top: ${y}px; width: ${wallW}px; height: ${length}px; transform: rotate(${horizontal ? -90 : 0}deg); `; mazeElement.appendChild(wall); }); const holes = [ { column: 0, row: 5 }, { column: 2, row: 0 }, { column: 2, row: 4 }, { column: 4, row: 6 }, { column: 6, row: 2 }, { column: 6, row: 8 }, { column: 8, row: 1 }, { column: 8, row: 2 }]. map(hole => ({ x: hole.column * (wallW + pathW) + (wallW / 2 + pathW / 2), y: hole.row * (wallW + pathW) + (wallW / 2 + pathW / 2) })); joystickHeadElement.addEventListener("mousedown", function (event) { if (!gameInProgress) { mouseStartX = event.clientX; mouseStartY = event.clientY; gameInProgress = true; window.requestAnimationFrame(main); noteElement.style.opacity = 0; joystickHeadElement.style.cssText = ` animation: none; cursor: grabbing; `; } }); window.addEventListener("mousemove", function (event) { if (gameInProgres.........完整代码请登录后点击上方下载按钮下载查看
网友评论0