使用Unity制作俄罗斯方块游戏

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. public class Block : MonoBehaviour {
  4.     //根据字符串来定义方块的形状//
  5. public string[] block;
  6.     private bool[,] blockMatrix;//方块的矩阵/
  7. private float fallSpeed;//下降速度/
  8. private int halfSize;
  9.     private float halfSizeFloat;
  10.     private int xPosition;
  11.     private int yPosition;
  12.     private bool dropped=false;//是否下降/
  13. private int size;
  14.     private float pressInterval=0;//按键时间间隔/
  15. void Start () {
  16.         size=block.Length;
  17.         int width=block[0].Length;    
  18.         //不符合条件的方块弹出错误信息//
  19. if(size<2){
  20.             Debug.LogError("Block must have at lest two lines!");
  21.             return;
  22.         }
  23.         if(width != size){
  24.             Debug.LogError("Block width and height must be the same!");
  25.             return;
  26.         }
  27.         if(size > Manager.user.maxBlockSize){
  28.             Debug.LogError("Block must not be larger than "+Manager.user.maxBlockSize);
  29.             return;
  30.         }
  31.         for(int i=1;i<size;i++){
  32.             if(block[i].Length != block[i-1].Length){
  33.                 Debug.LogError("All line in the block must be the same height!");
  34.                 return;
  35.             }
  36.         }
  37.         //halfSize为整型——用于标记数组,halfSizeFloat为浮点型——用于定位屏幕上的位置
  38. halfSize = size/2;
  39.         halfSizeFloat = size * 0.5f;
  40.         //将字符串数组转换成bool类型的数组
  41. blockMatrix=new bool [size,size];
  42.         for(int y=0;y<size;y++){
  43.             for(int x=0;x<size;x++){
  44.                 if(block[y][x]=="1"[0]){
  45.                     blockMatrix[x,y]=true;
  46.                     //字符串为1的地方创建一个cube//
  47.                     Transform t=Instantiate(Manager.user.cube,new Vector3(x-halfSizeFloat,(size-y)+halfSizeFloat-size,0.0f),Quaternion.identity) as Transform;
  48.                     //将其拖放到该Block下//
  49. t.parent=transform;
  50.                 }
  51.             }
  52.         }
  53.         //初始化block的位置,如果方块的大小为偶数,则+0,为奇数则+0.5f
  54.         transform.position= new Vector3 (Manager.user.GetFieldWidth()/2+(size%2==0? 0.0f:0.5f),transform.position.y,transform.position.z);
  55.         xPosition=(int)(transform.position.x-halfSizeFloat);
  56.         yPosition=Manager.user.GetFieldHeight()-1;
  57.         transform.position= new Vector3 (transform.position.x,yPosition-halfSizeFloat,transform.position.z);
  58.         fallSpeed=Manager.user.blockNormalSpeed;
  59.         //监测是否有重叠的方块,如果存在,则游戏结束!/
  60. if(Manager.user.CheckBlock(blockMatrix,xPosition,yPosition)){
  61.             Manager.user.GameOver();
  62.             return;
  63.         }
  64.         //监测输入
  65. StartCoroutine(CheckInput());
  66.         StartCoroutine(Delay((1.0f/Manager.user.blockNormalSpeed)*2.0f));
  67.         StartCoroutine(Fall());
  68.     }
  69.     /// <summary>
  70.     /// Delay the specified time.
  71.     /// </summary>  
  72.     IEnumerator Delay(float time){
  73.         float t=0.0f;
  74.         while(t<time &amp;&amp; !dropped){
  75.             t += Time.deltaTime;
  76.             yield return 0;
  77.         }
  78.     }
  79.     /// <summary>
  80.     /// 方块降落
  81. /// </summary>
  82.     IEnumerator Fall(){
  83.         while(true){
  84.             yPosition--;
  85.             //监测方块移动一行是否产生碰撞/
  86. if(Manager.user.CheckBlock(blockMatrix,xPosition,yPosition)){
  87.                 Manager.user.SetBlock(blockMatrix,xPosition,yPosition+1);
  88.                 Destroy(gameObject);
  89.                 break;
  90.             }
  91.             //方块降落一个单位/
  92. for(float i=yPosition+1;i>yPosition;i-= Time.deltaTime*fallSpeed){
  93.                 transform.position=new Vector3 (transform.position.x,i-halfSizeFloat,transform.position.z);
  94.                 yield return 0;
  95.             }
  96.         }
  97.     }
  98.     /// <summary>
  99.     /// Checks the input.
  100.     /// </summary>
  101.     IEnumerator CheckInput(){
  102.         while(true){
  103.             pressInterval += Time.deltaTime;
  104.             if(Input.GetKey(KeyCode.LeftArrow) &amp;&amp; pressInterval>0.1f){
  105.                 StartCoroutine(MoveHorizontal(-1));
  106.                 pressInterval=0;
  107.             }else if(Input.GetKey(KeyCode.RightArrow) &amp;&amp; pressInterval>0.1f){
  108.                 StartCoroutine(MoveHorizontal(1));
  109.                 pressInterval=0;
  110.             }
  111.             if(Input.GetKeyDown(KeyCode.UpArrow)){
  112.                 RotateBlock();
  113.             }
  114.             if(Input.GetKey(KeyCode.DownArrow)){
  115.                 fallSpeed=Manager.user.blockDropSpeed;
  116.                 dropped=true;
  117.                 break;
  118.             }
  119.             yield return 0;
  120.         }
  121.     }
  122.     /// <summary>
  123.     /// Moves the horizontal.
  124.     /// </summary>  
  125.     IEnumerator MoveHorizontal(int dir){
  126.         if(!Manager.user.CheckBlock(blockMatrix,xPosition+dir,yPosition)){
  127.             transform.position += new Vector3 (dir,transform.position.y,transform.position.z);
  128.             xPosition += dir;
  129.             yield return new WaitForSeconds(Manager.user.blockMoveDelay);
  130.         }
  131.     }
  132.     /// <summary>
  133.     /// Rotates the block.
  134.     /// 顺时针旋转90度,并将其结果存储到tempMatrix中/
  135.     /// </summary>
  136.     void RotateBlock(){
  137.         bool[,] tempMatrix=new bool [size,size];
  138.         for(int y=0;y<size;y++){
  139.             for(int x=0;x<size;x++){
  140.                 tempMatrix[y,x]=blockMatrix[x,(size-1)-y];
  141.             }
  142.         }     
  143.         //如果旋转后的方块没有与已存在的方块重叠,则将旋转后的矩阵复制,并且显示旋转后的方块/
  144. if(!Manager.user.CheckBlock(tempMatrix,xPosition,yPosition)){
  145.             System.Array.Copy(tempMatrix,blockMatrix,size*size);
  146.             transform.Rotate(Vector3.forward*(-90.0f));
  147.         }
  148.     }
  149. }
复制代码
Manager.cs
  1. using UnityEngine;
  2. using System.Collections;
  3. public class Manager : MonoBehaviour {      
  4.     public int FieldWidth = 18;//领域宽度/
  5. public int FieldHeight = 19;//领域高度/
  6. public int maxBlockSize = 4;//最大方块尺寸/
  7. public float blockNormalSpeed=2.0f;//方块正常下降速度//
  8. public int blockDropSpeed=30;//方块下降速度/
  9. public float blockMoveDelay=0.1f;//方块移动延迟/
  10. public int rowsClearedToSpeedup=10;//行清加速/
  11. public float speedupAmount=0.5f;//加速数量/
  12. public GameObject[] blocks;//块//
  13. public Transform cube;//盒子//  
  14. public Transform leftWall;//左边墙壁
  15. public Transform rightWall;//右边墙壁
  16. private int fieldWidth;
  17.     private int fieldHeight;
  18.     private bool[,] field;//场景区域的bool值/
  19. private Transform[] cubeReferences;
  20.     private  int[] cubePositions;
  21.     private int rowsCleared =0;
  22.     public static Manager user;
  23.     void Start () {
  24.         if(!user){
  25.             user=this;
  26.         }else{
  27.             Debug.LogError("在这个脚本中只允许存在一个实例!");
  28. return;
  29.         }
  30.         //场景区域宽度增加2*最大方块尺寸(左右各一个)/
  31. fieldWidth = FieldWidth + maxBlockSize*2;
  32.         //场景区域高度增加1*最大方块尺寸(顶端)/
  33. fieldHeight = FieldHeight + maxBlockSize;
  34.         field = new bool[fieldWidth,fieldHeight];
  35.         //将墙壁放入到field数组中,true=block,false=open
  36.         //0=bottom,fieldHeight-1=top
  37.         for(int i=0;i<fieldHeight;i++){
  38.             for(int j=0;j<maxBlockSize;j++){
  39.                 field[j,i]=true;
  40.                 field[fieldWidth-1-j,i]=true;
  41.             }
  42.         }
  43.         for(int i=0;i<fieldWidth;i++){ 
  44.             field[i,0]=true;
  45.         }
  46.         //设置墙壁的位置//
  47. leftWall.position=new Vector3 (maxBlockSize-1,leftWall.position.y,leftWall.position.z);
  48.         rightWall.position=new Vector3 (fieldWidth-maxBlockSize,rightWall.position.y,rightWall.position.z);
  49.         Camera.main.transform.position=new Vector3 (fieldWidth/2,fieldHeight/2-1,-10);
  50.         cubeReferences=new Transform [fieldWidth*fieldHeight];
  51.         cubePositions=new int [fieldWidth*fieldHeight];
  52.         //实例化一个方块//
  53. SpawnBlock();
  54.     } 
  55.     //实例化方块//
  56. void SpawnBlock(){
  57.         Instantiate(blocks[Random.Range(0,blocks.Length)]);
  58.     }
  59.     public int GetFieldWidth(){
  60.         return fieldWidth;
  61.     }
  62.     public int GetFieldHeight(){
  63.         return fieldHeight;
  64.     }
  65.     /// <summary>
  66.     /// 监测blockMatrix是否已经存在block
  67.     /// 我们从bottom向top监测
  68. /// </summary>  
  69.     public bool CheckBlock(bool[,] blockMatrix,int xPos,int yPos){
  70.         //GetLength(0)获取第一维元素的长度//
  71. int size=blockMatrix.GetLength(0);
  72.         //监测顺序为bottom-top,left-right
  73.         for(int y=size-1;y>=0;y--){
  74. for(int x=0;x<size;x++){
  75.                 if(blockMatrix[x,y] &amp;&amp; field[xPos+x,yPos-y]){
  76.                     return true;
  77.                 }
  78.             }
  79.         }
  80.         return false;
  81.     }
  82.     /// <summary>
  83.     /// 监测屏幕上停止的方块
  84. /// 仅仅使用孩子物体是不行的,因为孩子方块的方向是不同的
  85. /// 使用Y轴会使他们位置混乱的,所以我们要使用一致的CollapseRow
  86.     /// 在领域中,我们将blockMatrix写入到相应的位置
  87. /// </summary>  
  88.     public void SetBlock(bool[,] blockMatrix, int xPos,int yPos){
  89.         int size=blockMatrix.GetLength(0);
  90.         for(int y=0;y<size;y++){
  91.             for(int x=0;x<size;x++){
  92.                 if(blockMatrix[x,y]){
  93.                     Instantiate(cube,new Vector3(xPos+x,yPos-y,0.0f),Quaternion.identity);
  94.                     field[xPos+x,yPos-y]=true;
  95.                 }
  96.             }
  97.         }
  98.         StartCoroutine(CheckRows(yPos-size,size));
  99.         SpawnBlock();
  100.     }
  101.     /// <summary>
  102.     /// 监测领域中的每一行/
  103.     /// </summary>  
  104.     public IEnumerator CheckRows(int yStart,int size){
  105.         //等待一帧//
  106. yield return 0;
  107.         if(yStart<1)
  108.             yStart=1;//确保从bottom开始//
  109. for(int y=yStart;y<yStart+size;y++){
  110.             int x=0;
  111.             for(x=maxBlockSize;x<fieldWidth-maxBlockSize;x++){
  112.                 //不需要监测左右墙壁//
  113. if(!field[x,y])
  114.                     break;
  115.             }
  116.             //当该循环结束后,x=fieldWidth-maxBlockSize,这就表示该行已被填满了!!!!//
  117. if(x==fieldWidth-maxBlockSize){
  118.                 StartCoroutine(CollapseRows(y));
  119.                 y--;
  120.             }
  121.         }
  122.     }
  123.     /// <summary>
  124.     /// 消除一行
  125. /// </summary>  
  126.     public IEnumerator CollapseRows(int yStart){
  127.         //将数组中的行下移,最有效的就是删除当前行(yStart)/
  128. for(int y=yStart;y<fieldHeight-1;y++){
  129.             for(int x=maxBlockSize;x<fieldWidth-maxBlockSize;x++){
  130.                 field[x,y]=field[x,y+1];
  131.             }
  132.         }
  133.         //确保top层被清空/
  134. for(int x=maxBlockSize;x<fieldWidth-maxBlockSize;x++){
  135.             field[x,fieldHeight-1]=false;
  136.         }
  137.         //消除该行的cube,存储该行上面的cube//
  138.         GameObject[] cubes=GameObject.FindGameObjectsWithTag("cube");
  139.         int cubeToMove=0;
  140.         foreach( GameObject c in cubes){
  141.             if((int)(c.transform.position.y)>yStart){
  142.                 cubePositions[cubeToMove]=(int)c.transform.position.y;
  143.                 cubeReferences[cubeToMove++]=c.transform;
  144.             }else if((int)(c.transform.position.y)==yStart){
  145.                 //销毁/
  146. Destroy(c);
  147.             }
  148.         }
  149.         //将靠近的方块下一个立方/
  150.         //Mathf.Lerp的第三个参数固定在1.0,这样使transform.position.y更加的精确。/
  151. float t=0.0f;
  152.         while(t<=1.0f){
  153.             t += Time.deltaTime*5.0f;
  154.             for(int i=0;i<cubeToMove;i++){
  155.                 cubeReferences[i].position=new Vector3 (cubeReferences[i].position.x,Mathf.Lerp(cubePositions[i],cubePositions[i]-1,t),cubeReferences[i].position.z);
  156.             }
  157.             yield return 0;
  158.         }
  159.         //当行被清空的时候,让方块下降速度加快/
  160. if(++rowsCleared==rowsClearedToSpeedup){
  161.             blockNormalSpeed+=speedupAmount;
  162.             rowsCleared=0;
  163.         }
  164.     }
  165.     /// <summary>
  166.     /// Games the over.
  167.     /// </summary>
  168.     public void GameOver(){
  169.         Debug.Log("Game Over!");
  170.     }
  171.     /// <summary>
  172.     /// Prints the field.用于Debug
  173.     /// </summary>
  174.     void PrintField(){
  175.         string fieldChars="";
  176.         for(int y=fieldHeight-1;y>=0;y--){
  177. for(int x=0;x<fieldWidth;x++){
  178.                 fieldChars += field[y,x]?"1":"0";
  179.             }
  180.             fieldChars +="\n";
  181.         }
  182.         Debug.Log(fieldChars);
  183.     }
  184. }
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用Unity实现俄罗斯方块的小游戏,您可以按照以下步骤进行: 步骤1:创建游戏场景 在Unity中创建一个新的场景,并设置适当的摄像机和灯光。您可以使用2D或3D的方式来实现俄罗斯方块,具体取决于您的需求和偏好。 步骤2:创建游戏对象和脚本 创建俄罗斯方块的各种游戏对象,如方块、游戏区域、下落点等。然后,为每个游戏对象创建相应的脚本来控制它们的行为。 步骤3:实现方块的下落和移动 在游戏脚本中,实现方块的下落和移动逻辑。您可以使用定时器或帧更新来控制方块的下落速度,以及使用输入控制方块的左右移动和旋转。 步骤4:检测碰撞和消除行 实现方块与游戏区域的碰撞检测,以及行的消除逻辑。当方块落到底部或与其他方块碰撞时,将其固定在游戏区域中,并检查是否有完整的行可以消除。 步骤5:游戏结束和重置 实现游戏结束和重置逻辑。当方块堆积到达游戏区域的顶部时,游戏结束。您可以显示分数或其他游戏结束的界面,并提供重新开始游戏的选项。 步骤6:美化和音效 添加适当的图形和音效来提升游戏的体验。您可以使用精灵或模型来渲染方块,添加背景音乐和音效来增强游戏的氛围。 以上是一个基本的实现俄罗斯方块游戏的步骤。您可以根据自己的需求和创意来扩展和改进游戏。祝您实现一个有趣而成功的俄罗斯方块游戏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值