Unity餐厅经营游戏毕设:C#状态机与SQLite数据库架构实战
2026/9/15 14:01:19 网站建设 项目流程

简介:一份面向计算机相关专业毕业设计的高分Unity餐厅经营游戏项目,使用C#语言开发,完整包含游戏源码、配套数据库、毕业论文与演示视频。项目经导师指导并认可,评审得分98分,所有源码均已本地编译与严格调试,可稳定运行,能够为正在完成大作业或毕业设计的学生提供直观的参考。资源压缩包共916个文件,大小约226.97MB,主要文件类型包括C#脚本、Unity场景与预制体、三维模型与材质、动画与音频资源,以及PDF论文和SQL数据库脚本等,目录结构经过整理,便于按模块检索与学习。包内还附有演示视频,可快速了解游戏玩法与功能实现。目前已有78人学习下载,适合需要完整毕业设计案例、希望缩短开发周期的学习者。通过这个项目,可以系统掌握餐厅经营游戏的需求分析、系统设计、核心玩法实现以及论文写作结构,同时获得一套可直接运行的代码与资源模板,在此基础上进行功能扩展或界面修改,能有效提升项目实战能力。

1. 基于Unity的餐厅经营游戏,毕设选题的核心在哪

餐厅经营游戏在本科毕业设计里是个高频选题,因为它的功能边界足够清晰,C#、Unity、数据库三个关键词都能被覆盖到。但很多同学把精力全放在场景搭建和UI拖拽上,最后做出来的东西看起来是个游戏,答辩时却拿不出几个能说清的技术点。问题的根源在于,餐厅经营这件事本身是离散的、状态密集的:顾客什么时候来、点什么菜、后厨怎么做、服务员怎么上菜、钱怎么收,每一步都是一个独立的系统状态。如果一开始不把这些拆成可管理的数据结构和状态机,后面所有功能都会堆在Update里,越写越乱。

我一般会把这类项目的技术骨架定为三条线:Unity负责表现层,C#脚本承担玩法逻辑与状态流转,数据库做存档和经营数据的持久化。真正拉开差距的,不是谁家餐厅模型更精致,而是谁能把“顾客从进店到结账”这条链路用代码讲清楚。本设计标题里带了“源码+数据库+论文+演示视频”,意味着它要求的是一套完整的工程交付物,而不是一个能跑的场景原型。所以这篇博文会从架构、核心玩法、数据持久化、论文组织、答辩演示这个顺序展开,每一步都给出可抄的C#代码和参数设置思路。

适合读这篇内容的,是正在做Unity C#方向毕设的学生,以及想用最短时间理解经营类游戏核心循环的入门开发者。下面进入正题。

2. 餐厅经营游戏的C#脚本架构与Unity分层设计

2.1 先用MVC思路划分脚本职责,防止Unity场景变成代码垃圾场

餐厅经营游戏虽然核心逻辑不复杂,但功能模块多:顾客管理、订单队列、库存、货币、NPC动画、UI刷新、存档读档。如果全部挂在GameObject上互相GetComponent,到了后期每加一个功能都要翻遍所有脚本。更合理的做法是采用轻量级MVC分层——Model管数据,View管Scene里的表现,Controller管输入和业务规则。

├── Scripts/ │ ├── Models/ // 纯C#类,不继承MonoBehaviour │ │ ├── Customer.cs │ │ ├── Order.cs │ │ └── MenuItem.cs │ ├── Controllers/ // 继承MonoBehaviour,驱动业务逻辑 │ │ ├── GameManager.cs │ │ ├── CustomerFlowController.cs │ │ └── EconomyController.cs │ ├── Views/ // 处理UI显示、动画、特效 │ │ ├── CustomerView.cs │ │ ├── OrderBoardView.cs │ │ └── MoneyDisplayView.cs │ └── DataAccess/ // 数据库读写与序列化 │ ├── DatabaseManager.cs │ └── SaveSystem.cs

核心思路是:Model层全用普通C#类,不依赖Unity Engine的API,这样写单元测试也方便。Controller层通过事件或委托把状态变化通知给View层,View只负责刷新UI和播放动画,不参与业务判断。这样在合并代码和排查Bug时会轻松很多,你在答辩时也能直接说出“我的架构是分层解耦的”这种加分句。

2.2 C#事件系统驱动UI刷新,避免Update轮询

很多初学者会在Update里每帧刷新金钱文本和订单列表,逻辑简单时没毛病,但当订单列表变长后,每帧遍历List会产生不必要的GC Alloc。更优雅的方案是用C#内置的event关键字,让Controller在数据变化时主动通知View。

// Models/GameState.cs public class GameState { public int Money { get; private set; } public event Action<int> OnMoneyChanged; public void AddMoney(int amount) { Money += amount; OnMoneyChanged?.Invoke(Money); } public bool SpendMoney(int amount) { if (Money < amount) return false; Money -= amount; OnMoneyChanged?.Invoke(Money); return true; } }

这段代码里,OnMoneyChanged是事件,任何关心金钱变化的UI模块都可以订阅。比方说钱袋子图标上的数字变化时,MoneyDisplayView只需要在Start方法里执行gameState.OnMoneyChanged += RefreshText;,不需要每帧检测Money有没有变。

事件驱动的好处在于,它把数据变更和UI响应彻底解耦,同时天然支持多订阅者——一个金钱变化事件,既可以让顶部UI刷新,也可以让音效播放器判断是否播放金币音效。对Unity项目来说,这比每帧GetComponent再查属性要干净得多,也是面试和答辩时能展示“我理解观察者模式”的抓手。

2.3 GameManager作为唯一入口,控制餐厅的营业状态

GameManager是Controller层的总调度,负责初始化所有Manager、加载存档和推进经营状态。一般我会用单例模式,但注意不要滥用单例把项目变成一堆全局变量。GameManager可以管营业开始、暂停、结束这三个大状态,至于顾客怎么走、订单怎么做,交给专门的控制器。

状态触发条件主要行为
Preparing游戏启动加载配置、读取数据库、实例化场景物体
Running玩家点击“开始营业”允许顾客生成、订单倒计时、食材扣减
Paused玩家点击“暂停”或失去焦点Time.timeScale设为0,UI层弹出暂停面板
Closed营业时间结束停止生成新顾客,清空未完成订单,提交收益

这个状态机的实现不需要复杂的插件,用枚举加switch就够了。核心在于把大状态管理好,子系统的微小状态交给各自的控制器处理,否则GameManager会膨胀到几千行,后期谁也不敢动。答辩时可以说“我参考了状态模式来管理游戏主流程”,然后演示暂停时Time.timeScale的变化,这个细节很容易让老师觉得你对Unity生命周期有认识。

3. 顾客点单与厨房出餐的C#状态机实现

3.1 顾客从进店到离开,用枚举定义清晰的生命周期

餐厅经营游戏的核心体验就是顾客流程。一个顾客从进入餐厅到离开,至少经历这几个状态:进门、找座位、看菜单、点单、等餐、用餐、结账、离店。这个流程天然适合用状态机表达。C#里实现状态机不需要引入额外库,一个枚举加一个switch方法就够了。

public enum CustomerState { Entering, WaitingToOrder, Ordering, WaitingForFood, Eating, Paying, Leaving }

接下来在CustomerController里维护当前状态,并在Update里按状态驱动行为:

void Update() { switch (currentState) { case CustomerState.Entering: MoveTo(emptySeat.position); if (ReachedDestination()) SetState(CustomerState.WaitingToOrder); break; case CustomerState.WaitingToOrder: waitingTimer += Time.deltaTime; if (waitingTimer >= patienceTime) SetState(CustomerState.Leaving); // 等太久会走人 break; case CustomerState.Ordering: // 提交订单给OrderSystem OrderSystem.Instance.SubmitOrder(selectedDish); SetState(CustomerState.WaitingForFood); break; case CustomerState.WaitingForFood: if (OrderSystem.Instance.IsOrderReady(customerId)) SetState(CustomerState.Eating); break; case CustomerState.Eating: eatingTimer += Time.deltaTime; if (eatingTimer >= eatingDuration) SetState(CustomerState.Paying); break; case CustomerState.Paying: EconomyController.Instance.AddMoney(orderPrice); SetState(CustomerState.Leaving); break; case CustomerState.Leaving: MoveTo(exitPosition); if (ReachedDestination()) Destroy(gameObject); break; } }

这里的patienceTime是顾客耐心值,它直接决定了游戏难度。数值太小玩家来不及接待,数值太大游戏没有紧张感。我一般把普通顾客的耐心时间设置为15~20秒,高峰期可以在波次设定里随机乘0.8~1.2倍。eatingDuration建议7~10秒,太短显得不真实,太长影响翻台率。

3.2 订单数据结构与厨房队列的C#设计

订单是整个游戏里传递最频繁的数据结构。厨房、服务员、收银台都要读它。设计得好,后续加菜品、加厨师都很方便。

[System.Serializable] public class Order { public int orderId; public int customerId; public int dishId; public string dishName; public float orderTime; public float cookDuration; public float cookedTimer; public bool isReady; public OrderState state; } public enum OrderState { Pending, Cooking, Ready, Served, Completed }

厨房队列我推荐用Queue 或List 加Linq排序。按价格优先、制作时间短的菜先做,这是最简单的经营策略AI。核心代码在KitchenController里:

void Update() { if (cookingQueue.Count == 0) return; Order current = cookingQueue.Peek(); current.cookedTimer += Time.deltaTime; if (current.cookedTimer >= current.cookDuration) { current.isReady = true; current.state = OrderState.Ready; cookingQueue.Dequeue(); OnOrderReady?.Invoke(current); } }

实际开发时,cookDuration不要写死在脚本里,应该从菜谱配置表读取。菜品数据用ScriptableObject存起来,设计者改数值不用改代码,这个细节在论文里能占到一段——内容组织上,ScriptableObject的配置化设计比人物建模更值得写进“系统设计与实现”章节。

3.3 餐厅格子布局与C#寻路的最简方案

Unity里的寻路方案,路径最灵活的是NavMesh Agent,但餐厅环境是规整的地砖格子,用NavMesh反而有些杀鸡用牛刀,而且动态避让在地块狭小的餐厅里容易卡住。更稳妥的方案是AStar算法配合二维数组地图。

public class MapGrid { private int width, height; private bool[,] walkable; public List<Vector2Int> FindPath(Vector2Int start, Vector2Int end) { // 标准A*实现 // 1. 创建OpenList和ClosedList // 2. 计算每个节点的G、H、F值 // 3. 从终点回溯得到路径 // 4. 返回节点列表 } }

这里有一个常见误区:每帧都寻路。顾客只有当目标座位变化或路径被阻挡时才需要重新计算路径,其余时间只需沿着路径点移动。在Update里加一个isPathStale标记,能省掉大量不必要的CPU开销。餐厅场景里几十个顾客同时活动时,这个优化能让帧率提升10fps以上,你自己在Profiler里就能看到差距。

4. SQLite数据库设计与Unity数据持久化实操

4.1 为什么餐厅经营游戏的存档选SQLite而不是PlayerPrefs

Unity提供了PlayerPrefs做轻量数据存储,适合存音量、画质这类简单配置。但餐厅经营游戏里有菜单表、订单记录、顾客评价、每日营收流水,这些是结构化数据,用PlayerPrefs拼字符串存的话,读取解析麻烦还不安全。数据库课程设计里通常会拉一张表出来讲,SQLite天然成为首选——它是文件型数据库,无需安装服务端,Unity各个平台都支持,做毕业设计不需要额外部署环境。

数据库表设计是这个项目的门面,至少要有这几张表:

表名主要字段作用
MenuItemId, Name, Price, CookDuration, IngredientCost存储菜谱信息
OrderRecordId, CustomerName, DishId, TotalPrice, CreateTime记录每次点单
GameSaveSaveId, Money, Day, TotalCustomers, Rating存储玩家进度
CustomerReviewId, CustomerName, Score, Comment, CreateTime顾客评价,影响口碑

4.2 在Unity里用C#连接SQLite的最小可用代码

用SQLite在Unity里要引入Mono.Data.Sqlitesqlite3.dll,这个步骤经常卡住新手。注意Unity 2020以上版本建议用UnityEngine.Sqlite相关的第三方库,或者直接用原生的System.Data.SQLite。我推荐用最经典的做法:把sqlite3.dll放进Plugins文件夹,然后通过Mono.Data.Sqlite访问。

using Mono.Data.Sqlite; public class DatabaseManager { private string connectionString; public void Connect(string dbPath) { connectionString = "URI=file:" + dbPath; using (var connection = new SqliteConnection(connectionString)) { connection.Open(); // 创建表(如果不存在) string createTable = @" CREATE TABLE IF NOT EXISTS MenuItem ( Id INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT NOT NULL, Price REAL NOT NULL, CookDuration REAL NOT NULL, IngredientCost REAL NOT NULL );"; using (var command = new SqliteCommand(createTable, connection)) { command.ExecuteNonQuery(); } connection.Close(); } } public List<MenuItem> LoadMenu() { var menu = new List<MenuItem>(); using (var connection = new SqliteConnection(connectionString)) { connection.Open(); string query = "SELECT * FROM MenuItem"; using (var command = new SqliteCommand(query, connection)) using (var reader = command.ExecuteReader()) { while (reader.Read()) { menu.Add(new MenuItem { id = reader.GetInt32(0), name = reader.GetString(1), price = (float)reader.GetDouble(2) }); } } connection.Close(); } return menu; } }

connectionString里面的URI=file:是SQLite连接的标准写法,路径取决于你放数据库文件的位置。在Unity编辑器里,Application.dataPath能直接指向Assets目录,但打包后这个路径是只读的,所以存档文件一般要放到Application.persistentDataPath下面。具体做法是:首次运行把StreamingAssets里的初始数据库复制到persistentDataPath,后续读写都走后者。这个细节在演示视频里可以专门展示一次,效果很好。

4.3 数据库写入时机:别在Update里写库,用缓存批量提交

一个高频踩坑点:每一单都立刻写数据库,会导致频繁IO,卡顿严重。常见做法是缓存策略——在内存里累积数据,每隔5秒或游戏日结束时批量写入。

public class EconomyController : MonoBehaviour { private List<OrderRecord> pendingRecords = new List<OrderRecord>(); private float saveTimer; void Update() { saveTimer += Time.deltaTime; if (saveTimer >= 5f) { saveTimer = 0f; FlushRecordsToDatabase(); } } void FlushRecordsToDatabase() { // 使用事务批量插入 // 将pendingRecords一次性写入OrderRecord表 // 可以显著减少IO次数 } }

批量提交时用事务包裹,插入速度提升非常明显。数据量小的时候体感不深,但如果你把每一条订单记录都当成一条INSERT,游戏高峰期一局十分钟可能产生几十条记录,页面切换和保存瞬间就会感觉卡一下。事务处理的代码逻辑不复杂,把多条INSERT用SqliteTransaction包起来,最后Commit即可。

4.4 数据库连接泄漏的排查方法

Unity的编辑器日志不会直接报“连接泄漏”,但你在Profiler里看内存会发现MonObject数量只增不减。最直接的排查方法是在Debug日志里输出连接当前状态。一旦发现_connection.ConnectionString在非操作期间还是非空,多半是忘记Close了。养成习惯:所有连接操作都写到using块里,确保Dispose被调用。这个习惯在答辩时老师问“你的代码有没有资源泄漏问题”时可以直接答出来。

5. 论文写作结构、演示视频录制与答辩加分项

5.1 毕业论文的系统架构图,怎么画才显得工作量够

论文的系统设计章节,至少应该包含系统总体架构图、功能模块图、数据库ER图、关键流程图。很多同学从网上下模板改一改,图里全是系统自带图标,答辩老师一眼就看穿了。更稳妥的做法是画一张分层的架构图,从上到下分成“表现层—逻辑层—数据层”三块,然后在每块旁边标注你用到的Unity关键类名,这样既具体又有真实工作量。

数据库ER图不要画得太复杂,重点突出Menu、Order、Customer这三张表的关系。把Order表作为核心,左边连接Customer,右边连接MenuItem,再标出外键关系,一张清晰的ER图能讲1分钟,这1分钟是让你从“做过项目”升级到“理解设计”的高光时刻。

5.2 演示视频的内容编排,避免面试官打断

演示视频的时长控制在5~7分钟内,太多老师没有耐心看完。视频里应按照“主界面→营业开启→顾客进入→点单→出餐→结账→查看数据库记录→存档退出”这个顺序录制。其中查看数据库记录这段最容易出彩,展示一下订单成功写入SQLite,可以说明你不是只做了个Demo,而是有数据闭环。

演示视频里尽量展示边界情况:顾客耐心耗尽离开、金钱不足、厨房队列满载时新订单的处理方式。这些细节说明你对异常流程有考虑,不是只有happy path。

5.3 答辩答辩,答的是什么

老师最容易问的切入点是:“你怎么保证点菜之后菜一定会上,而且只上一次?”这时你可以讲出菜单状态机的状态流转,然后补充一句“如果网络不好或异常退出,重启游戏后未完成的订单从数据库里恢复,并且状态被标记为Unfinished,不会重新扣费也不会重复扣钱”。虽然Unity单机游戏没有真正网络问题,但这个回答能让老师看到你有状态恢复意识。比直接背源码强。

6. 收尾技巧:用Profiler验证性能和餐厅经营数值平滑

说到项目收尾,有个经常被忽略的动作:在演示前用Unity Profiler跑一遍,截取CPU和内存数据存进论文的“系统测试”章节。很多同学因为没有这一步,论文里只写“经测试系统运行稳定”,没有任何数字支撑,显得空洞。

具体做法是Window > Analysis > Profiler,在运行游戏分支“Development Build”之后录制三分钟数据,截取CPU Usage和Memory的曲线图。然后取平均值:比如Game Logic 12ms、Rendering 9ms、GC Alloc小于2MB。这些数字出现在论文里,比写十句“效率高”都有说服力。

还有一个经营数值的平滑处理技巧:餐厅经营里最怕经济系统崩溃,钱突然暴涨或狂跌。常见做法是在EconomyController里加一个动态难度的缓冲量——当玩家金钱低于某个阈值时,菜价不离谱但顾客来店频率增加;当金钱高出一个阈值时,顾客对出餐速度的要求提高。这个反馈循环数值不用太复杂,一个if判断两个乘数就会让游戏有节奏,用来讲“我的设计考虑了正负反馈”非常实用。

用最简单的方式验证你的经济系统是否平滑:玩5个游戏日,把每天的最终金钱画成折线图。正常趋势应该是阶梯式增长,但中间会有小回落(购买食材、扩建座位)。如果你的曲线是直线上升或者断崖式下跌,说明数值模型有问题,需要回去调整参数。这些都是复盘中值得写进论文的测试数据,也是答辩时能自信展示“系统经过验证”的依据。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询