0%

Unity做俄罗斯方块

用unity做了个俄罗斯方块

游戏界面

难点

判断方块移动范围

原本想着用碰撞器,但做出来效果不理想,后来还是用坐标判断

判断方块碰撞

原本想着用碰撞器,但是会出现间隙,后来还是用整形坐标判断

判断方块消除

用了坐标判断,只要把静止的方块位置信息存到二维数组 [x,y]=Transform,就可以判断一整行是否可以消除了

消除后上一行的方块移到下一行

常识

查过百科,俄罗斯方块的规格是 宽10 高20

那么每个方块就由多个1X1的方块组成

有7个 分别为 [I] [L-left] [L-right] [O] [T] [Z-left] [Z-right]

上代码

主要就两个 TetrisController 控制方块移动 ,GameManager 管理游戏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
using UnityEngine;

public class TetrisController : MonoBehaviour
{
public KeyCode rotate = KeyCode.W;
public KeyCode left = KeyCode.A;
public KeyCode right = KeyCode.D;
public KeyCode down = KeyCode.S;
public KeyCode fallToBottom = KeyCode.Space;

//场地宽
public float width = 10;

//场地高
public float height = 20;


//方块自动跌落时长
private float _currentFallDuration;

//按下按键间隔
private float _pressKeyDuration = 0.05f;

//按键计时器
private float _pressKeyTimer;

//方块跌落计时器
private float _timer;

private void Start()
{
_currentFallDuration = GameManager.Instance.fallDuration;
}

void Update()
{
_timer -= Time.deltaTime;
if (_timer <= 0)
{
Fall();
_timer = _currentFallDuration;
}

if (Input.GetKey(down))
{
_pressKeyTimer += Time.deltaTime;
if (_pressKeyTimer > _pressKeyDuration)
{
Fall();
_pressKeyTimer = 0;
}
}

if (Input.GetKeyDown(left))
{
Left();
SoundManager.PlaySoundByName(SoundManager.SoundName.Move);
}

if (Input.GetKeyDown(right))
{
Right();
SoundManager.PlaySoundByName(SoundManager.SoundName.Move);
}

if (Input.GetKeyDown(rotate))
{
Rotate();
SoundManager.PlaySoundByName(SoundManager.SoundName.Rotate);
}

if (Input.GetKeyDown(fallToBottom))
FallToBottom();
}

private void Rotate()
{
foreach (Transform child in transform)
{
var x = Mathf.RoundToInt(child.position.x);
var y = Mathf.RoundToInt(child.position.y);
}

transform.Rotate(new Vector3(0, 0, 90));
if (!ValidateMove())
transform.Rotate(new Vector3(0, 0, -90));

foreach (Transform child in transform)
{
var x = Mathf.RoundToInt(child.position.x);
var y = Mathf.RoundToInt(child.position.y);
}
}

private void Right()
{
transform.position += Vector3.right;
if (!ValidateMove())
transform.position -= Vector3.right;
}

private void Left()
{
transform.position += Vector3.left;
if (!ValidateMove())
transform.position -= Vector3.left;
}

/**
* 下落
*/
private void Fall()
{
transform.position += Vector3.down;
if (!ValidateMove())
{
transform.position -= Vector3.down;
//当已不能下落时,禁止方块移动,并将各方块坐标记录到二维数组
GameManager.Fasten(transform);
enabled = false;
GameManager.DropNextCute();
}
}

/**
* 落到最底部
*/
private void FallToBottom()
{
while (ValidateMove())
{
transform.position += Vector3.down;
}

transform.position -= Vector3.down;

GameManager.Fasten(transform);
enabled = false;
GameManager.DropNextCute();
}

/**
* 判断是否可以移动
*/
private bool ValidateMove()
{
foreach (Transform child in transform)
{
var x = Mathf.RoundToInt(child.position.x);
var y = Mathf.RoundToInt(child.position.y);
if (x >= width
|| x < 0
|| y < 0
)
return false;
}

return GameManager.CheckValidPosition(transform);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class GameManager : MonoBehaviour
{
[Header("方块列表")] public List<GameObject> cubeList;
[Header("方块生成位置")] public Vector2 generatePoint;
[Header("下一个方块显示位置")] public Vector2 nextCubePoint;
[Header("游戏面板")] public GameObject gameBoard;
[Header("游戏面板文本")] public Text gameBoardMessage;
[Header("时间")] public Text timeText;
[Header("分数")] public Text scoreText;
[Header("等级")] public Text levelText;
[Header("背景颜色")] public SpriteRenderer backgroundcolor;


//多久跌落一格
public float fallDuration = 1f;
public static GameManager Instance;

//二维数组 存放静止的方块位置
private static Transform[,] staticCube = new Transform[width, height];
private float originalFallDuration = 1.5f;
private static int width = 10;
private static int height = 20;
private bool _pause;
private bool _gameOver;
private float _score;
private int _level;
private float _time;

//下一个方块
private GameObject nextCube;

//删除行数
private int _deleteline;


private void Awake()
{
Instance = this;
}

private void Start()
{
originalFallDuration = fallDuration;
GenerateRamdomCube();
DropNextCute();
Time.timeScale = 1;
}

private void Update()
{
if (_gameOver)
{
Time.timeScale = 0;
return;
}

if (Input.GetKeyDown(KeyCode.Escape))
ShowGameBoard("暂停");

_time += Time.deltaTime;
timeText.text = ((int) _time).ToString();
scoreText.text = _score.ToString();

levelText.text = _level.ToString();

if (Input.GetKeyDown(KeyCode.R))
RandomBackgroundColor();
}

public void ShowGameBoard(string message)
{
gameBoardMessage.text = message;
gameBoard.SetActive(true);
Time.timeScale = 0;
}

public void Resume()
{
if (_gameOver)
Restart();
gameBoard.SetActive(false);
Time.timeScale = 1;
}

public void Restart()
{
Time.timeScale = 1;
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}


/**
* 检查是否有可删除行
*/
private void CheckDelete()
{
for (var i = height - 1; i >= 0; i--)
if (staticCube[0, i] != null)
{
CheckLine(i);
}

if (_deleteline > 0)
{
_score += _deleteline * 10 + (_deleteline - 1) * 10;
_deleteline = 0;
SoundManager.PlaySoundByName(SoundManager.SoundName.Delete);
CheckLevelUp();
}
}

/**
* 检查一行是否可以删
*/
private void CheckLine(int lineNumber)
{
for (var i = 0; i < width; i++)
{
if (staticCube[i, lineNumber] == null)
return;
}

DeleteLine(lineNumber);
}

/**
* 删除一行
*/
private void DeleteLine(int lineNumber)
{
for (var i = 0; i < width; i++)
{
Destroy(staticCube[i, lineNumber].gameObject);
staticCube[i, lineNumber] = null;
}

_deleteline++;

RandomBackgroundColor();
DownRow(lineNumber);
}

/**
* 下降一行
* lineNumber行以上的所有方块下降一行
*/
private void DownRow(int lineNumber)
{
for (var i = 0; i < width; i++)
{
for (var j = lineNumber + 1; j < height; j++)
{
if (staticCube[i, j] != null)
{
staticCube[i, j - 1] = staticCube[i, j];
staticCube[i, j] = null;
staticCube[i, j - 1].position += Vector3.down;
}
}
}
}

/**
* 固定方块
*/
public static void Fasten(Transform transform)
{
SoundManager.PlaySoundByName(SoundManager.SoundName.Ground);
foreach (Transform child in transform)
{
var x = Mathf.RoundToInt(child.position.x);
var y = Mathf.RoundToInt(child.position.y);
if (y >= height)
{
Instance._gameOver = true;
GameOver();
return;
}

staticCube[x, y] = child;
}

transform.DetachChildren();
Destroy(transform.gameObject);

Instance.CheckDelete();
}

/**
* 游戏结束
*/
private static void GameOver()
{
Instance.ShowGameBoard("木了~");
Instance._gameOver = true;
}

/**
* 生成随机方块
*/
public static void GenerateRamdomCube()
{
var randomCube = Instance.cubeList[Random.Range(0, Instance.cubeList.Count - 1)];
// var randomCube = Instance.cubeList[0];
Instance.nextCube = Instantiate(randomCube, Instance.nextCubePoint,
Quaternion.identity);
Instance.nextCube.GetComponent<TetrisController>().enabled = false;
}

/**
* 将下一个方块丢到场地
*/
public static void DropNextCute()
{
if (Instance.nextCube != null)
{
Instance.nextCube.transform.position = Instance.generatePoint;
Instance.nextCube.GetComponent<TetrisController>().enabled = true;
}

GenerateRamdomCube();
}

/**
* 检查该位置是否已有方块
*/
public static bool CheckValidPosition(Transform transform)
{
foreach (Transform child in transform)
{
var x = Mathf.RoundToInt(child.position.x);
var y = Mathf.RoundToInt(child.position.y);

if (y < height && staticCube[x, y] != null)
return false;
}

return true;
}

/**
* 每100分 升1级
* 每升1级方块下落时间减少0.05s
*/
private void CheckLevelUp()
{
var currentLevel = _level;
_level = Mathf.RoundToInt(_score / 100);
if (!currentLevel.Equals(_level))
SoundManager.PlaySoundByName(SoundManager.SoundName.LeveUp);

fallDuration = Mathf.Clamp(originalFallDuration - _level * 0.1f, 0.1f, 10f);
}

/**
* 随机背景颜色
*/
private void RandomBackgroundColor()
{
var r = Random.Range(0f, 1f);
var g = Random.Range(0f, 1f);
var b = Random.Range(0f, 1f);
backgroundcolor.color = new Color(r, g, b);
}
}