2025年记忆翻牌游戏——使用unity完成的2D游戏

记忆翻牌游戏——使用unity完成的2D游戏目录 前言 游戏循环基本原理 常见游戏事件 即时模式 GUI IMGUI Entity Component System ECS 概念 Model View Controller MVC 概念 游戏概述 具体的游戏实现 模型部分 Model 初始化 model

大家好,我是讯享网,很高兴认识大家。

目录

前言

游戏循环基本原理

常见游戏事件

即时模式GUI(IMGUI)

Entity-Component-System (ECS)概念

Model-View-Controller (MVC)概念

游戏概述

具体的游戏实现

模型部分(Model)

初始化model

初始化各种参数以及Table和List

视图部分(View)

控制部分(Controller)

判断是否将两张卡牌消去的主要逻辑实现

查找元素的个数以及返回所在位置

判断游戏是否结束

ECS(Entity Component System)的应用

结语

代码汇总


前言

在这一篇博客中,我将介绍如何使用unity以及Entity-Component-System (ECS)和Model-View-Controller (MVC)概念来制作一个简单的2D游戏——记忆翻牌游戏。在这一个过程中,我们将会了解游戏循环的基本原理,掌握常见游戏事件的使用及其执行顺序,学会使用unity的简单操作与编程,掌握游戏世界的基本概念。同时还对ECS以及MVC概念有一定的深入了解。

游戏循环基本原理

游戏循环是指游戏引擎在每一帧中执行的一系列步骤,以更新游戏状态并呈现画面。下面是Unity游戏循环的基本原理:

1. 初始化阶段(Initialization Phase):
    游戏引擎初始化:创建游戏窗口、图形设备的初始化等。
    场景加载:加载游戏场景、资源等。

2. 输入处理阶段(Input Phase):
    处理输入:监听玩家的输入,例如键盘、鼠标、触摸屏等。
    更新输入状态:根据输入更新游戏中的相应状态,例如控制角色移动。

3. 更新阶段(Update Phase):
    更新游戏状态:执行游戏逻辑、物理模拟、碰撞检测等。
    更新游戏对象:更新游戏中的各个对象的状态,例如移动、旋转、动画等。
    处理游戏事件:触发和处理游戏中的事件,例如触发特定条件下的事件回调。

4. 渲染阶段(Render Phase):
    准备渲染:设置渲染目标、清除缓冲区等准备工作。
    渲染场景:将游戏对象的可视化表现绘制到屏幕上,包括几何形状、材质、光照等。
    后期处理:应用各种后期效果,例如颜色校正、模糊、阴影等。
    呈现画面:将渲染结果显示在屏幕上,完成一帧的渲染。

5. 循环重复:
    上述步骤会在每一帧中依次执行,形成游戏循环,不断更新游戏状态和渲染画面。

常见游戏事件

当了解了游戏循环的基本原理后,我们可以探讨一些常见的游戏事件以及它们的执行顺序。以下是一些常见的游戏事件及其执行顺序的概述:

1. Start(开始)事件:
    在游戏对象被激活或场景加载完成后触发。
    用于初始化游戏对象的状态、设置初始数值、获取引用等。
    只会在游戏对象的生命周期中执行一次。

2. Update(更新)事件:
    在每一帧的更新阶段执行。
    用于处理游戏逻辑、输入响应、移动、旋转等实时更新的操作。
    执行顺序从场景中的每个激活的游戏对象开始,按照它们在场景中的顺序依次执行。

3. FixedUpdate(固定更新)事件:
    在每一帧的物理更新阶段执行。
    用于处理物理模拟、碰撞检测等与物理相关的操作。
    时间间隔固定,不受帧率的影响,通常每秒执行几次(例如默认每秒执行 50 次)。

4. LateUpdate(延迟更新)事件:
    在 Update 事件之后执行。
    用于处理在 Update 事件中可能会影响到其他对象的操作。
    例如相机跟随、动画更新等需要在其他对象更新完毕后再执行的操作。

5. OnCollisionEnter(碰撞进入)事件:
    在游戏对象与其他对象发生碰撞时触发。
    用于处理碰撞事件的逻辑,例如触发游戏效果、播放音效等。

6. OnTriggerEnter(触发器进入)事件:
    在游戏对象进入触发器时触发。
    用于处理触发器事件的逻辑,例如触发任务、切换场景等。

7. OnGUI(绘制GUI)事件:
    在渲染阶段之后执行。
    用于绘制游戏界面、UI 元素等。

需要注意的是,不同的游戏事件在执行顺序上可能会有差异,具体取决于事件的类型和注册方式。例如,MonoBehaviour 中的事件会按照脚本的顺序进行执行,而物理事件则与物理引擎的更新步骤相关联。

即时模式GUI(IMGUI)

即时模式 GUI(IMGUI)是 Unity 引擎提供的一种简单而直接的图形用户界面(GUI)系统。IMGUI 允许你在游戏循环的 OnGUI 事件中直接编写代码来创建和更新用户界面元素。

IMGUI 是基于立即绘制的概念,每当 OnGUI 事件被触发时,GUI 元素将被立即绘制到屏幕上。IMGUI 使用一系列 GUI 函数来创建不同类型的控件,例如按钮、文本框、滑块等。你可以通过调用这些函数来设置控件的属性、处理用户输入和响应事件。

以下是使用 IMGUI 的基本示例:

using UnityEngine; public class MyGUI : MonoBehaviour { private string playerName = "Player"; private int score = 0; private void OnGUI() { // 创建一个标签显示玩家名称和分数 GUI.Label(new Rect(10, 10, 200, 20), "Player Name: " + playerName); GUI.Label(new Rect(10, 30, 200, 20), "Score: " + score); // 创建一个按钮,点击后增加分数 if (GUI.Button(new Rect(10, 60, 80, 20), "Add Score")) { score += 10; } // 创建一个文本框,用于输入玩家名称 playerName = GUI.TextField(new Rect(10, 90, 120, 20), playerName); } }

讯享网

需要注意的是,IMGUI 是一种立即绘制的方式,每一帧都会重新绘制所有的 GUI 元素,这可能会对性能产生一定影响。因此,对于复杂的 GUI 界面或需要频繁更新的情况,Unity 推荐使用新的 UI 系统(例如 Canvas 和 RectTransform)来构建用户界面。


讯享网

Entity-Component-System (ECS)概念

ECS(Entity Component System)是一种用于构建游戏和应用程序的软件架构模式。它的核心思想是将游戏对象(Entities)拆分为组件(Components)和系统(Systems),以提供更高效、可扩展和可维护的开发方式。

在传统的面向对象编程中,游戏对象通常是由一个包含各种属性和行为的单个类表示。但是,当游戏对象变得复杂时,这种设计模式可能导致类的膨胀和难以管理。ECS 通过分离数据和行为,提供了一种更灵活的开发方式。

以下是 ECS 的三个核心概念:

1. 实体(Entity):
    实体代表游戏中的对象,可以是角色、敌人、道具等。
    实体本身只是一个标识符或唯一标识(ID),不包含任何数据或行为。

2. 组件(Component):
    组件是实体的数据部分,用于描述实体的特性和属性。
    组件是纯数据,通常是结构体或类,只包含属性和数据,没有任何行为。
    一个实体可以拥有多个组件,不同的组件可以用于描述实体的不同方面,例如位置、渲染、碰撞等。

3. 系统(System):
    系统是处理组件的行为部分,用于操作和处理具有特定组件集合的实体。
    系统根据组件的数据执行逻辑和操作,例如更新位置、渲染图形、处理碰撞等。
    系统是独立的,可以根据需要创建多个系统来处理不同类型的组件。

ECS 的核心思想是通过将实体解耦为组件和系统,使得系统可以高效地处理具有相同组件集合的实体,提高了性能和可扩展性。此外,ECS 还支持数据驱动的设计,使得开发者可以更容易地优化和并行化代码,以适应现代多核处理器的优势。

Model-View-Controller (MVC)概念

Model-View-Controller(MVC)是一种软件设计模式,用于组织和管理应用程序的结构和交互逻辑。它将应用程序划分为三个主要组件:模型(Model)、视图(View)和控制器(Controller)。每个组件具有不同的责任和功能,以实现分离关注点和提高代码的可维护性。

下面是 MVC 模式中各个组件的功能和职责:

1. 模型(Model):
    模型代表应用程序的数据和业务逻辑。
    它负责管理数据的读取、存储、处理和验证。
    模型通常包含数据结构、数据库操作、业务规则和算法等。

2. 视图(View):
    视图负责呈现模型中的数据给用户,并接收用户的输入。
    它通常是用户界面(UI)的一部分,例如图形界面、网页或命令行界面等。
    视图是对模型数据的可视化呈现,但本身不负责处理数据或逻辑。

3. 控制器(Controller):
    控制器充当模型和视图之间的中介,负责协调它们之间的交互。
    它接收用户的输入或请求,并根据需要更新模型和视图。
    控制器处理用户事件、调用适当的模型操作,并更新响应的视图。

MVC 模式的核心思想是分离关注点,使每个组件专注于自己的任务。模型负责数据和业务逻辑,视图负责用户界面的呈现,而控制器负责处理用户输入和协调模型与视图之间的交互。

游戏概述

好啦,在前面各种基础知识的了解下,我们可以开始制作我们的2D小游戏啦。我们现在准备制作的游戏是记忆翻牌游戏,具体玩法是:开始游戏时,所有卡牌是不可以见到数字的,只有当我们点击的时候,卡牌才会翻转过来,即可以看到卡牌上的数字。当我们点击了两张卡牌翻转后,如果这两张卡牌上的数字不相等,那么卡牌会再次翻转过去;如果这两张卡牌上的数字相等,那么这两张卡牌就会消失。一直这样子不断点击,使得卡牌两两消去,直到所有卡牌被消去为止,即游戏结束,可以点击Restart重新开始游戏。

我们先来看一下这一个游戏的实现效果吧!

先来看一下我们的游戏界面:

这是我们的游戏展示链接:

记忆翻牌游戏

具体的游戏实现

模型部分(Model)

初始化model

讯享网 //1.模型Model部分 //初始化model private List<int> numbers; private int[,] table; private int line_column=6; private int[,] Is_Click; private bool CanClick=true;

numbers:这是一个List,用于存放可能在表格中出现的数字,同时为了实现数字之间的匹配从而实现消去。

table:这是一个2D整数数组,表示表格中每一个位置的数字。

line_column:表示这一个矩阵(表格)的行和列是多少,为了实现数字之间的两两消去,这一个变量应该为偶数。

Is_Click:这是一个2D整数数组,表示表格中的哪一个位置被点击,同时表示哪一个位置上的数字已经被消去。

CanClick:这是一个布尔值的变量,表示当点击的卡牌有两个时,将不能再点击其它卡牌,直到这两张卡牌再次翻转过去。

初始化各种参数以及Table和List

 //初始化各种参数以及List和Table void Init(){ //首先初始化table、List table=new int[line_column,line_column]; Is_Click=new int[line_column,line_column]; numbers=new List<int>(); //将数字初始化到list中 for(int i=1;i<=line_column*line_column/2;i++){ numbers.Add(i); numbers.Add(i); } //随机洗牌,将列表中的数据随机打乱 System.Random random = new System.Random(); for (int i = 0; i < numbers.Count - 1; i++){ int j = random.Next(i, numbers.Count); int temp = numbers[i]; numbers[i] = numbers[j]; numbers[j] = temp; } //将列表中的数据转换到表格中来 //同时将Is_Click的所有数据初始化为0 for(int i=0;i<line_column;i++){ for(int j=0;j<line_column;j++){ table[i,j]=numbers[i*line_column+j]; Is_Click[i,j]=0; } } }

我们需要初始化table、Is_Click以及numbers,首先赋值给numbers,然后将这一个List里面的数据进行随机打乱,最后再将这一个List的数字映射到table中,同时还需要将Is_Click的所有数据初始化为0。

视图部分(View)

讯享网 //2.视图View部分 void OnGUI(){ GUI.Box(new Rect(255,50,70*line_column,70*line_column),""); if(GUI.Button(new Rect(215+line_column/2*70,65+line_column*70,100,30),"Restart")){ Init(); } for(int i=0;i<line_column;i++){ for(int j=0;j<line_column;j++){ if(Is_Click[i,j]==0&&GUI.Button(new Rect(255+j*70,50+i*70,70,70),"")&&CanClick){ Is_Click[i,j]=1; StartCoroutine(Is_Fade_Away()); } else{ if(Is_Click[i,j]==1){ GUI.Button(new Rect(255+j*70,50+i*70,70,70),table[i,j].ToString()); } } } } if(GameOver()){ GUI.Box(new Rect(260,50,70*line_column,70*line_column),"\n\n\n\n\n\n\n\nCongratulations!\n"); } }

这是一个OnGUI的主函数,我们首先需要创建一个Box,然后在这一个Box中生成一个Button展示字符串“Restart!”,实现重新开始游戏的功能。随后开始生成line_column*line_column的矩阵,生成line_column*line_column个Button。然后开始进行逻辑判断,当点击该卡牌,同时Is_Click为0时,将这一个Is_Click的值赋为1,然后将所有Is_Click为1的卡牌翻转,展示数字。而当点击了两张卡牌后,就需要进行判断,这两张卡牌是否相等,如果相等,就将Is_Click的值赋为2,如果不相等,就将这两张卡牌所在的位置的Is_Click赋为0。最后,直到所有的卡牌对应的位置的Is_Click的值为2,即所有的卡牌都已经被消去了,那么游戏结束,打印“Congratulations!”。

控制部分(Controller)

判断是否将两张卡牌消去的主要逻辑实现

 //3.控制Components/Controller部分 //判断是否将两张卡牌消去的主要逻辑实现 IEnumerator Is_Fade_Away(){ CanClick=false; List<int> counts=Find_element(1); if(counts.Count==2){ int i_1=counts[0]/line_column; int j_1=counts[0]-i_1*line_column; int i_2=counts[1]/line_column; int j_2=counts[1]-i_2*line_column; if(table[i_1,j_1]==table[i_2,j_2]){ yield return new WaitForSeconds(0.5f); Is_Click[i_1,j_1]=2; Is_Click[i_2,j_2]=2; } else{ yield return new WaitForSeconds(0.5f); Is_Click[i_1,j_1]=0; Is_Click[i_2,j_2]=0; } } CanClick=true; }

判断卡牌是否消去,首先需要获取Is_Click中值为1的元素的位置,然后判断为1的元素有多少个,如果只有1个1,将不做处理,如果有2个1,那么将进行判断这两个位置上的数字是否相等。如果相等,就将Is_Click的值赋为2;如果不相等,就将Is_Click的值重新赋为0。同时注意到,为了限制在只能点击两张卡牌,即只能同时看到两张卡牌的数字,我们在进入到这一个函数的时候将CanClick设为了false,然后在退出这一个函数的时候将CanClick设为了true。同时,为了实现当两张卡牌不等时,为了增加记忆时间,我们将使用协程的方法使得函数在这里等待0.5秒的时间。

查找元素的个数以及返回所在位置

讯享网 //查找Is_Click里面值为1的元素有多少个,将位置返回 List<int> Find_element(int element){ List<int> counts=new List<int>(); for(int i=0;i<line_column;i++){ for(int j=0;j<line_column;j++){ if(Is_Click[i,j]==element){ counts.Add(i*line_column+j); } } } return counts; }

这个函数用于查找在Is_Click中有多少个1以及返回每一个1所在的位置,我们将这些所在位置存在一个List中,并且将这一个List作为返回值返回。

判断游戏是否结束

 //判断游戏是否结束 bool GameOver(){ bool temp=true; for(int i=0;i<line_column;i++){ for(int j=0;j<line_column;j++){ if(Is_Click[i,j]!=2){ temp=false; break; } } } return temp; }

当且仅当Is_Click里面的所有数据都是2时才会结束游戏,就算有一个不为2也会是返回false。

ECS(Entity Component System)的应用

虽然上述代码中没有明确实现ECS模式,但我们可以考虑如何将它引入游戏中。例如,我们可以创建一个Card组件,其中包含卡片的值、状态等信息,然后编写系统来处理卡片的翻转和匹配。这样,我们可以更好地分离游戏逻辑和数据。

结语

以上就是我们使用unity以及结合ECS和MVC概念制作而成的一个2D小游戏,其中MVC的三个部分明确详细,并且相互制约,相互联系。通过这一个小游戏,我们可以更加清晰地学会了ECS以及MVC概念的具体含义以及应用,也对于入门unity有了更加深刻的理解和帮助。那么,就让我们一起动起手来,使用unity制作一个属于你们的游戏吧!

代码汇总

讯享网using System.Collections; using System.Collections.Generic; using UnityEngine; public class ConcerationGame : MonoBehaviour { //1.模型Model部分 //初始化model private List<int> numbers; private int[,] table; private int line_column=6; private int[,] Is_Click; private bool CanClick=true; // Start is called before the first frame update void Start() { Init(); } //初始化各种参数以及List和Table void Init(){ //首先初始化table、List table=new int[line_column,line_column]; Is_Click=new int[line_column,line_column]; numbers=new List<int>(); //将数字初始化到list中 for(int i=1;i<=line_column*line_column/2;i++){ numbers.Add(i); numbers.Add(i); } //随机洗牌,将列表中的数据随机打乱 System.Random random = new System.Random(); for (int i = 0; i < numbers.Count - 1; i++){ int j = random.Next(i, numbers.Count); int temp = numbers[i]; numbers[i] = numbers[j]; numbers[j] = temp; } //将列表中的数据转换到表格中来 //同时将Is_Click的所有数据初始化为0 for(int i=0;i<line_column;i++){ for(int j=0;j<line_column;j++){ table[i,j]=numbers[i*line_column+j]; Is_Click[i,j]=0; } } } //2.视图View部分 void OnGUI(){ GUI.Box(new Rect(255,50,70*line_column,70*line_column),""); if(GUI.Button(new Rect(215+line_column/2*70,65+line_column*70,100,30),"Restart")){ Init(); } for(int i=0;i<line_column;i++){ for(int j=0;j<line_column;j++){ if(Is_Click[i,j]==0&&GUI.Button(new Rect(255+j*70,50+i*70,70,70),"")&&CanClick){ Is_Click[i,j]=1; StartCoroutine(Is_Fade_Away()); } else{ if(Is_Click[i,j]==1){ GUI.Button(new Rect(255+j*70,50+i*70,70,70),table[i,j].ToString()); } } } } if(GameOver()){ GUI.Box(new Rect(260,50,70*line_column,70*line_column),"\n\n\n\n\n\n\n\nCongratulations!\n"); } } //3.控制Components/Controller部分 //判断是否将两张卡牌消去的主要逻辑实现 IEnumerator Is_Fade_Away(){ CanClick=false; List<int> counts=Find_element(1); if(counts.Count==2){ int i_1=counts[0]/line_column; int j_1=counts[0]-i_1*line_column; int i_2=counts[1]/line_column; int j_2=counts[1]-i_2*line_column; if(table[i_1,j_1]==table[i_2,j_2]){ yield return new WaitForSeconds(0.5f); Is_Click[i_1,j_1]=2; Is_Click[i_2,j_2]=2; } else{ yield return new WaitForSeconds(0.5f); Is_Click[i_1,j_1]=0; Is_Click[i_2,j_2]=0; } } CanClick=true; } //查找Is_Click里面值为1的元素有多少个,将位置返回 List<int> Find_element(int element){ List<int> counts=new List<int>(); for(int i=0;i<line_column;i++){ for(int j=0;j<line_column;j++){ if(Is_Click[i,j]==element){ counts.Add(i*line_column+j); } } } return counts; } //判断游戏是否结束 bool GameOver(){ bool temp=true; for(int i=0;i<line_column;i++){ for(int j=0;j<line_column;j++){ if(Is_Click[i,j]!=2){ temp=false; break; } } } return temp; } } 

小讯
上一篇 2025-01-17 16:32
下一篇 2025-02-08 23:18

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/30675.html