网站一跳率,营业执照怎么申报年报,孝感网站建设xgsh,北京建设信源公司网站更新日期#xff1a;2025年6月14日。 项目源码#xff1a;后续章节发布 索引 系列简介俄罗斯方块【Tetris】一、游戏最终效果二、玩法简介三、正式开始1.定义游戏窗口类2.规划游戏窗口、视口区域3.绘制方块背景板4.四连方块①.四连方块简介②.定义四连方块类型③.定义四连方块… 更新日期2025年6月14日。 项目源码后续章节发布 索引 系列简介俄罗斯方块【Tetris】一、游戏最终效果二、玩法简介三、正式开始1.定义游戏窗口类2.规划游戏窗口、视口区域3.绘制方块背景板4.四连方块①.四连方块简介②.定义四连方块类型③.定义四连方块④.生成四连方块⑤.更新四连方块⑥.绘制四连方块 5.控制四连方块移动、旋转6.四连方块碰壁检测7.堆叠方块8.消除方块9.绘制游戏操作说明10.暂停游戏11.退出游戏 系列简介
本系列博客准备整点不一样的活有没有想过开发在Unity编辑器中运行的游戏打开一个EditorWindow直接玩
当然这样的游戏体量会有亿点点小毕竟EditorWindow的资源有限所以主要为休闲、逻辑、解密类游戏但好处是打开一个窗口即玩一键即可关闭窗口秉承了Unity开箱即用的原则。
且每个小游戏的体量足够小仅仅为一个脚本不含其他任何资源。
只不过这需要对Unity编辑器开发具备基础的了解然后我们就可以正式开始了。
俄罗斯方块【Tetris】
本篇的目标是开发一个俄罗斯方块【Tetris】小游戏。
一、游戏最终效果 Unity编辑器小游戏俄罗斯方块 二、玩法简介
俄罗斯方块是一款经典的益智游戏其玩法简单却富有挑战性。
玩家需要通过旋转和移动从屏幕顶部不断落下的四连方块将它们放置在游戏板的底部。目标是填满一行或多行当一行被完全填满时该行会消除玩家获得分数。游戏会随着时间推移逐渐加快难度玩家需要尽可能多地消除行数避免方块堆积到游戏板顶部否则游戏结束。
三、正式开始
1.定义游戏窗口类
首先定义俄罗斯方块的游戏窗口类MiniGame_Tetris其继承至MiniGameWindow【小游戏窗口基类】 /// summary/// 俄罗斯方块/// /summarypublic class MiniGame_Tetris : MiniGameWindow{}注意MiniGameWindow包含在小游戏基础模块中其在EditorWindow中模拟实现了一套小游戏的基础开发组件譬如 1.游戏视口渲染Viewport 2.游戏逻辑更新Update 3.MiniGameObject游戏对象类似于运行时的GameObject 4.动画组件Animation用于在EditorWindow中播放动画 5.简易物理系统重力、碰撞检测、射线检测等。 后续放出源码后将会深入讲解此基础模块。 2.规划游戏窗口、视口区域
通过覆写虚属性实现规划游戏视口区域大小 /// summary/// 游戏名称/// /summarypublic override string Name 俄罗斯方块 [Tetris];/// summary/// 游戏窗体大小/// /summarypublic override Vector2 WindowSize new Vector2(400, 430);/// summary/// 游戏视口区域/// /summarypublic override Rect ViewportRect new Rect(5, 25, 200, 400);注意游戏窗体大小必须 游戏视口区域。 然后通过代码打开此游戏窗口 [MenuItem(MiniGame/俄罗斯方块 [Tetris], priority 1)]private static void Open_MiniGame_Tetris(){MiniGameWindow.OpenWindowMiniGame_Tetris();}便可以看到游戏的窗口、视口区域如下左侧深色凹陷区域为视口区域 3.绘制方块背景板
俄罗斯方块游戏实现起来还是比较简单的因为他的整个游戏背景仅是一系列方块组成所以我们先来绘制如下这样的方块背景板 定义背景宽度横向方块数量、背景高度纵向方块数量、方块大小 private const int WIDTH 10;private const int HEIGHT 20;private const int BLOCKSIZE 20;用一个bool型二维数组存储所有背景方块 //所有背景方块private bool[,] _panel new bool[WIDTH, HEIGHT];注意为什么是bool型 因为bool型正好可以表示一个方块的状态true为该位置存在方块false为该位置不存在方块。 然后在OnGameViewportGUI方法中绘制背景板 private Rect _blockRect new Rect();private GUIStyle _blockGS;protected override void OnGameViewportGUI(){base.OnGameViewportGUI();DrawPanel();}/// summary/// 绘制画布/// /summaryprivate void DrawPanel(){for (int h 0; h HEIGHT; h){for (int w 0; w WIDTH; w){//存在方块显示青色不存在显示灰色GUI.color _panel[w, h] ? Color.cyan : Color.gray;DrawBlock(w, h);GUI.color Color.white;}}}/// summary/// 绘制方块/// /summaryprivate void DrawBlock(int x, int y){_blockRect.Set(x * BLOCKSIZE, y * BLOCKSIZE, BLOCKSIZE, BLOCKSIZE);GUI.Box(_blockRect, , _blockGS);}注意OnGameViewportGUI即为游戏视口区域的GUI绘制方法其中左上角坐标为(0, 0)右下角坐标为(ViewportRect.width, ViewportRect.height) 4.四连方块
①.四连方块简介
在俄罗斯方块中我们所控制的从顶部下落的方块叫做四连方块四连方块共有7种类型 他们的字母命名如下
方块中文别称英文别称I长条、棍子long bar, stick, straightTO方形、田square, block, sunJgamma, left gun, inverse L, reverseLright gunSinverse skew, right snakeZskew, left snake, reverse S
②.定义四连方块类型
使用一个枚举TetrominoType代表四连方块类型 /// summary/// 四连方块类型/// /summarypublic enum TetrominoType{/// summary/// 口口口口/// /summaryI,/// summary/// 口/// 口口口/// /summaryJ,/// summary/// ㅤㅤ 口/// 口口口/// /summaryL,/// summary/// 口口/// 口口/// /summaryO,/// summary/// ㅤ口口/// 口口/// /summaryS,/// summary/// 口口/// ㅤ 口口/// /summaryZ,/// summary/// ㅤ 口/// 口口口/// /summaryT}③.定义四连方块
定义一个类Tetromino代表四连方块 /// summary/// 四连方块/// /summarypublic class Tetromino{/// summary/// 四连方块的位置/// /summarypublic Vector2Int Position;/// summary/// 四连方块的旋转/// /summarypublic int Rotation;/// summary/// 四连方块的类型/// /summarypublic TetrominoType Type;/// summary/// 方块1的位置偏移/// /summarypublic Vector2Int Block1Offset;/// summary/// 方块2的位置偏移/// /summarypublic Vector2Int Block2Offset;/// summary/// 方块3的位置偏移/// /summarypublic Vector2Int Block3Offset;/// summary/// 方块4的位置偏移/// /summarypublic Vector2Int Block4Offset;}这里的Position代表了四连方块处于方块背景板中的具体位置而Block1Offset 至 Block4Offset这四个变量分别代表了四连方块中的四个小方块基于Position的位置偏移值。
通过Position 偏移值Offset即可算出小方块真实的位置然后将该位置标记为true即表明了该位置存在方块从而该位置便会渲染为青色。
④.生成四连方块
游戏一开始便会生成一个四连方块 //定义一个缓存对象避免每次重复新建对象private Tetromino _tetrominoCache new Tetromino();/// summary/// 当前的四连方块/// /summarypublic Tetromino CurrentTetromino { get; private set; }protected override void OnInit(){base.OnInit();CurrentTetromino GenerateTetromino();}/// summary/// 生成四连方块/// /summaryprivate Tetromino GenerateTetromino(){//四连方块坐标重置到(4, -1)y -1使得他超出到屏幕上方不可见_tetrominoCache.Position new Vector2Int(4, -1);//四连方块旋转归零_tetrominoCache.Rotation 0;//随机一个方块类型_tetrominoCache.Type (TetrominoType)Random.Range(0, 7);//更新一下四个小方块的位置_tetrominoCache.UpdateBlocks();return _tetrominoCache;}注意OnInit即为游戏窗口打开后的初始化方法在其中完成游戏的初始化操作。 ⑤.更新四连方块
每次重新生成、旋转等都需要更新四连方块也即是更新其中四个小方块的位置偏移 /// summary/// 更新方块/// /summarypublic void UpdateBlocks(){switch (Type){case TetrominoType.I:UpdateI();break;case TetrominoType.J:UpdateJ();break;case TetrominoType.L:UpdateL();break;case TetrominoType.O:UpdateO();break;case TetrominoType.S:UpdateS();break;case TetrominoType.Z:UpdateZ();break;case TetrominoType.T:UpdateT();break;}}根据不同的四连方块类型需要重新计算四个小方块的位置偏移比如I类型 private void UpdateI(){//未旋转角度0、旋转180度时显示为横着的——所以四个小方块横向排列y坐标偏移为0if (Rotation 0 || Rotation 180){Block1Offset new Vector2Int(-1, 0);Block2Offset new Vector2Int(0, 0);Block3Offset new Vector2Int(1, 0);Block4Offset new Vector2Int(2, 0);}//旋转90度时、旋转180度时显示为竖着的|所以四个小方块竖向排列x坐标偏移为0else if (Rotation 90 || Rotation 270){Block1Offset new Vector2Int(0, -1);Block2Offset new Vector2Int(0, 0);Block3Offset new Vector2Int(0, 1);Block4Offset new Vector2Int(0, 2);}}其他类型也同理这里就不赘述了。
⑥.绘制四连方块
接下来便是绘制四连方块 protected override void OnGameViewportGUI(){base.OnGameViewportGUI();//绘制方块背景板DrawPanel();//绘制四连方块绘制顺序靠后会在层级上挡住前面的背景板DrawTetromino();}/// summary/// 绘制四连方块/// /summaryprivate void DrawTetromino(){if (CurrentTetromino ! null){//四连方块绘制为黄色GUI.color Color.yellow;//分别计算四个小方块的偏移量然后绘制该小方块DrawBlock(CurrentTetromino.Position.x CurrentTetromino.Block1Offset.x, CurrentTetromino.Position.y CurrentTetromino.Block1Offset.y);DrawBlock(CurrentTetromino.Position.x CurrentTetromino.Block2Offset.x, CurrentTetromino.Position.y CurrentTetromino.Block2Offset.y);DrawBlock(CurrentTetromino.Position.x CurrentTetromino.Block3Offset.x, CurrentTetromino.Position.y CurrentTetromino.Block3Offset.y);DrawBlock(CurrentTetromino.Position.x CurrentTetromino.Block4Offset.x, CurrentTetromino.Position.y CurrentTetromino.Block4Offset.y);GUI.color Color.white;}}现在运行程序大概就是这样的 5.控制四连方块移动、旋转
我们可以控制四连方块左右移动、旋转、加速下落等。
在OnGamePlayingEvent方法中编写控制逻辑 protected override void OnGamePlayingEvent(Event e, Vector2 mousePosition){base.OnGamePlayingEvent(e, mousePosition);if (CurrentTetromino ! null){if (e.type EventType.KeyDown){switch (e.keyCode){case KeyCode.A://A键左移CurrentTetromino.Position.x - 1;//检测是否超出边界或碰壁if (CheckTetrominoMove(CurrentTetromino)){//如果是则撤销移动CurrentTetromino.Position.x 1;}Repaint();break;case KeyCode.D://D键右移CurrentTetromino.Position.x 1;if (CheckTetrominoMove(CurrentTetromino)){CurrentTetromino.Position.x - 1;}Repaint();break;case KeyCode.W://W键旋转int last CurrentTetromino.Rotation;//顺时针旋转90度CurrentTetromino.Rotation 90;if (CurrentTetromino.Rotation 360) CurrentTetromino.Rotation 0;CurrentTetromino.UpdateBlocks();//同样的如果超出边界或碰壁撤销旋转if (CheckTetrominoMove(CurrentTetromino)){CurrentTetromino.Rotation last;CurrentTetromino.UpdateBlocks();}Repaint();break;case KeyCode.S://S键加速下落直接增加下落速度即可_downSpeed 0.1f;break;}}}}注意OnGamePlayingEvent即为游戏输入事件检测方法在其中编写与输入事件相关的逻辑。 6.四连方块碰壁检测
四连方块尝试左右移动、旋转时都需要检测是否超出边界或碰壁 /// summary/// 四连方块尝试左右移动、旋转时检测是否超出边界或碰壁/// /summaryprivate bool CheckTetrominoMove(Tetromino tetromino){int x1 tetromino.Position.x tetromino.Block1Offset.x;int y1 tetromino.Position.y tetromino.Block1Offset.y;if (x1 0 || x1 WIDTH || y1 0 || y1 HEIGHT || _panel[x1, y1]){return true;}int x2 tetromino.Position.x tetromino.Block2Offset.x;int y2 tetromino.Position.y tetromino.Block2Offset.y;if (x2 0 || x2 WIDTH || y2 0 || y2 HEIGHT || _panel[x2, y2]){return true;}int x3 tetromino.Position.x tetromino.Block3Offset.x;int y3 tetromino.Position.y tetromino.Block3Offset.y;if (x3 0 || x3 WIDTH || y3 0 || y3 HEIGHT || _panel[x3, y3]){return true;}int x4 tetromino.Position.x tetromino.Block4Offset.x;int y4 tetromino.Position.y tetromino.Block4Offset.y;if (x4 0 || x4 WIDTH || y4 0 || y4 HEIGHT || _panel[x4, y4]){return true;}return false;}7.堆叠方块
四连方块落到底部后或碰到其他已落底的方块将堆叠到底部。
在OnGamePlayingUpdate方法中编写四连方块的下落逻辑 private float _downSpeed 0.005f;private float _timer 0;protected override void OnGamePlayingUpdate(){base.OnGamePlayingUpdate();if (CurrentTetromino ! null){_timer _downSpeed;if (_timer 1){_timer - 1;//四连方块下落一格CurrentTetromino.Position.y 1;//检测是否落地CheckTetrominoDown(CurrentTetromino);//重绘窗口游戏视口内容产生变化时都需要调一下Repaint();}}}注意OnGamePlayingUpdate即为游戏逻辑更新方法在其中编写游戏逻辑更新相关的代码。 接下来是四连方块堆叠的逻辑 /// summary/// 四连方块尝试下落时检测是否落地如果落地则存储到背景板并检测是否可消除/// /summaryprivate void CheckTetrominoDown(Tetromino tetromino){//检测是否落地if (TetrominoIsDown(tetromino)){//如果已落底则倒回一格我们是先下落一格再判断的是否落底所以要倒回去tetromino.Position.y - 1;//存储到背景板堆叠到底部SetTetrominoToPanel(tetromino);//检测是否可消除EliminatePanel();//重新生成四连方块CurrentTetromino GenerateTetromino();_downSpeed 0.005f;}}/// summary/// 检测四连方块是否落地/// /summaryprivate bool TetrominoIsDown(Tetromino tetromino){//分别检测四个小方块是否抵达边界或碰到已落底的方块//任意小方块满足条件则证明整个四连方块已落底int x1 tetromino.Position.x tetromino.Block1Offset.x;int y1 tetromino.Position.y tetromino.Block1Offset.y;if (x1 0 x1 WIDTH y1 0 y1 HEIGHT _panel[x1, y1]){return true;}int x2 tetromino.Position.x tetromino.Block2Offset.x;int y2 tetromino.Position.y tetromino.Block2Offset.y;if (x2 0 x2 WIDTH y2 0 y2 HEIGHT _panel[x2, y2]){return true;}int x3 tetromino.Position.x tetromino.Block3Offset.x;int y3 tetromino.Position.y tetromino.Block3Offset.y;if (x3 0 x3 WIDTH y3 0 y3 HEIGHT _panel[x3, y3]){return true;}int x4 tetromino.Position.x tetromino.Block4Offset.x;int y4 tetromino.Position.y tetromino.Block4Offset.y;if (x4 0 x4 WIDTH y4 0 y4 HEIGHT _panel[x4, y4]){return true;}if (y1 HEIGHT || y2 HEIGHT || y3 HEIGHT || y4 HEIGHT){return true;}return false;}/// summary/// 设置四连方块到画板/// /summaryprivate void SetTetrominoToPanel(Tetromino tetromino){//在方块背景板中分别将四个小方块所在的位置设置为true也即是该位置存在方块int x1 tetromino.Position.x tetromino.Block1Offset.x;int y1 tetromino.Position.y tetromino.Block1Offset.y;if (x1 0 x1 WIDTH y1 0 y1 HEIGHT){_panel[x1, y1] true;}int x2 tetromino.Position.x tetromino.Block2Offset.x;int y2 tetromino.Position.y tetromino.Block2Offset.y;if (x2 0 x2 WIDTH y2 0 y2 HEIGHT){_panel[x2, y2] true;}int x3 tetromino.Position.x tetromino.Block3Offset.x;int y3 tetromino.Position.y tetromino.Block3Offset.y;if (x3 0 x3 WIDTH y3 0 y3 HEIGHT){_panel[x3, y3] true;}int x4 tetromino.Position.x tetromino.Block4Offset.x;int y4 tetromino.Position.y tetromino.Block4Offset.y;if (x4 0 x4 WIDTH y4 0 y4 HEIGHT){_panel[x4, y4] true;}}8.消除方块
在四连方块堆叠到底部的同时就需要检测一次是否可消除方块我们只需要一行一行的检测即可因为消除方块的前提就是堆满一行 /// summary/// 检测消除/// /summaryprivate void EliminatePanel(){//单次总消除行数一次消除行数越多得分越高int lines 0;//从最底部向上检测for (int h HEIGHT - 1; h 0; h--){bool isEliminate true;for (int w 0; w WIDTH; w){//只要一行中发现一个空方格就不可消除if (!_panel[w, h]){isEliminate false;break;}}if (isEliminate){//消除此行EliminateLine(h);lines 1;h;}}//得分_score lines * lines;}/// summary/// 消除一行/// /summaryprivate void EliminateLine(int line){//消除一行的逻辑也即是其上方的所有行向下顺移一格for (int h line; h 1; h--){for (int w 0; w WIDTH; w){_panel[w, h] _panel[w, h - 1];}}}9.绘制游戏操作说明
游戏的得分操作说明等其他UI统一绘制在OnOtherGUI方法中 protected override void OnOtherGUI(){base.OnOtherGUI();Rect rect new Rect(ViewportRect.x ViewportRect.width 5, ViewportRect.y 5, 50, 20);GUI.Label(rect, Score:);rect.x 50;rect.width 100;EditorGUI.IntField(rect, _score);rect.Set(ViewportRect.x ViewportRect.width 5, ViewportRect.y ViewportRect.height - 100, 25, 20);GUI.Button(rect, W);rect.x 30;rect.width 100;GUI.Label(rect, Rotate);rect.Set(ViewportRect.x ViewportRect.width 5, ViewportRect.y ViewportRect.height - 75, 25, 20);GUI.Button(rect, S);rect.x 30;rect.width 100;GUI.Label(rect, Fast down);rect.Set(ViewportRect.x ViewportRect.width 5, ViewportRect.y ViewportRect.height - 50, 25, 20);GUI.Button(rect, A);rect.x 30;rect.width 100;GUI.Label(rect, Move left);rect.Set(ViewportRect.x ViewportRect.width 5, ViewportRect.y ViewportRect.height - 25, 25, 20);GUI.Button(rect, D);rect.x 30;rect.width 100;GUI.Label(rect, Move right);} 注意OnOtherGUI为绘制游戏视口区域之外的其他UI的方法但不做强制限制。 这里绘制出来的效果如下 至此一个简单的俄罗斯方块小游戏就完成了试玩效果如下俄罗斯方块【Tetris】。
10.暂停游戏
游戏窗口的左上角有一个三角形播放按钮默认情况下打开窗口后该按钮处于按下状态游戏播放中点击该按钮可暂停游戏 11.退出游戏
默认退出游戏按键为Esc键也可覆写该虚属性重定义退出键 /// summary/// 退出键/// /summarypublic virtual KeyCode QuitKey { get; } KeyCode.Escape;