您的位置:首页 > 移动开发 > Unity3D

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

2013-10-28 11:33 1061 查看

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


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=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);
}
}


4. 项目下载和讨论

大家可以到我的CSDN资源中下载~
如果有什么问题,大家可以给我留言,我尽快给大家回复~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐