unity save load系统 快速搭建
我的最终目标是快读建立一个关卡数据自动读入储存功能:
1. 每个关卡有自己的编号,如果没有自定义该关卡,则读取默认编号的初始布局,如果有自定义该关卡,则读取新定义的关卡。
2.在游戏中如果对布局做出了更改,随时储存新的修改。
3.save和load系统与玩法系统耦合度低,无需管理。
小试牛刀-soundmanager
先从一个简单的soundmanager开始学习。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;namespace BBG
{public class SaveManager : SingletonComponent<SaveManager>{#region Member Variablesprivate List<ISaveable> saveables;private JSONNode loadedSave;#endregion#region Properties/// <summary>/// Path to the save file on the device/// </summary>public string SaveFilePath { get { return Application.persistentDataPath + "/save.json"; } }/// <summary>/// List of registered saveables/// </summary>private List<ISaveable> Saveables{get{if (saveables == null){saveables = new List<ISaveable>();}return saveables;}}#endregion#if UNITY_EDITOR[UnityEditor.MenuItem("Tools/Bizzy Bee Games/Delete Save Data")]public static void DeleteSaveData(){if (!System.IO.File.Exists(SaveManager.Instance.SaveFilePath)){UnityEditor.EditorUtility.DisplayDialog("Delete Save File", "There is no save file.", "Ok");return;}bool delete = UnityEditor.EditorUtility.DisplayDialog("Delete Save File", "Delete the save file located at " + SaveManager.Instance.SaveFilePath, "Yes", "No");if (delete){System.IO.File.Delete(SaveManager.Instance.SaveFilePath);#if BBG_MT_IAP || BBG_MT_ADSSystem.IO.Directory.Delete(BBG.MobileTools.Utils.SaveFolderPath, true);#endifUnityEditor.EditorUtility.DisplayDialog("Delete Save File", "Save file has been deleted.", "Ok");}}#endif#region Unity Methodsprivate void Start(){Debug.Log("Save file path: " + SaveFilePath);}private void OnDestroy(){Save();}private void OnApplicationPause(bool pause){if (pause){Save();}}#endregion#region Public Methods/// <summary>/// Registers a saveable to be saved/// </summary>public void Register(ISaveable saveable){Saveables.Add(saveable);}/// <summary>/// Loads the save data for the given saveable/// </summary>public JSONNode LoadSave(ISaveable saveable){return LoadSave(saveable.SaveId);}/// <summary>/// Loads the save data for the given save id/// </summary>public JSONNode LoadSave(string saveId){// Check if the save file has been loaded and if not try and load itif (loadedSave == null && !LoadSave(out loadedSave)){return null;}// Check if the loaded save file has the given save idif (!loadedSave.AsObject.HasKey(saveId)){return null;}// Return the JSONNode for the save idreturn loadedSave[saveId];}#endregion#region Private Methods/// <summary>/// Saves all registered saveables to the save file/// </summary>private void Save(){Dictionary<string, object> saveJson = new Dictionary<string, object>();for (int i = 0; i < saveables.Count; i++){saveJson.Add(saveables[i].SaveId, saveables[i].Save());}System.IO.File.WriteAllText(SaveFilePath, Utilities.ConvertToJsonString(saveJson));}/// <summary>/// Tries to load the save file/// </summary>private bool LoadSave(out JSONNode json){json = null;if (!System.IO.File.Exists(SaveFilePath)){return false;}json = JSON.Parse(System.IO.File.ReadAllText(SaveFilePath));return json != null;}#endregion}
}
以上代码中的Register函数很重要,其他的需要储存数据的模块,比如soundmanager,就需要继承Isavable,并且在初始化时register自己给savemanager:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;namespace BBG
{public class SoundManager : SingletonComponent<SoundManager>, ISaveable{#region Classes[System.Serializable]private class SoundInfo{public string id = "";public AudioClip audioClip = null;public SoundType type = SoundType.SoundEffect;public bool playAndLoopOnStart = false;[Range(0, 1)] public float clipVolume = 1;}private class PlayingSound{public SoundInfo soundInfo = null;public AudioSource audioSource = null;}#endregion#region Enumspublic enum SoundType{SoundEffect,Music}#endregion#region Inspector Variables[SerializeField] private List<SoundInfo> soundInfos = null;#endregion#region Member Variablesprivate List<PlayingSound> playingAudioSources;private List<PlayingSound> loopingAudioSources;public string SaveId { get { return "sound_manager"; } }#endregion#region Propertiespublic bool IsMusicOn { get; private set; }public bool IsSoundEffectsOn { get; private set; }#endregion#region Unity Methodsprotected override void Awake(){base.Awake();SaveManager.Instance.Register(this);playingAudioSources = new List<PlayingSound>();loopingAudioSources = new List<PlayingSound>();if (!LoadSave()){IsMusicOn = true;IsSoundEffectsOn = true;}}private void Start(){for (int i = 0; i < soundInfos.Count; i++){SoundInfo soundInfo = soundInfos[i];if (soundInfo.playAndLoopOnStart){Play(soundInfo.id, true, 0);}}}private void Update(){for (int i = 0; i < playingAudioSources.Count; i++){AudioSource audioSource = playingAudioSources[i].audioSource;// If the Audio Source is no longer playing then return it to the pool so it can be re-usedif (!audioSource.isPlaying){Destroy(audioSource.gameObject);playingAudioSources.RemoveAt(i);i--;}}}#endregion#region Public Methods/// <summary>/// Plays the sound with the give id/// </summary>public void Play(string id){Play(id, false, 0);}/// <summary>/// Plays the sound with the give id, if loop is set to true then the sound will only stop if the Stop method is called/// </summary>public void Play(string id, bool loop, float playDelay){SoundInfo soundInfo = GetSoundInfo(id);if (soundInfo == null){Debug.LogError("[SoundManager] There is no Sound Info with the given id: " + id);return;}if ((soundInfo.type == SoundType.Music && !IsMusicOn) ||(soundInfo.type == SoundType.SoundEffect && !IsSoundEffectsOn)){return;}AudioSource audioSource = CreateAudioSource(id);audioSource.clip = soundInfo.audioClip;audioSource.loop = loop;audioSource.time = 0;audioSource.volume = soundInfo.clipVolume;if (playDelay > 0){audioSource.PlayDelayed(playDelay);}else{audioSource.Play();}PlayingSound playingSound = new PlayingSound();playingSound.soundInfo = soundInfo;playingSound.audioSource = audioSource;if (loop){loopingAudioSources.Add(playingSound);}else{playingAudioSources.Add(playingSound);}}/// <summary>/// Stops all playing sounds with the given id/// </summary>public void Stop(string id){StopAllSounds(id, playingAudioSources);StopAllSounds(id, loopingAudioSources);}/// <summary>/// Stops all playing sounds with the given type/// </summary>public void Stop(SoundType type){StopAllSounds(type, playingAudioSources);StopAllSounds(type, loopingAudioSources);}/// <summary>/// Sets the SoundType on/off/// </summary>public void SetSoundTypeOnOff(SoundType type, bool isOn){switch (type){case SoundType.SoundEffect:if (isOn == IsSoundEffectsOn){return;}IsSoundEffectsOn = isOn;break;case SoundType.Music:if (isOn == IsMusicOn){return;}IsMusicOn = isOn;break;}// If it was turned off then stop all sounds that are currently playingif (!isOn){Stop(type);}// Else it was turned on so play any sounds that have playAndLoopOnStart set to trueelse{PlayAtStart(type);}}#endregion#region Private Methods/// <summary>/// Plays all sounds that are set to play on start and loop and are of the given type/// </summary>private void PlayAtStart(SoundType type){for (int i = 0; i < soundInfos.Count; i++){SoundInfo soundInfo = soundInfos[i];if (soundInfo.type == type && soundInfo.playAndLoopOnStart){Play(soundInfo.id, true, 0);}}}/// <summary>/// Stops all sounds with the given id/// </summary>private void StopAllSounds(string id, List<PlayingSound> playingSounds){for (int i = 0; i < playingSounds.Count; i++){PlayingSound playingSound = playingSounds[i];if (id == playingSound.soundInfo.id){playingSound.audioSource.Stop();Destroy(playingSound.audioSource.gameObject);playingSounds.RemoveAt(i);i--;}}}/// <summary>/// Stops all sounds with the given type/// </summary>private void StopAllSounds(SoundType type, List<PlayingSound> playingSounds){for (int i = 0; i < playingSounds.Count; i++){PlayingSound playingSound = playingSounds[i];if (type == playingSound.soundInfo.type){playingSound.audioSource.Stop();Destroy(playingSound.audioSource.gameObject);playingSounds.RemoveAt(i);i--;}}}private SoundInfo GetSoundInfo(string id){for (int i = 0; i < soundInfos.Count; i++){if (id == soundInfos[i].id){return soundInfos[i];}}return null;}private AudioSource CreateAudioSource(string id){GameObject obj = new GameObject("sound_" + id);obj.transform.SetParent(transform);return obj.AddComponent<AudioSource>();;}#endregion#region Save Methodspublic Dictionary<string, object> Save(){Dictionary<string, object> json = new Dictionary<string, object>();json["is_music_on"] = IsMusicOn;json["is_sound_effects_on"] = IsSoundEffectsOn;return json;}public bool LoadSave(){JSONNode json = SaveManager.Instance.LoadSave(this);if (json == null){return false;}IsMusicOn = json["is_music_on"].AsBool;IsSoundEffectsOn = json["is_sound_effects_on"].AsBool;return true;}#endregion}
}
如上所述的soundmanager,里面有两个内容是告知savemanager如何自动储存信息的
public string SaveId { get { return "sound_manager"; } } public Dictionary<string, object> Save(){Dictionary<string, object> json = new Dictionary<string, object>();json["is_music_on"] = IsMusicOn;json["is_sound_effects_on"] = IsSoundEffectsOn;return json;}
另外,观察soundmanager可知,它在初始化时,去做了一次loadsave函数,也就是去找savemanager要数据,如果要到了,怎样设置,如果没有要到,怎样设置。
public bool LoadSave(){JSONNode json = SaveManager.Instance.LoadSave(this);if (json == null){return false;}IsMusicOn = json["is_music_on"].AsBool;IsSoundEffectsOn = json["is_sound_effects_on"].AsBool;return true;}
实战—关卡储存管理
先看一下这个gamemanager中与储存相关的代码
public Dictionary<string, object> Save(){Dictionary<string, object> json = new Dictionary<string, object>();json["num_stars_earned"] = SaveNumStarsEarned();json["last_completed"] = SaveLastCompleteLevels();json["level_statuses"] = SaveLevelStatuses();json["level_save_datas"] = SaveLevelDatas();json["star_amount"] = StarAmount;json["hint_amount"] = HintAmount;json["num_levels_till_ad"] = NumLevelsTillAd;return json;}private List<object> SaveNumStarsEarned(){List<object> json = new List<object>();foreach (KeyValuePair<string, int> pair in packNumStarsEarned){Dictionary<string, object> packJson = new Dictionary<string, object>();packJson["pack_id"] = pair.Key;packJson["num_stars_earned"] = pair.Value;json.Add(packJson);}return json;}private List<object> SaveLastCompleteLevels(){List<object> json = new List<object>();foreach (KeyValuePair<string, int> pair in packLastCompletedLevel){Dictionary<string, object> packJson = new Dictionary<string, object>();packJson["pack_id"] = pair.Key;packJson["last_completed_level"] = pair.Value;json.Add(packJson);}return json;}private List<object> SaveLevelStatuses(){List<object> json = new List<object>();foreach (KeyValuePair<string, Dictionary<int, int>> pair in packLevelStatuses){Dictionary<string, object> packJson = new Dictionary<string, object>();packJson["pack_id"] = pair.Key;string levelStr = "";foreach (KeyValuePair<int, int> levelPair in pair.Value){if (!string.IsNullOrEmpty(levelStr)) levelStr += "_";levelStr += levelPair.Key + "_" + levelPair.Value;}packJson["level_statuses"] = levelStr;json.Add(packJson);}return json;}private List<object> SaveLevelDatas(){List<object> savedLevelDatas = new List<object>();foreach (KeyValuePair<string, LevelSaveData> pair in levelSaveDatas){Dictionary<string, object> levelSaveDataJson = pair.Value.Save();levelSaveDataJson["id"] = pair.Key;savedLevelDatas.Add(levelSaveDataJson);}return savedLevelDatas;}private bool LoadSave(){JSONNode json = SaveManager.Instance.LoadSave(this);if (json == null){return false;}LoadNumStarsEarned(json["num_stars_earned"].AsArray);LoadLastCompleteLevels(json["last_completed"].AsArray);LoadLevelStatuses(json["level_statuses"].AsArray);LoadLevelSaveDatas(json["level_save_datas"].AsArray);StarAmount = json["star_amount"].AsInt;HintAmount = json["hint_amount"].AsInt;NumLevelsTillAd = json["num_levels_till_ad"].AsInt;return true;}private void LoadNumStarsEarned(JSONArray json){for (int i = 0; i < json.Count; i++){JSONNode childJson = json[i];string packId = childJson["pack_id"].Value;int numStarsEarned = childJson["num_stars_earned"].AsInt;packNumStarsEarned.Add(packId, numStarsEarned);}}private void LoadLastCompleteLevels(JSONArray json){for (int i = 0; i < json.Count; i++){JSONNode childJson = json[i];string packId = childJson["pack_id"].Value;int lastCompletedLevel = childJson["last_completed_level"].AsInt;packLastCompletedLevel.Add(packId, lastCompletedLevel);}}private void LoadLevelStatuses(JSONArray json){for (int i = 0; i < json.Count; i++){JSONNode childJson = json[i];string packId = childJson["pack_id"].Value;string[] levelStatusStrs = childJson["level_statuses"].Value.Split('_');Dictionary<int, int> levelStatuses = new Dictionary<int, int>();for (int j = 0; j < levelStatusStrs.Length; j += 2){int levelIndex = System.Convert.ToInt32(levelStatusStrs[j]);int status = System.Convert.ToInt32(levelStatusStrs[j + 1]);levelStatuses.Add(levelIndex, status);}packLevelStatuses.Add(packId, levelStatuses);}}/// <summary>/// Loads the game from the saved json file/// </summary>private void LoadLevelSaveDatas(JSONArray savedLevelDatasJson){// Load all the placed line segments for levels that have progressfor (int i = 0; i < savedLevelDatasJson.Count; i++){JSONNode savedLevelDataJson = savedLevelDatasJson[i];JSONArray savedPlacedLineSegments = savedLevelDataJson["placed_line_segments"].AsArray;JSONArray savedHints = savedLevelDataJson["hints"].AsArray;List<List<CellPos>> placedLineSegments = new List<List<CellPos>>();for (int j = 0; j < savedPlacedLineSegments.Count; j++){placedLineSegments.Add(new List<CellPos>());for (int k = 0; k < savedPlacedLineSegments[j].Count; k += 2){placedLineSegments[j].Add(new CellPos(savedPlacedLineSegments[j][k].AsInt, savedPlacedLineSegments[j][k + 1].AsInt));}}List<int> hintLineIndices = new List<int>();for (int j = 0; j < savedHints.Count; j++){hintLineIndices.Add(savedHints[j].AsInt);}string levelId = savedLevelDataJson["id"].Value;int numMoves = savedLevelDataJson["num_moves"].AsInt;LevelSaveData levelSaveData = new LevelSaveData();levelSaveData.placedLineSegments = placedLineSegments;levelSaveData.numMoves = numMoves;levelSaveData.hintLineIndices = hintLineIndices;levelSaveDatas.Add(levelId, levelSaveData);}}#endregion
我们发现,因为数据较为复杂,无论是load还是save,都针对不同数据有自己的辅助函数。
gamemanager中,有一个startlevel,它需要一个packinfo(总关卡信息,可暂时忽略),以及一个leveldata.
这里拿到的leveldata,是制作者本身就默认写好的值,
如果这个leveldata的id,已经存在于levelsavedata的字典中,就说明这个leveldata经过了修改,因此要读取的是新的levelsavedata中的配置数据。
如果这个leveldata的id没有存在于levelsavedata的字典中,就说明这次是第一次打开这个level,那么需要新建一个savedata:
下面的代码记录了这个功能。
/// <summary>/// Starts the level./// </summary>public void StartLevel(PackInfo packInfo, LevelData levelData){ActivePackInfo = packInfo;ActiveLevelData = levelData;// Check if the lvel has not been started and if there is loaded save data for itif (!levelSaveDatas.ContainsKey(levelData.Id)){levelSaveDatas[levelData.Id] = new LevelSaveData();}gameGrid.SetupLevel(levelData, levelSaveDatas[levelData.Id]);UpdateHintAmountText();UpdateLevelButtons();GameEventManager.Instance.SendEvent(GameEventManager.EventId_LevelStarted);ScreenManager.Instance.Show("game");// Check if it's time to show an interstitial adif (NumLevelsTillAd <= 0){NumLevelsTillAd = numLevelsBetweenAds;#if BBG_MT_ADSBBG.MobileTools.MobileAdsManager.Instance.ShowInterstitialAd();#endif}}
其他的功能基本上和soundmanager一样:
比如,在初始化时,注册自己,并试图loadsave.
比如,在游戏中断时,进行保存
protected override void Awake(){base.Awake();GameEventManager.Instance.RegisterEventHandler(GameEventManager.EventId_ActiveLevelCompleted, OnActiveLevelComplete);SaveManager.Instance.Register(this);packNumStarsEarned = new Dictionary<string, int>();packLastCompletedLevel = new Dictionary<string, int>();packLevelStatuses = new Dictionary<string, Dictionary<int, int>>();levelSaveDatas = new Dictionary<string, LevelSaveData>();if (!LoadSave()){HintAmount = startingHints;NumLevelsTillAd = numLevelsBetweenAds;}gameGrid.Initialize();if (startingStars > 0){StarAmount = startingStars;}}private void OnDestroy(){Save();}private void OnApplicationPause(bool pause){if (pause){Save();}}
至此,重点结束。
然后,其他功能的脚本,可以通过获得currentlevelsavedata的方式去修改其数据,方便在关闭界面时进行数据更新。
/// <summary>/// Sets the numMoves and updates the Text UI/// </summary>private void SetNumMoves(int amount){currentLevelSaveData.numMoves = amount;moveAmountText.text = currentLevelSaveData.numMoves.ToString();}
围绕这个功能,还可以方便设计undo/redo功能
相关文章:
unity save load系统 快速搭建
我的最终目标是快读建立一个关卡数据自动读入储存功能: 1. 每个关卡有自己的编号,如果没有自定义该关卡,则读取默认编号的初始布局,如果有自定义该关卡,则读取新定义的关卡。 2.在游戏中如果对布局做出了更改&#x…...
前端 TS 快速入门之四:函数
1. 为函数定义类型 一般使用中,我们可以不必完整写出函数类型,因为 TypeScript 会为我们自动推断出类型,需要注意的是:类型中的参数名称可以不必和值中的参数名称匹配,只要它们类型是兼容的便可。 // 书写完成函数类型…...
Linux 救援模式
Linux突然坏了 第三次坏了 第一次是找不到盘,修复好了 第二次是找不到卷,但是能启动,启动界面选择救援模式,可以正常使用 第三次,尝试修复卷,启动后,找不到文件系统了,只能从光盘…...
HOT100自查题集
前言 遗忘是人类的本能,经常自查是必不可少的。现在面试手撕已经是必备项目,手撕题目大部分来自于力扣 HOT100 以及对应方向的一些编码题。我对 HOT 100 的题目以及Java相关的一些编码题目进行了分类整理,并提供解题思路,用作复习…...
Post-Process1-水下
一、新建第三人称游戏项目,我这里选择C,你也可以选择Blueprint。 新建一个Level,命名为DemoUnderWater 保存一下,命名为DownUnderWater 添加水插件 选择Yes 勾选Show Engine Content和Show Plugin Content,在左侧可以看…...
通过pipeline配置sonar自动化实现过程解析
这篇文章主要介绍了通过pipeline配置sonar自动化实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.sonar配置webhooks, 2.url填写jenkins的地址:http://jenkinsurl/sonarqu…...
Spring framework Day 23:容器事件
前言 容器事件是 Spring Framework 中的一个重要概念,它提供了一种机制,使我们能够更好地了解和响应 Spring 容器中发生的各种事件。通过容器事件,我们可以在特定的时间点监听和处理容器中的各种状态变化、操作和事件触发,以实现…...
【Dockerfile】基于openjdk镜像添加常用命令工具
背景 官方openjdk镜像是Red Hat操作系统,可以使用microdnf命令安装 bash-4.4# cat /etc/redhat-release Red Hat Enterprise Linux release 8.5 (Ootpa)拉取openjdk镜像 docker pull openjdk:17.0.2 编写Dockerfile touch Dockerfile-openjdk-utilsFROM openjd…...
目录内图片转PDF(多图片打印助手)
最近同事经常找我帮她打印试卷,很奇葩的是,她的试卷都是*红书上下载的图片,一张张打印不好看,而且可能打印不完全,大小也不协调,所以有了这个脚本。 【需要给小孩打印图片格式的试卷时也比较实用】 -----…...
React TypeScript安装npm第三方包时,些包并不是 TypeScript 编写的
npm install types/包名称 例如:npm install types/jquery 学习链接...
Java 基础 面试 多线程
1.多线程 1.1 线程(Thread) 线程时一个程序内部的一条执行流程,java的main方法就是由一条默认的主线程执行 1.2 多线程 多线程是指从软硬件上实现的多条执行流程的技术(多条线程由CPU负责调度执行) 许多平台都离不开多…...
软考 系统架构设计师系列知识点之软件构件(1)
所属章节: 第2章. 计算机系统基础知识 第3节. 计算机软件 2.3.7 软件构件 1. 概述 构件又称为组件,是一个自包容、可复用的程序集。构建是一个程序集、或者说是一组程序的集合。这个集合可能会以各种方式体现出来,如源程序或二进制代码。这…...
RHCE8 资料整理(三)
RHCE8 资料整理 第三篇 网络相关配置第11章 网络配置11.1 网络基础知识11.2 查看网络信息11.3 图形化界面修改11.4 通过配置文件修改11.5 命令行管理11.6 主机名的设置 第12章 ssh12.1 ssh基本用法12.2 打开远程图形化界面12.3 ssh无密码登录12.4 ssh安全设置12.5 ssh限制用户1…...
C++ 使用httplib库,发送HTTP请求
简介 C 使用httplib库,发送HTTP请求 接口信息 ip地址 192.168.16.166 端口 8899 接口地址/abc/tk 请求方式GET 响应内容: { “result”: true, “message”: “”, “tk”: “yueguangsaxialexiangshuitan0ihai”, “datetimeout”: “2023-10-22 21…...
OceanBase自动安装部署演示环境demo
OceanBase自动安装部署 前提条件 官方给出硬件条件需要满足以下要求 本文操作系统为:Red Hat Enterprise Linux 8 64 位 下载链接:https://pan.baidu.com/s/1rZ39xJFhk0HdmC4wEJcxvg 提取码:c01x 下载并安装 all-in-one 安装包 执行如下…...
windows系统mysql服务启动失败
原因 电脑重启navicat连接mysql失败,在电脑-管理-服务没有mysql服务 解决方案 找到mysql的安装目录进入bin目录 执行mysqld --install 进行重新安装 提示服务安装成功 net start mysql mysql 启动成功 ...
B-tree(PostgreSQL 14 Internals翻译版)
概览 B树(作为B树访问方法实现)是一种数据结构,它使您能够通过从树的根向下查找树的叶节点中所需的元素。为了明确地标识搜索路径,必须对所有树元素进行排序。B树是为有序数据类型设计的,这些数据类型的值可以进行比较和排序。 下面的机场代…...
React环境初始化
环境初始化 学习目标: 能够独立使用React脚手架创建一个React项目 1.使用脚手架创建项目 官方文档:(https://create-react-app.bootcss.com/) - 打开命令行窗口 - 执行命令 npx create-react-app projectName 说明:…...
Hadoop3教程(二十八):(生产调优篇)NN、DN的多目录配置及磁盘间数据均衡
文章目录 (148)NN多目录配置(149)DataNode多目录配置及磁盘间数据平衡磁盘间数据均衡 参考文献 (148)NN多目录配置 NN多目录的意思是,本地目录可以配置成多个,且每个目录存放内容相…...
博客续更(五)
十一、后台模块-菜单列表 菜单指的是权限菜单,也就是一堆权限字符串 1. 查询菜单 1.1 接口分析 需要展示菜单列表,不需要分页。可以针对菜单名进行模糊查询。也可以针对菜单的状态进行查询。菜单要按照父菜单id和orderNum进行排序 请求方式 请求路径…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
