写这个的原因是 我从网上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 判断