【Unity】俄罗斯方块
要有意识的开始让自己比较全局视角地做一个项目,所以这个项目先从游戏玩法等说明开始,虽然俄罗斯方块是所有人都清楚的,但正是如此,才可以更直接的看出差异
游戏规则:
1. 有一块用于摆放方块的平面虚拟场地,其标准大小:行宽为10,列高为20,以一个小方块为一个单位。
2. 有一组由4个方块组成的规则图形,共有7种,分别以I、J、L、S、T、Z、O这7个字母的形状来命名,如图3-2所示。

3. 玩家可以进行如下操作。
◆ 以90度为单位旋转方块;
◆ 以格子为单位左右移动方块;
◆ 让方块加速下落。
方块移到区域最下方或者着地到其他方块上无法移动时,就会固定在该处,而新的方块会出现在区域上方开始落下。
4. 当区域中的某一行格子全部由方块填满,则该行方块会消失并成为玩家的得分。删除的行越多,得分越高。当方块堆到区域最上方而无法消除时,则该游戏结束。
设计思路:
1、游戏平台的生成,一般都是竖着放,所以可以在平台左侧或右侧增加个面板统计分数或提示下一个方块的形状,大概是下面这样的

2、随机生成方块,用一个数组存放7种方块组,用生成随机数的方式选择生成方块组形状;增强功能的话,可以做个hold,将某个方块存储下来;以及显示下一个方块的形状这样的增强功能;但基础版就是随机生成;
3、方块上挂载脚本,可以旋转,加速下落,边界判断,还有消除
4、消除得分统计
我看了下传统的实现思路是,把场地看作是一个10*20的坐标系,每一个方块由4个子方块构成,每个方块就是坐标系中的一个点,通过把所有方块放在一个10*20的数组里,最后通过遍历数组判断是否一整行都有方块,有的话就消除方块,并把数据都往下移动一个位置。
程序实现:
没想到这个过程耗时4-5个小时,接下来看下有哪些收获:
1、因为实现的思路是用格子,整个容器是一个10*20的200个格子的容器(边界就直接通过宽高判断sprite的position是否超出,这个地方也是下面讲的要偏移的原因,否则会出现sprite有一半卡在外面的情况)
但是我们的Sprite方块是以中心点去看坐标的,所以我们需要把容器的左下角作为零点坐标(但是因为sprite的中心点也在零点坐标,就需要给整个容器偏移下,使得方块可以在容器内对齐走格子,不会出现错位的情况),所以在看到很多教程上面相机的position都有个0.5的偏移,才知道是为了对齐,教程上也没细说。
这个地方视频比较好理解,可以参考ytb上的这个https://www.youtube.com/watch?v=T5P8ohdxDjo 3:35 开始的说明


2、在sprite的方块上直接挂脚本,负责方块的左右移动,按“S”键向下的时候给个加速;按“W”键向上的时候,给方块做旋转;
1)加速这个地方,直接用时间间隔判断,即每1S向下移动一个身位;判断如果按了“S”,则把时间间隔改短,这个地方视频里的写法很好。
if(Time.time - lastTime > (Input.GetKey(KeyCode.S)? fallTime/10: fallTime))
{
this.transform.position += new Vector3(0,-1,0);
if (!ValidMove())
{
this.transform.position -= new Vector3(0,-1,0);
}
lastTime = Time.time;
}
2)旋转这个地方也花了一些时间,这个地方主要是本地坐标和世界坐标的知识点;
因为方块是不规则的形状,所以要根据图形的重心点进行旋转,实现方式就是给每个prefab配置一个旋转点,因为旋转点是基于object本地的位置,然后再使transform.TransformPoint(transPoint) 转换成世界坐标,用transform.RotateAround 进行旋转
else if (Input.GetKeyDown(KeyCode.W))
{
this.transform.RotateAround(transform.TransformPoint(transPoint),new Vector3(0,0,1),90);
if(!ValidMove())
this.transform.RotateAround(transform.TransformPoint(transPoint),new Vector3(0,0,1),-90);
}
3)最后一个点是让sprite之间可以叠起来,这个我做之前就预想过是遍历数组的形式来实现,通过把所有的sprite最终的位置(每个子方块)都记录到数组里,这样就可以知道哪个地方可以继续叠加,哪个不行了)本来还想说形状不一致,应该很难写,但其实一个方法就可以了;
public void AddTOGrid()
{
foreach(Transform children in transform)
{
int roundX = Mathf.RoundToInt(children.transform.position.x);
int roundY = Mathf.RoundToInt(children.transform.position.y);
grid[roundX,roundY] = children;
}
}
思路是在方块下落的update里,判断最后落底的那一步,如果没有办法再移动了(说明定型了,就把位置信息写到数组里)因为有个判断边界的方法,再在这个方法里面加个判断,除了触碰到边界,如果这个数组的位置已经有占位的了,就相当于也是一个边界了,这样不规则的图形也是一样,有一个子方块碰到的话,就可以停止了(秒啊)
最后,消除的思路就是把数组从上往下遍历,同一行都有方块占了的话,就删除,接着有2种玩法(一个是悬空的方块不会往下落,一个是会往下落),对应的去更新数组即可;因为数据上是记录的Transform组件,移除后就可以对应删除object。这块没写,等后面接着弄。
把我做的这个放github上,有需要的自取。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)