打砖块小游戏


date:02/24
author:yjxcoder

1. 打砖块

1.1. 今日所学内容

1.1.1. 打砖块游戏现版本实现逻辑

  1. 定义好游戏相关的数据结构。有几大重要数据结构如下:

    1. brickList[]代表第i行,j列砖块的颜色类型。blockImage则是存着第i行,j列砖块的图片对象。

       //绘制方块
       function drawBrick () {
       	for (var i = 0; i < 5; i++) {
       		for(var j = 0; j < 4; j++){
       			if(brickList[i][j] > 0){
       				// list代表i行,j列砖块的颜色类型
       				// blockImage则是存着每个图片对象
       				var img = brickImage[brickList[i][j] - 1];
       				gameContext.drawImage(img,
       					i * brickWidth + 2*i + 7,
       					j * brickHeight + 2*j + 50,
       					brickWidth,
       					brickHeight);
       			}
       		}
       	}
       }
      
    2. 小球和砖块对象

       // 小球初始化
       var ball = new Ball();
       ball.img.onload = function()
       {
       	ball.drawBall();
       }
       
       var board = new Border();
       
       board.img.onload = function()
       {
       	board.drawBoard();
       }
       
       createBrick();
       drawBrick();
      
  2. 刚进入游戏时,对定义的游戏变量进行初始化,创建好砖块,小球和挡板。使用gameContext的drawImage()在显示板中画出他们来。

     //加载图片
     loadImage();
     function loadImage () {
     	for (var i = 0; i < 6; i++) {
     		var img = new Image();
     		var index = i + 4;
     		var src = "img/" + index + ".png"
     		img.src = src;
     		brickImage[i] = img;
     	}
     }
     // 小球初始化
     var ball = new Ball();
     ball.img.onload = function()
     {
     	ball.drawBall();
     }
     
     var board = new Border();
     
     board.img.onload = function()
     {
     	board.drawBoard();
     }
     
     createBrick();
     drawBrick();
     
     function createBrick(){
     	for (var i = 0; i < 5; i++) {
     		// 3. 
     		brickList[i] = [];
     		for(var j = 0; j < 4; j++){
     			brickList[i][j] = Math.floor(Math.random()*6) + 1;
     		}
     	}
     }
    
  3. 设置一个定时器,在定时器中不断更新小球位置、并重新绘制小球、挡板和砖块。

     timer = setInterval(function () {
     	//清空画布
     	gameContext.clearRect(0, 0, gameCanvas.width, gameCanvas.height);
     	
     	//检测碰撞
     	collide();
     	
     	//绘制上方柱子
     	// gameContext.drawImage(pillar,325,250,565,10,5,40,515,8);
     	
     	//更新小球位置
     	ball.updateBallFrame();
     	
     	//绘制小球
     	ball.drawBall();
     	
     	//绘制挡板
     	board.drawBoard();
     	
     	//绘制砖块
     	drawBrick();
     },30);
    

1.1.2. 打砖块游戏重要代码分析

  1. 生成一张随机图片放在第i行,j列砖块的图片对象数组中。

     //加载图片
     loadImage();
     function loadImage () {
     	for (var i = 0; i < 6; i++) {
     		var img = new Image();
     		var index = i + 4;
     		var src = "img/" + index + ".png"
     		img.src = src;
     		brickImage[i] = img;
     	}
     }
    
     //获取小球图片对象
     function getBallImage () {
     	var img = new Image();
     	img.src = "img/ball.png";
     	return img;
     }
    
  2. 根据第i行,j列的图片对象二维数组,绘制4行,5列的砖块。使用此数据结构的优点是:用二维数组存好砖块图片信息,当刷新画布时,重新绘制的砖块也是从数组中读取,则不会,即使在定时器中有不断地调用绘制砖块,但也不会改变原来的砖块的颜色。

     //绘制砖块
     function drawBrick () {
     	for (var i = 0; i < 5; i++) {
     		for(var j = 0; j < 4; j++){
     			if(brickList[i][j] > 0){
     				// list代表i行,j列砖块的颜色类型
     				// blockImage则是存着每个图片对象
     				var img = brickImage[brickList[i][j] - 1];
     				gameContext.drawImage(img,
     					i * brickWidth + 2*i + 7,
     					j * brickHeight + 2*j + 50,
     					brickWidth,
     					brickHeight);
     			}
     		}
     	}
     }
    
     // this则代表new的对象
     function Brick()
     {
     	this.currentX = 0;
     	this.currentY = 0;
     	this.img = getImage();
     	//画砖块
     	this.drawBrick = function(i, j)
     	{
     		gameContext.drawImage(this.img, 0, 0, brickWidth, brickHeight, brickWidth * i, brickHeight * (this.currentY + j), brickWidth, brickHeight);
     	}
     }
    
  3. 小球对象内部的构造函数,包括球的位置,宽高,移速,图片等属性。

     // 在构造函数中,定义小球的位置,宽高,移速,图片等属性 2. 
     function Ball()
     {
     	//位置
     	this.x = 250;
     	this.y = 300;
     	
     	//宽高
     	this.width = 30;
     	this.height = 30;
     
     	//移动速度
     	this.vx = 2;
     	this.vy = -2;
     	
     	//小球图片
     	this.img = getBallImage();
     	
     	//更新小球位置
     	this.updateBallFrame = function () {
     		this.x = this.x + this.vx;
     		this.y = this.y + this.vy;
     	}
     	
     	//绘制小球
     	this.drawBall = function () {
     		gameContext.drawImage(this.img,this.x,this.y,this.width,this.height);
     	}
     }
    
  4. 定时器中不断刷新,并且消除画布,绘制画布。

     timer = setInterval(function () {
     	//清空画布
     	gameContext.clearRect(0, 0, gameCanvas.width, gameCanvas.height);
     	
     	//检测碰撞
     	collide();
     	
     	//绘制上方柱子
     	// gameContext.drawImage(pillar,325,250,565,10,5,40,515,8);
     	
     	//更新小球位置
     	ball.updateBallFrame();
     	
     	//绘制小球
     	ball.drawBall();
     	
     	//绘制挡板
     	board.drawBoard();
     	
     	//绘制砖块
     	drawBrick();
     },30);
    
  5. 小球的碰撞检测,这里根据小球的移速,做了小球与砖块和挡板之间的碰撞检测。还未完善。此处是本游戏的难重点。

     //碰撞检测 
     function collide () {
     	//小球的的下一次位置
     	var nextX = ball.x + ball.vx;
     	var nextY = ball.y + ball.vy;
     	
     	//小球的中间位置的x,y坐标
     	var midX = ball.x + ball.width / 2;
     	var midY = ball.y+ ball.height / 2;
     	
     	//与挡板的碰撞检测
     	if( ball.y + ball.height > board.y //球的下端接触到挡板 
     		&& ball.y + ball.height < board.y + board.height //但球的下端不超过挡板
     		&& midX > board.x //球队中部位于挡板上
     		&& midX < board.x + board.width //球队中部位于挡板上
     		)
     	{
     		ball.vy = -2;
     	}
     	
     	//与砖块碰撞检测
     	for (var i = 0; i < 5; i++) {
     		for(var j = 0; j < 4; j++){
     			if(brickList[i][j] > 0)
     			{
     				//砖块的四条边的位置
     				var leftX = i * brickWidth + 2*i + 7;
     				var rightX = leftX + brickWidth;
     				var topY = j * brickHeight + 2*j + 50;
     				var bottomY = topY + brickHeight;
     				
     				
     				//底部碰撞
     				if(nextY > topY && nextY < bottomY 
     					&& midX > leftX && midX < rightX)
     				{
     					ball.vy *= -1;
     					brickList[i][j] = 0;
     				}
     			}
     		}
     	}
     }
    

1.2. 残留问题

1.2.1. 小球的碰撞还没有完全做完,小球与地面碰撞时显示游戏失败,与左右面板碰撞时反弹,再给我一些时间应该能完成。此版本之前我想了好几个绘制的方案,方案一会出现只绘制一种颜色的砖块的问题,原因是brick.img.onload()加载时间过长,canvas的drawImage()无法绘制所有种类的颜色图片方案二导致在定时器的刷新下,所有砖块的颜色不断变化。最后采用brickList[]代表第i行,j列砖块的颜色类型。blockImage则是存着第i行,j列砖块的图片对象的数据结构解决了此问题。此份代码在定时器中不断调用函数的方法,后续在对代码改动时,会十分简单方便。

1.3. 完整代码

	<!DOCTYPE html>
	<html>
		<head>
			<meta charset="UTF-8">
			<title></title>
			<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
			<style type="text/css">
				*{
					margin: 0;
					padding: 0;
				}
				
				.container{
					height:402px;
					width: 532px;
				}
				
				 /*1. 默认为本层(本级)目录. 2.canvas不好设置定位。*/
				#canvasBoard{
					background-image: url(img/bg2.png);
				}
				
				#baffle{
					background-image: url(img/board.png);
					width: 126px;
					height: 9px;
				}
				
			</style>
		</head>
		<body>
			<div class="container">
				<canvas id="canvasBoard" width="524px" height="375px">
					
				</canvas>
			</div>
			
		</body>
		<script type="text/javascript">
			// 整个游戏画布
			var gameCanvas = document.getElementById("canvasBoard");
			var gameContext = gameCanvas.getContext("2d");
			
			var timer;
			
			var brick;
			//定义砖块宽高
			var brickWidth = 100;
			var brickHeight = 25;
			//砖块位置数组
			var brickList = [];
			
			//定义砖块图片对象数组
			var brickImage = [];
			
			//加载图片
			loadImage();
			function loadImage () {
				for (var i = 0; i < 6; i++) {
					var img = new Image();
					var index = i + 4;
					var src = "img/" + index + ".png"
					img.src = src;
					brickImage[i] = img;
				}
			}
			// 小球初始化
			var ball = new Ball();
			ball.img.onload = function()
			{
				ball.drawBall();
			}
			
			var board = new Border();
			
			board.img.onload = function()
			{
				board.drawBoard();
			}
			
			createBrick();
			drawBrick();
			
			function createBrick(){
				for (var i = 0; i < 5; i++) {
					// 3. 
					brickList[i] = [];
					for(var j = 0; j < 4; j++){
						brickList[i][j] = Math.floor(Math.random()*6) + 1;
					}
				}
			}
			
			
			
			//绘制砖块
			function drawBrick () {
				for (var i = 0; i < 5; i++) {
					for(var j = 0; j < 4; j++){
						if(brickList[i][j] > 0){
							// list代表i行,j列砖块的颜色类型
							// blockImage则是存着每个图片对象
							var img = brickImage[brickList[i][j] - 1];
							gameContext.drawImage(img,
								i * brickWidth + 2*i + 7,
								j * brickHeight + 2*j + 50,
								brickWidth,
								brickHeight);
						}
					}
				}
			}
			
			// 在构造函数中,定义小球的位置,宽高,移速,图片等属性 2. 
			function Ball()
			{
				//位置
				this.x = 250;
				this.y = 300;
				
				//宽高
				this.width = 30;
				this.height = 30;
			
				//移动速度
				this.vx = 2;
				this.vy = -2;
				
				//小球图片
				this.img = getBallImage();
				
				//更新小球位置
				this.updateBallFrame = function () {
					this.x = this.x + this.vx;
					this.y = this.y + this.vy;
				}
				
				//绘制小球
				this.drawBall = function () {
					gameContext.drawImage(this.img,this.x,this.y,this.width,this.height);
				}
			}
			
			function Border()
			{
				//挡板位置
				this.x = 242;
				this.y = 330;	
				//挡板宽高
				this.width = 50;
				this.height = 22;
				
				//挡板图片
				this.img = getBoardImage();
				
				//挡板绘制
				this.drawBoard = function () {
					gameContext.drawImage(this.img,this.x,this.y,this.width,this.height);
				}
			}
			// this则代表new的对象
			function Brick()
			{
				this.currentX = 0;
				this.currentY = 0;
				this.img = getImage();
				//画砖块
				this.drawBrick = function(i, j)
				{
					gameContext.drawImage(this.img, 0, 0, brickWidth, brickHeight, brickWidth * i, brickHeight * (this.currentY + j), brickWidth, brickHeight);
				}
			}
			
			timer = setInterval(function () {
				//清空画布
				gameContext.clearRect(0, 0, gameCanvas.width, gameCanvas.height);
				
				//检测碰撞
				collide();
				
				//绘制上方柱子
				// gameContext.drawImage(pillar,325,250,565,10,5,40,515,8);
				
				//更新小球位置
				ball.updateBallFrame();
				
				//绘制小球
				ball.drawBall();
				
				//绘制挡板
				board.drawBoard();
				
				//绘制砖块
				drawBrick();
			},30);
			
			function getBoardImage()
			{
				var img = new Image();
				img.src = "img/board.png"; 
				return img;
			}
			
			//获取小球图片对象
			function getBallImage () {
				var img = new Image();
				img.src = "img/ball.png";
				return img;
			}
			
			//碰撞检测 
			function collide () {
				//小球的的下一次位置
				var nextX = ball.x + ball.vx;
				var nextY = ball.y + ball.vy;
				
				//小球的中间位置的x,y坐标
				var midX = ball.x + ball.width / 2;
				var midY = ball.y+ ball.height / 2;
				
				//与挡板的碰撞检测
				if( ball.y + ball.height > board.y //球的下端接触到挡板 
					&& ball.y + ball.height < board.y + board.height //但球的下端不超过挡板
					&& midX > board.x //球队中部位于挡板上
					&& midX < board.x + board.width //球队中部位于挡板上
					)
				{
					ball.vy = -2;
				}
				
				//与砖块碰撞检测
				for (var i = 0; i < 5; i++) {
					for(var j = 0; j < 4; j++){
						if(brickList[i][j] > 0)
						{
							//砖块的四条边的位置
							var leftX = i * brickWidth + 2*i + 7;
							var rightX = leftX + brickWidth;
							var topY = j * brickHeight + 2*j + 50;
							var bottomY = topY + brickHeight;
							
							
							//底部碰撞
							if(nextY > topY && nextY < bottomY 
								&& midX > leftX && midX < rightX)
							{
								ball.vy *= -1;
								brickList[i][j] = 0;
							}
						}
					}
				}
			}
			
			//鼠标移动函数
			gameCanvas.onmousemove = function (e) {
				var ev = e || window.event;
				//清除上次挡板痕迹
				gameContext.clearRect(board.x,board.y,board.width,board.height);
				
				//判断挡板右侧是否超出屏幕
				if(ev.clientX < gameCanvas.width - board.width)
				{
					board.x = ev.clientX;
				}
				else
				{
					board.x = gameCanvas.width - board.width;
				}
				board.drawBoard();
			}
		</script>
	</html>
发布了6 篇原创文章 · 获赞 6 · 访问量 460

猜你喜欢

转载自blog.csdn.net/qq_40092110/article/details/104486701