Unity数据持久化之PlayerPrefs
这里写目录标题
- PlayerPrefs概述
- 基本方法
- PlayerPrefs存储位置
- 实践小项目
- 反射知识补充
- 数据管理类的创建
- 反射存储数据----常用成员
- 反射存储数据----List成员
- 反射存储数据----Dictionary成员
- 反射存储数据----自定义类成员
- 反射读取数据----常用成员
- 反射读取数据----List成员
- 反射读取数据----Dic成员
- 反射读取数据----自定义类成员
- 加密思路
- 生成资源包
PlayerPrefs概述
什么是数据持久化
数据持久化就是将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中的数据模型的统称。即将游戏数据存储到硬盘,硬盘中数据读取到游戏中,也就是传统意义上的存盘。
基本方法
PlayerPrefs是什么
是Unity
提供的可以用于存储读取玩家数据的公共类
存储相关
PlayerPrefs
的数据存储,类似于键值对存储,一个键对应一个值。
提供了存储3中数据的方法:int、float、string
键:string
类型
值:int float string
对应3种API
PlayerPrefs.SetInt("myAge",18);
PlayerPrefs.SetString("myHeight",177f);
PlayerPrefs.SetString("myName","123");
直接调用Set相关方法,只会把数据存到内存里。当游戏结束时,Unity会自动把数据存到硬盘中。如果游戏不是正常结束的,而是崩溃,数据是不会存到硬盘中的。
只要调用该方法,就会马上存储到硬盘中。
PlayerPrefs.Save();
PlayerPrefs
是有局限性的,它只能存3种类型的数据,如果想要存储别的类型的数据,只能降低精度,或者上升精度来进行存储。
如果不同类型用同一键名进行存储,会进行覆盖。
PlayerPrefs.SetFloat("myAge",20.2f);
读取相关
运行时,只要使用了Set相关方法设置了对应键值对,即使没有马上存储在Save在本地,也可以读取出信息。
//int
int age = PlayerPrefs.GetInt("myAge");
age = PlayerPrefs.GetInt("myAge",100);
//float
float height = PlayerPrefs.GetFloat("myHeight",1000f);
//string
string name = PlayerPrefs.GetString("myName");
第二个参数默认值的作用就是在得到没有的数据的时候,就可以用它来进行基础数据的初始化。
判断数据是否存在。
if(PlayerPrefs.HasKey("myName"))
{
}
删除数据
删除指定键值对
PlayerPrefs.DeleteKey("myAge");
删除所有存储的信息
PlayerPrefs.DeleteAll();
PlayerPrefs存储位置
PlayerPrefs存储的数据存在哪里?
1、Windows
PlayerPrefs
存储在:HKCU\Software\[公司名称]\[产品名称]项下的注册表中
其中公司和产品名称是在“Project Settings
”中设置的名称
查找过程:
运行regedit
HKEY_CURRENT_USER
SOFTWARE
Unity
UnityEditor
公司名称
产品名称
2、Android
/data/data/包名/shared_prefs/pkg-name.xml
3、IOS
/Library/Preferences/[应用ID].plist
PlayerPrefs数据唯一性
PlayerPrefs中不同数据的唯一性,是由key决定的,不同的key决定了不同的数据。同一项目中,如果不同数据key相同,会造成数据丢失。要保证数据不丢失就要建立一个保证key唯一的规则。
实践小项目
反射知识补充
反射相关内容请移步:C#进阶学习
反射知识回顾
反射3剑客
1、Type
:用于获取类的所有信息、字段、属性、方法等等。
2、Assembly
:用于获取程序集,通过程序集获取Type
。
3、Activator
:用于快速实例化对象。
判断一个类型的对象是否可以让另一个类型为自己分配空间(父类装子类)
public class Father
{
}
public class Son:Father
{
}
Type fatherType = typeof(Father);
Type sonType = typeof(Son);
//判断是否可以通过传入的类型为自己分配空间
if(fatherType.IsAssignableFrom(sonType))
{
}
通过反射获取泛型类型
List<string> list = new List<string>();
Type listType = list.GetType();
Type[] types = listType.GetGenericArguments();//获取到泛型类型stringDictionary<string,float> dic = new Dictionary<string,float>();
Type dicType = dic.GetType();
types = dicType.GetGenericArguments();
数据管理类的创建
//PlayerPrefs数据管理类,统一管理数据的存储和读取
public class PlayerPrefsDataMgr
{private static PlayerPrefsDataMgr instance = new PlayerPrefsDataMgr();public static PlayerPrefsDataMgr Instance{get{return instance;}}private PlayerPrefsDataMgr(){}//私有构造//存储数据public void SaveData(object data,string keyName){}//读取数据public object LoadData(Type type,string keyName){return null;}
}
反射存储数据----常用成员
//存储数据
//通过Type得到传入数据对象的所有的字段,然后结合PlayerPrefs来进行存储
public void SaveData(object data,string keyName)
{//获取传入数据对象的所有字段Type dataType = data.GetType(); FieldInfo[] infos = dataTypes.GetFields();//自己定义一个key规则,进行数据存储//keyName_数据类型_字段类型_字段名//遍历这些字段进行数据存储string saveKeyName = "";FieldInfo info;for(int i = 0;i < infos.Length;i++){//字段的类型:info.FieldType.Name//字段的名字:info.Nameinfo = infos[i];//得到具体的字段信息saveKeyName = keyName + "_" + dataType.Name + "_" + info.FieldType.Name + "_" + info.Name;//封装一个方法进行存储//获取值:info.GetValue(data)SaveValue(info.GetValue(data),saveKeyName);}
}
private void SaveValue(object value,string keyName)
{//PlayerPrefs只支持3种类型存储//判断数据类型是什么类型,然后调用具体的方法来存储Type fieldType = value.GetType();if(fieldType == typeof(int)){PlayerPrefs.SetInt(keyName,(int)value);}else if(fieldType == typeof(float)){PlayerPrefs.SetFloat(keyName,(float)value);}else if(fieldType == typeof(string)){PlayerPrefs.SetString(keyName,value.ToString());}else if(fieldType == typeof(bool)){PlayerPrefs.SetInt(keyName,(bool)value ? 1 : 0);}
}
反射存储数据----List成员
private void SaveValue(object value,string keyName)
{//PlayerPrefs只支持3种类型存储//判断数据类型是什么类型,然后调用具体的方法来存储Type fieldType = value.GetType();if(fieldType == typeof(int)){PlayerPrefs.SetInt(keyName,(int)value);}else if(fieldType == typeof(float)){PlayerPrefs.SetFloat(keyName,(float)value);}else if(fieldType == typeof(string)){PlayerPrefs.SetString(keyName,value.ToString());}else if(fieldType == typeof(bool)){PlayerPrefs.SetInt(keyName,(bool)value ? 1 : 0);}//判断字段是不是IList的子类else if(typeof(IList).IsAssignableFrom(fieldType)){//父类装子类IList list = value as IList;//先存储数量PlayerPrefs.SetInt(keyName,list.Count);//存储具体的值int index = 0;foreach(object obj in list){SaveValue(obj,keyName + index);++index;}
反射存储数据----Dictionary成员
private void SaveValue(object value,string keyName)
{//PlayerPrefs只支持3种类型存储//判断数据类型是什么类型,然后调用具体的方法来存储Type fieldType = value.GetType();if(fieldType == typeof(int)){PlayerPrefs.SetInt(keyName,(int)value);}else if(fieldType == typeof(float)){PlayerPrefs.SetFloat(keyName,(float)value);}else if(fieldType == typeof(string)){PlayerPrefs.SetString(keyName,value.ToString());}else if(fieldType == typeof(bool)){PlayerPrefs.SetInt(keyName,(bool)value ? 1 : 0);}//判断字段是不是IList的子类else if(typeof(IList).IsAssignableFrom(fieldType)){//父类装子类IList list = value as IList;//先存储数量PlayerPrefs.SetInt(keyName,list.Count);//存储具体的值int index = 0;foreach(object obj in list){SaveValue(obj,keyName + index);++index;}else if(typeof(IDictionary).IsAssignableFrom(fieldType)){IDictionary dic = value as IDictionary;//存字典长度PlayerPrefs.SetInt(keyName,dic.Count);//遍历存储Dic里面的具体值int index = 0;foreach(object key in dic){SaveValue(key,keyName + "_key_" + index);SaveValue(dic[key],keyName + "_value_" + index);++index;}}
反射存储数据----自定义类成员
private void SaveValue(object value,string keyName)
{//PlayerPrefs只支持3种类型存储//判断数据类型是什么类型,然后调用具体的方法来存储Type fieldType = value.GetType();if(fieldType == typeof(int)){PlayerPrefs.SetInt(keyName,(int)value);}else if(fieldType == typeof(float)){PlayerPrefs.SetFloat(keyName,(float)value);}else if(fieldType == typeof(string)){PlayerPrefs.SetString(keyName,value.ToString());}else if(fieldType == typeof(bool)){PlayerPrefs.SetInt(keyName,(bool)value ? 1 : 0);}//判断字段是不是IList的子类else if(typeof(IList).IsAssignableFrom(fieldType)){//父类装子类IList list = value as IList;//先存储数量PlayerPrefs.SetInt(keyName,list.Count);//存储具体的值int index = 0;foreach(object obj in list){SaveValue(obj,keyName + index);++index;}else if(typeof(IDictionary).IsAssignableFrom(fieldType)){IDictionary dic = value as IDictionary;//存字典长度PlayerPrefs.SetInt(keyName,dic.Count);//遍历存储Dic里面的具体值int index = 0;foreach(object key in dic){SaveValue(key,keyName + "_key_" + index);SaveValue(dic[key],keyName + "_value_" + index);++index;}}else{SaveData(value,keyName);}
反射读取数据----常用成员
public object LoadData(Type type,string keyName)
{//根据你传入的Type,创建一个对象用于存储数据object data = Activator.CreateInstance(type);//得到所有字段FieldInfo[] infos = type.GetFields();//用于拼接key的字符串string loadKeyName = "";//用于存储单个字段信息的对象FieldInfo info;for(int i = 0;i < infos.Length;i++){info = infos[i];//key的拼接规则一定是和存储时一模一样,这样才能找到对应数据loadKeyName = keyName + "_" + type.Name + "_" + info.FieldType.Name + "_" + info.Name;//有key就可以结合,PlayerPrefs来读取数据info.SetValue(data,LoadValue(info.FieldType,loadKeyName));}return data;
}
private object LoadValue(Type fieldType,string keyName)
{//根据字段类型判断用哪个API读取if(fieldType == typeof(int)){return PlayerPrefs.GetInt(keyName,0);}else if(fieldType == typeof(float)){return PlayerPrefs.GetFloat(keyName,0);}else if(fieldType == typeof(string)){return PlayerPrefs.GetString(keyName,"");}else if(fieldType == typeof(bool)){//自定义存储bool值规则return PlayerPrefs.GetInt(keyName,0) == 1 ? true : false;}return null;
}
反射读取数据----List成员
private object LoadValue(Type fieldType,string keyName)
{//根据字段类型判断用哪个API读取if(fieldType == typeof(int)){return PlayerPrefs.GetInt(keyName,0);}else if(fieldType == typeof(float)){return PlayerPrefs.GetFloat(keyName,0);}else if(fieldType == typeof(string)){return PlayerPrefs.GetString(keyName,"");}else if(fieldType == typeof(bool)){//自定义存储bool值规则return PlayerPrefs.GetInt(keyName,0) == 1 ? true : false;}else if(typeof(IList).IsAssignableFrom(fieldType)){//得到长度int count = PlayerPrefs.GetInt(keyName,0);//实例化一个List对象进行赋值IList list = Activator.CreateInstance(fieldType) as IList;for(int i = 0;i < count;i++){//得到List中泛型的类型list.Add(LoadValue(fieldType.GetGenericArguments()[0],keyName + i));}return list;}return null;
}
反射读取数据----Dic成员
private object LoadValue(Type fieldType,string keyName)
{//根据字段类型判断用哪个API读取if(fieldType == typeof(int)){return PlayerPrefs.GetInt(keyName,0);}else if(fieldType == typeof(float)){return PlayerPrefs.GetFloat(keyName,0);}else if(fieldType == typeof(string)){return PlayerPrefs.GetString(keyName,"");}else if(fieldType == typeof(bool)){//自定义存储bool值规则return PlayerPrefs.GetInt(keyName,0) == 1 ? true : false;}else if(typeof(IList).IsAssignableFrom(fieldType)){//得到长度int count = PlayerPrefs.GetInt(keyName,0);//实例化一个List对象进行赋值IList list = Activator.CreateInstance(fieldType) as IList;for(int i = 0;i < count;i++){//得到List中泛型的类型list.Add(LoadValue(fieldType.GetGenericArguments()[0],keyName + i));}return list;}else if(typeof(IDictionary).IsAsssignableFrom(fieldType)){//得到字典的长度int count = PlayerPrefs.GetInt(keyName,0);//实例化一个字典对象,用父类装子类IDictionary dic = Activator.CreateInstance(fieldType);Type[] kvType = fieldType.GetGenericArguments();for(int i = 0;i < count;i++){dic.Add(LoadValue(kvType[0],keyName + "_key_" + i),LoadValue(kvType[1],keyName + "_value_" + i));}return dic;}return null;
}
反射读取数据----自定义类成员
private object LoadValue(Type fieldType,string keyName)
{//根据字段类型判断用哪个API读取if(fieldType == typeof(int)){return PlayerPrefs.GetInt(keyName,0);}else if(fieldType == typeof(float)){return PlayerPrefs.GetFloat(keyName,0);}else if(fieldType == typeof(string)){return PlayerPrefs.GetString(keyName,"");}else if(fieldType == typeof(bool)){//自定义存储bool值规则return PlayerPrefs.GetInt(keyName,0) == 1 ? true : false;}else if(typeof(IList).IsAssignableFrom(fieldType)){//得到长度int count = PlayerPrefs.GetInt(keyName,0);//实例化一个List对象进行赋值IList list = Activator.CreateInstance(fieldType) as IList;for(int i = 0;i < count;i++){//得到List中泛型的类型list.Add(LoadValue(fieldType.GetGenericArguments()[0],keyName + i));}return list;}else if(typeof(IDictionary).IsAsssignableFrom(fieldType)){//得到字典的长度int count = PlayerPrefs.GetInt(keyName,0);//实例化一个字典对象,用父类装子类IDictionary dic = Activator.CreateInstance(fieldType);Type[] kvType = fieldType.GetGenericArguments();for(int i = 0;i < count;i++){dic.Add(LoadValue(kvType[0],keyName + "_key_" + i),LoadValue(kvType[1],keyName + "_value_" + i));}return dic;} else {return LoadData(fieldType,keyName);}return null;
}
加密思路
找不到
1、把存在硬盘上的内容放在一个不容易找到的地方
2、多层文件夹包裹
3、名字辨识度低
但是对于PlayerPrefs不太适用,因为位置已经固定了,改不了。
看不懂
让数据的key和value让别人看不懂,俗称加密,为key和value加密。
解不出
不让别人获取到你加密的规则。
注意: 单机游戏加密只是提高别人修改你数据的门槛,只要别人获取到你的源代码,知道你的加密规则,一切都没有任何意义。但是对于一般玩家来说几乎是不可能的事情。
生成资源包
相关文章:

Unity数据持久化之PlayerPrefs
这里写目录标题 PlayerPrefs概述基本方法PlayerPrefs存储位置实践小项目反射知识补充数据管理类的创建反射存储数据----常用成员反射存储数据----List成员反射存储数据----Dictionary成员反射存储数据----自定义类成员反射读取数据----常用成员反射读取数据----List成员反射读取…...
uniapp微信公众号H5分享
如果项目文件node_modules中没有weixin-js-sdk文件,则直接使用本文章提供的; 如果不生效,则在template.h5.html中引入 <script src"https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script> 首先引入weixin-js-…...

深入理解指针(c语言)
目录 一、使用指针访问数组二、数组名的理解1、数组首元素的地址2、整个数组 三、一维数组传参的本质四、冒泡排序五、二级指针六、指针数组 一、使用指针访问数组 可以使用指针来访问数组元素。例如,可以声明一个指针变量并将其指向数组的第一个元素,然…...
高级语言期末2015级唐班B卷
1.编写函数,按照如下公式计算圆周率π的值(精确到1e-5) #include <stdio.h>double pai() {double last0;double flag1;int n1;while(flag-last>1e-5) {lastflag;flag*1.0*(2*n)*(2*n)/((2*n-1)*(2*n1));n;}return 2*last; }int main…...

开发一款招聘小程序需要具备哪些功能?
随着时代的发展,找工作的方式也在不断变得简单,去劳务市场、人才市场的方式早就已经过时了,现在大多数年轻人都是直接通过手机来找工作。图片 找工作类的平台不但能扩大企业的招聘渠道,还能节省招聘的成本,方便求职者进…...

嵌入式学习-qt-Day3
嵌入式学习-qt-Day3 一、思维导图 二、作业 完善对话框,点击登录对话框,如果账号和密码匹配,则弹出信息对话框,给出提示”登录成功“,提供一个Ok按钮,用户点击Ok后,关闭登录界面,跳…...
零基础到高级:Android音视频开发技能路径规划
音视频开发趋势 Android音视频开发领域目前正处于一个高速发展的阶段,主要趋势如下: 超高清视频:4K视频亚毫米级显示清晰,更加逼真,为开发更加逼真的虚拟现实应用提供了基础。AI技术:自适应码率控制、视频…...

阿里云香港轻量应用服务器网络线路cn2?
阿里云香港轻量应用服务器是什么线路?不是cn2。 阿里云香港轻量服务器是cn2吗?香港轻量服务器不是cn2。阿腾云atengyun.com正好有一台阿里云轻量应用服务器,通过mtr traceroute测试了一下,最后一跳是202.97开头的ip,1…...
python中websockets与主线程传递参数
目录 一、子线程创建websockets服务端接收客户端数据 二、主线程内启动子线程接收并处理数据 一、子线程创建websockets服务端接收客户端数据并存入队列 发送的消息客户端与服务端统一,多种消息加入判断的标签 服务端:web_server.py import asynci…...

js谐音梗创意小游戏《望子成龙》
🌻 前言 龙年到来,祥瑞满天。愿您如龙般矫健,事业腾飞;如龙鳞闪耀,生活美满。祝您龙年大吉,万事如意! 龙年伊始,我给各位设计了一款原创的小游戏,话不多说,直…...

第十篇:node处理404和服务器错误
🎬 江城开朗的豌豆:个人主页 🔥 个人专栏 :《 VUE 》 《 javaScript 》 📝 个人网站 :《 江城开朗的豌豆🫛 》 ⛺️ 生活的理想,就是为了理想的生活 ! 目录</...
左右互博。
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网 讨厌鬼在和小甜妹在玩石头游戏。 游戏一开始有 nnn 堆石子,第 iii 堆石子,有 aia_iai 个石子。两人轮流进行游戏。 轮到某个人时,这个人先选数量为 x(x&…...
android通过广播打印ram使用信息
在内存非常吃紧的情况下,android设备会开始kill部分非系统进程甚至系统进程来保证基本的系统运行。在这种情况下如何获取设备过去某段时间的ram使用情况至关重要。 通过开发者模式中的“内存”可以完美得知设备内存使用信息。 我们可以通过此途径,设计一…...
内存管理——线性内存,进程空间
低2G为进程空间 开始地址结束地址大小属性00xFFFFF1M保留0x1000000x102FFF栈不固定位置、大小0x1030000x143FFF堆不固定位置、大小0x400000主程序文件不固定位置、大小加载dll不固定位置、大小0x7ffdd000TIB位置,大小编译时固定0x7FFFE000系统与用户共享数据块位置…...

入门Python必读的流程控制语句
流程控制 if-else 语法: if 条件:语句else:语句 例子: a1 #使用方式一 if a>1:print(大于1) else:print(小于等于1) #使用方式二 print(大于1) if a>1 else print(小于等于1) 输出: >>小于等于1 >>小于等于1 if-elif-else 语法: if 条件:语句elif 条件:…...

day05-进程通信
1> 将互斥机制的代码实现重新敲一遍 代码: #include<myhead.h>int num520;//临界资源//1.创建互斥锁 pthread_mutex_t fastmutex;//定义任务函数 void *task1(void *arg){printf("1111111\n");//3.临界区上面获取锁资源(上锁&#…...

如何将OpenAI Sora生成的普通AI视频转化为Vision Pro的空间视频,沉浸式体验
【基于AI的Vision Pro空间视频】工作流:这个工作流程用于将2D视频转换为适用于 Vision Pro的Spatial视频: 1、使用Deep3D将2D视频转换为3D SBS: 使用Deep3D工具将2D视频转换为3D SBS格式: 转换例子:Prediction– lucataco/deep3d – Replicatehttps://replicate.com/…...

爬虫基础(下)
requests模块可以用来获取网络数据; 那么对于爬虫来说,要获取下图网页中的内容,就需要网页的URL。 复制链接方法是,打开网页,点击链接框,右键选择复制。 requests.get()函数可用于模拟浏览器请求网页的过…...

【八股文面试】Java基础常见面试题总结(上)
Java基础常见面试题总结(上) Java有哪些特性 简单易学;面向对象(封装,继承,多态);平台无关性( Java 虚拟机实现平台无关性);支持多线程( C 语言没有内置的多…...

c++:蓝桥杯的基础算法2(构造,模拟)+练习巩固
目录 构造 构造的基础概念: 模拟 练习1:扫雷 练习2:灌溉 练习3:回文日期 构造 构造的基础概念: 构造算法是一种用于解决特定问题的算法设计方法。在C语言中,构造算法通常涉及到创建一个函数或类来实…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...

逻辑回归暴力训练预测金融欺诈
简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...

Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合
作者:来自 Elastic Udayasimha Theepireddy (Uday), Brian Bergholm, Marianna Jonsdottir 通过搜索 AI 和云创新推动教育领域的数字化转型。 我们非常高兴地宣布,Elastic 已获得 AWS 教育 ISV 合作伙伴资质。这一重要认证表明,Elastic 作为 …...

[拓扑优化] 1.概述
常见的拓扑优化方法有:均匀化法、变密度法、渐进结构优化法、水平集法、移动可变形组件法等。 常见的数值计算方法有:有限元法、有限差分法、边界元法、离散元法、无网格法、扩展有限元法、等几何分析等。 将上述数值计算方法与拓扑优化方法结合&#…...