canvas制作小游戏(欢乐的小鸟)

使用js画布制作小游戏,分为bird、sky、land、pipes、game和渲染页面index模块

一、创建画布

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      color: saddlebrown;
    }

    canvas {
      border: 2px solid saddlebrown;
      position: absolute;
      margin: auto;
      left: 0;
      top: 0;
      right: 0;
      bottom: 0;
    }

    button {
      width: 120px;
    }
  </style>
</head>
<p>回车开始游戏</p>
<p>空格向上或上键跳跃</p>
<button>入门</button><br>
<button>简单(默认)</button><br>
<button>困难</button><br>
<button>地狱</button>

<canvas width="800" height="600"></canvas>

<script>
  let ctx = document.querySelector("canvas").getContext("2d")
</script>
</body>

</html>

 

二、画出陆地模块

class Land {
  constructor() {
    this.imgx = 0
    this.imgy = 0
    this.width = 800
    this.height = 112
    this.canvasx = 0
    this.canvasy = 600 - 112
    this.canvasx2 = 600
    // this.speed = 0
    // this.a = 0
  }
  show () {
    ctx.drawImage(imgland, this.imgx, this.imgy, 336, this.height, this.canvasx, this.canvasy, this.width, this.height)
    ctx.drawImage(imgland, this.imgx, this.imgy, 336, this.height, this.canvasx2, this.canvasy, this.width, this.height)
  }
  move () {
    this.canvasx -= 1
    this.canvasx2 -= 1
    if (this.canvasx2 == 0) {
      this.canvasx = 0
      this.canvasx2 = 600
    }
  }
}

 

三、画出天空模块

class Sky {
  constructor() {
    this.imgx = 0
    this.imgy = 0
    this.width = 800
    this.height = 600 - 112
    this.canvasx = 0
    this.canvasy = 0
    this.canvasx2 = 600
  }
  show () {
    ctx.drawImage(imgsky, this.imgx, this.imgy, this.width, this.height, this.canvasx, this.canvasy, this.width, this.height)
    ctx.drawImage(imgsky, this.imgx, this.imgy, this.width, this.height, this.canvasx2, this.canvasy, this.width, this.height)
  }
  move () {
    this.canvasx -= 1
    this.canvasx2 -= 1
    if (this.canvasx2 == 0) {
      this.canvasx = 0
      this.canvasx2 = 600
    }
  }
}

 

四、画出小鸟模块

class Bird {
  constructor(arg) {
    this.game = arg
    this.imgx = 0
    this.imgy = 0
    this.width = 52
    this.height = 45
    this.canvasx = 180
    this.canvasy = 200
    this.flytimer = null
    this.speed = 0//速度
    this.a = 1.2//加速度
  }
  show () {
    ctx.drawImage(imgbird, this.imgx, this.imgy, this.width, this.height, this.canvasx, this.canvasy, this.width / 1.3, this.height / 1.3)
    //碰到管道
    let arr = this.game.pipes.pipePositions
    for (let i = 0; i < arr.length; i++) {
      let birdBorderRight = this.canvasx + this.width / 1.3
      let birdBorderBottom = this.canvasy + this.height / 1.3
      //管道位置上下左右
      let left = arr[i].top.canvasx
      let right = arr[i].top.canvasx + 52 + this.width / 1.3
      let topY = arr[i].top.canvasy + arr[i].top.canvaswh + this.height / 1.3
      let bottomY = arr[i].bottom.canvasy
      //小鸟触碰管道
      let topBool = birdBorderRight >= left && birdBorderRight <= right && birdBorderBottom <= topY
      let bottomBool = birdBorderRight >= left && birdBorderRight <= right && birdBorderBottom >= bottomY
      if (topBool || bottomBool) {
        this.game.isGameOver = true
      }
    }
    //判断是否落地
    if ((this.canvasy + this.height / 1.3) >= this.game.land.canvasy) {
      this.game.isGameOver = true
    }
  }
  fly () {
    this.imgx += this.width
    if (this.imgx >= this.width * 3) {
      this.imgx = 0
    }
  }
  drop () {
    this.speed += this.a
    this.canvasy = this.canvasy + this.speed
    //界限
    if (this.canvasy <= -10) {
      this.canvasy = -10
    }
  }
  jump () {
    this.speed = -11
  }
}

 

五、启动游戏

class Game {
  constructor() {
    this.land = new Land()
    this.bird = new Bird(this)
    this.pipes = new Pipes()
    this.sky = new Sky()
    this.isGameOver = false//游戏是否结束
    this.paused = true//暂停
    this.grade = 21//难度
    this.rank = 0//分数
  }
  init () {
    ctx.clearRect(0, 0, 800, 600)
    this.land.show()
    this.sky.show()
    this.bird.show()
    ctx.fillText(`${this.rank}`, 760, 10)
  }
  start () {
    this.timershow = setInterval(() => {
      ctx.clearRect(0, 0, 800, 600)
      this.land.show()
      this.sky.show()
      this.pipes.show()
      this.bird.show()
      ctx.fillText(`${this.rank}`, 760, 10)
      this.isGameOverListener()
    }, 20)
    //大地移动
    this.timerLandmove = setInterval(() => {
      this.land.move()
    }, 20)
    // 云移动
    this.timerSkymove = setInterval(() => {
      this.sky.move()
    }, 50)
    //管道移动
    this.timerPipesmove = setInterval(() => {
      this.pipes.move()
    }, 10)
    //随机出现管道
    this.timerPipes = setInterval(() => {
      this.pipes.randomPosition()
    }, 2000)

    //鸟下落
    this.timerBirddrop = setInterval(() => {
      this.bird.drop()
    }, this.grade)
    //鸟翅膀运动
    this.timerfly = setInterval(() => {
      this.bird.fly()
    }, 100)
  }
  stop () {
    clearInterval(this.timershow)
    clearInterval(this.timerBirddrop)
    clearInterval(this.timerLandmove)
    clearInterval(this.timerPipes)
    clearInterval(this.timerPipesmove)
    clearInterval(this.timerSkymove)
    clearInterval(this.timerfly)
  }
  changeGrade () {
    //每过三个管道难度增加
    if (this.rank % 3 == 0) {
      this.grade -= 1
    }
    console.log(this.grade)
  }
  isGameOverListener () {
    //计分
    for (let i = 0; i < this.pipes.pipePositions.length; i++) {
      let el = this.pipes.pipePositions[i]
      if (this.bird.canvasx < (el.top.canvasx + el.top.imgw)) {
        this.rank = i
        break
      }
    }
    if (this.isGameOver) {
      this.stop()
      ctx.font = 'bold 144px consolas'
      ctx.font = 'bold 24px arial'
      ctx.fillStyle = 'pink'
      ctx.fillText(`Gameover! ${this.rank}分`, 300, 200)
      ctx.fillText(`未破记录`, 335, 250)
    }
  }
}

六、在页面渲染出

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      color: saddlebrown;
    }

    canvas {
      border: 2px solid saddlebrown;
      position: absolute;
      margin: auto;
      left: 0;
      top: 0;
      right: 0;
      bottom: 0;
    }

    button {
      width: 120px;
    }
  </style>
</head>
<p>回车开始游戏</p>
<p>空格向上或上键跳跃</p>
<button onclick="game.grade=30;this.style.backgroundColor='red'">入门</button><br>
<button onclick="game.grade=20;this.style.backgroundColor='red'">简单(默认)</button><br>
<button onclick="game.grade=12;this.style.backgroundColor='red'">困难</button><br>
<button onclick="game.grade=7;this.style.backgroundColor='red'">地狱</button>

<canvas width="800" height="600"></canvas>

<script src="./js/Bird.js"></script>
<script src="./js/Land.js"></script>
<script src="./js/Sky.js"></script>
<script src="./js/Pipes.js"></script>
<script src="./js/game.js"></script>

<script>
  let ctx = document.querySelector("canvas").getContext("2d")
  let imgland = new Image()
  let imgsky = new Image()
  let imgpipedown = new Image()
  let imgpipeup = new Image()
  let imgbird = new Image()
  imgsky.src = "./img/sky.png"
  imgland.src = "./img/land.png"
  imgpipedown.src = "./img/pipeDown.png"
  imgpipeup.src = "./img/pipeUp.png"
  imgbird.src = "./img/bird.png"


  let game = new Game()


  window.onload = function () {
    game.init()
    //按了enter键的3种情况:重新开始,暂停,继续
    document.onkeydown = function (e) {
      if (e.keyCode == 13) {
        if (game.isGameOver) {
          location.reload()
        }
        if (game.paused) {
          game.start()
          game.paused = !game.paused
        } else {
          game.stop()
          game.paused = !game.paused
        }
      }
      if (e.key == ' ' || e.keyCode == 38) {
        game.bird.jump()
      }
    }
  }


</script>
</body>

</html>

代码例表

完整代码如下

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script type="text/javascript">
			var img1 = new Image()
			img1.src = "img/sky.png"
			img1.src = "img/land.png"
			img1.src = "img/pipeDown.png"
			img1.src = "img/pipeUp.png"
			img1.src = "img/bird.png"
		</script>
	</head>
	<body>
		<style type="text/css">
			* {
				margin: 0px;
				padding: 0px;
				color: saddlebrown;
			}

			#flappyBird {
				border: 2px saddlebrown solid;
				position: absolute;
				margin: auto;
				left: 0px;
				top: 0px;
				right: 0px;
				bottom: 0px;
			}
			button{
				width: 120px;
			}
		</style>
		<p>回车开始游戏</p>
		<p>空格向上跳跃</p>
		<button type="button" onclick="javaScpript:game.grade=20;this.style.backgroundColor='red'">入门</button>  <br>
		<button type="button" onclick="javaScpript:game.grade=15;this.style.backgroundColor='red'">简单(默认)</button><br>
		<button type="button" onclick="javaScpript:game.grade=10;this.style.backgroundColor='red'">困难</button><br>
		<button type="button" onclick="javaScpript:game.grade=7;this.style.backgroundColor='red'">地狱</button><br>
		<canvas id="flappyBird" width="800" height="600"></canvas>
		<script type="text/javascript">

			class Bird {
				constructor(arg, arg2) {
					this.ctx = arg
					this.game = arg2
					this.imgx = 0
					this.imgy = 0
					this.width = 52 //  156/3
					this.height = 45
					this.canvasx = 180
					this.canvasy = 200
					this.flytimer = null
					this.speed = 0, //速度
					this.a = 1.2 //加速度					
					this.img = new Image()
					this.img.src = "img/bird.png"
					this.img.onload = () => {
						// this.fly()
						this.show()
					}
				}

				show() {
					// 1.绘鸟
					this.ctx.drawImage(this.img, this.imgx, this.imgy, this.width, this.height, this.canvasx, this.canvasy, this.width /1.3, this.height / 1.3)
					
					//2.判断是否碰柱子,游戏结束
					 let arr=this.game.Pipes.pipePositions
					 // console.log(arr.length)
					for(let i=0;i<arr.length;i++){
						// console.log(6666,i)
						//小鸟右,下边
						let birdBorderRight=this.canvasx+this.width/1.3
						let birdBorderBottom=this.canvasy+this.height/1.3
						//上下水管
						let left=arr[i].top.canvasx
						let right=arr[i].top.canvasx+52+this.width/1.3
						let topY=arr[i].top.canvasy+arr[i].top.canvaswh	+this.height/1.3					
						let bottomY=arr[i].bottom.canvasy
						//碰上下水管
						let topBool=birdBorderRight>=left&&birdBorderRight<=right&&birdBorderBottom<=topY
						let bottomBool=birdBorderRight>=left&&birdBorderRight<right&&birdBorderBottom>=bottomY
						console.log(topBool,bottomBool)
						if(topBool||bottomBool){
							this.game.isGameOver=true
						}						
					}
					
					//3.判断是否落地,游戏结束
					
					if((this.canvasy+this.height/1.3)>=this.game.Land.canvasy){
						this.game.isGameOver=true						
					}
				
				}

				fly() {
					//换翅膀
					this.imgx += this.width
					if (this.imgx >= this.width * 3) {
						this.imgx = 0
					}

				}
				drop() {
					//换y下落 匀加速运动
					this.speed += this.a
					this.canvasy = this.canvasy + this.speed
					//最多碰到天花板
					if (this.canvasy <= -10) {
						this.canvasy = -10;																		
					}
				}


				jump() {
					this.speed = -13
				}
			}

			class Pipes {
				constructor(arg) {
					this.ctx = arg
					this.speed = 0, //速度
						this.a = 0 //加速度					
					this.imgtop = new Image()
					this.imgtop.src = "img/pipeUp.png"
					this.imgbottom = new Image()
					this.imgbottom.src = "./img/pipeDown.png"
					this.pipePositions = []
				}
				show() {
					// this.ctx.drawImage(this.imgtop, this.imgx., this.imgy, 336, this.height, this.canvasx, this.canvasy, this.width,this.height)
					this.pipePositions.forEach((el) => {
						this.ctx.drawImage(el.top.img, el.top.imgx, el.top.imgy, el.top.imgw, el.top.imgheight, el.top.canvasx, el.top.canvasy, el.top.canvasw, el.top.canvaswh)
						this.ctx.drawImage(el.bottom.img, el.bottom.imgx, el.bottom.imgy, el.bottom.imgw, el.bottom.imgheight, el.bottom
							.canvasx, el.bottom.canvasy, el.bottom.canvasw, el.bottom.canvaswh)
					})
				}
				random(min, max) {
					return Math.round(Math.random() * (max - min) + min)
				}
				//调用一次在地图最右边添加一对水管
				randomPosition() {
					let minHeight = 60, // 柱子最小的高度
						gap = 150, // 中间的间隙
						maxHeight = 488 - gap - minHeight; //天顶与大地的距离是488
					let h1 = this.random(minHeight, maxHeight) //上柱子高度
					let h2 = 488 - gap - h1 //下柱子高度
					let top = {
						img: this.imgtop,
						imgx: 0,
						imgy: 0,
						imgw: 52,
						imgheight: 420,
						canvasx: 800,
						canvasy: 0,
						canvasw: 52,
						canvaswh: h1
					}
					let bottom = {
						img: this.imgbottom,
						imgx: 0,
						imgy: 0,
						imgw: 52,
						imgheight: 420,
						canvasx: 800,
						canvasy: 488 - h2,
						canvasw: 52,
						canvaswh: h2
					};
					this.pipePositions.push({
						top,
						bottom
					})
				}
				move() {
					this.pipePositions.forEach((el) => {
						el.top.canvasx -= 1;
						el.bottom.canvasx -= 1
					})
				}
			}

			class Land {
				constructor(arg) {
					this.ctx = arg
					this.imgx = 0
					this.imgy = 0
					this.width = 800 //  156/3
					this.height = 112
					this.canvasx = 0
					this.canvasx2 = 600
					this.canvasy = 600 - 112
					this.movetimer = null
					this.speed = 0, //速度
						this.a = 0 //加速度					
					this.img = new Image()
					this.img.src = "img/land.png"
					this.img2 = new Image()
					this.img2.src = "img/land.png"
					this.img.onload = () => {
						this.show()
					}
				}
				show() {
					this.ctx.drawImage(this.img, this.imgx, this.imgy, 336, this.height, this.canvasx, this.canvasy, this.width, this
						.height)
					this.ctx.drawImage(this.img2, this.imgx, this.imgy, 336, this.height, this.canvasx2, this.canvasy, this.width,
						this.height)
				}
				move() {
					this.canvasx -= 1
					this.canvasx2 -= 1
					if (this.canvasx2 == 0) {
						this.canvasx = 0
						this.canvasx2 = 600
					}
				}

			}
			class Sky {
				constructor(arg) {
					this.ctx = arg
					this.imgx = 0
					this.imgy = 0
					this.width = 800 //  156/3
					this.height = 600
					this.canvasx = 0
					this.canvasx2 = 600
					this.canvasy = 0
					this.movetimer = null
					this.speed = 0, //速度
						this.a = 0 //加速度					
					this.img = new Image()
					this.img.src = "img/sky.png"
					this.img2 = new Image()
					this.img2.src = "img/sky.png"
					this.img.onload = () => {
						this.show()
					}
				}
				show() {
					this.ctx.drawImage(this.img, this.imgx, this.imgy, this.width, this.height, this.canvasx, this.canvasy, this.width,
						this.height)
					this.ctx.drawImage(this.img2, this.imgx, this.imgy, this.width, this.height, this.canvasx2, this.canvasy, this.width,
						this.height)
				}
				move() {
					this.canvasx -= 1
					this.canvasx2 -= 1
					if (this.canvasx2 == 0) {
						this.canvasx = 0
						this.canvasx2 = 600
					}
				}

			}

			class Game {
				constructor(elid) {
					this.canvas = document.querySelector(elid)
					this.ctx = this.canvas.getContext("2d")
					this.isGameOver = false
					this.paused = true
					this.grade=15
					this.timer = null
					this.timerPipes = null
					this.timerfly = null
					this.rank = 0
					this.Sky = new Sky(this.ctx)
					this.Land = new Land(this.ctx)
					this.Pipes = new Pipes(this.ctx)
					this.Bird = new Bird(this.ctx, this)
				}
				start() {
					this.timershow = setInterval(() => {
						//清屏展示
						this.ctx.clearRect(0, 0, 800, 600)
						this.Sky.show()
						this.Land.show()
						this.Pipes.show()
						this.Bird.show()
						this.ctx.fillText(`${this.rank}`, 760, 10)
						//观察碰撞就结束游戏并计分
						this.isGameOverListener()
					}, 20)


					//换坐标鸟下落
					this.timerBirddrop = setInterval(() => {
						this.Bird.drop()
					}, this.grade)

					//换坐标云彩移动
					this.timerSkymove = setInterval(() => {
						this.Sky.move()
					}, 50)

					//换坐标大地移动
					this.timerLandmove = setInterval(() => {
						this.Land.move()
					}, 10)

					//水管们换坐标移动
					this.timerPipesmove = setInterval(() => {
						this.Pipes.move()
					}, 10)
					//随机出现水管
					this.timerPipes = setInterval(() => {
						this.Pipes.randomPosition()
					}, 2000)

					//换图翅膀煽动
					this.timerfly = setInterval(() => {
						this.Bird.fly()
					}, 100)



				}
				stop() {
					clearInterval(this.timershow)
					clearInterval(this.timerBirddrop)
					clearInterval(this.timerSkymove)
					clearInterval(this.timerLandmove)
					clearInterval(this.timerPipesmove)
					clearInterval(this.timerPipes)
					clearInterval(this.timerfly)
				}
				isGameOverListener() {
					// 计分
					for (let i = 0; i < this.Pipes.pipePositions.length; i++) {
						let el = this.Pipes.pipePositions[i]
						if (this.Bird.canvasx < (el.top.canvasx + el.top.imgw)) {
							// console.log(666,index)
							this.rank = i
							break;
						}
					}

					if (this.isGameOver) {
						//结束游戏
						this.stop()
						//计分弹窗
						this.ctx.font = 'bold 144px consolas';
						this.ctx.font = 'bold 24px arial';
						this.ctx.fillStyle = 'red';
						this.ctx.fillText(`Gameover! ${this.rank} 分`, 300, 200)
						this.ctx.fillText(`未破记录,不能存档:蒋老师388分,`, 230, 250)
					}

				}

			}


			let game = new Game("#flappyBird")

			document.onkeydown = function(e) {
				//按了enter键的3种情况:重新开始,暂停,继续
				if (e.key == 'Enter') {
					if (game.isGameOver) {
						location.reload()
					}
					if (game.paused) {
						game.start();
						game.paused = !game.paused
					} else {
						game.stop();
						game.paused = !game.paused
					}

				}
				if (e.key == ' ') {
					game.Bird.jump()
				}
			}
		</script>

	</body>
</html>

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值