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: $.........完整代码请登录后点击上方下载按钮下载查看
网友评论0