<Unity3D>使用Unity来制作俄罗斯方块游戏

1. 操作环境

Unity3D 4.1.0版本、Win 7

备注:该方法并非本人原创,我也是根据别人的代码来学习的。

2. 思路分析

该方法中,只有2个脚本,一个是控制方块的(Block.cs),另外一个是控制游戏场景的(Manager.cs)。游戏完成后效果如下图:

2.1 方块的构造(Block.cs)

俄罗斯方块一共有7种不同的方块。每个种类有4个小方块组成的。我们使用一个string[ ]数组来记录方块的形状。

从上图我们可以看出,1表示有方块,0表示没有方块。我们就可以根据该数组来实例化了,而且该数组的长度和宽度是相同的。这样方便我们对他进行旋转、移动等操作。

2.2 游戏场景(Manager.cs)

我们通过自己的喜欢来设计场景,但是要保证宽度必须是小方块的整数倍。不然就会产生无法填满的结果了。在这里我设计了领域宽度为18单位(FieldWidth = 18),领域高度为19单位(FieldHeight = 19)。我们定义个bool值数组来表示领域中的状态,0表示该处为空(即无方块),1表示有方块存在。我们对方块的操作都需要来监测场景中的状态,当满足其状态要求时,就可以执行相应的操作了。

3. 代码

Block.CS
using UnityEngine;
using System.Collections;

public class Block : MonoBehaviour {
      
	//根据字符串来定义方块的形状//
	public string[] block;
	
	private bool[,] blockMatrix;//方块的矩阵/
	private float fallSpeed;//下降速度/
	private int halfSize;
	private float halfSizeFloat;
	private int xPosition;
	private int yPosition;
	private bool dropped=false;//是否下降/
	private int size;
	
	private float pressInterval=0;//按键时间间隔/
	
	void Start () {	
	
		size=block.Length;
		int width=block[0].Length;		
		//不符合条件的方块弹出错误信息//
		if(size<2){
		
			Debug.LogError("Block must have at lest two lines!");
			return;
		}
		if(width != size){
		
			Debug.LogError("Block width and height must be the same!");
			return;
		}
		if(size > Manager.user.maxBlockSize){
		
			Debug.LogError("Block must not be larger than "+Manager.user.maxBlockSize);
			return;
		}
		for(int i=1;i<size;i++){
		
			if(block[i].Length != block[i-1].Length){
			
				Debug.LogError("All line in the block must be the same height!");
				return;
			}
		}
		//halfSize为整型——用于标记数组,halfSizeFloat为浮点型——用于定位屏幕上的位置
		halfSize = size/2;
		halfSizeFloat = size * 0.5f;
		
		//将字符串数组转换成bool类型的数组
		blockMatrix=new bool [size,size];
		for(int y=0;y<size;y++){
		
			for(int x=0;x<size;x++){
			
				if(block[y][x]=="1"[0]){
				
					blockMatrix[x,y]=true;
					//字符串为1的地方创建一个cube//
					Transform t=Instantiate(Manager.user.cube,new Vector3(x-halfSizeFloat,(size-y)+halfSizeFloat-size,0.0f),Quaternion.identity) as Transform;
					//将其拖放到该Block下//
					t.parent=transform;
				}
			}
		}
		//初始化block的位置,如果方块的大小为偶数,则+0,为奇数则+0.5f
		transform.position= new Vector3 (Manager.user.GetFieldWidth()/2+(size%2==0? 0.0f:0.5f),transform.position.y,transform.position.z);
		xPosition=(int)(transform.position.x-halfSizeFloat);
		yPosition=Manager.user.GetFieldHeight()-1;
		transform.position= new Vector3 (transform.position.x,yPosition-halfSizeFloat,transform.position.z);
		fallSpeed=Manager.user.blockNormalSpeed;
		
		//监测是否有重叠的方块,如果存在,则游戏结束!/
		if(Manager.user.CheckBlock(blockMatrix,xPosition,yPosition)){
		
			Manager.user.GameOver();
			return;
		}
		
		//监测输入
		StartCoroutine(CheckInput());
		StartCoroutine(Delay((1.0f/Manager.user.blockNormalSpeed)*2.0f));
		StartCoroutine(Fall());
	}
	
	
	/// <summary>
	/// Delay the specified time.
	/// </summary>	
	IEnumerator Delay(float time){
	
		float t=0.0f;
		while(t<time && !dropped){
		
			t += Time.deltaTime;
			yield return 0;
		}
	}
	
	/// <summary>
	/// 方块降落
	/// </summary>
	IEnumerator Fall(){
	
		while(true){
		
			yPosition--;
			//监测方块移动一行是否产生碰撞/
			if(Manager.user.CheckBlock(blockMatrix,xPosition,yPosition)){
			
				Manager.user.SetBlock(blockMatrix,xPosition,yPosition+1);
				Destroy(gameObject);
				break;
			}
			//方块降落一个单位/
			for(float i=yPosition+1;i>yPosition;i-= Time.deltaTime*fallSpeed){
			
				transform.position=new Vector3 (transform.position.x,i-halfSizeFloat,transform.position.z);
				yield return 0;
			}
		}
	}
	
	/// <summary>
	/// Checks the input.
	/// </summary>
	IEnumerator CheckInput(){
	
		while(true){
			
			pressInterval += Time.deltaTime;
			
			if(Input.GetKey(KeyCode.LeftArrow) && pressInterval>0.1f){
			
				StartCoroutine(MoveHorizontal(-1));
				pressInterval=0;
				
			}else if(Input.GetKey(KeyCode.RightArrow) && pressInterval>0.1f){
			
				StartCoroutine(MoveHorizontal(1));
				pressInterval=0;
			}
			
			if(Input.GetKeyDown(KeyCode.UpArrow)){
			
				RotateBlock();
			}
			if(Input.GetKey(KeyCode.DownArrow)){
			
				fallSpeed=Manager.user.blockDropSpeed;
				dropped=true;
				break;
			}
			
			yield return 0;
		}
	}
	/// <summary>
	/// Moves the horizontal.
	/// </summary>	
	IEnumerator MoveHorizontal(int dir){
	
		if(!Manager.user.CheckBlock(blockMatrix,xPosition+dir,yPosition)){
		
			transform.position += new Vector3 (dir,transform.position.y,transform.position.z);
			xPosition += dir;
			yield return new WaitForSeconds(Manager.user.blockMoveDelay);
		}
	}
	
	/// <summary>
	/// Rotates the block.
	/// 顺时针旋转90度,并将其结果存储到tempMatrix中/
	/// </summary>
	void RotateBlock(){
		
		bool[,] tempMatrix=new bool [size,size];
		for(int y=0;y<size;y++){
		
			for(int x=0;x<size;x++){
			
				tempMatrix[y,x]=blockMatrix[x,(size-1)-y];
			}
		}		
		//如果旋转后的方块没有与已存在的方块重叠,则将旋转后的矩阵复制,并且显示旋转后的方块/
		if(!Manager.user.CheckBlock(tempMatrix,xPosition,yPosition)){
		
			System.Array.Copy(tempMatrix,blockMatrix,size*size);
			transform.Rotate(Vector3.forward*(-90.0f));
		}
	}
}

Manager.cs
using UnityEngine;
using System.Collections;

public class Manager : MonoBehaviour {        
	
	public int FieldWidth = 18;//领域宽度/
	public int FieldHeight = 19;//领域高度/
	public int maxBlockSize = 4;//最大方块尺寸/
	public float blockNormalSpeed=2.0f;//方块正常下降速度//
	public int blockDropSpeed=30;//方块下降速度/
	public float blockMoveDelay=0.1f;//方块移动延迟/
	public int rowsClearedToSpeedup=10;//行清加速/
	public float speedupAmount=0.5f;//加速数量/
	public GameObject[] blocks;//块//
	public Transform cube;//盒子//	
	
	public Transform leftWall;//左边墙壁
	public Transform rightWall;//右边墙壁
	
	private int fieldWidth;
	private int fieldHeight;
	private bool[,] field;//场景区域的bool值/
	private Transform[] cubeReferences;
	private  int[] cubePositions;
	private int rowsCleared =0;
	public static Manager user;
	
	void Start () {
	
		if(!user){
		
			user=this;
		}else{
		
			Debug.LogError("在这个脚本中只允许存在一个实例!");
			return;
		}
		//场景区域宽度增加2*最大方块尺寸(左右各一个)/
		fieldWidth = FieldWidth + maxBlockSize*2;
		//场景区域高度增加1*最大方块尺寸(顶端)/
		fieldHeight = FieldHeight + maxBlockSize;
		field = new bool[fieldWidth,fieldHeight];
		
		//将墙壁放入到field数组中,true=block,false=open
		//0=bottom,fieldHeight-1=top
		for(int i=0;i<fieldHeight;i++){
		
			for(int j=0;j<maxBlockSize;j++){
			
				field[j,i]=true;
				field[fieldWidth-1-j,i]=true;
			}
		}
		for(int i=0;i<fieldWidth;i++){	
		
			field[i,0]=true;
			
		}
		
		//设置墙壁的位置//
		leftWall.position=new Vector3 (maxBlockSize-1,leftWall.position.y,leftWall.position.z);
		rightWall.position=new Vector3 (fieldWidth-maxBlockSize,rightWall.position.y,rightWall.position.z);
		Camera.main.transform.position=new Vector3 (fieldWidth/2,fieldHeight/2-1,-10);
		
		cubeReferences=new Transform [fieldWidth*fieldHeight];
		cubePositions=new int [fieldWidth*fieldHeight];
		
		//实例化一个方块//
		SpawnBlock();
	}	
	
	//实例化方块//
	void SpawnBlock(){
	
		Instantiate(blocks[Random.Range(0,blocks.Length)]);
	}
	
	
	public int GetFieldWidth(){
	
		return fieldWidth;
	}
	public int GetFieldHeight(){
	
		return fieldHeight;
	}
	
	/// <summary>
	/// 监测blockMatrix是否已经存在block
	/// 我们从bottom向top监测
	/// </summary>	
	public bool CheckBlock(bool[,] blockMatrix,int xPos,int yPos){
	
		//GetLength(0)获取第一维元素的长度//
		int size=blockMatrix.GetLength(0);
		//监测顺序为bottom-top,left-right
		for(int y=size-1;y>=0;y--){
		
			for(int x=0;x<size;x++){
			
				if(blockMatrix[x,y] && field[xPos+x,yPos-y]){
				
					return true;
				}
			}
		}
		return false;
	}

	/// <summary>
	/// 监测屏幕上停止的方块
	/// 仅仅使用孩子物体是不行的,因为孩子方块的方向是不同的
	/// 使用Y轴会使他们位置混乱的,所以我们要使用一致的CollapseRow
	/// 在领域中,我们将blockMatrix写入到相应的位置
	/// </summary>	
	public void SetBlock(bool[,] blockMatrix, int xPos,int yPos){
	
		int size=blockMatrix.GetLength(0);
		for(int y=0;y<size;y++){
		
			for(int x=0;x<size;x++){
			
				if(blockMatrix[x,y]){
				
					Instantiate(cube,new Vector3(xPos+x,yPos-y,0.0f),Quaternion.identity);
					field[xPos+x,yPos-y]=true;
				}
			}
		}
		
		StartCoroutine(CheckRows(yPos-size,size));
		SpawnBlock();
	}
	
	/// <summary>
	/// 监测领域中的每一行/
	/// </summary>	
	public IEnumerator CheckRows(int yStart,int size){
	
		//等待一帧//
		yield return 0;
		if(yStart<1)
			yStart=1;//确保从bottom开始//
		for(int y=yStart;y<yStart+size;y++){
		
			int x=0;
			for(x=maxBlockSize;x<fieldWidth-maxBlockSize;x++){
			
				//不需要监测左右墙壁//
				if(!field[x,y])
					break;
			}
			//当该循环结束后,x=fieldWidth-maxBlockSize,这就表示该行已被填满了!!!!//
			if(x==fieldWidth-maxBlockSize){
			
				StartCoroutine(CollapseRows(y));
				y--;
			}
		}
	}
	
	/// <summary>
	/// 消除一行
	/// </summary>	
	public IEnumerator CollapseRows(int yStart){
	
		//将数组中的行下移,最有效的就是删除当前行(yStart)/
		for(int y=yStart;y<fieldHeight-1;y++){
		
			for(int x=maxBlockSize;x<fieldWidth-maxBlockSize;x++){
			
				field[x,y]=field[x,y+1];
			}
		}
		//确保top层被清空/
		for(int x=maxBlockSize;x<fieldWidth-maxBlockSize;x++){
		
			field[x,fieldHeight-1]=false;
		}
		
		//消除该行的cube,存储该行上面的cube//
		GameObject[] cubes=GameObject.FindGameObjectsWithTag("cube");
		int cubeToMove=0;
		foreach( GameObject c in cubes){
			
			if((int)(c.transform.position.y)>yStart){
			
				cubePositions[cubeToMove]=(int)c.transform.position.y;
				cubeReferences[cubeToMove++]=c.transform;
			}else if((int)(c.transform.position.y)==yStart){
			
				//销毁/
				Destroy(c);
			}
		}
		
		//将靠近的方块下一个立方/
		//Mathf.Lerp的第三个参数固定在1.0,这样使transform.position.y更加的精确。/
		float t=0.0f;
		while(t<=1.0f){
		
			t += Time.deltaTime*5.0f;
			for(int i=0;i<cubeToMove;i++){
			
				cubeReferences[i].position=new Vector3 (cubeReferences[i].position.x,Mathf.Lerp(cubePositions[i],cubePositions[i]-1,t),cubeReferences[i].position.z);
				
			}
			yield return 0;
		}
		//当行被清空的时候,让方块下降速度加快/
		if(++rowsCleared==rowsClearedToSpeedup){
		
			blockNormalSpeed+=speedupAmount;
			rowsCleared=0;
		}
		
	}
	/// <summary>
	/// Games the over.
	/// </summary>
	public void GameOver(){
	
		Debug.Log("Game Over!");
	}
	
	/// <summary>
	/// Prints the field.用于Debug
	/// </summary>
	void PrintField(){
		
		string fieldChars="";
		for(int y=fieldHeight-1;y>=0;y--){
		
			for(int x=0;x<fieldWidth;x++){
			
				fieldChars += field[y,x]?"1":"0";
			}
			fieldChars +="\n";
		}
		Debug.Log(fieldChars);
	}
}

4. 项目下载和讨论

大家可以到我的CSDN资源中下载~
如果有什么问题,大家可以给我留言,我尽快给大家回复~


  • 9
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值