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. }