游戏展示
游戏开发
Shape / 形状
- Assets > Create > Sprites > Square
- Hierarchy
Ctrl+D
, W
, Crrl
+ 方向键
- Inspector
- Transform
- Scale
- X:
0.9
- Y:
0.9
- X:
- Scale
- Sprite Renderer
- Color:
Pink
- Color:
- Transform
制作 7 种方块

Move Left Right / 左右移动
添加坐标原点


ValidMove()
每一个小方块如果超出边界,为非法操作,需要退回上一状态。
1 | bool ValidMove() |
2 | { |
3 | foreach (Transform child in transform) |
4 | { |
5 | int roundedX = Mathf.RoundToInt(child.transform.position.x); |
6 | int roundedY = Mathf.RoundToInt(child.transform.position.y); |
7 | |
8 | if (roundedX < 0 || roundedX >= width || roundedY < 0 || roundedY >= height) |
9 | { |
10 | return false; |
11 | } |
12 | } |
13 | |
14 | return true; |
15 | } |
左右移动,如果移动非法,还原。
1 | void Update() |
2 | { |
3 | if (Input.GetKeyDown(KeyCode.LeftArrow)) |
4 | { |
5 | transform.position += new Vector3(-1, 0, 0); |
6 | |
7 | if (!ValidMove()) |
8 | { |
9 | transform.position -= new Vector3(-1, 0, 0); |
10 | } |
11 | } |
12 | else if (Input.GetKeyDown(KeyCode.RightArrow)) |
13 | { |
14 | transform.position += new Vector3(1, 0, 0); |
15 | |
16 | if (!ValidMove()) |
17 | { |
18 | transform.position -= new Vector3(1, 0, 0); |
19 | } |
20 | } |
21 | |
22 | if (Time.time - previousTime > (Input.GetKey(KeyCode.DownArrow) ? fallTime / 10 : fallTime)) |
23 | { |
24 | transform.position += new Vector3(0, -1, 0); |
25 | |
26 | previousTime = Time.time; |
27 | } |
28 | } |
Rotate / 旋转
找到旋转的中心,我这里没有按照图片上的操作,除了 田
字方块为 (0.5, 0.5, 0)
为旋转中心外,其它均为 0
号方块的中心 (0, 0, 0)

1 | void Update() |
2 | { |
3 | // ... |
4 | else if (Input.GetKeyDown(KeyCode.UpArrow)) |
5 | { |
6 | // local position -> global position |
7 | transform.RotateAround(transform.TransformPoint(rotationPoint), new Vector3(0, 0, 1), 90f); |
8 | |
9 | if (!ValidMove()) |
10 | { |
11 | transform.RotateAround(transform.TransformPoint(rotationPoint), new Vector3(0, 0, 1), -90f); |
12 | } |
13 | } |
14 | } |

Collider / 碰撞检测
生成器生成方块
1 | using UnityEngine; |
2 | |
3 | public class Spawn : MonoBehaviour |
4 | { |
5 | public GameObject[] tetrominoes; |
6 | |
7 | void Start() |
8 | { |
9 | NewTetromino(); |
10 | } |
11 | |
12 | public void NewTetromino() |
13 | { |
14 | Instantiate(tetrominoes[Random.Range(0, tetrominoes.Length)], transform.position, Quaternion.identity); |
15 | } |
16 | } |

使用二维数组存放小方块的值
1 | private void AddToGrid() |
2 | { |
3 | foreach (Transform child in transform) |
4 | { |
5 | int roundedX = Mathf.RoundToInt(child.transform.position.x); |
6 | int roundedY = Mathf.RoundToInt(child.transform.position.y); |
7 | |
8 | grid[roundedX, roundedY] = child; |
9 | } |
10 | } |

当前小方块为 null
,才可以移动
1 | bool ValidMove() |
2 | { |
3 | foreach (Transform child in transform) |
4 | { |
5 | // ... |
6 | |
7 | if (grid[roundedX, roundedY] != null) |
8 | { |
9 | return false; |
10 | } |
11 | } |
12 | |
13 | return true; |
14 | } |

Clean Line / 消行

算法
1 | 1. 检测每一行,如果成行 |
2 | 1. 删除当前行 |
3 | 1. 上面的行下移 |
1 | private void CheckForLines() |
2 | { |
3 | for (int row = height - 1; row >= 0; row--) |
4 | { |
5 | if (HasLine(row)) |
6 | { |
7 | DeleteLine(row); |
8 | RowDown(row); |
9 | } |
10 | } |
11 | } |
如果成行
1 | private bool HasLine(int row) |
2 | { |
3 | for (int col = 0; col < width; col++) |
4 | { |
5 | if (grid[col, row] == null) |
6 | { |
7 | return false; |
8 | } |
9 | } |
10 | |
11 | return true; |
12 | } |
删除当前行
1 | private void DeleteLine(int row) |
2 | { |
3 | for (int col = 0; col < width; col++) |
4 | { |
5 | Destroy(grid[col, row].gameObject); |
6 | grid[col, row] = null; |
7 | } |
8 | } |
上面的行下移
private void RowDown(int currentRow)
{
for (int row = currentRow; row < height; row++)
{
for (int col = 0; col < width; col++)
{
if (grid[col, row] != null)
{
grid[col, row - 1] = grid[col, row];
grid[col, row] = null;
grid[col, row - 1].transform.position -= new Vector3(0, 1, 0);
}
}
}
}

Audio / 音效
Background
添加 Audio Source
- Inspector
- Audio Source
- AudioClip:
Tetris
- Play On Awake:
true
- Loop:
true
- AudioClip:
- Audio Source

导出游戏
- File
- Build Settings (Shift + Cmd + B)
- Project Settings

小结
按照教程终于实现了,10 分钟的教程,做了几个小时才完成,主要是对大方块的 (x, y, z)
坐标一开始没有归整,0 号方块应该中心点是 (0, 0, 0)
,在部分大方块都应该按照 0 号方块旋转。
参考:
下载
游戏
Release: https://github.com/GameDevLog/GameDevLogTemplete/releases
源码
GitHub: https://github.com/GameDevLog/GameDevLogTemplete
找到游戏列表对应的 GameDevLog