【用unity实现100个游戏之10】复刻经典俄罗斯方块游戏

文章目录

  • 前言
  • 开始项目
  • 网格生成
  • Block方块脚本
  • 俄罗斯方块基类,绘制方块形状
  • 移动逻辑
  • 限制移动
  • 自由下落
  • 下落后设置对应风格为不可移动类型
  • 检查当前方块是否可以向指定方向移动
  • 旋转逻辑
  • 消除逻辑
  • 游戏结束逻辑
  • 怪物生成
  • 源码
  • 参考
  • 完结

前言

当今游戏产业中,经典游戏的复刻一直是一项受欢迎且具有挑战性的任务。俄罗斯方块是一个深入人心、令人上瘾的经典游戏,在过去几十年里一直享有广泛的流行度。其简单而富有策略性的玩法吸引了无数玩家的关注。因此,我决定利用Unity引擎来复刻这款经典游戏,以让更多的人重新体验其中的乐趣。

通过使用Unity引擎,我能够利用其强大的工具和功能,从头开始构建一个与原版俄罗斯方块游戏相似的游戏。我将努力保持原版游戏的核心要素,包括七种不同形状的方块(俄罗斯方块),玩家可以通过旋转和移动这些方块来填充完整的水平行,完成消除并得分的目标。

除了保留原版玩法外,我还计划为游戏添加一些额外的功能和改进。例如,增加多样化的难度级别,使得游戏适合任何玩家的技能水平。我还计划添加特殊的道具或技能,使游戏更加丰富有趣。此外,我还将注重游戏的视觉效果和音效,以提升玩家的沉浸感。

我的目标是创造一个令人难以抗拒的游戏体验,让玩家们在回忆经典之余,也能感受到崭新的乐趣。无论是单人挑战高分,还是与朋友们一较高下,这个复刻版的俄罗斯方块游戏都将带给玩家们小时候的回忆和喜悦。

我非常兴奋能够应用Unity引擎来实现这个愿望,并期待将来能与大家分享这款复刻版俄罗斯方块游戏。在这个过程中,我将努力改进和完善游戏,以确保它可以在各种平台上流畅运行,并为玩家们带来最佳的游戏体验。

谢谢大家的支持和关注!让我们一起回味经典,畅享游戏的乐趣吧!

先来看看实现的最终效果
在这里插入图片描述

开始项目

链接:https://pan.baidu.com/s/1BMlwclVV2gOdnNppSc7v6A
提取码:hxfs

网格生成

泛型单例

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SingleBase<T> : MonoBehaviour where T : class
{public static T Instance;protected virtual void Awake(){Instance = this as T;}
}

游戏管理类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class GameManager : SingleBase<GameManager>
{protected override void Awake(){base.Awake();}private void Start(){OnStartGame();}//开始游戏void OnStartGame(){//网格生成MapManager.Instance.InitMap();}
}

简单工厂类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///<summary>
//简单工厂
///</summary>
public static class SimpleFactory
{//创建Resources/Model中的物体public static GameObject CreateModel(string name, Transform parent){return Object.Instantiate(Resources.Load("Model/" + name), parent) as GameObject;}
}

网格地图生成

//常量类
public static class Defines
{public static readonly int RowCount = 15;//网格行数public static readonly int ColCount = 10;//网格列数public static readonly float Offset = 0.9f;//格子间隔
}

网格地图管理器

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//网格地图管理器
public class MapManager : SingleBase<MapManager>
{//初始化网格地图public void InitMap(){for (int row = 0; row < Defines.RowCount; row++){for (int col = 0; col < Defines.ColCount; col++){GameObject obj = SimpleFactory.CreateModel("block", transform);obj.transform.localPosition = new Vector3(col * Defines.Offset, -row * Defines.Offset, 0);}}}
}

挂载GameManager和MapManager
在这里插入图片描述
运行效果
在这里插入图片描述

Block方块脚本

新建Block 方块脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public enum BlockType
{Empty,//空Static,//不动Model,//模型
}//方块脚本
public class Block : MonoBehaviour
{public BlockType type;public int RowIndex;public int ColIndex;public SpriteRenderer sp;public Sprite normalSprite;//默认的图片public Sprite modelSprite;//怪物图片public void Init(int row, int col, BlockType type){this.type = type;this.RowIndex = row;this.ColIndex = col;}private void Awake(){sp = GetComponent<SpriteRenderer>();normalSprite = sp.sprite;modelSprite = Resources.Load<Sprite>("Icon/gbl");}private void Start(){SetTypeToSp();}public void SetTypeToSp(){switch (this.type){case BlockType.Empty:sp.sprite = normalSprite;sp.color = Color.white;break;case BlockType.Static:sp.color = Color.red;break;case BlockType.Model:sp.sprite = modelSprite;sp.color = Color.white;break;}}
}

网格地图管理器MapManager 调用

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//网格地图管理器
public class MapManager : SingleBase<MapManager>
{public Block[,] blockArr;//初始化网格地图public void InitMap(){blockArr = new Block[Defines.RowCount,Defines.ColCount];for (int row = 0; row < Defines.RowCount; row++){for (int col = 0; col < Defines.ColCount; col++){GameObject obj = SimpleFactory.CreateModel("block", transform);obj.transform.localPosition = new Vector3(col * Defines.Offset, -row * Defines.Offset, 0);Block b = obj.AddComponent<Block>();b.Init(row, col, BlockType.Model);//存储到二维数组blockArr[row, col] = b;}}}
}

运行效果
在这里插入图片描述

俄罗斯方块基类,绘制方块形状

MapManager新增方法

//切换对应下标的方块的类型
public void ChangeType(int row, int col, BlockType type)
{Block b = blockArr[row, col];b.type = type;b.SetTypeToSp();
}

俄罗斯方块基类

//俄罗斯方块基类
public class TetrisBase
{public int rowIndex;//对应整个网格的行坐标public int colIndex;//对应整个网格的列坐标public int rowCount;//存储形状的行总数public int colCount;//存储形状的列总数public BlockType[,] blockArr;//存储枚举的二维数组//初始化public virtual void Init(){}//画自己(更改网格对应的图片精灵)public virtual void DrawMe(){for (int row = 0; row < rowCount; row++){for (int col = 0; col < colCount; col++){if (blockArr[row, col] != BlockType.Empty){int map_rowIndex = rowIndex + row;int map_colIndex = colIndex + col;MapManager.Instance.ChangeType(map_rowIndex, map_colIndex, blockArr[row, col]);}}}}//擦除自己public virtual void WipeMe(){for (int row = 0; row < rowCount; row++){for (int col = 0; col < colCount; col++){if (blockArr[row, col] != BlockType.Empty){int map_rowIndex = rowIndex + row;int map_colIndex = colIndex + col;MapManager.Instance.ChangeType(map_rowIndex, map_colIndex, BlockType.Empty);}}}}//向下移动public virtual void MoveDown(){rowIndex++;}//左移动public virtual void MoveLeft(){colIndex--;}//右移动public virtual void MoveRight(){colIndex++;}
}

T形状的俄罗斯方块

//T形状的俄罗斯方块
public class Tetris_T : TetrisBase
{public override void Init(){rowCount = 2;colCount = 3;blockArr = new BlockType[,]{{BlockType.Model,BlockType.Model,BlockType.Model},{BlockType.Empty,BlockType.Model,BlockType.Empty}};}
}

GameManager调用,绘制方块形状

TetrisBase currentTetris;//当前操作的俄罗斯//开始游戏
void OnStartGame()
{//网格生成MapManager.Instance.InitMap();currentTetris = CreateTetris();//画出来currentTetris.DrawMe();
}//创建
TetrisBase CreateTetris()
{TetrisBase t = new Tetris_T();t.Init();t.colIndex = Defines.ColCount / 2 - t.colCount / 2;return t;
}

效果
在这里插入图片描述

移动逻辑

定义移动方向枚举

//移动方向
public enum Direction
{None,Right,Left,Down
}

修改俄罗斯方块基类TetrisBase

//根据方向移动
public virtual void MoveByDir(Direction dir)
{//擦除自己WipeMe();switch (dir){case Direction.None:break;case Direction.Right:MoveRight();break;case Direction.Left:MoveLeft();break;case Direction.Down:MoveDown();break;}DrawMe();//画自己
}

GameManager新增用户操作

void Update(){InputCtl();
}
//用户操作
public void InputCtl()
{if (Input.GetKeyDown(KeyCode.A))currentTetris.MoveByDir(Direction.Left);if (Input.GetKeyDown(KeyCode.D))currentTetris.MoveByDir(Direction.Right);if (Input.GetKeyDown(KeyCode.S))currentTetris.MoveByDir(Direction.Down);
}

运行效果
在这里插入图片描述

限制移动

俄罗斯方块基类TetrisBase新增方法

//是否能移动的方向
public virtual bool IsCanMove(Direction dir)
{int _rowIndex = rowIndex;int _colIndex = colIndex;switch (dir){case Direction.None:break;case Direction.Right:_colIndex++;break;case Direction.Left:_colIndex--;break;case Direction.Down:_rowIndex++;break;}//超出网格if (_colIndex < 0 || _colIndex + colCount > Defines.ColCount || _rowIndex + rowCount > Defines.RowCount){return false;}return true;
}

GameManager调用

//用户操作
public void InputCtl()
{if (Input.GetKeyDown(KeyCode.A)){if (currentTetris.IsCanMove(Direction.Left)){currentTetris.MoveByDir(Direction.Left);}}if (Input.GetKeyDown(KeyCode.D)){if (currentTetris.IsCanMove(Direction.Right)){currentTetris.MoveByDir(Direction.Right);}}if (Input.GetKeyDown(KeyCode.S)){if (currentTetris.IsCanMove(Direction.Down)){currentTetris.MoveByDir(Direction.Down);}}
}

运行效果
在这里插入图片描述

自由下落

Defines新增常用类

public static readonly float downTime = 1;//下落时间间隔

修改GameManager

float timer;//开始游戏
void OnStartGame()
{timer = Defines.downTime;
}
void Update()
{AutoMoveDown();
}//自动下落
public void AutoMoveDown()
{timer -= Time.deltaTime;if (timer <= 0){timer = Defines.downTime;if (currentTetris.IsCanMove(Direction.Down)){currentTetris.MoveByDir(Direction.Down);}else{//不能移动重新创建currentTetris = CreateTetris();currentTetris.DrawMe();}}
}

效果
在这里插入图片描述

下落后设置对应风格为不可移动类型

MapManager新增方法

//设置不可移动后的俄罗斯方块对应的位置为Static类型
public void SetStatic(TetrisBase t)
{for (int row = 0; row < t.rowCount; row++){for (int col = 0; col < t.colCount; col++){if (t.blockArr[row, col] != BlockType.Empty){int map_rowIndex = row + t.rowIndex;int map_colIndex = col + t.colIndex;ChangeType(map_rowIndex, map_colIndex, BlockType.Static);}}}
}

GameManager调用

if (!currentTetris.IsCanMove(Direction.Down))
{//设置不可移动类型MapManager.Instance.SetStatic(currentTetris);
}

运行效果
在这里插入图片描述

检查当前方块是否可以向指定方向移动

解决方块下落重叠的问题

修改俄罗斯方块基类TetrisBase的IsCanMove方法

遍历当前方块的每个单元格,如果单元格不为空且对应最近的地图单元格是静态的(即已经被其他方块占据),则返回false表示不能移动,否则返回true表示可以移动

//是否能移动的方向
public virtual bool IsCanMove(Direction dir)
{int _rowIndex = rowIndex;int _colIndex = colIndex;switch (dir){case Direction.None:break;case Direction.Right:_colIndex++;break;case Direction.Left:_colIndex--;break;case Direction.Down:_rowIndex++;break;}//超出网格if (_colIndex < 0 || _colIndex + colCount > Defines.ColCount || _rowIndex + rowCount > Defines.RowCount){return false;}//检查当前方块是否可以向指定方向移动for (int row = 0; row < rowCount; row++){for (int col = 0; col < colCount; col++){if (blockArr[row, col] != BlockType.Empty){int map_rowIndex = _rowIndex + row;int map_colIndex = _colIndex + col;Block b = MapManager.Instance.blockArr[map_rowIndex, map_colIndex];if (b.type == BlockType.Static){return false;}}}}return true;
}

效果
在这里插入图片描述

旋转逻辑

TetrisBase俄罗斯方块基类新增控制旋转方法

//旋转
public void Rotate()
{//二维数组互换int new_rowCount = colCount;int new_colCount = rowCount;//互换行列后是否超出网格if (rowIndex + new_rowCount > Defines.RowCount || colIndex + new_colCount > Defines.ColCount) return;BlockType[,] tempArr = new BlockType[new_rowCount, new_colCount];for (int row = 0; row < new_rowCount; row++){for (int col = 0; col < new_colCount; col++){tempArr[row, col] = blockArr[new_colCount - 1 - col, row];if (tempArr[row, col] != BlockType.Empty){//对应位置是静态类型static不能旋转if (MapManager.Instance.blockArr[row + this.rowIndex, col + this.colIndex].type == BlockType.Static) return;}}}//擦除WipeMe();rowCount = new_rowCount;colCount = new_colCount;blockArr = tempArr;DrawMe();//画
}

GameManager调用

if (Input.GetKeyDown(KeyCode.W)) currentTetris.Rotate();

效果
在这里插入图片描述

消除逻辑

GameManager新增方法

# 调用
//检测删除行
CheckDelete();//检测删除行
public void CheckDelete()
{//最后一行开始遍历for (int row = Defines.RowCount - 1; row >= 0; row--){int count = 0;//静态类型的个数for(int col = 0; col < Defines.ColCount; col++){BlockType type = MapManager.Instance.blockArr[row, col].type;if (type == BlockType.Static)count++;}if(count == Defines.ColCount){for (int dropRow = row; dropRow > 1; dropRow--){for (int dropCol = 0; dropCol < Defines.ColCount; dropCol++){//上一行类型覆盖当前行BlockType type = MapManager.Instance.blockArr[dropRow - 1, dropCol].type;MapManager.Instance.ChangeType(dropRow, dropCol, type);}}row++;}}
}

效果
在这里插入图片描述

游戏结束逻辑

修改GameManager代码

bool isStop = false;//游戏结束标识//开始游戏
void OnStartGame()
{isStop = false;
}void Update()
{if (isStop == true){return;}
}//当前俄罗斯生成的时候对应位置是不可移动(覆盖)说明游戏结束
public bool IsGameOver()
{for (int row = 0; row < currentTetris.rowCount; row++){for (int col = 0; col < currentTetris.colCount; col++){BlockType type = currentTetris.blockArr[row, col];if (type != BlockType.Empty){int map_rowIndex = row + currentTetris.rowIndex;int map_colIndex = col + currentTetris.colIndex;if (MapManager.Instance.blockArr[map_rowIndex, map_colIndex].type == BlockType.Static) return true;}}}return false;
}

调用

//自动下落
public void AutoMoveDown()
{timer -= Time.deltaTime;if (timer <= 0){timer = Defines.downTime;if (currentTetris.IsCanMove(Direction.Down)){currentTetris.MoveByDir(Direction.Down);}else{//设置不可移动类型MapManager.Instance.SetStatic(currentTetris);//检测删除行CheckDelete();//不能移动重新创建currentTetris = CreateTetris();if (IsGameOver() == true){isStop = true;Debug.Log("game over");return;}currentTetris.DrawMe();}}
}

效果
在这里插入图片描述

怪物生成

修改GameManager代码

//检测删除行
public void CheckDelete()
{//最后一行开始遍历for (int row = Defines.RowCount - 1; row >= 0; row--){//。。。if(count == Defines.ColCount){for (int dropCol = 0; dropCol < Defines.ColCount; dropCol++){//当前行生成哥布林SimpleFactory.CreateModel("gbl", null).transform.position = MapManager.Instance.blockArr[row, dropCol].transform.position;}//。。。}}
}

怪物触碰boss造成伤害和特效,还有游戏结束效果,就自己扩展了,也很简单,还有不同形状的方块

效果
在这里插入图片描述

源码

要啥源码,好好看,好好学!

参考

【视频】https://www.bilibili.com/video/BV1Fr4y1x7mx

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,于是最近才开始自习unity。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/108891.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Matlab学习-自定义函数

Matlab学习-自定义函数 常用自定义函数 文章目录 Matlab学习-自定义函数1. 打印时间2. 计算统计参数3. 画图函数 1. 打印时间 function result calculate_time(time)% Function describe : calculate time% Input : time:N*1% Output : result.hour/min/sec hour/min/sec…

初始化一个 vite + vue 项目

创建项目 首先使用以下命令创建一个vite项目 npm create vite然后根据提示命令 cd 到刚创建的项目目录下&#xff0c;使用npm install安装所需要的依赖包&#xff0c;再使用npm run dev即可启动项目 配置 vite.config.js 添加process.env配置&#xff0c;如果下面 vue-route…

【Java基础篇 | 面向对象】—— 继承

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【JavaSE_primary】 本专栏旨在分享学习JavaSE的一点学习心得&#xff0c;欢迎大家在评论区讨论&#x1f48c; 继承允许一个类继承另一个…

Java8新特性(Stream流)

Stream流是用于对数组和集合进行便捷操作的。 Stream流 学习Stream流的步骤&#xff1a;获取Stream流&#xff0c;Stream流常见的中间方法&#xff0c;Stream流常见的终结方法。 Stream流的创建 获取数组的Stream流&#xff1a;Arrays.stream(arr)获取List集合的Stream流&a…

分享一个基于微信小程序的汽车租赁小程序 车辆出租小程序 汽车租借小程序源码 lw 调试

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人七年开发经验&#xff0c;擅长Java、Python、PHP、.NET、微信小程序、爬虫、大数据等&#xff0c;大家有这一块的问题可以一起交流&#xff01; &#x1f495;&…

curl快速学习指南:从新手到专家

文章目录 curl入门指南curl的基本语法curl的常用选项curl示例代码curl入门指南 curl是一个强大的工具,它可以用于从服务器获取或发送数据。它支持多种协议,包括HTTP,HTTPS,FTP等。curl语法简单,易于学习。本教程将介绍curl的基本语法和使用方法,并通过示例代码帮助您理解…

【C#项目实战】控制台游戏 勇士斗恶龙(2)——游戏场景的设置以及玩家战斗逻辑

君兮_的个人主页 即使走的再远&#xff0c;也勿忘启程时的初心 C/C 游戏开发 Hello,米娜桑们&#xff0c;这里是君兮_&#xff0c;最近开始正式的步入学习游戏开发的正轨&#xff0c;想要通过写博客的方式来分享自己学到的知识和经验&#xff0c;这就是开设本专栏的目的。希望…

固定资产管理中净值怎么算

在资产管理的领域中&#xff0c;我们经常听到“净值”这个词。然而&#xff0c;对于许多人来说&#xff0c;净值的概念仍然模糊不清。本文将试图揭示固定资产管理的净值计算方法&#xff0c;并提供一些创新的观点。  我们需要明确什么是净值。在财务术语中&#xff0c;净值是…

模拟实现C语言--strlen函数

模拟实现C语言–strlen函数 模拟实现C语言--strlen函数一、strlen函数是什么&#xff1f;二、strlen函数的模拟实现2.1 计数器方式实现strlen函数2.2 不创建临时变量计数器方式实现strlen函数2.3 指针-指针方式实现strlen函数 三、strlen函数的返回类型 一、strlen函数是什么&a…

hive的建表语句

hive建表语句CREATE TABLE ccwn_zh_event_push (customerid string,cardnumber string,accountnumber string,eventcode string,eventtime string,activities string,activityRefuseCode string,lables string)PARTITIONED BY(dt string)ROW FORMAT SERDE org.apache.hadoop.hi…

plt函数显示图片 在图片上画边界框 边界框坐标转换

一.读取图片并显示图片 %matplotlib inline import torch from d2l import torch as d2l读取图片 image_path ../data/images/cat_dog_new.jpg # 创建画板 figure d2l.set_figsize() image d2l.plt.imread(image_path) d2l.plt.imshow(image);二.给出一个(x左上角,y左上角,…

9.12|day 5|day 44 |完全背包| 518. 零钱兑换 II | 377. 组合总和 Ⅳ

● 完全背包 主要是看清01背包和完全背包的区别 //01背包 for(int i 0;i<weight.size();i){ for(int j bagWeight;j>weight[i];j--){dp[j] Math.max(dp[j],dp[j-weight[i]]value[i]); } } //完全背包 for(int i 0;i<weight.size();i){for(int j weight[i];j<…