【日志】unity俄罗斯方块——边界限制检测
Bug修复记录
项目场景
尝试使用Unity独自制作俄罗斯方块(也许很没有必要,网上随便一搜就有教程)
问题描述
俄罗斯方块的边缘检测出错了,对方块进行旋转后,无法到达最左侧或者最下侧的位置,以及其他问题。演示图如下所示:
边缘检测代码如下所示:
// 检查方块是否可以移动 private bool IsCanMove() { // 检查是否超出边界 for (int i = 0; i < transform.childCount - 1; i++) { Vector3 pos = transform.GetChild(i).position; if (pos.y < 0 || pos.x = 10) { return false; } } return true; }
修复方案
怀疑在旋转该物体的时候位置发生了轻微偏移,导致检测结果为超出边界,但是在视觉上看不出来。通过对坐标的值进行取整处理可以解决这个问题,修复代码如下:
// 检查方块是否可以移动 private bool IsCanMove() { // 检查是否超出边界 for (int i = 0; i < transform.childCount - 1; i++) { int roundX = Mathf.RoundToInt(transform.GetChild(i).position.x); int roundY = Mathf.RoundToInt(transform.GetChild(i).position.y); if (roundY < 0 || roundX = 10) { return false; } } return true; }
今日收获
场景摆放
关于场景摆放这真的是一个很头疼的问题,想必跟我一样的新手都会考虑如何使方块每步的移动与方块的大小一致,然后考虑各种计算方块大小、这个场景要做多大以及这个场景该怎么限制。
其实这个问题不用想那么多,把每个素材都做成1unity单位的大小就好了,场景先设置成10x15的大小,同时把左下角的位置移到(0,0)的位置,调整相机位置。这样就把一个没方向没大小没标准的问题变成了一个二维数组的问题,之后的问题就是对这个二维数组进行操作。
方块创建
方块的创建有太多种方式了,但是考虑到配置并创建每一种方块以及旋转,我选择了使用预制体,这样方便旋转,还能更直观了解这些方块在二维数组中的变化。
状态列表
这个是目前项目中最令人满意的地方,因为我只是用了一个方法就把输入系统和方块的操作联系了起来(没错,这里使用了之前提到的事件系统:Unity笔记——事件中心-CSDN博客)
// 方块的状态public enum MoveType{ Move, Rotate, MoveDown, MoveRight, MoveLeft, Stop}// 具体的方块控制逻辑 private void Start() { EventCenter.AddListener(EventName.CONTROL_MOVE, controlMove); } private void OnDestroy() { EventCenter.RemoveListener(EventName.CONTROL_MOVE, controlMove); } // 改变移动类型 private void controlMove(object[] args = null) { moveType = (MoveType)args[0]; } // 方块移动 private void TryMove(MoveType moveType = MoveType.Move) { switch (moveType) { case MoveType.Move: MoveFall(); break; case MoveType.Rotate: Rotate(); break; case MoveType.MoveDown: MoveDown(); break; case MoveType.MoveLeft: MoveLeft(); break; case MoveType.MoveRight: MoveRight(); break; case MoveType.Stop: Stop(); break; } }
// 输入系统 if (Input.GetKeyDown(KeyCode.W)) { EventCenter.TriggerEvent(EventName.CONTROL_MOVE, MoveType.Rotate); } if (Input.GetKeyDown(KeyCode.S)) { EventCenter.TriggerEvent(EventName.CONTROL_MOVE, MoveType.MoveDown); } if (Input.GetKeyUp(KeyCode.S)) { EventCenter.TriggerEvent(EventName.CONTROL_MOVE, MoveType.Move); } if (Input.GetKeyDown(KeyCode.A)) { EventCenter.TriggerEvent(EventName.CONTROL_MOVE, MoveType.MoveLeft); } if (Input.GetKeyDown(KeyCode.D)) { EventCenter.TriggerEvent(EventName.CONTROL_MOVE, MoveType.MoveRight); }
动态加载组件
这一块就没有太多可说的地方了,直接new一个新对象,AddComponent相关脚本或者组件。不过,在动态将所有方块预制体加入GameObject[]的时候发现其实用List存储这些预制体其实也都是一样的,感觉没有太多差别,直接GameObject[]能直接导入该文件下所有的预制体。
private GameObject[] blocks; private List blocklist; private void Awake() { blockList = new List(); generatorPoint = new GameObject(); generatorPoint.transform.position = new Vector3(0, 0, 0); generatorPoint.transform.parent = transform; } private void Start() { generatorPoint.transform.position = createPos; blocks = Resources.LoadAll(\"Prefabs/Block\"); foreach (GameObject block in blocks) { blockList.Add(block); } SpawnBoard(); } // 生成方块 public void SpawnBoard() { randomIndex = Random.Range(0, blockList.Count); blockList[randomIndex].AddComponent(); Instantiate(blockList[randomIndex], generatorPoint.transform.position, Quaternion.identity, transform); }