JavaScript小游戏(附源码)

巨人的肩膀:https://github.com/bradtraversy/vanillawebprojects

实现的是一个弹球的小游戏,设计的难点就是对于运动是碰撞的多种判断和移动的方向,其实在代码注释中就很清晰明了了。其中新加入了在网页上面飘散雪花的功能,同样在代码中体现。

style.css

* {
  box-sizing: border-box;
}

body {
  background-color: #0095dd;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: Arial, Helvetica, sans-serif;
  min-height: 100vh;
  margin: 0;
}

h1 {
  font-size: 45px;
  color: #fff;
}

canvas {
  background: #f0f0f0;
  display: block;
  border-radius: 5px;
}

.btn {
  cursor: pointer;
  border: 0;
  padding: 10px 20px;
  background: #000;
  color: #fff;
  border-radius: 5px;
}

.btn:focus {
  outline: 0;
}

.btn:hover {
  background: #222;
}

.btn:active {
  transform: scale(0.98);
}

.rules-btn {
  position: absolute;
  top: 30px;
  left: 30px;
}

.rules {
  position: absolute;
  top: 0;
  left: 0;
  background: #333;
  color: #fff;
  min-height: 100vh;
  width: 400px;
  padding: 20px;
  line-height: 1.5;
  transform: translateX(-400px);
  transition: transform 1s ease-in-out;
}

.rules.show {
  transform: translateX(0);
}

script.js

const rulesBtn = document.getElementById('rules-btn');
const closeBtn = document.getElementById('close-btn');
const rules = document.getElementById('rules');
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

let score = 0;

const brickRowCount = 9;		//9行 	
const brickColumnCount = 5;		//5列
const delay = 500; //delay to reset the game

// Create ball props
const ball = {
  x: canvas.width / 3,
  y: canvas.height / 2,
  size: 10,
  speed: 4,
  dx: 4,
  dy: -4,		//偏移量
  visible: true
};

// Create paddle props
const paddle = {
  x: canvas.width / 2 - 40,
  y: canvas.height - 20,
  w: 80,
  h: 10,
  speed: 8,
  dx: 0,
  visible: true
};

// Create brick props
const brickInfo = {		//砖块的信息
  w: 70,
  h: 20,
  padding: 10,
  offsetX: 45,
  offsetY: 60,
  visible: true
};

// Create bricks
const bricks = [];
for (let i = 0; i < brickRowCount; i++) {
  bricks[i] = [];
  for (let j = 0; j < brickColumnCount; j++) {
    const x = i * (brickInfo.w + brickInfo.padding) + brickInfo.offsetX;
    const y = j * (brickInfo.h + brickInfo.padding) + brickInfo.offsetY;
    bricks[i][j] = { x, y, ...brickInfo };
  }
}

// Draw ball on canvas
function drawBall() {
  ctx.beginPath();
  ctx.arc(ball.x, ball.y, ball.size, 0, Math.PI * 2);
  ctx.fillStyle = ball.visible ? '#0095dd' : 'transparent';
  ctx.fill();
  ctx.closePath();
}

function drawPaddle() {
	ctx.beginPath();
	//创建矩形 X坐标 y坐标 矩形宽度 矩形高度
	ctx.rect(paddle.x, paddle.y, paddle.w, paddle.h);
	ctx.fillStyle = paddle.visible ? '#0095dd' : 'transparent';
	ctx.fill();
	ctx.closePath();
}

function drawScore() {
	ctx.font = '20px Arial';		//字体的大小和类型
	ctx.fillText(`Score : ${score}`,canvas.width - 100, 30);
}


function drawBricks() {
	bricks.forEach(column => {			//所有块中的每一列
		column.forEach(brick => {		//所有列中的每一个块
			ctx.beginPath();
			ctx.rect(brick.x, brick.y, brick.w, brick.h);
			ctx.fillStyle = brick.visible ? '#0095dd' : 'transparent';
			ctx.fill();
			ctx.closePath();
		});
	});
}


function movePaddle() {
	paddle.x += paddle.dx;		//初始加上paddle所占据的长度
	
	//检测Wall
	if(paddle.x + paddle.w > canvas.width) {
		paddle.x = canvas.width - paddle.w;		//检测其不会出右边界
	}
	if(paddle.x < 0) {
		paddle.x = 0;		//限制其不会出左边界
	}
}

function moveBall() {
	ball.x += ball.dx;
	ball.y += ball.dy;
		
	//碰撞检测(Wall collision, L R)
	if(ball.x + ball.size > canvas.width || ball.x - ball.size < 0) {
		ball.dx *= -1;		//dx反向 可以形成一个夹角
	}
	//碰撞检测(Wall collision, T ,B)
	if(ball.y + ball.size > canvas.height || ball.y - ball.size < 0) {
		ball.dy *= -1;		//反向
	}
	
	//console.log(ball.x, ball.y);
	
	//碰撞检测(Paddle)
	if(
	ball.x - ball.size > paddle.x &&				//在板子的左边界靠右
	ball.x + ball.size < paddle.x + paddle.w &&		//在板子的右边界靠左
	ball.y + ball.size > paddle.y					//当球碰到板子
	){
		ball.dy = -ball.speed;		//速度反向
	}
	
	
	//碰撞检测(Brick)
	bricks.forEach(column => {
		column.forEach(brick =>{
			if(brick.visible) {
				if(
				ball.x - ball.size > brick.x&&			//左边界的右边
				ball.x + ball.size < brick.x + brick.w &&	//右边界的左边
				ball.y + ball.size > brick.y &&			//碰到上边界
				ball.y - ball.size < brick.y + brick.h	//碰到下边界
				){
					ball.dy *= -1;
					brick.visible = false;
					IncreaseScore();
				}
			}
		});
	});
	
	//板子没接住,碰到了地面Reset
	if(ball.y + ball.size > canvas.height) {
		showAllBricks();
		score = 0;
	}
}

function IncreaseScore() {
	score ++;
	if(score % (brickRowCount * brickColumnCount) ==0) {
		alert("这么牛的吗?");
		ball.visible = false ;
		paddle.visible = false;
		
		//直接清零 重新开始
		setTimeout(function(){
			showAllBricks();
			score = 0;
			paddle.x = canvas.width / 2 - 40;
			paddle.y = canvas.height - 20;
			ball.x = canvas.width / 2;
			ball.y = canvas.height / 2;
			ball.visible = true;
			paddle.visible = true;
		},delay);
	}
}

//全部展现出来
function showAllBricks() {
	bricks.forEach(column => {
		column.forEach(brick => (brick.visible = true));
	});
}

//调用所有draw函数

function draw() {
	//clear canvas
	ctx.clearRect(0, 0, canvas.width, canvas.height);
	
	drawBall();
	drawPaddle();
	drawScore();
	drawBricks();
} 

function update() {
  movePaddle();
  moveBall();

  // Draw everything
  draw();

  requestAnimationFrame(update);
}

update();

// Keydown event
function keyDown(e) {
  if (e.key === 'Right' || e.key === 'ArrowRight') {
    paddle.dx = paddle.speed;
  } else if (e.key === 'Left' || e.key === 'ArrowLeft') {
    paddle.dx = -paddle.speed;
  }
}

// Keyup event
function keyUp(e) {
  if (
    e.key === 'Right' ||
    e.key === 'ArrowRight' ||
    e.key === 'Left' ||
    e.key === 'ArrowLeft'
  ) {
    paddle.dx = 0;
  }
}

function snow() {
            //  1、定义一片雪花模板
            var flake = document.createElement('div');
            // 雪花字符 ❄❉❅❆✻✼❇❈❊✥✺
            flake.innerHTML = '❉';
            flake.style.cssText = 'position:absolute;color:#ffffff;';
            //获取页面的高度 相当于雪花下落结束时Y轴的位置
            var documentHieght = window.innerHeight;
            //获取页面的宽度,利用这个数来算出,雪花开始时left的值
            var documentWidth = window.innerWidth;

            //定义生成一片雪花的毫秒数
            var millisec = 10;
            //2、设置第一个定时器,周期性定时器,每隔一段时间(millisec)生成一片雪花;
            setInterval(function() { //页面加载之后,定时器就开始工作
                //随机生成雪花下落 开始 时left的值,相当于开始时X轴的位置
                var startLeft = Math.random() * documentWidth;

                //随机生成雪花下落 结束 时left的值,相当于结束时X轴的位置
                var endLeft = Math.random() * documentWidth;

                //随机生成雪花大小
                var flakeSize = 3 + 20 * Math.random();

                //随机生成雪花下落持续时间
                var durationTime = 4000 + 7000 * Math.random();

                //随机生成雪花下落 开始 时的透明度
                var startOpacity = 0.7 + 0.3 * Math.random();

                //随机生成雪花下落 结束 时的透明度
                var endOpacity = 0.2 + 0.2 * Math.random();

                //克隆一个雪花模板   true复制所有的子孙结点
                var cloneFlake = flake.cloneNode(true);
                //第一次修改样式,定义克隆出来的雪花的样式
                cloneFlake.style.cssText += `
                        left: ${startLeft}px;
                        opacity: ${startOpacity};
                        font-size:${flakeSize}px;
                        top:-25px;
                            transition:${durationTime}ms;`
                //拼接到页面中
                document.body.appendChild(cloneFlake);
                //设置第二个定时器,一次性定时器,
                //当第一个定时器生成雪花,并在页面上渲染出来后,修改雪花的样式,让雪花动起来;
                setTimeout(function() {
                    //第二次修改样式
                    cloneFlake.style.cssText += `
                                left: ${endLeft}px;
                                top:${documentHieght}px;
                                opacity:${endOpacity};`;

                    //4、设置第三个定时器,当雪花落下后,删除雪花。
                    setTimeout(function() {
                        cloneFlake.remove();
                    }, durationTime);
                }, 0);

            }, millisec);
        }
        snow();


// Keyboard event handlers
document.addEventListener('keydown', keyDown);
document.addEventListener('keyup', keyUp);

// Rules and close event handlers
rulesBtn.addEventListener('click', () => rules.classList.add('show'));
closeBtn.addEventListener('click', () => rules.classList.remove('show'));

破冰游戏.html

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<meta http-equiv="X-UA-Compatible" content="ie=edge" />
		<link rel="stylesheet" href="style1.css"/>
		<title>破冰</title>
	</head>
	<body>
		<h1>BreakIce!</h1> 
		<button id="rules-btn" class="btn rules-btn">Show Rules</button>
		<div id="rules" class="rules">
			<h2>How to play:</h2>
			<p>
				Use your right and left keys to move the board to bounce(反弹) the ball
				and break the ice.
			</p>
			<p>
				If you miss the ball, your score and the ice will reset.
			</p>
			<button id="close-btn" class="btn">
				Close
			</button>
		</div>
		
			<canvas id="canvas" width="800" height="600"></canvas>
			
			<script src="script1.js"></script>
	</body>
</html>

猜你喜欢

转载自blog.csdn.net/qq_52245648/article/details/121893557