1. 操作环境
复制代码
Manager.cs
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));
- }
- }
- }
- 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);
- }
- }