Unity框架学习--场景切换管理器

活动场景

用脚本实例化的游戏对象都会生成在活动场景中。

哪个场景是活动场景,则当前的天空盒就会使用该场景的天空盒。

只能有一个场景是活动场景。

在Hierarchy右击一个场景,点击“Set Active Scene”可以手动把这个场景设置为活动场景。也可以使用SceneManager.SetActiveScene方法把一个加载了的场景设置为活动场景。

异步加载

AsyncOperation相关的代码应写在一个协同程序中。

AsyncOperation 对象名=SceneManager.LoadSceneAsync(string 场景名)
开启异步加载场景,并把异步加载的信息存储在AsyncOperation型对象中。

AsyncOperation型对象.allowSceneActivation
返回bool型,表示是否允许在场景加载完毕后立即激活该场景。
值为true表示一旦该场景异步加载完毕,则会立即激活该场景。
值为false表示即使加载场景完毕也不会激活该场景,直到用代码再次把这个变量的值改为true,才会激活该场景。

AsyncOperation型对象.progress
返回float型,范围是0-1。表示异步加载的进度,开始是0,完成时是1
注意:当AsyncOperation型变量.allowSceneActivation的值为false,这个参数的值最多会卡在0.9,直到AsyncOperation型变量.allowSceneActivation的值变为true,这个参数的值才会变为1

AsyncOperation型对象.isDone
返回bool型。表示该异步加载是否完成。如果完成,则值为true,如果未完成,则值为false。
当AsyncOperation型对象.progress的值为1时,此时这个变量的值才为true,但这样就会激活新的新场景,一般很难观测到AsyncOperation型对象.isDone是true

AsyncOperation型对象.priority
返回int型,用于设置异步操作的优先级。
当有多个异步操作排队时,将优先执行更高优先级的异步操作。但如果异步操作在后台线程上启动, 则更改优先级没有任何效果。

AsyncOperation.completed
这个是一个有一个AsyncOperation型参数的Action事件。该AsyncOperation型参数存储了本次异步加载的信息。
当异步加载完成,也就是AsyncOperation型对象.isDone的值为true时,会执行一次这个事件。
 

LoadSceneManager  代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.Events;/// <summary>
/// 切换场景的管理器
/// </summary>
public class LoadSceneManager : SingletonPatternBase<LoadSceneManager>
{/// <summary>/// 重新切换当前场景/// </summary>public void LoadActiveScene(){SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);}/// <summary>/// 切换到下一个场景,是否场景循环,最后一个场景切换是切换为第一个场景/// </summary>public void LoadNextScene(bool isCyclical=false){int buildIndex = SceneManager.GetActiveScene().buildIndex + 1;//检测要切换的新场景的下标是否越界     这个参数的意思是BuildSettings中的场景总数if (buildIndex > SceneManager.sceneCountInBuildSettings-1){if (isCyclical)buildIndex = 0;else{Debug.LogWarning($"加载场景失败!要加载的场景的索引是{buildIndex},越界了");return;}}SceneManager.LoadScene(buildIndex);}/// <summary>/// 加载上一个场景,包括可选择的场景循环功能/// </summary>public void LoadPreviousScene(bool isCyslical=false){int buildIndex = SceneManager.GetActiveScene().buildIndex - 1;if (buildIndex < 0){if (isCyslical){buildIndex = SceneManager.sceneCountInBuildSettings - 1;}else{Debug.LogWarning($"加载场景失败!要加载的场景索引是{buildIndex},越界了!");return;}}SceneManager.LoadScene(buildIndex);}/// <summary>/// 异步加载场景  根据名字来切换场景/// </summary>public void LoadSceneAsync(string sceneName, UnityAction<float> loading = null,UnityAction<AsyncOperation> completed = null, bool setActiveAfterCompleted = true, LoadSceneMode mode = LoadSceneMode.Single){MonoManager.Instance.StartCoroutine(LoadSceneCoroutine(sceneName, loading, completed, setActiveAfterCompleted, mode));}IEnumerator LoadSceneCoroutine(string sceneName, UnityAction<float> loading = null,UnityAction<AsyncOperation> completed = null, bool setActiveAfterCompleted = true, LoadSceneMode mode = LoadSceneMode.Single){//开始加载资源AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(sceneName, mode);asyncOperation.allowSceneActivation = false;  //资源加载最多到0.9//等待资源加载完毕while (asyncOperation.progress < 0.9f){loading?.Invoke(asyncOperation.progress);yield return null;}//当asyncOperation.allowSceneActivation 为false,则asyncOperation.Progress最多只能到达0.9//我们人为把它们凑成1,可以方便外部进度条的显示loading?.Invoke(1);asyncOperation.allowSceneActivation = setActiveAfterCompleted;//加载资源完毕后执行的逻辑completed?.Invoke(asyncOperation);}/// <summary>/// 异步加载场景 根据索引来切换场景/// </summary>public void LoadSceneAsync(int sceneIndex,UnityAction<float>loading=null, UnityAction completed = null,bool setActiveAfterCompleted=true, LoadSceneMode mode = LoadSceneMode.Single){MonoManager.Instance.StartCoroutine(LoadSceneCoroutine(sceneIndex,loading, completed,setActiveAfterCompleted, mode));}IEnumerator LoadSceneCoroutine(int sceneIndex, UnityAction<float> loading = null,UnityAction completed = null, bool setActiveAfterCompleted = true, LoadSceneMode mode = LoadSceneMode.Single){//开始加载资源AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(sceneIndex, mode);asyncOperation.allowSceneActivation = false;  //资源加载最多到0.9//等待资源加载完毕while (asyncOperation.progress < 0.9f){loading?.Invoke(asyncOperation.progress);yield return null;}//当asyncOperation.allowSceneActivation 为false,则asyncOperation.Progress最多只能到达0.9//我们人为把它们凑成1,可以方便外部进度条的显示loading?.Invoke(1);asyncOperation.allowSceneActivation = setActiveAfterCompleted;//加载资源完毕后执行的逻辑completed?.Invoke();}}

测试脚本

[MenuItem("我的菜单/同步切换场景/重新切换到上一个场景")]static void Method3(){LoadSceneManager.Instance.LoadPreviousScene();}[MenuItem("我的菜单/异步切换场景/重新切换到场景1")]static void Method4(){LoadSceneManager.Instance.LoadSceneAsync("New Scene 1", (obj) =>{Debug.Log("加载进度是:" + obj * 100 + "%");}, (obj) =>{Debug.Log("加载完成了!");});}

Unity Scene类(场景类)

Scene类的对象用来存储场景的信息。

Scene型对象.buildIndex
返回int型,表示该场景在Build Settings窗口中的索引。
如果该场景是无效的场景,则这个变量的值为-1
如果该场景是通过AssetBundle加载的场景,则这个变量的值也为-1

Scene型对象.isDirty
返回bool型,表示该场景是否被修改了。
当我们在编辑器模式下修改了某一个场景,但是没有保存,则此时这个变量的值为true。一旦该场景保存了,则这个变量的值为false

Scene型对象.isLoaded
如果该场景已经加载了,则返回true。如果该场景还没有加载或没有加载完成,则返回false

Scene型对象.name
返回string型,表示该场景的名字,结尾不包含后缀.unity
也就是它在Project窗口中的名字。
注意:该场景必须已经加载了,这个变量才能正确返回它的场景名。如果该场景还没有加载或没有加载完成,则返回的值是"Null"

Scene型对象.path
返回string型,表示该场景的路径,结尾包含后缀.unity
例如:
Assets/AssetBundleAssets/Scenes/TestScenes/Test.unity

Scene型对象.rootCount
返回int型,表示该场景中所有根游戏对象身上的Transform组件的总数。

Scene型对象.GetRootGameObjects
返回GameObject[]型,表示该场景中所有根游戏对象。
隐藏的根游戏对象也会包含在其中,但是DontDestoryOnLoad的根游戏对象不会包含在其中。

Scene型对象.IsValid
如果一个场景是存在的,它就是有效场景,这个变量的值就为true
如果一个场景是不存在的,它就是无效场景,这个变量的值就为false
注意:从EditorSceneManager.OpenScene返回的场景的 IsValid 的值是 false

两个场景对象之间可以使用运算符!=
如果这两个场景不同,则返回true,否则返回false

两个场景对象之间可以使用运算符==
如果这两个场景相同,则返回true,否则返回false

Unity SceneManager类(场景管理器,用于加载场景、切换场景)

首先把要跳转的场景都拖进File——Build Settings中

必须先引入命名空间:using UnityEngine.SceneManagement;

SceneManager.sceneCount
当前已经加载的场景的数量。

SceneManager.sceneCountInBuildSettings
已经添加到Build Settings窗口中的场景的数量。
如果在编辑器模式下,则进入播放模式之前已经打开的场景也会包含在内,如果它没有手动拖到Build Settings窗口,则它在Build Settings窗口中也是有索引的,只是我们看不到,这个索引比可以看得到的最大1。

SceneManager.CreateScene(int 场景名)
创建一个空场景,这个空场景会叠加到当前场景。
如果创建的场景名重复,则会报错。
场景名可以在Hierarchy窗口看到。
如果要在编辑时创建场景,例如在创建编辑器脚本或工具时需要创建场景,则应使用EditorSceneManager.NewScene

SceneManager.CreateScene(string 场景名,CreateSceneParameters 创建场景的各种参数)
创建一个空场景,这个场景会叠加到当前场景。
如果创建的场景名重复,则会报错。
场景名可以在Hierarchy窗口看到。
如果要在编辑时创建场景,例如在创建编辑器脚本或工具时需要创建场景,则应使用EditorSceneManager.NewScene

SceneManager.GetActiveScene()
返回Scene型对象,表示当前场景的信息。

SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
使用同步的方式,重新切换到当前场景。

SceneManager.GetActiveScene().buildIndex+1表示下一个场景在Build Settings窗口中的索引。

SceneManager.GetActiveScene().name
string型。表示当前场景的名字。可以配合SceneManager.LoadScene使用,重新加载当前场景

SceneManager.GetSceneAt(int 索引)
在当前已加载的场景的列表中,返回指定索引的场景的信息。
索引必须大于等于0,最小为0。如果为负数,或者索引越界,则会报错。

SceneManager.GetSceneByBuildIndex(int 场景在Build Settings窗口中的索引)
根据传入的索引,返回Build Settings中的场景的信息。
该场景必须在Build Settings窗口中,且当前已经加载,这样才会返回它的信息。否则会返回一个无效的Scene型对象。
索引必须大于等于0,最小为0。如果为负数,或者索引越界,则会报错。

SceneManager.GetSceneByName(string 场景名)
在当前已经加载的场景中,查找指定场景名的场景。
如果找到了,则返回一个Scene对象,表示这个场景的信息。如果找不到,则返回一个无效的Scene对象。
场景名可以是Build Settings窗口中所示名称的最后一部分,在这种情况下,将返回第一个匹配的场景信息。
场景名也可以是Build Settings窗口中显示的路径,在这种情况下,将返回精确匹配到的场景信息。
场景名不区分大小写。
场景名一定不得包含.unity扩展名。

SceneManager.GetSceneByPath(string 场景的路径)
在当前已经加载的场景中,查找具有指定资源路径的场景。
如果找到了,则返回一个Scene对象,表示这个场景的信息。如果找不到,则返回一个无效的Scene对象。
场景的路径应是项目文件夹的相对路径,例如:“Assets/MyScenes/MyScene.unity”

SceneManager.LoadScene(int 场景在Build Settings窗口中的索引,LoadSceneMode 加载场景的模式);
        同步加载指定索引的场景。
        第一个参数可以使用string型,这样就会根据场景名或者场景路径来加载指定的场景。此时这个名字的场景要么提前放到Build Settings窗口中,要么之前已经用了AssetBundle加载,这样一来这个方法才会有效。如果都没有,则会报错。注意:场景名不包含.unity的后缀。还有,场景名不要重名,如果重名,则会加载匹配到的第一个场景。可以使用场景路径,例如:Assets/AssetBundleAssets/Scenes/TestScenes/Scene1.unity
        如果第二个参数使用LoadSceneMode.Single,则加载完毕后,会自动切换到该场景。原来场景会被卸载。默认就是使用这个。
        如果第二个参数使用LoadSceneMode.Additive,则加载完毕后,该场景会叠加到原来的场景中。原来的场景不会被卸载,且活动场景依然是原来的场景。
        使用此方法加载场景,不会立即加载,而是会在下一帧才开始加载。而且由于此方法是同步的,所以可能会出现卡顿现象,建议使用异步加载的LoadSceneAsync方法。

SceneManager.LoadSceneAsync(int 场景在Build Settings窗口中的索引,LoadSceneMode 加载场景的模式);
        异步加载指定索引的场景。
        第一个参数可以使用string型,这样就会根据场景名或场景路径来加载指定的场景。此时这个名字的场景要么提前放到Build Settings窗口中,要么之前已经用了AssetBundle加载,这样一来这个方法才会有效。如果都没有,则会报错。注意:场景名不包含.unity的后缀。还有,场景名不要重名,如果重名,则会加载匹配到的第一个场景。可以使用场景路径,例如:Assets/AssetBundleAssets/Scenes/TestScenes/Scene1.unity
        如果第二个参数使用LoadSceneMode.Single,则加载完毕后,可以切换到该场景,那么原来场景会被卸载。默认就是使用这个。
        如果第二个参数使用LoadSceneMode.Additive,则加载完毕后,该场景可以叠加到原来的场景中。原来的场景不会被卸载,且活动场景依然是原来的场景。
        使用此方法加载场景不会卡住游戏,往往配合场景中的进度条来使用,可以一边加载场景,一边控制进度条的推进。
        这个方法的返回值类型的是AsyncOperation类型,可以根据这个类型的对象来判断异步加载是否完成。具体可以参考AsyncOperation类。

SceneManager.MergeScenes(Scene 场景1,Scene 场景2)
将场景1的所有游戏对象全部转移到场景2,且场景1会被卸载掉。
一般在加载场景时,使用了LoadSceneMode.Additive来加载,才可能会使用这个方法。
场景1和场景2都必须是已加载的场景。
如果原来的场景1是活动场景,这样它被卸载掉之后,Hierarchy窗口中最上面的第一个场景就会变成活动场景。
如果原来的场景1不是活动场景,这样它被卸载掉之后,活动场景不变。

SceneManager.MoveGameObjectToScene(GameObject 要移动的游戏对象,Scene 移动到场景)
将一个游戏对象从它所在的场景移动到目标场景中。
一般在加载场景时,使用了LoadSceneMode.Additive来加载,才可能会使用这个方法。
要移动到的场景必须是已加载的场景。
如果要移动的游戏对象为null,则本方法无效。
如果移动到的场景没有加载完成,或者是无效的场景,则会报错。

SceneManager.SetActiveScene(Scene 场景名)
将指定的场景设置为活动场景。

SceneManager.UnloadSceneAsync(int 场景在Build Settings窗口中的索引)
SceneManager.UnloadSceneAsync(string 场景名或场景路径)
SceneManager.UnloadSceneAsync(Scene 场景对象)
SceneManager.UnloadSceneAsync(int 场景在Build Settings窗口中的索引,UnloadSceneOptions 卸载场景的选项)
SceneManager.UnloadSceneAsync(string 场景名或场景路径,UnloadSceneOptions 卸载场景的选项)
SceneManager.UnloadSceneAsync(Scene 场景对象,UnloadSceneOptions 卸载场景的选项)
销毁指定的场景和这个场景中的所有游戏对象。
本方法只对加载时用了LoadSceneMode.Additive所加载的场景有效。如果当前游戏中只有一个场景,则本方法无效,控制台会报黄色的警告。
本方法不会卸载内存中的场景资源,如果要释放资源,应在调用这个方法后,再调用Resources.UnloadUnusedAssets方法
返回值的类型是AsyncOperation类型,可以根据这个对象来确定异步操作是否完成。

SceneManager.activeSceneChanged
UnityAction<Scene,Scene>型的事件。
当活动场景发生变化时,会执行一次这个事件。
第一个参数表示原来的活动场景,第二个参数表示后来的活动场景。

SceneManager.sceneLoaded
UnityAction<Scene,LoadSceneMode>型的事件。
每当有新的场景被加载时,会执行一次这个事件。
第一个参数表示加载的新的场景,第二个参数表示这个场景加载的模式。

SceneManager.sceneUnloaded
UnityAction<Scene>型的事件。
每当有场景被卸载时,会执行一次这个事件。
参数表示卸载的场景对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/44371.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HTML <style> 标签

实例 <html> <head> <style type="text/css"> h1 {color:red} p {color:blue} </style> </head><body> <h1>Header 1</h1> <p>A paragraph.</p> </body> </html>定义和用法 <style>…

【Java后端封装数据】常见后端封装数据的格式,用于返回给前端使用(109)

数据格式一&#xff1a;包装 List Map 返回&#xff0c;常用于数据展示&#xff1b; // Controller&#xff1a;public Result selectRegConfig(RequestBody String param) {try {Map<String, Object> paramMap JsonUtils.readValue(param, Map.class);return Result.su…

Mac OS下应用Python+Selenium实现web自动化测试

在Mac环境下应用PythonSelenium实现web自动化测试 在这个过程中要注意两点&#xff1a; 1.在终端联网执行命令“sudo pip install –U selenium”如果失败了的话&#xff0c;可以尝试用命令“sudo easy_install selenium”来安装selenium; 2.安装好PyCharm后新建project&…

DTC 19服务学习1

在UDS&#xff08;统一诊断服务&#xff09;协议中&#xff0c;0x19是用于DTC&#xff08;诊断故障代码&#xff09;信息的服务。以下是你提到的子服务的功能和作用&#xff1a; 0x01 - 报告DTC按状态掩码。这个子服务用于获取当前存储在ECU中的DTC列表。状态掩码用于过滤DTC&a…

中间件:RocketMQ安装部署

单机部署 下载 cd /opt/soft/archive wget https://archive.apache.org/dist/rocketmq/4.9.4/rocketmq-all-4.9.4-bin-release.zip unzip -d ../ rocketmq-all-4.9.4-bin-release.zip配置 broker.conf 的brokerIP1 为公网ip 启动命令&#xff1a; nohup sh bin/mqnamesrv &a…

4.小程序的运行机制

启动过程 把小程序的代码包下载到本地解析app.json全局配置文件执行app.js小程序入口文件&#xff0c;调用App()创建小程序的实例渲染小程序首页小程序启动完成 页面渲染过程 加载解析页面的.json配置文件加载页面.wxml模板和.scss样式执行页面的.ts文件&#xff0c;调用Pag…

数据可视化-canvas-svg-Echarts

数据可视化 技术栈 canvas <canvas width"300" height"300"></canvas>当没有设置宽度和高度的时候&#xff0c;canvas 会初始化宽度为 300 像素和高度为 150 像素。切记不能通过样式去设置画布的宽度与高度宽高必须通过属性设置&#xff0c;…

对话 4EVERLAND:Web3 是云计算的新基建吗?

在传统云计算的发展过程中&#xff0c;数据存储与计算的中心化问题&#xff0c;对用户来说一直存在着潜在的安全与隐私风险——例如单点故障可能会导致网络瘫痪和数据泄露等危险。同时&#xff0c;随着越来越多 Web3 项目应用的落地&#xff0c;对于数据云计算的性能要求也越来…

js脚本自动化之葫芦娃

什么是葫芦娃? 贵州特产平台(扶贫助农平台)有很多,但都大同小异,就连模样都像一个娘生的,所以戏称为葫芦娃平台 #小程序://航旅黔购/1nkYlNRVzm0Gg9x #小程序://贵旅优品/7zz6mtnSVgDfyqa #小程序://新联惠购/ibFdsuhWqIbczEd #小程序://贵盐黔品/u2TgExCUdkavrFe #小程…

数据驱动成功:商城小程序分析与改进

在当今数字化时代&#xff0c;商城小程序成为了企业与消费者之间互动的重要途径。然而&#xff0c;一个成功的商城小程序不仅仅是一个购物平台&#xff0c;更需要通过数据分析不断进行改进和优化&#xff0c;以提升用户体验和营销效果。本文将深入探讨如何利用数据驱动的方式进…

mysql 超大 sql 文件导入过程

问题 最近遇到 2 个超大 sql 文件导入&#xff0c;好一通折腾 文档在哪里 调优参数太多&#xff0c;文档都看不过来 找到这些参数也费劲, ubuntu 在 /etc/mysql/mysql.conf.d/mysqld.cnf 中找到这个链接 ...... # # The MySQL Server configuration file. # # For explanat…

对前端PWA应用的部分理解和基础Demo

一、什么是PWA应用&#xff1f; 1、PWA简介 ​ 渐进式Web应用&#xff08;Progressive Web App&#xff09;&#xff0c;简称PWA&#xff0c;是 Google 在 2015 年提出的一种使用web平台技术构建的应用程序&#xff0c;官方认为其核心在于Reliable&#xff08;可靠的&#xf…

git压缩/合并多次commit提交为1次commit提交

git压缩/合并N次commit提交为1次commit提交 假设有最近3次提交&#xff1a; commit_id1 commit_id2 commit_id3目标是把以上3次commit合并成1个commit&#xff0c;注意&#xff0c;最新的commit提交在最上面。 在git bash里面的操作步骤&#xff1a; &#xff08;1&#xff0…

基于深度学习的铁路异物侵限检测算法研究_整体认知感觉欠点意思,但是有一个新的变形卷积-Octave 卷积

相比于其他的交通运输方式&#xff0c;铁路运输具有准时性高、连续性强、速度快、运输量大、运输成本低以及安全可靠等优点。同时由于国家高速铁路网络建设的不断推进&#xff0c;铁路运输逐渐成为我国客运与货运的主要运输方式。虽然铁路运输为人们出行和货物运输带来的极大的…

【BASH】回顾与知识点梳理(三十五)

【BASH】回顾与知识点梳理 三十五 三十五. 二十七至三十四章知识点总结及练习35.1 总结35.2 练习RAIDLVMsystemd 35.3 简答题 该系列目录 --> 【BASH】回顾与知识点梳理&#xff08;目录&#xff09; 三十五. 二十七至三十四章知识点总结及练习 35.1 总结 Quota 可公平的分…

MySQL数据库——SQL(3)-DQL(基本查询、条件查询、聚合函数、分组查询、排序查询、分页查询、案例练习)

目录 语法 基本查询 1.查询多个字段 2.设置别名 3.去除重复记录 示例 条件查询 1.语法 2.条件 示例 聚合函数 介绍 常见聚合函数 语法 示例 分组查询 语法 示例 排序查询 1.语法 2.排序方式 示例 分页查询 语法 示例 DQL案例练习 执行顺序 DQL总结…

简单理解Linux中的一切皆文件

一款操作系统要管理各种各样不同的硬件&#xff0c;因为硬件的不同所以它们使用的文件系统也不同。但是按道理来说&#xff0c;文件系统的不同对于用户来说可不是一件好事&#xff0c;操作不同的硬件就要使用不同的方法。 但是Linux有一切皆文件。 简单来说&#xff0c;Linux…

基于单片机DHT11温湿度NRF2401无线通信控制系统

一、系统方案 本设计采用STC89C5单片机作为主控制器&#xff0c;从机采用DHT11传感器采集温湿度、按键设置报警阀值&#xff0c;液晶1602显示&#xff0c;蜂鸣器报警&#xff0c;无线NRF2401模块。 二、硬件设计 原理图如下&#xff1a; 三、单片机软件设计 1、首先是系统…

使用 umap 图形化展示原文在嵌入后的位置情况

使用 umap_plot 图形化展示原文在嵌入后的位置情况 1. 效果展示2. 工具函数3. 示例代码14. 示例代码2 1. 效果展示 2. 工具函数 import umap import altair as altfrom numba.core.errors import NumbaDeprecationWarning, NumbaPendingDeprecationWarning import warningswar…

G0第26章:微服务概述与gRPCprotocol buffers

Go微服务与云原生 1、微服务架构介绍 单体架构&#xff08;电商&#xff09; SOA架构&#xff08;电商&#xff09; 微服务架构&#xff08;电商&#xff09; 优势 挑战 拆分 发展史 第一代:基于RPC的传统服务架构 第二代:Service Mesh(istio) 微服务架构分层 核心组件 Summar…