1、游戏图片
2、HTML部分
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body{
margin: 0;
}
.game{
position: relative;
width: 800px;
height: 600px;
margin: 0 auto;
overflow: hidden;
}
.sky {
position: absolute;
width: 200%;
height: 100%;
background-image: url('./img/sky.png');
margin: 0 auto;
}
.game .bird {
background: url("./img/bird.png");
position: absolute;
width: 33px;
height: 26px;
left: 150px;
top: 150px;
}
.game .bird.swing1{
background-position: -8px -10px;
}
.game .bird.swing2{
background-position: -60px -10px;
}
.game .bird.swing3{
background-position: -113px -10px;
}
.pipeDown{
position: absolute;
background-image: url('./img/pipeUp.png');
width: 52px;
height: 100px;
left: 500px;
bottom: 112px;
}
.pipeUp{
position: absolute;
background-image: url('./img/pipeDown.png');
background-position: bottom;
width: 52px;
height: 100px;
left: 500px;
top: 0;
}
.ground{
position: absolute;
background-image: url('./img/land.png');
width: 200%;
height: 112px;
left: 0;
bottom: 0;
}
.score{
position: absolute;
width: 100px;
height: 36px;
background-color: lightblue;
right: 0;
top: 0;
text-align: center;
line-height: 36px;
font-size: 24px;
z-index: 100;
}
p{
text-align: center;
}
</style>
</head>
<body>
<div class="game">
<div class="sky"></div>
<div class="bird swing1"></div>
<div class="ground"></div>
<div class="score">0</div>
</div>
<p>按w或者按上开始游戏</p>
<script src="./JS/Rectangle.js"></script>
<script src="./JS/Sky.js"></script>
<script src="./JS/Land.js"></script>
<script src="./JS/Bird.js"></script>
<script src="./JS/Pipe.js"></script>
<script src="./JS/Game.js"></script>
</body>
</html>
3、JS部分
baseGame class
/**
* 基础的游戏类
* 属性: 宽度 高度、横坐标、纵坐标、横向速度、纵向速度、对应的dom元素
*/
class Rectangle {
constructor(width,height,x,y,vx,vy,dom){
this.width = width;
this.height = height;
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
this.dom = dom;
this.render();
}
/**
* 渲染
*/
render(){
this.dom.style.width = this.width + "px";
this.dom.style.height = this.height + "px";
this.dom.style.left = this.x + "px";
this.dom.style.top = this.y + "px";
}
onMove(){
}
/**
* 在duration时间下物体移动
* @param {number} duration 间隔
*/
move(duration){
this.x += this.vx * duration;
this.y += this.vy * duration;
if(this.onMove) this.onMove();
this.render();
}
}
sky ground class
const skyDom = document.querySelector('.sky');
skyStyle = getComputedStyle(skyDom);
const widthSky = parseFloat(skyStyle.width);
const heightSky = parseFloat(skyStyle.width);
class Sky extends Rectangle{
constructor(){
super(widthSky, heightSky, 0, 0, -100, 0, skyDom);
}
onMove(){
if(this.x <= -widthSky / 2) {
this.x = 0;
}
}
}
const landDom = document.querySelector('.ground');
landStyle = getComputedStyle(landDom);
const widthLand = parseFloat(landStyle.width);
const heightLand = parseFloat(landStyle.width);
class Land extends Rectangle{
constructor(){
super(widthLand, heightLand, 0, 488, -100, 0, landDom);
}
onMove(){
if(this.x <= -widthLand / 2) {
this.x = 0;
}
}
}
bird class
const birdDom = document.querySelector('.bird');
birdStyle = getComputedStyle(birdDom);
const widthBird = parseFloat(birdStyle.width);
const heightBird = parseFloat(birdStyle.height);
class Bird extends Rectangle{
gravity = 1000;
constructor(){
super(widthBird, heightBird, 150, 200, 0, 100, birdDom);
this.swingState = 1;
this.bindEvent();
}
// 綁定鼠標按下
bindEvent(){
document.addEventListener('keydown', (e) => {
if(e.key === 'w' || e.key === 'ArrowUp'){
this.vy += -550;
}
})
this.startSwing();
}
// 小鳥扇翅膀
startSwing(){
if(this.timer){
return ;
}
this.timer = setInterval( () => {
birdDom.classList.remove('swing'+this.swingState);
this.swingState = (++this.swingState % 3) + 1;
birdDom.classList.add('swing'+this.swingState);
},300)
}
stopSwing(){
clearInterval(this.timer);
this.timer = null;
}
onMove(){
if(this.y >= 463) {
this.y = 463;
this.vy =0;
}
if(this.y <= 0 ){
this.y = 0;
this.vy = 0;
}
}
move(duration){
super.move(duration);
this.vy += this.gravity * duration;
}
}
pipe class
const game = document.querySelector('.game');
const gameWidth = game.clientWidth;
class Pipe extends Rectangle{
isExited = true;
constructor(height, top, speed, dom){
super(52, height, gameWidth, top, speed, 0, dom);
}
onMove(){
if(this.x < -this.width){
this.dom.remove();
this.isExited = false;
}
}
}
function getRandomNumber(min, max){
return Math.floor(Math.random() * (max - min)) + min;
}
class PipePare{
stopTimer = null;
constructor(speed){
this.up = document.createElement('div');
this.down = document.createElement('div');
this.up.classList.add('pipeUp');
this.down.classList.add('pipeDown');
game.appendChild(this.up);
game.appendChild(this.down);
this.spaceHeihgt = 150; // 柱子之間的空隙
const fristPipeHeight = getRandomNumber(0, 488-150);
this.upPipe = new Pipe(fristPipeHeight, 0, speed, this.up);
this.downPipe = new Pipe(338 - fristPipeHeight, 150 + fristPipeHeight, speed, this.down);
}
stop(){
if(this.stopTimer){
clearInterval(this.stopTimer);
this.stopTimer = null;
}
this.down.remove();
this.up.remove();
}
move(duration){
this.stopTimer = setInterval( () => {
this.upPipe.move(duration);
this.downPipe.move(duration);
},10)
}
isCollision(bird){
const birdStyle = getComputedStyle(document.querySelector('.bird'))
let y = Number.parseInt(birdStyle.top);
if(this.upPipe.x < bird.x + bird.width && this.upPipe.x > bird.x){
return y < this.upPipe.height || y + bird.height > this.upPipe.height + this.spaceHeihgt;
}
}
}
class GamePipePair{
pipeTimer = null;
pipeArr = []; // 用于显示的管道数组
// 记录小鸟越过的数组
scoreArr = [];
constructor(speed){
this.speed = speed;
this.pair = new PipePare(speed);
this.init();
}
init(){
this.pipeTimer = setInterval( ()=> {
let tmp = new PipePare(this.speed);
tmp.move(0.01);
this.pipeArr.push(tmp);
this.scoreArr.push(tmp);
}, 2000)
}
getScore(bird){
return this.scoreArr.filter(item => item.upPipe.x <= bird.x).length;
}
stop(){
this.pipeArr.forEach(item => {
item.stop();
})
if(this.pipeTimer){
clearInterval(this.pipeTimer);
this.pipeTimer = null;
}
}
collisionDetection(bird){ // 小鸟对象传入
this.pipeArr = this.pipeArr.filter(item => item.upPipe.isExited); // 过滤掉了不存在的
// 检查小鸟是否碰撞到柱子
for(let i = 0 ;i < this.pipeArr.length;i++){
if(this.pipeArr[i].isCollision(bird)){
return true;
}
}
return false;
}
}
game class
class Game {
score = 0;
land;
sky;
bird;
backgroundTimer = null;
pipeController = null;
constructor(){
this.land = new Land();
this.sky = new Sky();
this.bird = new Bird();
this.state = 0; // 0 遊戲結束 1遊戲開始進行中
this.init();
}
init(){
document.onkeydown = (e) => {
if((e.key === 'w' || e.key === 'ArrowUp') && this.state === 0){
console.log('開始遊戲');
this.state = 1;
this.startGame();
}
}
}
startGame(){
this.pipeController = new GamePipePair(-100);
if(this.backgroundTimer) return ;
this.backgroundTimer = setInterval(() => {
this.land.move(0.01);
this.sky.move(0.01);
this.bird.move(0.01);
this.updateScore();
if(this.pipeController){
if(this.pipeController.collisionDetection(this.bird)){
this.endGame();
console.log('game ended');
}
}
}, 10)
}
updateScore(){
const score = document.querySelector('.score');
this.score = this.pipeController.getScore(this.bird)
score.innerHTML = this.score;
}
endGame(){
if(this.backgroundTimer && this.state === 1){
this.pipeController.stop();
this.state = 0;
this.bird.stopSwing();
clearInterval(this.backgroundTimer);
this.backgroundTimer = null;
document.onkeydown = null;
if(confirm(`游戏结束!!
你最后的得分是${this.score}分
你想要再玩一局嘛 想玩点确定哦!!!
`)){
this.init();
}
}
}
}
const birdGame = new Game();
4、源码+静态资源
像素鸟源码地址