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.CSusing 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.csusing 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资源中下载~如果有什么问题,大家可以给我留言,我尽快给大家回复~