> 如果您需要一个项目才能练习,请以下是我们在上一个教程中完成的项目的版本。它已经使用了一对源自产生的物品的游戏内相互作用的对象进行升级 - 一把药水和一把剑。它们可以产生并
拾取>(
下载项目启动文件
>项目github页面
项目zip下载
钥匙要点
>利用委托事件系统来通知对象何时保存其状态,增强保存/加载系统的模块化和灵活性。
>使用可序列化类来保存对象属性(例如位置),可以轻松地扩展或修改针对不同类型的对象。>
确保正确订阅和取消订阅以将事件保存在对象脚本中以避免错误并有效地管理游戏状态。
- 开发一个可靠的系统,用于使用二进制格式来保存和加载,以处理复杂的数据结构并确保数据完整性。
- 测试并扩展系统以包括新的对象类型和功能,证明了系统的适应性和针对不同游戏开发需求的可伸缩性。
- 实施理论
- >我们需要在实现该对象之前分解保存和加载对象的系统。首先,我们需要某种将产生和绝望对象的对象
- 级别的主体。它需要在级别中保存对象(如果我们正在加载级别而不是重新启动),Despawn拾取对象,通知对象需要保存自己并管理对象列表。听起来很多,所以让我们将其放入流程图中:
-
>基本上,整个逻辑都将对象列表保存到硬盘驱动器中 - 下次加载级别时,将在我们开始播放时产生的所有对象。听起来很容易,但是魔鬼在细节中:我们如何知道要保存哪些对象,以及我们如何将它们产生回去?
>>委托和事件
>在上一篇文章中,我提到我们将使用委托事件系统通知对象需要保存自己的对象。让我们首先解释什么是代表和事件。
>>您可以阅读官方代表文档和官方事件文档。但请放心:即使我也不了解官方文档中的很多技术,所以我会用简单的英语说:
委托> public delegate void SaveDelegate(object sender, EventArgs args);
这个委托描述了一个函数,该函数什么都没有返回(void),并接受.NET/MONO框架的两个标准参数:代表事件发送者的通用对象,以及最终可以使用的参数,您可以使用这些数据传递各种数据。您真的不必担心这一点,我们可以将(Null,Null)作为争论,但必须在那里。>
>事件。它仅接受与委托(蓝图)匹配的函数,并且您可以在运行时删除并删除函数。 然后,您可以随时触发事件,这意味着运行当前框中的所有功能 - 立即运行。考虑以下事件声明: >订阅并取消订阅
public event SaveDelegate SaveEvent;
订阅事件基本上意味着“将功能放在框中”。语法很简单。让我们假设我们的事件在我们著名的全球视频类别中声明,并且我们有一些名为> potiondroppobable
的药水对象类,需要订阅事件:> >
>当我们拥有它时,我们只是在脚本开始或清醒时将该功能放在框中,然后在被销毁时将其删除。 (取消订阅非常重要:如果您尝试调用被破坏对象的函数,则在运行时会得到null引用异常)。我们通过访问已声明的事件对象,然后使用添加操作员然后使用功能名称来做到这一点。
>注意:我们没有使用括号或参数调用函数;我们只是使用该函数的名称,别无其他。
>因此,让我们解释一下这一切最终在游戏的某个示例流中的作用。
>逻辑流> >
这四个对象在保存事件中注册其功能:> >然后,假设玩家决定保存游戏 - 也许他们确实需要回答现在响了三遍的手机(嘿,您做了一个很棒的游戏):
基本上,发生的事情是框中的功能一个一个一个一个一个一个一个触发的功能,并且在所有功能完成之前,游戏才能运行到任何地方。每个“保存自身”的对象基本上都在列表中写下自己 - 在下一个游戏负载上,将由级别的主对象进行检查,列表中的所有对象都将进行实例化(产生)。就是这样:如果您到目前为止遵循了这篇文章,那么您基本上就可以立即开始实施它。尽管如此,我们将在这里进行一些具体的代码示例,一如既往,如果您想看看整个事物应该如何看起来。
>
f5/f9
来保存/加载,则产生的对象将消失。
盒子产卵器和产卵的对象本身使用了一个简单的可交互接口机械师,该机制使我们能够识别到这些对象,在屏幕上写入文本,然后使用[e]键与它们进行交互。
没有更多的存在。我们在这里的任务是:
- 列出药水 的列表
- 列出剑对象的列表
- 实施全局保存事件
- >使用保存功能订阅事件
- 实现级别主对象 如果我们加载了游戏,则
- 使级别主产生所有保存的对象。 >
>
>对象类列表>以类似的方式,我们需要一些代表我们对象的可序列化类。要编写它们,我们需要知道我们需要保存的属性 - 对于我们的玩家,我们有很多东西要保存。幸运的是,对于对象,您很少需要比他们的世界地位更多。在我们的示例中,我们只需要保存对象的位置,而无需保存。
为了很好地构建我们的代码,我们将从一个简单的类开始,从我们的序列化末尾开始:
>public delegate void SaveDelegate(object sender, EventArgs args);
>到目前为止,您可能已经注意到我使用了很多词> droppable
。这是因为我们需要区分可辍学的(产生)对象和可放置的对象,这些对象遵循不同的保存和产卵规则。我们稍后再解决。>
现在,与玩家的数据不同,我们知道在任何给定时间实际上只有一个玩家,我们可以拥有多个诸如药水之类的对象。我们需要列出一个动态列表,并表示此列表所属的场景:我们不能在Level1中spawn Level2的对象。这很容易做到,再次在序列化中。在我们的最后一堂课下面写这篇文章:
public delegate void SaveDelegate(object sender, EventArgs args);
列出这些列表实例的最佳场所将是我们的全球控制类别:
public event SaveDelegate SaveEvent;
>现在我们的列表非常好:稍后我们将在需要产卵项目时从LevelMaster对象访问它们,并从GlobalControl内部的硬盘驱动器中保存/加载,就像我们已经使用Player Data一样。
>委托和事件
啊,终于。让我们实施著名的活动。
在GlobalControl中>
//In PotionDroppable's Start or Awake function: GlobalObject.Instance.SaveEvent += SaveFunction; //In PotionDroppable's OnDestroy() function: GlobalObject.Instance.SaveEvent -= SaveFunction; //[...] public void SaveFunction (object sender, EventArgs args) { //Here is code that saves this instance of an object. }
如您所见,我们正在使活动成为静态参考,因此以后更逻辑且更易于工作。>
>有关事件实施的最后一个说明:只有包含事件声明的类才能解雇事件。任何人都可以通过访问globalcontrol.saveevent = ...来订阅它,但是只有GlobalControl类才能使用SaveEvent(null,null)触发它。尝试使用globalcontrol.saveevent(null,null);从其他地方导致编译器错误!>
就是事件实施!让我们订阅一些东西!>
>事件订阅开始聆听
我们需要一个在事件触发时运行的函数 - 对于每个对象。让我们在在触发事件时进行反应。
>pickups
在PotionDroppable中,添加以下内容: 我们正确地进行了订阅并取消订阅活动。现在,问题仍然存在,>文件夹中转到PotionDroppoppable脚本。注意:Sword还没有设置其脚本;我们将稍后完成!
>[Serializable] public class SavedDroppablePotion { public float PositionX, PositionY, PositionZ; } [Serializable] public class SavedDroppableSword { public float PositionX, PositionY, PositionZ; }
如何将此对象保存在列表中?> 我们首先需要确保我们有针对当前场景初始化的对象列表。 在GlobalControl.cs中: 此功能需要每个级别触发一次。问题是,我们的全球控制范围及其开始和清醒功能仅发射一次。我们将通过简单地从我们的级别主对象调用此函数来解决这个问题,我们将在稍后创建。 我们将需要一个小的辅助功能来返回当前的活动场景列表。在GlobalControl.cs中:[Serializable] public class SavedDroppableList { public int SceneID; public List<saveddroppablepotion> SavedPotions; public List<saveddroppablesword> SavedSword; public SavedDroppableList(int newSceneID) { this.SceneID = newSceneID; this.SavedPotions = new List<saveddroppablepotion>(); this.SavedSword = new List<saveddroppablesword>(); } }</saveddroppablesword></saveddroppablepotion></saveddroppablesword></saveddroppablepotion>
现在,我们确定我们始终有一个列表可以保存我们的物品。让我们回到我们的药水脚本:public List<saveddroppablelist> SavedLists = new List<saveddroppablelist>(); </saveddroppablelist></saveddroppablelist>
public delegate void SaveDelegate(object sender, EventArgs args); public static event SaveDelegate SaveEvent;
这是我们所有句法糖涂层真正发光的地方。当您需要时,这是非常可读,易于理解且易于改变的需求!简而言之,我们创建了一个新的“药水”表示,并将其保存在实际列表中。创建一个级别的主对象
首先,一小部分准备工作。在我们现有的项目中,我们有一个全局变量,可以告诉我们是否正在加载场景。但是,我们没有这样的变量来告诉我们是否使用门过渡。我们希望当我们返回上一个房间时,所有掉落的对象仍然存在,即使我们没有在两者之间的任何位置保存/加载游戏。 为此
在TransitionScript中>
public delegate void SaveDelegate(object sender, EventArgs args);
我们已经准备好制作一个正常工作的levelmaster对象。
>public event SaveDelegate SaveEvent;
>现在,我们只需要读取列表并在加载游戏时从它们中产生对象。这就是级别主体对象所做的。让我们创建一个新脚本,并将其称为levelmaster
:> 这是很多代码,所以让我们将其分解。
>//In PotionDroppable's Start or Awake function: GlobalObject.Instance.SaveEvent += SaveFunction; //In PotionDroppable's OnDestroy() function: GlobalObject.Instance.SaveEvent -= SaveFunction; //[...] public void SaveFunction (object sender, EventArgs args) { //Here is code that saves this instance of an object. }
>如果我们
是加载场景,我们需要获取我们的本地副本保存的对象列表(只是为了在重复访问GlobalControl,
>和 的重复访问时节省一些性能使语法更可读)。 接下来,我们只需遍历列表,并在其中催生所有药水对象。产卵的确切语法基本上是实例化方法过载之一。我们必须将实例化方法的结果投入到> gameObject(由于某种原因,默认返回类型是简单的对象),以便我们可以访问其转换并更改其位置。 >这是对象产生的地方:如果您需要在产卵时间更改任何其他值,那么这是这样做的地方。>
我们需要将我们的级别主体放在每个场景中,并为其分配有效的预制:>
>现在我们只缺少一个关键作品:我们需要实际启动活动,将列表序列化为硬盘驱动器并从中读取。我们将在GlobalControl中的现有保存和加载功能中简单地做到这一点:这似乎也有很多代码,但是其中大多数已经存在。 (如果您遵循我以前的教程,您将识别二进制序列化命令;这里唯一的新事物是FiresaveEvent函数,还有一个保存我们列表的附加文件。就是这样!
>初始测试
如果您现在运行项目,则每次点击> f5
andf9 [Serializable] public class SavedDroppablePotion { public float PositionX, PositionY, PositionZ; } [Serializable] public class SavedDroppableSword { public float PositionX, PositionY, PositionZ; }
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>这样)。但是,还有一个问题要解决:我们没有保存剑。
这仅仅是为了演示如何在建立类似基础的基础后向项目中添加新的可保存的对象。
扩展系统
>因此,假设您已经有了一个新的对象覆盖系统,就像我们已经对剑对象所做的一样。它们目前的交互不多(基本物理学之外),因此我们需要编写一个类似于药水的脚本,这将使我们能够“捡起”剑并使其正确保存。
>当前要产生的剑的预制可以在资产> prefabs文件夹中找到。
>让它起作用。转到资产>脚本>拾音器,在那里您会看到
potiondroppable
脚本。旁边,创建一个新的SwordDroppoppable脚本:public delegate void SaveDelegate(object sender, EventArgs args);
不要忘记“可相互作用”的接口实现。非常重要的是:如果没有它,您的剑将不会被摄像机射线广播所识别,并且将保持不可分割效果。另外,请仔细检查剑前是否属于项目层。否则,Raycast将再次忽略它。现在,将此脚本添加到剑前的第一个孩子(实际上具有网状渲染器和其他组件):
>现在,我们需要产生它们。在级别的大师中,在我们的循环下产生了药水:
public event SaveDelegate SaveEvent;
…就是这样。每当您需要保存的新项目类型时:
- 添加Serializables类
- 创建订阅以保存事件 的项目脚本
- 在级别主持人中添加实例化逻辑。 >
结论
>目前,系统很粗糙:硬盘上有多个保存文件,对象的旋转未保存(剑,玩家等),并且在场景过渡期间定位播放器的逻辑(但没有)加载)有点古怪。
一旦实心系统到位,这些现在都是要解决的小问题,我邀请您尝试修改本教程和完成的项目,以查看您是否可以改善系统。>示例
项目有很大不同。
如所承诺的,这是完成的项目,如果您需要参考,或者是因为您被卡在某个地方。根据本教程的说明和相同的命名计划,将实现保存系统。下载完成的项目:
Project GitHub页面
项目zip下载
>在Unity中掌握保存和加载功能的常见问题(常见问题解答)5
>在Unity 5中实现保存系统的最佳方法是什么? PlayerPrefs是一种在游戏会话之间存储和检索数据的简单方法。它使您可以以整数,浮点和字符串的形式保存和加载数据。但是,重要的是要注意,PlayerPrefs不安全,不应用于敏感数据。对于更复杂或安全的数据,您可能需要考虑使用二进制格式或JSON Serialializer。在Unity 5中,可以使用PlayerPrefs,JSON序列化或二进制格式来实现。 PlayerPrefs是最简单的方法,允许您保存和加载整数,浮点和字符串。 JSON序列化更为复杂,但可以提高灵活性和安全性。二进制格式是最安全的方法,但也是最复杂的方法。
>执行顺序如何影响Unity 5?
> >如何在Unity 5?
中保护保存数据5可以通过使用二进制格式或加密来实现。二进制格式将您的数据转换为不容易读取的二进制格式。加密通过以一种只能用特定键来解码的方式来编码您的数据来添加额外的安全性。>
>在Unity 5中使用PlayerPrefs用于保存和加载功能的局限性是什么? 🎜>虽然PlayerPrefs是在Unity 5中实现和加载功能的一种简单便捷的方法,但它具有多个限制。首先,它仅支持整数,浮子和字符串。其次,它不安全,很容易被操纵。最后,playerPrefs具有尺寸限制,这对于具有大量数据的游戏可能是一个问题。>我如何保存和加载Unity 5?保存和加载中的复杂数据结构使用JSON序列化或二进制格式可以实现Unity 5中的复杂数据结构。 JSON序列化使您可以将复杂的数据结构转换为可以轻松保存和加载的字符串格式。二进制格式是一种更安全的方法,可将您的数据转换为二进制格式。 >我可以在Unity 5中的不同平台上保存和加载数据吗?跨平台兼容性。例如,playerpRefs是跨平台兼容的,但是二进制格式可能并非在所有平台上兼容。
>我如何在Unity 5?
中加载问题的问题对问题进行故障排除。可以通过检查脚本的执行顺序,确保您的数据正确序列化或格式化并进行测试,从而在Unity 5中保存和加载功能。您打算在平台上发布的游戏。可以通过最大程度地减少保存和加载的数据量,使用有效的数据结构,并确保您的脚本被良好地实现。
如何实现Unity中的AutoSave功能5?
可以通过创建一个脚本来自动保存游戏的脚本,以定期或在特定事件中自动保存您的游戏,可以在Unity 5中实现AutoSave功能。该脚本应使用相同的方法来保存数据与您的手动保存系统。
>
以上是掌握Unity中的保存和加载功能5的详细内容。更多信息请关注PHP中文网其他相关文章!

中国女性科技力量在AI领域的崛起:荣耀与DeepSeek合作背后的女性故事女性在科技领域的贡献日益显着。中国科技部数据显示,女性科技工作者数量庞大,在AI算法开发中展现出独特的社会价值敏感性。本文将聚焦荣耀手机,探究其率先接入DeepSeek大模型背后的女性团队力量,展现她们如何推动科技进步,重塑科技发展价值坐标系。 2024年2月8日,荣耀正式上线DeepSeek-R1满血版大模型,成为安卓阵营首家接入DeepSeek的厂商,引发用户热烈反响。这一成功背后,女性团队成员在产品决策、技术攻坚和用户

DeepSeek公司在知乎发布技术文章,详细介绍了其DeepSeek-V3/R1推理系统,并首次公开关键财务数据,引发业界关注。文章显示,该系统单日成本利润率高达545%,创下全球AI大模型盈利新高。DeepSeek的低成本策略使其在市场竞争中占据优势。其模型训练成本仅为同类产品的1%-5%,V3模型训练成本仅为557.6万美元,远低于竞争对手。同时,R1的API定价仅为OpenAIo3-mini的1/7至1/2。这些数据证明了DeepSeek技术路线的商业可行性,也为AI大模型的高效盈利树立了

网站建设只是第一步:SEO与反向链接的重要性 建立网站只是将其转化为宝贵营销资产的第一步。您需要进行SEO优化,以提高网站在搜索引擎中的可见度,吸引潜在客户。反向链接是提升网站排名的关键,它向谷歌和其他搜索引擎表明您的网站权威性和可信度。 并非所有反向链接都有利:识别并避免有害链接 并非所有反向链接都有益。有害链接会损害您的排名。优秀的免费反向链接检查工具可以监控链接到您网站的来源,并提醒您注意有害链接。此外,您还可以分析竞争对手的链接策略,从中学习借鉴。 免费反向链接检查工具:您的SEO情报员

美的即将发布搭载DeepSeek大模型的首款空调——美的鲜净感空气机T6,发布会定于3月1日下午1点30分举行。这款空调配备先进的空气智驾系统,可根据环境智能调节温度、湿度和风速等参数。更重要的是,它集成了DeepSeek大模型,支持超过40万条AI语音指令。美的此举引发业界热议,尤其关注白电产品与大模型结合的意义。不同于传统空调简单的温度设定,美的鲜净感空气机T6能够理解更复杂、更模糊的指令,并根据家庭环境智能调节湿度等,显着提升用户体验。

DeepSeek-R1赋能百度文库与网盘:深度思考与行动的完美融合短短一个月内,DeepSeek-R1已迅速融入众多平台。百度凭借大胆的战略布局,将DeepSeek作为第三方模型伙伴,整合进自身生态系统,这标志着其“大模型 搜索”生态战略的重大进展。百度搜索和文心智能体平台率先接入DeepSeek及文心大模型的深度搜索功能,为用户提供免费的AI搜索体验。同时,“百度一下,你就知道”的经典slogan回归,新版百度APP也整合了文心大模型和DeepSeek的能力,推出“AI搜索”、“全网信息提炼”

AI及时工程代码生成:开发人员指南 代码开发的景观有望进行重大转变。 掌握大型语言模型(LLM)和及时工程对于未来几年对开发人员至关重要。 Th

此基于GO的网络漏洞扫描仪有效地确定了潜在的安全弱点。 它利用了GO的并发功能的速度功能,包括服务检测和漏洞匹配。让我们探索它的能力和道德


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

SublimeText3 英文版
推荐:为Win版本,支持代码提示!

适用于 Eclipse 的 SAP NetWeaver 服务器适配器
将Eclipse与SAP NetWeaver应用服务器集成。

WebStorm Mac版
好用的JavaScript开发工具

SublimeText3 Linux新版
SublimeText3 Linux最新版

MinGW - 适用于 Windows 的极简 GNU
这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。