【Unity】QFramework通用背包系统优化:使用Odin优化编辑器
前言
在学习凉鞋老师的课程《QFramework系统设计:通用背包系统》第四章时,笔者使用了Odin插件,对Item和ItemDatabase的SO文件进行了一些优化,使物品页面更加紧凑、更易拓展。
核心逻辑和功能没有改动,整体代码量减少了,并且增加了一个复制ItemConfig的小功能。
需要注意:
- 在ItemConfigGroup的列表中中删除ItemConfig时,应该点红色的X按钮,不要点最右侧的叉号,不然关联的ItemConfig SO文件不会被同时删除;
- QFramework带有的自定义属性功能可能会和Odin冲突,建议只使用其中一种;
为了和原教程区分,下文将使用ItemConfig和ItemConfigGroup类来代替Item和ItemDatabase类。
修改前后对比:

代码
IItem接口:
using UnityEngine;namespace QFramework
{public interface IItem{string GetKey { get; }string GetName { get; }string GetDescription { get; }Sprite GetIcon { get; }bool GetStackable { get; }bool GetHasMaxStackableCount { get; }int GetMaxStackableCount { get; }ItemLanguagePackage.LocalItem LocalItem { get; set; }bool GetBoolean(string propertyName);}
}
ItemConfig类:
using Sirenix.OdinInspector;
using UnityEditor;
using UnityEngine;namespace QFramework
{[CreateAssetMenu(menuName = "@ItemKit/Create ItemConfig")]public class ItemConfig : ScriptableObject, IItem{public ItemConfigGroup ItemConfigGroup { get; set; }[HideLabel][PreviewField(48, ObjectFieldAlignment.Left)][HorizontalGroup("名称类型", 54), VerticalGroup("名称类型/left")]public Sprite Icon = null;private void OnValidate(){this.name = Key;}[VerticalGroup("名称类型/left")][Button("X"), GUIColor(1, 0, 0)]private void RemoveThisConfig(){if (EditorUtility.DisplayDialog("删除物品", "确定要删除吗?\n(此操作不可恢复)", "删除", "取消")){ItemConfigGroup.ItemConfigs.Remove(this);AssetDatabase.RemoveObjectFromAsset(this);AssetDatabase.SaveAssets();AssetDatabase.Refresh();}}[VerticalGroup("名称类型/left")][Button("Dup"), GUIColor("yellow")]private void DuplicateThisConfig() // 增加复制/插入功能{if (ItemConfigGroup == null){Debug.LogError("ItemConfigGroup is null!");return;}ItemConfigGroup.DuplicateItemConfig(ItemConfigGroup.ItemConfigs.IndexOf(this), this);}[VerticalGroup("名称类型/right"), LabelWidth(42)][LabelText("名称")]public string Name = string.Empty;[VerticalGroup("名称类型/right"), LabelWidth(42)][LabelText("描述")][TextArea(minLines: 1, maxLines: 4)]public string Description = string.Empty;[VerticalGroup("名称类型/right"), LabelWidth(42)][LabelText("关键字")]public string Key = string.Empty;[VerticalGroup("名称类型/right"), LabelWidth(42)][LabelText("是武器")]public bool IsWeapon = false;[HorizontalGroup("属性")][VerticalGroup("属性/stackable"), LabelWidth(66)][LabelText("可堆叠")]public bool IsStackable = true;[ShowIf("IsStackable")][VerticalGroup("属性/stackable"), LabelWidth(66)][Indent][LabelText("有最大值")]public bool HasMaxStackableCount = false;[ShowIf("IsStackable"), EnableIf("HasMaxStackableCount")][DisplayIf(new string[] { "IsStackable", "HasMaxStackableCount" }, new[] { false, false })][VerticalGroup("属性/stackable"), LabelWidth(66)][Indent(2)][LabelText("最大值")]public int MaxStackableCount = 99;public string GetName => ItemKit.CurrentLanguage == ItemKit.DefaultLanguage ? Name : LocalItem.Name;public string GetKey => Key;public string GetDescription => ItemKit.CurrentLanguage == ItemKit.DefaultLanguage ? Description : LocalItem.Description;public Sprite GetIcon => Icon;public bool GetStackable => IsStackable;public bool GetHasMaxStackableCount => HasMaxStackableCount;public int GetMaxStackableCount => MaxStackableCount;public ItemLanguagePackage.LocalItem LocalItem { get; set; }public bool GetBoolean(string propertyName){if (propertyName == "IsWeapon"){return IsWeapon;}return false;}}
}
ItemConfigGroup类:
using Sirenix.OdinInspector;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System;
using UnityEditor;namespace QFramework
{[CreateAssetMenu(menuName = "@ItemKit/Create Item ConfigGroup")]public class ItemConfigGroup : ScriptableObject{public string NameSpace = "QFramework.Example";[Searchable][TableList(ShowIndexLabels = true)]public List<ItemConfig> ItemConfigs = new List<ItemConfig>();[Button("添加 ItemConfig", ButtonSizes.Large), GUIColor("yellow")]private void AddItemConfig(){// 创建一个新的 ItemConfig 实例ItemConfig itemConfig = CreateInstance<ItemConfig>();itemConfig.ItemConfigGroup = this;itemConfig.name = nameof(ItemConfig);itemConfig.Name = "新物品";itemConfig.Key = "item_new";// 将新创建的 itemConfig 添加到 ItemConfigGroup 的资源中AssetDatabase.AddObjectToAsset(itemConfig, this);// 在 ItemConfigs 列表中添加一个新的元素ItemConfigs.Add(itemConfig);// 保存所有更改到资源AssetDatabase.SaveAssets();// 刷新资源AssetDatabase.Refresh();}public void DuplicateItemConfig(int index, ItemConfig itemConfig){// 创建一个新的 ItemConfig 实例ItemConfig itemConfigSO = CreateInstance<ItemConfig>();itemConfigSO.ItemConfigGroup = this;itemConfigSO.name = itemConfig.Key;itemConfigSO.Name = string.Empty;itemConfigSO.Key = "item_new";itemConfigSO.IsWeapon = itemConfig.IsWeapon;itemConfigSO.IsStackable = itemConfig.IsStackable;itemConfigSO.HasMaxStackableCount = itemConfig.HasMaxStackableCount;itemConfigSO.MaxStackableCount = itemConfig.MaxStackableCount;// 将新创建的 itemConfig 添加到 ItemConfigGroup 的资源文件中AssetDatabase.AddObjectToAsset(itemConfigSO, this);// 在 ItemConfigs 列表中添加一个新的元素ItemConfigs.Insert(index + 1, itemConfigSO);// 保存所有更改到资源AssetDatabase.SaveAssets();// 刷新资源AssetDatabase.Refresh();}[Button("生成 Items 代码", ButtonSizes.Large), GUIColor("green")]private void GenerateCode(){var itemDatabase = this;// 获取当前 ItemDatabase 脚本的文件路径,并确定生成代码的保存位置string filePath = AssetDatabase.GetAssetPath(itemDatabase).GetFolderPath() + "/Items.cs";// 使用 QFramework 中的代码生成功能// 创建一个代码作用域树,用于生成代码结构ICodeScope rootCode = new RootCode()// 添加命名空间.Using("UnityEngine").Using("QFramework")// 空一行.EmptyLine()// 定义命名空间.Namespace(itemDatabase.NameSpace, ns =>{// 在命名空间中定义一个类ns.Class("Items", String.Empty, false, false, c =>{// 为每个 itemDB.ItemConfigs 生成一个静态字符串字段foreach (ItemConfig itemConfig in itemDatabase.ItemConfigs){c.Custom($"public static string {itemConfig.Key} = \"{itemConfig.Key}\";");Debug.Log(itemConfig.Key);}});});// 创建或覆盖文件,并准备写入生成的代码// 使用 using 语句自动管理 StreamWriter 的生命周期。// 当离开 using 代码块的作用域时,fileWriter 的 Dispose 方法会被自动调用,确保文件资源被正确关闭。using StreamWriter fileWriter = File.CreateText(filePath);// 创建一个代码写入器,将代码作用域树转换为字符串FileCodeWriter codeWriter = new FileCodeWriter(fileWriter);// 生成代码并写入文件rootCode.Gen(codeWriter);// 保存所有未保存的资源更改AssetDatabase.SaveAssets();// 刷新 Unity 编辑器的资源数据库AssetDatabase.Refresh();}private void OnValidate(){foreach (ItemConfig itemConfig in ItemConfigs){if (itemConfig != null){itemConfig.name = itemConfig.Key;}elseItemConfigs.Remove(itemConfig);}}}
}
相关文章:
【Unity】QFramework通用背包系统优化:使用Odin优化编辑器
前言 在学习凉鞋老师的课程《QFramework系统设计:通用背包系统》第四章时,笔者使用了Odin插件,对Item和ItemDatabase的SO文件进行了一些优化,使物品页面更加紧凑、更易拓展。 核心逻辑和功能没有改动,整体代码量减少…...
基本算法--贪心
1.简述 贪心法的效率非常高,复杂度常常为O(1),是一种局部最优的解题方法,而很多问题都需要求全局最优,,所以在使用贪心法之前需要评估是否能从局部最优推广到全局最优。 2.思路 作为算法的贪…...
13. 串口接收模块的项目应用案例
1. 使用串口来控制LED灯工作状态 使用串口发送指令到FPGA开发板,来控制第7课中第4个实验的开发板上的LED灯的工作状态。 LED灯的工作状态:让LED灯按指定的亮灭模式亮灭,亮灭模式未知,由用户指定,8个变化状态为一个循…...
Python re找到特定pattern并将此pattern重复n次
要找到字符串s中的数字,并将这些数字重复3次: import re s "abc123def456ghi789" # 找到所有的数字 numbers re.findall(r\d, s) # 重复每个数字3次 repeated_numbers [num * 3 for num in numbers] # 将重复的数字放回原位置 #…...
ChatGpt报错:We ran into an issue while authenticating you解决办法
在登录ChatGpt时报错:Oops!,We ran into an issue while authenticating you.(我们在验证您时遇到问题),记录一下解决过程。 完整报错: We ran into an issue while authenticating you. If this issue persists, please contact…...
如何从 iPhone 恢复已删除的视频:简单有效方法
无论您是在尝试释放空间时不小心删除了 iPhone 上的视频,还是在出厂时清空了手机,现在所有数据都消失了,都不要放弃。有一些方法可以恢复这些视频。 在本文中,我们将向您展示六种最有效的数据恢复方法,可以帮助您从 i…...
【python量化交易】qteasy使用教程02 - 获取和管理金融数据
qteasy教程2 - 获取并管理金融数据 qteasy教程2 - 获取并管理金融数据开始前的准备工作获取基础数据以及价格数据下载交易日历和基础数据查看股票和指数的基础数据下载沪市股票数据从本地获取股价数据生成K线图 数据类型的查找定期下载数据到本地回顾总结 qteasy教程2 - 获取并…...
数据库学习案例20240206-ORACLE NEW RAC agent and resource关系汇总。
1 集群架构图 整体集群架构图如下: 1 数据库启动顺序OHASD层面 操作系统进程init.ohasd run启动ohasd.bin init.ohasd run 集群自动启动是否被禁用 crsctl enable has/crsGIHOME所在文件系统是否被正常挂载。管道文件npohasd是否能够被访问, cd /var/t…...
TypeScript 入门
课程地址 ts 开发环境搭建 npm i -g typescript查看安装位置: $ npm root -g C:\Users\Daniel\AppData\Roaming\npm\node_modules创建 hello.ts: console.log("hello, ts");编译 ts 文件,得到 js 文件: $ tsc foo.…...
linux 磁盘相关操作
1.U盘接入虚拟机 (1)在插入u盘时,虚拟机会检测usb设备,在弹出窗口选择连接到虚拟机即可。 (2)或 直接在虚拟机--->可移动设备--->找到U盘---->连接 2.检测U盘是否被虚拟机识别 ls /dev/sd* 查…...
PyTorch: torch.max()函数详解
torch.max函数详解:基于PyTorch的深入探索 🌵文章目录🌵 🌳引言🌳🌳torch.max()函数简介🌳🌳torch.max()的返回值🌳🌳torch.max()的应用示例🌳&am…...
Rust基础拾遗--核心功能
Rust基础拾遗 前言1.所有权与移动1.1 所有权 2.引用3.特型与泛型简介3.1 使用特型3.2 特型对象3.3 泛型函数与类型参数 4.实用工具特型5.闭包 前言 通过Rust程序设计-第二版笔记的形式对Rust相关重点知识进行汇总,读者通读此系列文章就可以轻松的把该语言基础捡起来…...
MySQL:常用指令
MySQL官网 一、在Windows 系统 cmd窗口里执行的命令 启动:net start MySQL停止:net stop MySQL卸载:sc delete MySQL 二、在macOS系统终端里执行的命令 启动:mysql.server start停止:mysql.server stop重启:mysql.server restart 三、执行帮…...
Scrapy:Python中强大的网络爬虫框架
Scrapy:Python中强大的网络爬虫框架 在当今信息爆炸的时代,从互联网上获取数据已经成为许多应用程序的核心需求。Scrapy是一款基于Python的强大网络爬虫框架,它提供了一种灵活且高效的方式来提取、处理和存储互联网上的数据。本文将介绍Scrap…...
linux系统非关系型数据库redis的配置文件
redis配置文件 Redis的配置文件位于Redis安装目录下,文件名为redis.conf,配置项说明如下 Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程 daemonize no当Redis以守护进程方式运行时,Red…...
电力负荷预测 | 基于LSTM、TCN的电力负荷预测(Python)
文章目录 效果一览文章概述源码设计参考资料效果一览 文章概述 电力负荷预测 | 基于LSTM、TCN的电力负荷预测(Python) 源码设计 #------------------...
Java+SpringBoot实习管理系统探秘
✍✍计算机编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡ Java实战 |…...
c入门第十六篇——学生成绩管理系统
师弟:“师兄,我最近构建了一个学生成绩管理系统,有空试用一下么?” 我:“好啊!” 一个简单的学生成绩管理系统,基本功能包括:添加学生信息、显示所有学生信息、按学号查找学生信息、…...
大文件上传如何做断点续传?
文章目录 一、是什么分片上传断点续传 二、实现思路三、使用场景小结 参考文献 一、是什么 不管怎样简单的需求,在量级达到一定层次时,都会变得异常复杂 文件上传简单,文件变大就复杂 上传大文件时,以下几个变量会影响我们的用…...
SpringCloud-Eureka原理分析
Eureka是Netflix开源的一款用于实现服务注册与发现的工具。在微服务架构中,服务的动态注册和发现是必不可少的组成部分,而Eureka正是为了解决这一问题而诞生的。 一、为何需要Eureka 在微服务架构中,服务之间的协同合作和高效通信是至关重要…...
3大核心策略:构建高效抖音内容采集系统的技术实践
3大核心策略:构建高效抖音内容采集系统的技术实践 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback support. …...
UE5新手避坑:用C++实现关卡切换和字符串处理,别再复制粘贴了
UE5 C实战避坑指南:关卡切换与字符串处理的高效实践 刚接触UE5 C开发的程序员们,是否经常遇到关卡切换不生效、字符串比较结果诡异、GetAllActorsOfClass导致性能骤降等问题?本文将深入剖析这些典型陷阱,带你从底层机制理解正确做…...
解锁开源工具QMK Toolbox:完全掌握机械键盘个性化定制
解锁开源工具QMK Toolbox:完全掌握机械键盘个性化定制 【免费下载链接】qmk_toolbox A Toolbox companion for QMK Firmware 项目地址: https://gitcode.com/gh_mirrors/qm/qmk_toolbox QMK Toolbox是一款开源的设备管理工具,专为QMK固件设计&…...
南京大学发布“视频侦探“系统:让AI像侦探一样从长视频中找线索
这项由南京大学与中科院自动化所联合进行的研究发表于2026年的计算机视觉与模式识别(CVPR)会议,论文编号为arXiv:2603.22285。有兴趣深入了解的读者可以通过该编号查询完整论文内容。当我们观看一部两小时的电影时,想要回答"主角在什么时候第一次露…...
Llama-3.2V-11B-cot实战:基于SpringBoot构建企业级智能客服原型
Llama-3.2V-11B-cot实战:基于SpringBoot构建企业级智能客服原型 最近在帮一个朋友的公司做技术选型,他们想快速搭建一个智能客服原型,既要成本可控,又要能快速集成到现有的Java技术栈里。聊了一圈,发现很多团队都卡在…...
HDSceneColor节点]原理解析与实际应用
渲染管线兼容性详解HD Scene Color节点的可用性完全取决于所使用的渲染管线,这是开发者在选择和使用该节点时必须首先考虑的因素。高清渲染管线(HDRP)支持HDRP是Unity针对高端平台和高端硬件设计的高保真渲染解决方案HD Scene Color节点专为H…...
VISA 标准深度剖析:寄存器基控制规范与函数接口研究
VISA 标准深度剖析:寄存器基控制规范与函数接口研究 VISA(Virtual Instrument Software Architecture)是仪器控制领域的标准 API,它为不同总线(GPIB、USB、LAN、PXI 等)提供了统一的编程接口。本文将 VISA 函数按功能分为 8 大类,并逐一解析其作用、核心函数及使用场景…...
论文详解 | 基于轨迹数据的多层空间交互网络动态社区发现与时序分析
论文详解 | 基于轨迹数据的多层空间交互网络动态社区发现与时序分析 一、论文基础信息与核心概述 1.1 论文基础信息 项目 详情 论文标题 Dynamical community detection and spatiotemporal analysis in multilayer spatial interaction networks using trajectory data 1.2 …...
SpringBoot + MongoDB 5分钟快速集成:从0到1实操指南
目录 MongoDB 快速集成 常用API MongoDB MongoDB 是一个基于分布式文件存储的文档型数据库,属于 NoSQL 数据库中最接近关系型数据库的产品,旨在为 Web 应用提供高性能、高可用和可扩展的数据存储解决方案 。 MongoDB以灵活的无模式文档模型…...
CLIP 实战宝典:从零开始掌握文本与图像编码的终极技巧
1. CLIP模型基础入门:图文匹配的魔法钥匙 第一次听说CLIP模型时,我正被一个图像搜索项目折磨得焦头烂额。传统方法需要先标注海量数据,再训练复杂的分类器,整个过程就像用算盘计算火箭轨道。直到遇见CLIP,才发现原来图…...
