【unity实战】使用unity制作一个红点系统
前言
注意,本文是本人的学习笔记记录,这里先记录基本的代码,后面用到了再回来进行实现和整理
素材
https://assetstore.unity.com/packages/2d/gui/icons/2d-simple-ui-pack-218050

框架:
RedPointSystem.cs
using System.Collections.Generic;
using UnityEngine;namespace RedpointSystem
{public class RedPointNode{public int redNum; // 红点数量public string strKey; // 节点关键字public Dictionary<string, RedPointNode> children; // 子节点字典public delegate void RedPointChangeDelegate(int redNum); // 红点变化委托public RedPointChangeDelegate OnRedPointChange; // 红点变化事件public RedPointNode(string key){strKey = key;children = new Dictionary<string, RedPointNode>();}}public class RedPointSystem{private static RedPointSystem instance = new RedPointSystem(); // 单例实例public static RedPointSystem Instance // 单例访问属性{get { return instance; }}public RedPointNode root; // 根节点private RedPointSystem(){this.root = new RedPointNode(RedPointKey.Root); // 根节点初始化}// 添加节点public RedPointNode AddNode(string key){if (FindNode(key) != null){return null; // 如果节点已存在,则返回空}string[] keys = key.Split('|'); // 按'|'分割关键字RedPointNode curNode = root;curNode.redNum += 1; // 根节点红点数量加一curNode.OnRedPointChange?.Invoke(curNode.redNum); // 触发红点变化事件foreach (string k in keys){if (!curNode.children.ContainsKey(k)){curNode.children.Add(k, new RedPointNode(k)); // 如果子节点不包含该关键字,则添加新节点}curNode = curNode.children[k];curNode.redNum += 1; // 子节点红点数量加一curNode.OnRedPointChange?.Invoke(curNode.redNum); // 触发红点变化事件}return curNode;}// 查找节点public RedPointNode FindNode(string key){string[] keys = key.Split('|'); // 按'|'分割关键字RedPointNode curNode = root;foreach (string k in keys){if (!curNode.children.ContainsKey(k)){return null; // 如果子节点不包含该关键字,则返回空}curNode = curNode.children[k];}return curNode;}// 删除节点public void DeleteNode(string key){if (FindNode(key) == null){return; // 如果节点不存在,则返回}DeleteNode(key, root);}// 递归删除节点private RedPointNode DeleteNode(string key, RedPointNode node){string[] keys = key.Split('|'); // 按'|'分割关键字if (key == "" || keys.Length == 0){node.redNum = Mathf.Clamp(node.redNum - 1, 0, node.redNum); // 调整节点红点数量node.OnRedPointChange?.Invoke(node.redNum); // 触发红点变化事件return node;}string newKey = string.Join("|", keys, 1, keys.Length - 1); // 获取新的关键字RedPointNode curNode = DeleteNode(newKey, node.children[keys[0]]); // 递归删除子节点node.redNum = Mathf.Clamp(node.redNum - 1, 0, node.redNum); // 调整节点红点数量node.OnRedPointChange?.Invoke(node.redNum); // 触发红点变化事件// 移除红点数量为零的子节点if (curNode.children.Count > 0){foreach (RedPointNode child in curNode.children.Values){if (child.redNum == 0){child.children.Remove(child.strKey);}}}return node;}// 设置回调函数public void SetCallBack(string key, RedPointNode.RedPointChangeDelegate cb){RedPointNode node = FindNode(key);if (node == null){return; // 如果节点不存在,则返回}node.OnRedPointChange += cb; // 设置红点变化事件的回调函数}// 获取红点数量public int GetRedpointNum(string key){RedPointNode node = FindNode(key);if (node == null){return 0; // 如果节点不存在,则返回0}return node.redNum; // 返回节点的红点数量}}public class RedPointKey{// 根节点关键字public const string Root = "Root";// Play节点及其子节点关键字public const string Play = "Play";public const string Play_LEVEL1 = "Play|Level1"; // Play节点下的Level1节点public const string Play_LEVEL1_HOME = "Play|Level1|HOME"; // Level1节点下的HOME子节点public const string Play_LEVEL1_SHOP = "Play|Level1|SHOP"; // Level1节点下的SHOP子节点public const string Play_LEVEL2 = "Play|Level2"; // Play节点下的Level2节点public const string Play_LEVEL2_HOME = "Play|Level2|HOME"; // Level2节点下的HOME子节点public const string Play_LEVEL2_SHOP = "Play|Level2|SHOP"; // Level2节点下的SHOP子节点}}
使用案例
RootPanel.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using RedpointSystem;public class RootPanel : MonoBehaviour
{public GameObject Canvas; // UI画布对象public MenuPanel menuPanel; // 菜单面板对象public LevelPanel levelPanel; // 关卡面板对象private void Awake(){// 在Awake方法中初始化红点节点,表示需要显示红点的条件// 示例:如果跨过每月最后一天的0点,则显示Play|Level1|HOME节点的红点RedPointSystem.Instance.AddNode(RedPointKey.Play_LEVEL1_HOME);// 示例:如果任务完成,可以领奖,则显示Play|Level1|SHOP节点的红点RedPointSystem.Instance.AddNode(RedPointKey.Play_LEVEL1_SHOP);// 其他条件类似,根据具体逻辑添加不同的红点节点RedPointSystem.Instance.AddNode(RedPointKey.Play_LEVEL2_HOME);RedPointSystem.Instance.AddNode(RedPointKey.Play_LEVEL2_SHOP);}private void Start() {// 在Start方法中设置菜单面板可见,关卡面板不可见menuPanel.gameObject.SetActive(true);levelPanel.gameObject.SetActive(false);}
}
MenuPanel.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using RedpointSystem;public class MenuPanel : MonoBehaviour
{public GameObject playBtn; // 播放按钮对象public GameObject continueBtn; // 继续按钮对象public GameObject optionsBtn; // 选项按钮对象public GameObject QuitBtn; // 退出按钮对象public LevelPanel LevelPanel; // 关卡面板对象void Start(){// 在Start方法中为播放按钮添加点击事件监听器,绑定到OnPlay方法playBtn.GetComponent<Button>().onClick.AddListener(OnPlay);// 初始化红点状态InitRedPointState();}void OnPlay(){// 点击播放按钮后,隐藏菜单面板,显示关卡面板this.gameObject.SetActive(false);LevelPanel.gameObject.SetActive(true);}void InitRedPointState(){// 获取Play节点的红点数量int redNum = RedPointSystem.Instance.GetRedpointNum(RedPointKey.Play);// 根据红点数量更新红点状态RefreshRedPointState(redNum);// 设置回调函数,当Play节点的红点数量发生变化时刷新红点状态RedPointSystem.Instance.SetCallBack(RedPointKey.Play, RefreshRedPointState);}void RefreshRedPointState(int redNum){// 查找播放按钮下的红点和数字对象Transform redPoint = playBtn.transform.Find("RedPoint");Transform redNumText = redPoint.transform.Find("Num");// 根据红点数量决定是否显示红点if (redNum <= 0){redPoint.gameObject.SetActive(false);}else{redPoint.gameObject.SetActive(true);redNumText.GetComponent<Text>().text = redNum.ToString(); // 更新红点数字文本}}
}
LevelPanel.cs
using RedpointSystem;
using UnityEngine;
using UnityEngine.UI;public class LevelPanel : MonoBehaviour
{// UI元素引用public GameObject Back1Btn; // 返回按钮public MenuPanel menuPanel; // 菜单面板引用public GameObject Level1Btn; // 关卡1按钮public GameObject Level1Container; // 关卡1容器public GameObject Level1HomeBtn; // 关卡1的主页按钮public GameObject Level1ShopBtn; // 关卡1的商店按钮public GameObject Level2Btn; // 关卡2按钮public GameObject Level2Container; // 关卡2容器public GameObject Level2HomeBtn; // 关卡2的主页按钮public GameObject Level2ShopBtn; // 关卡2的商店按钮void Start(){// 初始时隐藏关卡容器Level1Container.SetActive(false);Level2Container.SetActive(false);// 给返回按钮添加点击事件监听器Back1Btn.GetComponent<Button>().onClick.AddListener(OnBackClick);// 给关卡1按钮添加点击事件监听器Level1Btn.GetComponent<Button>().onClick.AddListener(OnLevel1Click);// 给关卡2按钮添加点击事件监听器Level2Btn.GetComponent<Button>().onClick.AddListener(OnLevel2Click);// 给关卡1主页按钮添加点击事件监听器Level1HomeBtn.GetComponent<Button>().onClick.AddListener(OnLevel1HomeBtn);// 给关卡1商店按钮添加点击事件监听器Level1ShopBtn.GetComponent<Button>().onClick.AddListener(OnLevel1ShopBtn);// 给关卡2主页按钮添加点击事件监听器Level2HomeBtn.GetComponent<Button>().onClick.AddListener(OnLevel2HomeBtn);// 给关卡2商店按钮添加点击事件监听器Level2ShopBtn.GetComponent<Button>().onClick.AddListener(OnLevel2ShopBtn);// 初始化红点状态InitRedPointState();}// 返回按钮点击事件处理void OnBackClick(){// 隐藏当前关卡面板,显示菜单面板this.gameObject.SetActive(false);menuPanel.gameObject.SetActive(true);}// 关卡1按钮点击事件处理void OnLevel1Click(){// 切换显示关卡1容器的可见性Level1Container.gameObject.SetActive(!Level1Container.gameObject.activeSelf);}// 关卡2按钮点击事件处理void OnLevel2Click(){// 切换显示关卡2容器的可见性Level2Container.gameObject.SetActive(!Level2Container.gameObject.activeSelf);}// 关卡1主页按钮点击事件处理void OnLevel1HomeBtn(){// 删除关卡1主页红点RedPointSystem.Instance.DeleteNode(RedPointKey.Play_LEVEL1_HOME);}// 关卡1商店按钮点击事件处理void OnLevel1ShopBtn(){// 删除关卡1商店红点RedPointSystem.Instance.DeleteNode(RedPointKey.Play_LEVEL1_SHOP);}// 关卡2主页按钮点击事件处理void OnLevel2HomeBtn(){// 删除关卡2主页红点RedPointSystem.Instance.DeleteNode(RedPointKey.Play_LEVEL2_HOME);}// 关卡2商店按钮点击事件处理void OnLevel2ShopBtn(){// 删除关卡2商店红点RedPointSystem.Instance.DeleteNode(RedPointKey.Play_LEVEL2_SHOP);}void InitRedPointState(){// 初始化关卡1按钮的红点状态RefreshRedPointState(RedPointSystem.Instance.GetRedpointNum(RedPointKey.Play_LEVEL1),Level1Btn.transform.Find("RedPoint"));// 设置关卡1红点回调RedPointSystem.Instance.SetCallBack(RedPointKey.Play_LEVEL1, (int redNum) =>{RefreshRedPointState(redNum, Level1Btn.transform.Find("RedPoint"));});// 初始化关卡2按钮的红点状态RefreshRedPointState(RedPointSystem.Instance.GetRedpointNum(RedPointKey.Play_LEVEL2),Level2Btn.transform.Find("RedPoint"));// 设置关卡2红点回调RedPointSystem.Instance.SetCallBack(RedPointKey.Play_LEVEL2, (int redNum) =>{RefreshRedPointState(redNum, Level2Btn.transform.Find("RedPoint"));});// 初始化关卡1主页按钮的红点状态RefreshRedPointState(RedPointSystem.Instance.GetRedpointNum(RedPointKey.Play_LEVEL1_HOME),Level1HomeBtn.transform.Find("RedPoint"));// 设置关卡1主页红点回调RedPointSystem.Instance.SetCallBack(RedPointKey.Play_LEVEL1_HOME, (int redNum) =>{RefreshRedPointState(redNum, Level1HomeBtn.transform.Find("RedPoint"));});// 初始化关卡1商店按钮的红点状态RefreshRedPointState(RedPointSystem.Instance.GetRedpointNum(RedPointKey.Play_LEVEL1_SHOP),Level1ShopBtn.transform.Find("RedPoint"));// 设置关卡1商店红点回调RedPointSystem.Instance.SetCallBack(RedPointKey.Play_LEVEL1_SHOP, (int redNum) =>{RefreshRedPointState(redNum, Level1ShopBtn.transform.Find("RedPoint"));});// 初始化关卡2主页按钮的红点状态RefreshRedPointState(RedPointSystem.Instance.GetRedpointNum(RedPointKey.Play_LEVEL2_HOME),Level2HomeBtn.transform.Find("RedPoint"));// 设置关卡2主页红点回调RedPointSystem.Instance.SetCallBack(RedPointKey.Play_LEVEL2_HOME, (int redNum) =>{RefreshRedPointState(redNum, Level2HomeBtn.transform.Find("RedPoint"));});// 初始化关卡2商店按钮的红点状态RefreshRedPointState(RedPointSystem.Instance.GetRedpointNum(RedPointKey.Play_LEVEL2_SHOP),Level2ShopBtn.transform.Find("RedPoint"));// 设置关卡2商店红点回调RedPointSystem.Instance.SetCallBack(RedPointKey.Play_LEVEL2_SHOP, (int redNum) =>{RefreshRedPointState(redNum, Level2ShopBtn.transform.Find("RedPoint"));});}void RefreshRedPointState(int redNum, Transform redPoint){Transform redNumText = redPoint.transform.Find("Num");// 如果红点数小于等于0,隐藏红点图标if (redNum <= 0){redPoint.gameObject.SetActive(false);}else{// 否则显示红点图标,并更新红点数显示redPoint.gameObject.SetActive(true);redNumText.GetComponent<Text>().text = redNum.ToString();}}
}
源码
整理好了会放上来
参考
https://www.bilibili.com/video/BV1jx4y1t7Uz/?spm_id_from=333.999.0.0&vd_source=2526a18398a079ddb95468a0c73f126e
https://www.bilibili.com/read/cv35873128/
完结
赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!
好了,我是向宇,https://xiangyu.blog.csdn.net
一位在小公司默默奋斗的开发者,出于兴趣爱好,最近开始自学unity,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!php是工作,unity是生活!如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~

相关文章:
【unity实战】使用unity制作一个红点系统
前言 注意,本文是本人的学习笔记记录,这里先记录基本的代码,后面用到了再回来进行实现和整理 素材 https://assetstore.unity.com/packages/2d/gui/icons/2d-simple-ui-pack-218050 框架: RedPointSystem.cs using System.…...
开发指南046-机构树控件
为了简化编程,平台封装了很多前端组件。机构树就是常用的组件之一。 基本用法: import QlmOrgTree from /qlmcomponents/tree/QlmOrgTree <QlmOrgTree></QlmOrgTree> 功能: 根据权限和控制参数显示机构树。机构树数据来源于核…...
SpringBatch文件读写ItemWriter,ItemReader使用详解
SpringBatch文件读写ItemWriter,ItemReader使用详解 1. ItemReaders 和 ItemWriters1.1. ItemReader1.2. ItemWriter1.3. ItemProcessor 2.FlatFileItemReader 和 FlatFileItemWriter2.1.平面文件2.1.1. FieldSet 2.2. FlatFileItemReader2.3. FlatFileItemWriter 3…...
如何评估AI模型:评估指标的分类、方法及案例解析
如何评估AI模型:评估指标的分类、方法及案例解析 引言第一部分:评估指标的分类第二部分:评估指标的数学基础第三部分:评估指标的选择与应用第四部分:评估指标的局限性第五部分:案例研究第六部分:…...
程序员学CFA——经济学(七)
经济学(七) 汇率外汇市场外汇市场的功能外汇市场的参与者卖方买方 汇率的计算汇率报价基础货币与计价货币直接报价与间接报价外汇报价习惯 名义汇率和实际汇率货币的升值与贬值交叉汇率计算即期汇率与远期汇率即期汇率与远期汇率的概念远期升水/贴水远期…...
imx335帧率改到10fps的方法
验证: imx335.c驱动默认的帧率是30fps,要将 IMX335 的帧率更改为 10fps,需要调整与帧率相关的参数。FPS(frames per second,每秒帧数)通常由 sensor 的曝光时间(exposure time)和垂直总时间(VTS,Vertical Total Size)共同决定。VTS 定义了 sensor 完成一帧图像采集…...
Large Language Model系列之二:Transformers和预训练语言模型
Large Language Model系列之二:Transformers和预训练语言模型 1 Transformer模型 Transformer模型是一种基于自注意力机制的深度学习模型,它最初由Vaswani等人在2017年的论文《Attention Is All You Need》中提出,主要用于机器翻译任务。随…...
java后端项目启动失败,解决端口被占用问题
报错信息: Web server failed to start . Port 8020 was already in use. 1、查看端口号 netstat -ano | findstr 端口号 2、终止进程 taskkill /F /PID 进程ID 举例:关闭8020端口...
PostgreSQL安装/卸载(CentOS、Windows)
说明:PostgreSQL与MySQL一样,是一款开源免费的数据库技术,官方口号:The World’s Most Advanced Open Source Relational Database.(世界上最先进的开源关系数据库),本文介绍如何在Windows、Cen…...
OutOfMemoryError异常OOM排查
目录 参考工具MAT(Memory Analyzer)一、产生原因二、测试堆溢出 java.lang.OutOfMemoryError: Java heap space测试代码运行手动导出dump文件mat排查打开dump文件查看Leak Suspects(泄露疑点)参考 【JVM】八、OOM异常的模拟 MAT工具分析Dump文件(大对象定位) 用arthas排…...
【Python】Arcpy将excel点生成shp文件
根据excel点经纬度数据,生成shp,参考博主的代码,进行了修改,在属性表中保留excel中的数据。 参考资料:http://t.csdnimg.cn/OleyT 注意修改以下两句中的数字。 latitude float(row[1]) longitude float(row[2])imp…...
torch之从.datasets.CIFAR10解压出训练与测试图片 (附带网盘链接)
前言 从官网上下载的是长这个样子的 想看图片,咋办咧,看下面代码 import torch import torchvision import numpy as np import os import cv2 batch_size 50transform_predict torchvision.transforms.Compose([torchvision.transforms.ToTensor(),…...
什么ISP?什么是IAP?
做单片机开发的工程师经常会听到两个词:ISP和IAP,但新手往往对这两个概念不是很清楚,今天就来和大家聊聊什么是ISP,什么是IAP? 一、ISP ISP的全称是:In System Programming,即在系统编程&…...
外卖霸王餐系统怎么快速盈利赚钱?
微客云外卖霸王餐系统,作为近年来外卖行业中的一股新兴力量,以其独特的商业模式和营销策略,迅速吸引了大量消费者的目光。该系统通过提供显著的折扣和返利,让顾客能够以极低的价格甚至免费享受到美味的外卖,同时&#…...
Linux环境下安装Nodejs
Linux环境下安装Nodejs 下载地址:https://nodejs.org/zh-cn/download/package-manager 一、使用压缩包自定义安装 上述链接下载好对应版本的软件包后,我存放到 /evn/nodejs 目录下(根据自己实际情况设置) 设置软链接 sudo ln…...
【Rust】字符串String类型学习
什么是String Rust的核心语言中只有一个String类型,那就是String slice,str通常被当作是&str的借用。String类型是通过标准库提供的,而不是直接编码到核心语言中,它是一个可增长的、可变的、utf-8编码的类型。str和String都是utf-8编码的…...
先验概率 后验概率 最大似然估计 自编码器AE
先验概率 先验概率:由因求果中的因 作用:后验概率是比较难以计算的,我们通常使用贝叶斯公式由先验概率计算后验概率。 贝叶斯公式:P(B|A)P(A|B)P(B)/P(A),其中P(B|A)为后验概率,P(A|B)为先验概率。 后验…...
qt 鼠标接近某线时,形状变化举例
1.qt 鼠标接近某线时,形状变化举例 在Qt中,要实现鼠标接近某条线时形状发生变化的效果,你需要利用QWidget的enterEvent和leaveEvent,或者更通用的mouseMoveEvent来检测鼠标的位置,并相应地改变鼠标的光标形状。 以下…...
800块,我从淘宝上买AGV……
导语 大家好,我是社长,老K。专注分享智能制造和智能仓储物流等内容。 新书《智能物流系统构成与技术实践》人俱乐部 从淘宝上打算够购买一台AGV小车,上去一搜,嘿,你别说,还真有。便宜的才200块钱。 很兴奋把…...
C++相关概念和易错语法(21)(虚函数、协变、析构函数的重写)
多态的核心是虚函数,本文从虚函数出发,根据原理慢慢推进得到结论,进而理解多态 1.虚函数 先看一下下面的代码,想想什么导致了这个结果 #include <iostream> using namespace std;class A { public:virtual void test(){co…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
