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

1. using UnityEngine;
2. using System.Collections;
3. 
4. public class Block : MonoBehaviour {

5. 
6.     //根据字符串来定义方块的形状//
7. public string[] block;
8. 
9.     private bool[,] blockMatrix;//方块的矩阵/
10. private float fallSpeed;//下降速度/
11. private int halfSize;
12.     private float halfSizeFloat;
13.     private int xPosition;
14.     private int yPosition;
15.     private bool dropped=false;//是否下降/
16. private int size;
17. 
18.     private float pressInterval=0;//按键时间间隔/
19. 
20. void Start () {

21. 
22.         size=block.Length;
23.         int width=block[0].Length;     
24.         //不符合条件的方块弹出错误信息//
25. if(size<2){

26. 
27.             Debug.LogError("Block must have at lest two lines!");
28.             return;
29.         }
30.         if(width != size){

31. 
32.             Debug.LogError("Block width and height must be the same!");
33.             return;
34.         }
35.         if(size > Manager.user.maxBlockSize){

36. 
37.             Debug.LogError("Block must not be larger than "+Manager.user.maxBlockSize);
38.             return;
39.         }
40.         for(int i=1;i<size;i++){

41. 
42.             if(block[i].Length != block[i-1].Length){

43. 
44.                 Debug.LogError("All line in the block must be the same height!");
45.                 return;
46.             }
47.         }
48.         //halfSize为整型——用于标记数组,halfSizeFloat为浮点型——用于定位屏幕上的位置
49. halfSize = size/2;
50.         halfSizeFloat = size * 0.5f;
51. 
52.         //将字符串数组转换成bool类型的数组
53. blockMatrix=new bool [size,size];
54.         for(int y=0;y<size;y++){

55. 
56.             for(int x=0;x<size;x++){

57. 
58.                 if(block[y][x]=="1"[0]){

59. 
60.                     blockMatrix[x,y]=true;
61.                     //字符串为1的地方创建一个cube//
62.                     Transform t=Instantiate(Manager.user.cube,new Vector3(x-halfSizeFloat,(size-y)+halfSizeFloat-size,0.0f),Quaternion.identity) as Transform;
63.                     //将其拖放到该Block下//
64. t.parent=transform;
65.                 }
66.             }
67.         }
68.         //初始化block的位置,如果方块的大小为偶数,则+0,为奇数则+0.5f
69.         transform.position= new Vector3 (Manager.user.GetFieldWidth()/2+(size%2==0? 0.0f:0.5f),transform.position.y,transform.position.z);
70.         xPosition=(int)(transform.position.x-halfSizeFloat);
71.         yPosition=Manager.user.GetFieldHeight()-1;
72.         transform.position= new Vector3 (transform.position.x,yPosition-halfSizeFloat,transform.position.z);
73.         fallSpeed=Manager.user.blockNormalSpeed;
74. 
75.         //监测是否有重叠的方块,如果存在,则游戏结束!/
76. if(Manager.user.CheckBlock(blockMatrix,xPosition,yPosition)){

77. 
78.             Manager.user.GameOver();
79.             return;
80.         }
81. 
82.         //监测输入
83. StartCoroutine(CheckInput());
84.         StartCoroutine(Delay((1.0f/Manager.user.blockNormalSpeed)*2.0f));
85.         StartCoroutine(Fall());
86.     }
87. 
88. 
89.     /// <summary>
90.     /// Delay the specified time.
91.     /// </summary>   
92.     IEnumerator Delay(float time){

93. 
94.         float t=0.0f;
95.         while(t<time && !dropped){

96. 
97.             t += Time.deltaTime;
98.             yield return 0;
99.         }
100.     }
101. 
102.     /// <summary>
103.     /// 方块降落
104. /// </summary>
105.     IEnumerator Fall(){

106. 
107.         while(true){

108. 
109.             yPosition--;
110.             //监测方块移动一行是否产生碰撞/
111. if(Manager.user.CheckBlock(blockMatrix,xPosition,yPosition)){

112. 
113.                 Manager.user.SetBlock(blockMatrix,xPosition,yPosition+1);
114.                 Destroy(gameObject);
115.                 break;
116.             }
117.             //方块降落一个单位/
118. for(float i=yPosition+1;i>yPosition;i-= Time.deltaTime*fallSpeed){

119. 
120.                 transform.position=new Vector3 (transform.position.x,i-halfSizeFloat,transform.position.z);
121.                 yield return 0;
122.             }
123.         }
124.     }
125. 
126.     /// <summary>
127.     /// Checks the input.
128.     /// </summary>
129.     IEnumerator CheckInput(){

130. 
131.         while(true){

132. 
133.             pressInterval += Time.deltaTime;
134. 
135.             if(Input.GetKey(KeyCode.LeftArrow) && pressInterval>0.1f){

136. 
137.                 StartCoroutine(MoveHorizontal(-1));
138.                 pressInterval=0;
139. 
140.             }else if(Input.GetKey(KeyCode.RightArrow) && pressInterval>0.1f){

141. 
142.                 StartCoroutine(MoveHorizontal(1));
143.                 pressInterval=0;
144.             }
145. 
146.             if(Input.GetKeyDown(KeyCode.UpArrow)){

147. 
148.                 RotateBlock();
149.             }
150.             if(Input.GetKey(KeyCode.DownArrow)){

151. 
152.                 fallSpeed=Manager.user.blockDropSpeed;
153.                 dropped=true;
154.                 break;
155.             }
156. 
157.             yield return 0;
158.         }
159.     }
160.     /// <summary>
161.     /// Moves the horizontal.
162.     /// </summary>   
163.     IEnumerator MoveHorizontal(int dir){

164. 
165.         if(!Manager.user.CheckBlock(blockMatrix,xPosition+dir,yPosition)){

166. 
167.             transform.position += new Vector3 (dir,transform.position.y,transform.position.z);
168.             xPosition += dir;
169.             yield return new WaitForSeconds(Manager.user.blockMoveDelay);
170.         }
171.     }
172. 
173.     /// <summary>
174.     /// Rotates the block.
175.     /// 顺时针旋转90度,并将其结果存储到tempMatrix中/
176.     /// </summary>
177.     void RotateBlock(){

178. 
179.         bool[,] tempMatrix=new bool [size,size];
180.         for(int y=0;y<size;y++){

181. 
182.             for(int x=0;x<size;x++){

183. 
184.                 tempMatrix[y,x]=blockMatrix[x,(size-1)-y];
185.             }
186.         }      
187.         //如果旋转后的方块没有与已存在的方块重叠,则将旋转后的矩阵复制,并且显示旋转后的方块/
188. if(!Manager.user.CheckBlock(tempMatrix,xPosition,yPosition)){

189. 
190.             System.Array.Copy(tempMatrix,blockMatrix,size*size);
191.             transform.Rotate(Vector3.forward*(-90.0f));
192.         }
193.     }
194. }


复制代码

Manager.cs

1. using UnityEngine;
2. using System.Collections;
3. 
4. public class Manager : MonoBehaviour {       
5. 
6.     public int FieldWidth = 18;//领域宽度/
7. public int FieldHeight = 19;//领域高度/
8. public int maxBlockSize = 4;//最大方块尺寸/
9. public float blockNormalSpeed=2.0f;//方块正常下降速度//
10. public int blockDropSpeed=30;//方块下降速度/
11. public float blockMoveDelay=0.1f;//方块移动延迟/
12. public int rowsClearedToSpeedup=10;//行清加速/
13. public float speedupAmount=0.5f;//加速数量/
14. public GameObject[] blocks;//块//
15. public Transform cube;//盒子//   
16. 
17. public Transform leftWall;//左边墙壁
18. public Transform rightWall;//右边墙壁
19. 
20. private int fieldWidth;
21.     private int fieldHeight;
22.     private bool[,] field;//场景区域的bool值/
23. private Transform[] cubeReferences;
24.     private  int[] cubePositions;
25.     private int rowsCleared =0;
26.     public static Manager user;
27. 
28.     void Start () {

29. 
30.         if(!user){

31. 
32.             user=this;
33.         }else{

34. 
35.             Debug.LogError("在这个脚本中只允许存在一个实例!");
36. return;
37.         }
38.         //场景区域宽度增加2*最大方块尺寸(左右各一个)/
39. fieldWidth = FieldWidth + maxBlockSize*2;
40.         //场景区域高度增加1*最大方块尺寸(顶端)/
41. fieldHeight = FieldHeight + maxBlockSize;
42.         field = new bool[fieldWidth,fieldHeight];
43. 
44.         //将墙壁放入到field数组中,true=block,false=open
45.         //0=bottom,fieldHeight-1=top
46.         for(int i=0;i<fieldHeight;i++){

47. 
48.             for(int j=0;j<maxBlockSize;j++){

49. 
50.                 field[j,i]=true;
51.                 field[fieldWidth-1-j,i]=true;
52.             }
53.         }
54.         for(int i=0;i<fieldWidth;i++){  
55. 
56.             field[i,0]=true;
57. 
58.         }
59. 
60.         //设置墙壁的位置//
61. leftWall.position=new Vector3 (maxBlockSize-1,leftWall.position.y,leftWall.position.z);
62.         rightWall.position=new Vector3 (fieldWidth-maxBlockSize,rightWall.position.y,rightWall.position.z);
63.         Camera.main.transform.position=new Vector3 (fieldWidth/2,fieldHeight/2-1,-10);
64. 
65.         cubeReferences=new Transform [fieldWidth*fieldHeight];
66.         cubePositions=new int [fieldWidth*fieldHeight];
67. 
68.         //实例化一个方块//
69. SpawnBlock();
70.     }  
71. 
72.     //实例化方块//
73. void SpawnBlock(){

74. 
75.         Instantiate(blocks[Random.Range(0,blocks.Length)]);
76.     }
77. 
78. 
79.     public int GetFieldWidth(){

80. 
81.         return fieldWidth;
82.     }
83.     public int GetFieldHeight(){

84. 
85.         return fieldHeight;
86.     }
87. 
88.     /// <summary>
89.     /// 监测blockMatrix是否已经存在block
90.     /// 我们从bottom向top监测
91. /// </summary>   
92.     public bool CheckBlock(bool[,] blockMatrix,int xPos,int yPos){

93. 
94.         //GetLength(0)获取第一维元素的长度//
95. int size=blockMatrix.GetLength(0);
96.         //监测顺序为bottom-top,left-right
97.         for(int y=size-1;y>=0;y--){

98. 
99. for(int x=0;x<size;x++){

100. 
101.                 if(blockMatrix[x,y] && field[xPos+x,yPos-y]){

102. 
103.                     return true;
104.                 }
105.             }
106.         }
107.         return false;
108.     }
109. 
110.     /// <summary>
111.     /// 监测屏幕上停止的方块
112. /// 仅仅使用孩子物体是不行的,因为孩子方块的方向是不同的
113. /// 使用Y轴会使他们位置混乱的,所以我们要使用一致的CollapseRow
114.     /// 在领域中,我们将blockMatrix写入到相应的位置
115. /// </summary>   
116.     public void SetBlock(bool[,] blockMatrix, int xPos,int yPos){

117. 
118.         int size=blockMatrix.GetLength(0);
119.         for(int y=0;y<size;y++){

120. 
121.             for(int x=0;x<size;x++){

122. 
123.                 if(blockMatrix[x,y]){

124. 
125.                     Instantiate(cube,new Vector3(xPos+x,yPos-y,0.0f),Quaternion.identity);
126.                     field[xPos+x,yPos-y]=true;
127.                 }
128.             }
129.         }
130. 
131.         StartCoroutine(CheckRows(yPos-size,size));
132.         SpawnBlock();
133.     }
134. 
135.     /// <summary>
136.     /// 监测领域中的每一行/
137.     /// </summary>   
138.     public IEnumerator CheckRows(int yStart,int size){

139. 
140.         //等待一帧//
141. yield return 0;
142.         if(yStart<1)
143.             yStart=1;//确保从bottom开始//
144. for(int y=yStart;y<yStart+size;y++){

145. 
146.             int x=0;
147.             for(x=maxBlockSize;x<fieldWidth-maxBlockSize;x++){

148. 
149.                 //不需要监测左右墙壁//
150. if(!field[x,y])
151.                     break;
152.             }
153.             //当该循环结束后,x=fieldWidth-maxBlockSize,这就表示该行已被填满了!!!!//
154. if(x==fieldWidth-maxBlockSize){

155. 
156.                 StartCoroutine(CollapseRows(y));
157.                 y--;
158.             }
159.         }
160.     }
161. 
162.     /// <summary>
163.     /// 消除一行
164. /// </summary>   
165.     public IEnumerator CollapseRows(int yStart){

166. 
167.         //将数组中的行下移,最有效的就是删除当前行(yStart)/
168. for(int y=yStart;y<fieldHeight-1;y++){

169. 
170.             for(int x=maxBlockSize;x<fieldWidth-maxBlockSize;x++){

171. 
172.                 field[x,y]=field[x,y+1];
173.             }
174.         }
175.         //确保top层被清空/
176. for(int x=maxBlockSize;x<fieldWidth-maxBlockSize;x++){

177. 
178.             field[x,fieldHeight-1]=false;
179.         }
180. 
181.         //消除该行的cube,存储该行上面的cube//
182.         GameObject[] cubes=GameObject.FindGameObjectsWithTag("cube");
183.         int cubeToMove=0;
184.         foreach( GameObject c in cubes){

185. 
186.             if((int)(c.transform.position.y)>yStart){

187. 
188.                 cubePositions[cubeToMove]=(int)c.transform.position.y;
189.                 cubeReferences[cubeToMove++]=c.transform;
190.             }else if((int)(c.transform.position.y)==yStart){

191. 
192.                 //销毁/
193. Destroy(c);
194.             }
195.         }
196. 
197.         //将靠近的方块下一个立方/
198.         //Mathf.Lerp的第三个参数固定在1.0,这样使transform.position.y更加的精确。/
199. float t=0.0f;
200.         while(t<=1.0f){

201. 
202.             t += Time.deltaTime*5.0f;
203.             for(int i=0;i<cubeToMove;i++){

204. 
205.                 cubeReferences[i].position=new Vector3 (cubeReferences[i].position.x,Mathf.Lerp(cubePositions[i],cubePositions[i]-1,t),cubeReferences[i].position.z);
206. 
207.             }
208.             yield return 0;
209.         }
210.         //当行被清空的时候,让方块下降速度加快/
211. if(++rowsCleared==rowsClearedToSpeedup){

212. 
213.             blockNormalSpeed+=speedupAmount;
214.             rowsCleared=0;
215.         }
216. 
217.     }
218.     /// <summary>
219.     /// Games the over.
220.     /// </summary>
221.     public void GameOver(){

222. 
223.         Debug.Log("Game Over!");
224.     }
225. 
226.     /// <summary>
227.     /// Prints the field.用于Debug
228.     /// </summary>
229.     void PrintField(){

230. 
231.         string fieldChars="";
232.         for(int y=fieldHeight-1;y>=0;y--){

233. 
234. for(int x=0;x<fieldWidth;x++){

235. 
236.                 fieldChars += field[y,x]?"1":"0";
237.             }
238.             fieldChars +="\n";
239.         }
240.         Debug.Log(fieldChars);
241.     }
242. }