使用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>