unity小游戏实践(一) 俄罗斯方块

写这个的原因是 我从网上download了一个俄罗斯的小游戏,源码是js写的  在我看来相当的混乱而且有少量bug,然后我就改成了c#的版本,在此途中认真学习到了yield在c#中的用法和俄罗斯方块的设计思路。


首先说说我设计这种游戏的话肯定不会用到yield,因为我自认为不用这样的设计也能运行得很好,就是可能代码量会多一点。

然后看看js中的 yield,作用是挂起线程,等下次运行的时候直接跳到线程之下的步骤,

我总结了一下,主要有几个区域会用到的

js:

while(true)
{
  yield;
}
在c#中写成
while(true)
{
  yield return null;
}  搞定

for(i)
{   yield;  }

for(i)
{    yield return i;} 搞定   太吐血了     因为js的各种yield搞不清;

还有的函数体第一句fun(){ yield;}的
我认为是fun(){ yield return 1;}的 //一般是下一帧后运行  如果函数体每帧都运行的吧

yield fun()这样的  看情况而定,如果不是 yield return StartCoroutine(fun( )),那么就是StartCoroutine(fun( )),(看函数体是不是IEnumerator)


然后说说这个游戏的设计思路: 首先全局有一个静态的管理类Manager,它附在空物体中,在场景运行的时候执行初始化操作

一些关键参数 bool[,] field; 场景的2维bool数组存储

blockNormalSpeed = 2.0f;   正常速度
blockDropSpeed = 30.0f;   方块掉落速度
blockMoveDelay = 0.1f; //   移动延迟

初始化,确定场景的真实边界,并确定2维数组field的边界,使用true表示block,false表示space,并在2维数组中的上下左右分别做一条边的true  表示有边界了不能移动过去=,= 主要是方便以后判断用。。。。

然后初始化第一个方块SpawnBlock();  实际上是Instantiate(Blocks[Random.Range(0, Blocks.Length)]);

生成预定义范围内的随机的方块,这个方块附有脚本Block


然后我们说说怎么定义一个方块,用一个string[]数组以,注意为方便以后计算,这个数组的字符串以n的平方次来定义,比如

0000

1111  表示一个横条

0000

0000

一个四方的方块表示为

11

11

一个z字型表示为

010

011

001

都是这样的表示,比较而言浅显易懂。


Block的start函数是一个IEnumerator,它读取了string【】中的东西,然后生成几个子物体,子物体是一个个方块的模型,并且每一个都按string【】中的顺序排列

然后定义这个新的父物体的坐标。把它放到顶部的位置,并假设它还没有碰撞顶部边界,然后检查是否碰了除边界之外的物体(假设这个已经到了很顶端,则游戏结束了)

并且这个线程直接结束。  也就是说可看成这个start没有运行。然后开启协同程序判断3个东东:

    StartCoroutine(CheckInput());
    yield return StartCoroutine(Delay(1.0f)); //  WaitForSeconds, so that the delay can be cut short if player hits the drop button
    StartCoroutine(Fall());

、、、、、、、、、、、、、、、、

十分重要的东西   但是我想不到它这样来判断   有点让人吐血的

IEnumerator CheckInput()
{
    while (true) {
        float input = Input.GetAxis("Horizontal");
        if (input < 0.0) {
            Debug.Log("< 0.0");
            StartCoroutine(MoveHorizontal(-1)); //按下左键
        }
        else if (input > 0.0) {
            Debug.Log("> 0.0");
            StartCoroutine(MoveHorizontal(1));//按下右键
        }

        if (Input.GetButtonDown("Rotate")) {
            RotateBlock();  //旋转
        }
    
        if (Input.GetButtonDown("Drop")) {  //掉下
            fallSpeed = Manager.use.blockDropSpeed;
            dropped = 1;

            // c#中是否改成  StopCoroutine("CheckInput");

            break;    // Break out of while loop, so the coroutine stops (we don't care about input anymore)
        }
        yield return null;
    }
}

。。。。。。。。。。。。。。。。

判断是否碰到左右两边的方块   如果碰到就不能过去了,没碰到就更新新的位置

IEnumerator MoveHorizontal(int dir) {
    if (!Manager.use.CheckBlock (blockMatrix, xPosition + dir, yPosition)) {
        transform.position = new Vector3(transform.position.x + dir, transform.position.y, transform.position.z);
        xPosition += dir;
        yield return new WaitForSeconds (Manager.use.blockMoveDelay);
    }
}

。。。。。。。。。。。。。。。。。。。。

IEnumerator Fall()   一直在掉落着的函数,
{
    Debug.Log("Fall");
    while (true) {
        // Check to see if block would collide if moved down one row
        yPosition--;
        if (Manager.use.CheckBlock (blockMatrix, xPosition, yPosition)) {
            Manager.use.SetBlock (blockMatrix, xPosition, yPosition+1);
            Destroy(gameObject);
            break;
        }
        
        // Make on-screen block fall down 1 square
        // Also serves as a delay...if you want old-fashioned square-by-square movement, replace this with yield WaitForSeconds
        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 i;
        }
    }
}

。。。。。。。。。。。。。。。。。。。。。。。。

void RotateBlock () {
    Debug.Log("RotateBlock");
    // Rotate matrix 90° to the right and store the results in a temporary matrix
    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 the rotated block doesn't overlap existing blocks, copy the rotated matrix back and rotate on-screen block to match
    if (!Manager.use.CheckBlock (tempMatrix, xPosition, yPosition)) {
        System.Array.Copy (tempMatrix, blockMatrix, size*size);
        transform.Rotate (Vector3.forward * -90.0f);
    }
}
}

。。。。。。。。。。。。。

方块的作用就是这些    其实主要的判断逻辑在Manager中,

// See if the block matrix would overlap existing blocks in the playing field
// (Check from bottom-up, since in general gameplay usage it's a bit more efficient that way)

public bool CheckBlock (bool[,] blockMatrix, int xPos,int yPos)


// Make on-screen cubes from position in array when the block is stopped from falling any more
// Just using DetachChildren isn't feasible because the child cubes can be in different orientations,
// which can mess up their position on the Y axis, which we need to be consistent in CollapseRow
// Also write the block matrix into the corresponding location in the playing field


public void SetBlock(bool[,] blockMatrix, int xPos, int yPos)


。。。。。。。。。。。。。。。。。。。。。。

大概分析的就是这个地步    有点虎头蛇尾了  嘿嘿


这个游戏可更改的地方,搞一个

1 这样的可以穿过黑点达到中空的点的方块

还有一个

1001

0110

0110

0000这样的炸弹方块

还有一种

01

01这样的可以定时间内消除下面的对着的方块。


加一个全局判断以判断是否不游戏结束了

相关的函数增加 isgameover 判断






你可能感兴趣的:(游戏)