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

来源:互联网 发布:淮右军以人肉知乎 编辑:程序博客网 时间:2024/06/05 19:51

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


原创粉丝点击