Unity-Mirror网络框架-从入门到精通之Benchmark示例
文章目录
- 前言
- 什么是Benchmark?
- Benchmark 简要说明
- Benchmark示例
- BenchmarkNetworkManager
- MonsterMovement
- PlayerMovement
- InterestManagement
- 性能指标
- BenchmarkIdle示例
- BenchmarkPrediction示例
- BenchmarkStinkySteak示例
前言
在现代游戏开发中,网络功能日益成为提升游戏体验的关键组成部分。Mirror是一个用于Unity的开源网络框架,专为多人游戏开发设计。它使得开发者能够轻松实现网络连接、数据同步和游戏状态管理。本文将深入介绍Mirror的基本概念、如何与其他网络框架进行比较,以及如何从零开始创建一个使用Mirror的简单网络项目。
什么是Benchmark?
Benchmark示例 通常用于测试和评估Mirror网络框架在不同场景下的性能表现。该示例可以通过实施多种网络条件来测量服务器和客户端之间的数据传输速度、延迟、带宽使用情况以及不同数量的同时连接用户对整体性能的影响。
在测试过程中,开发者可以观察到游戏在进行高并发情况下的响应能力,例如在多人游戏场景中,如何处理大量玩家的输入、状态同步和事件广播等。同时,该示例还可以帮助开发者识别潜在的性能瓶颈,并对代码进行优化,以便实现更流畅的用户体验。
通过这些基准测试,开发者能够获取关键的性能指标,从而更好地调整游戏的网络架构和优化策略,以提高整个平台的稳定性和反应速度,同时确保用户在游戏中能够获得最佳的游戏体验。
Benchmark 简要说明
Mirror的Example包含哪些 基准测试 示例呢?
- 1.Benchmark:这个示例用于基准测试网络性能,通常会测试数据传输速率、延迟等网络参数,以帮助开发者评估他们的网络实现。
- 2.BenchmarkIdle:这个示例主要用于在空闲状态下进行基准测试,目的是评估在没有任何活动情况下的网络性能。这有助于理解网络在闲置时的行为情况。
- 3.BenchmarkPrediction:这个示例涉及到网络预测技术,通常用于展示如何在网络延迟情况下进行状态预测,以提高游戏的流畅度和响应性。
- 4.BenchmarkStinkySteak:这是一个更具趣味性的示例,通常用于展示服务器与客户端之间的交互。可能涉及一些有趣的机制或模拟网络延迟的场景,以帮助开发者理解网络通信是如何工作的。
这些示例可以帮助开发者更好地理解 Mirror 的功能和用法,并进行性能优化。
Benchmark示例
BenchmarkNetworkManager
基准测试的NetworkManager,主要是在StartServer后,创建了1000个自由飘荡的盒子,代表我们的大场景中的Monster。
逻辑很简单,就是SpawnAll,克隆1000个游戏对象,并且调用NetworkServer.Spawn(go),同步到网络中。
public class BenchmarkNetworkManager : NetworkManager{public GameObject spawnPrefab;public int spawnAmount = 5000;public float interleave = 1;void SpawnAll(){float sqrt = Mathf.Sqrt(spawnAmount);float offset = -sqrt / 2 * interleave;int spawned = 0;for (int spawnX = 0; spawnX < sqrt; ++spawnX){for (int spawnZ = 0; spawnZ < sqrt; ++spawnZ){if (spawned < spawnAmount){GameObject go = Instantiate(spawnPrefab);float x = offset + spawnX * interleave;float z = offset + spawnZ * interleave;go.transform.position = new Vector3(x, 0, z);NetworkServer.Spawn(go);++spawned;}}}}public override void OnStartServer(){base.OnStartServer();SpawnAll();}
MonsterMovement
这个脚本是模拟MMORPG中的Monster,在大场景中自由的移动。
这里着重说一下,Update函数是模拟怪物自由移动的,
但是Update上面有一个[ServerCallback]修饰,代表这是一个服务器才会执行的函数。
在Mirror框架中,如果一个函数被[Server]或者[ServerCallback]修饰,那么他就代表仅在服务器执行,客户端不执行。
这里相当于在服务器直接模拟了大量的AI怪物的移动。保证了不同客户端中显示的AI物体的同步。
public class MonsterMovement : NetworkBehaviour{public float speed = 1;public float movementProbability = 0.1f;public float movementDistance = 20;public override void OnStartServer(){start = transform.position;}[ServerCallback]void Update(){}}
PlayerMovement
PlayerMovement模拟的是玩家自由控制移动。纯客户端执行。
由于Player对象上有NetworkTransform组件,所以它的位置等信息会实时同步给其他客户端。
当我们在场景中走动的时候,我们发现可以看到的Monster,会根据自己的位置变化,距离Player位置近的会显示,距离远的会隐藏。这个是什么道理呢?
原来是Mirror给我们做了一个网络优化,在NetworkManager游戏对象上面,有一个组件名字叫:SpatialHashingInterestManagement(空间哈希兴趣管理),是一种用于优化网络游戏中对象的管理和交互的方法。它通过将游戏世界划分为多个小区域(或“哈希桶”),来决定哪个玩家需要接收哪些对象的信息,从而提高性能并减少不必要的数据传输。
他就类似在做大世界的MMORPG时,只显示距离我们近的范围的AI怪物,并非全地图绘制和执行所有怪物的逻辑的优化。
InterestManagement
Interest管理器,它的原理就是相当于把地图划分成了无数的格子,然后我们只能看到距离我们相近的9个格子内的网络对象。
如下图所示:
当我们在一个非常大的世界游戏中时,与其将完整的世界状态发送给每个玩家,不如考虑仅将玩家周围的内容发送给玩家。
Mirror给我们提供了InterestManagement的主要原因:
- 1.规模:想象一下魔兽世界。将整个世界发送给每个玩家将是疯狂的。为了扩展到数千个连接,我们只需要发送与任何给定玩家相关的内容。
- 2.可见性:在像 DotA/英雄联盟这样的 MOBA 游戏中,并不是每个人都应该一直看到其他人。玩家应该只看到自己的团队和周围的怪物。不仅如此,玩家还不应该看到墙后等。
- 3.作弊:在像 Counter-Strike 这样的游戏中,玩家自然看不到墙后的敌人,因为相机不会渲染它们。但是,如果整个世界状态在内存中都是已知的,那么黑客无论如何都可以利用这一点,将玩家显示在墙后。
InterestManagement是一个好主意。
Mirror给我们提供了很多内置的Interest Management组件
选择 Network Manager 并添加一个内置的 Interest Management 组件。
- 1.Spatial Hashing Interest Management:以前我们 Vector3.Distance 根据连接实体距离来判断。现在,我们将每个生成的实体放入一个 Grid 中,对于每个连接,我们将所有 8 个相邻的 Grid 条目都发送到客户端。这是非常快的。在早期的 uMMORPG 测试中,它比距离检查快 30 倍。该算法不太复杂,因此可以很好地扩展到大量实体。
- 2.Distance Interest Management:是简单的通过距离判断,来把范围内的连接发送给客户端,是InteractManager的最简单粗暴的做法。但是缺点就是,通过距离来判断每个连接成本较高。
- 3.Scene Interest Management:场景Interest 管理与附加场景一起使用,将对象联网到具有物理隔离的子场景。这意味着,即使您在服务器上加载了同一子场景的多个实例,对象之间的碰撞等也只会发生在该子场景内,而不会干扰其他子场景。
- 4.Scene Distance Interest Management:是上述 Scene 和 Distance 的组合。
- 5.Match Interest Management :匹配Interest管理,适用于非物理游戏,如纸牌、棋盘、街机游戏。
- 6.Team Interest Management:用于限制对团队成员的联网对象的可见性。这也可以用于仅限所有者的项目,取代 Network Owner Checker。
性能指标
在Benchmark示例中,您看到的参数主要用于监控和评估网络性能,具体含义如下:
- buffer:用于存储网络数据包的缓冲区。通常在网络传输中,数据包会被暂时存储在缓冲区中,以便后续处理或发送。
- driftEMA:表示“漂移的指数移动平均”(Exponential Moving Average of Drift)。这是一个用于平滑变量的统计量,帮助减少波动并提供更稳定的值,通常用于测量时间戳的漂移或延迟变化。
- DelTimeEMA:表示“延迟时间的指数移动平均”(Exponential Moving Average of Delay Time)。此参数用于跟踪网络延迟的平均值,以便评估网络响应的稳定性。
- BTM:可能代表“带宽时间测量”(Bandwidth Time Measurement),用于衡量网络在一定时间内能够传输的数据量,帮助评估网络性能。
- RTT:表示“往返时间”(Round-Trip Time),是从发送数据包开始,到接收到确认的时间。RTT是网络延迟的重要指标。
- PredErrUNADJ:表示“未调整的预测误差”(Unadjusted Prediction Error),用于测量在不调整任何参数的情况下,预测的值与实际值之间的差异。
- PredErrADJ:表示“调整后的预测误差”(Adjusted Prediction Error),与未调整的预测误差相似,但在计算时可能考虑了某些调整因素,从而得到更精确的预测效果。
这些参数共同用于评估和优化网络性能,特别是在多人游戏中,以帮助开发者理解网络延迟和数据传输的表现。
BenchmarkIdle示例
该示例,使用大多数空闲对象进行基准测试,以测试dirtyObjects技术。
这次创建的额对象都是空闲不动的,他们的数量大概有10000个
这产生了大量的流量。
该测试需要Telepathy才能不断开客户端。
这个示例的代码比较简单,就是纯创建10000个cube矩阵。
如下所示:
public class BenchmarkIdleNetworkManager : NetworkManager{[Header("Spawns")]public int spawnAmount = 10_000;public float interleave = 1;public GameObject spawnPrefab;[Range(0, 1)] public float spawnPositionRatio = 0.01f;System.Random random = new System.Random(42);void SpawnAll(){//for循环,创建10000个目标}public override Transform GetStartPosition(){startPositions.RemoveAll(t => t == null);if (startPositions.Count == 0)return null;int index = random.Next(0, startPositions.Count); // DETERMINISTICreturn startPositions[index];}public override void OnStartServer(){base.OnStartServer();SpawnAll();}
BenchmarkPrediction示例
Mirror的Predicted预测基准测试,是针对低端设备/VR进行的优化。
在不与对象交互的情况下,开销为零!
在交互过程中,开销来自同步和校正。
这个基准预测了不断同步和校正的对象。
=>真实游戏中效果会比这个示例好,因为这是我们可以用于分析的最坏情况!
NetworkManagerPredictionBenchmark适用于创建1000个物理小球,
RandomForce:是物理小球上的网络组件,用于不同客户端,不间断的给物理小球不停地施加随机的力,代表了真实物理世界中,不同客户端的玩家,同时操作处理同一个物理网络对象,造成的延迟和不稳定。
public class RandomForce : NetworkBehaviour{public float force = 10;public float interval = 3;PredictedRigidbody prediction;Rigidbody rb => prediction.predictedRigidbody;void Awake(){prediction = GetComponent<PredictedRigidbody>();}public override void OnStartClient(){float randomStart = Random.Range(0, interval);InvokeRepeating(nameof(ApplyForce), randomStart, interval);}[ClientCallback]void ApplyForce(){// calculate force in random direction but always upwardsVector2 direction2D = Random.insideUnitCircle;Vector3 direction3D = new Vector3(direction2D.x, 1.0f, direction2D.y);Vector3 impulse = direction3D * force;rb.AddForce(impulse, ForceMode.Impulse);CmdApplyForce(impulse);}[Command(requiresAuthority = false)] // everyone can call thisvoid CmdApplyForce(Vector3 impulse){rb.AddForce(impulse, ForceMode.Impulse);}
我们注意到,OnStartClient中,每个客户端都会开启一个定时器,不时的给小球施加一个力,然后调用了Command,把命令发送给服务器的同时,客户端自身也执行rb.AddForce,为什么这么做呢?
-
- 本地预测:
当客户端调用 rb.AddForce 时,它利用 PredictedRigidbody 组件在本地进行物理计算。这种方法让客户端可以立即感受到推动的效果,无需等待服务器的响应,从而提供更流畅、即时的反馈给玩家。
- 本地预测:
-
- 网络同步:
同时,CmdApplyForce 方法是一个 Command 方法,用于将力的应用请求发送到服务器。即使客户端立即对物体施加了力,该方法仍然会在服务器上执行一次相同的操作。这样,其他客户端也能看到这一变化,确保所有客户端的状态一致。
- 网络同步:
-
- 冲突说明:
由于物理计算是基于每个客户端本地进行的,所以如果网络延迟或不同的客户端施加的力不同,预测可能与服务器的实际状态有所偏差。
PredictedRigidbody 组件的设计宗旨是处理这些冲突。当服务器收到命令执行 rb.AddForce 时,物理效果会被应用,但如果与当前客户端的状态不一致,PredictedRigidbody 将自动处理这些调整,以确保同步。
- 冲突说明:
-
- 并发效果:
正如注释所述,这个方法允许每个连接的客户端对所有对象施加影响。因此,随着客户端数量的增加,场景的行为会变得更加复杂且动态。这种并发处理能够在基准测试时体现网络和同步机制的能力。
- 并发效果:
使用这种方式的目的是为了在保持快速响应和流畅玩家体验的同时,确保每个玩家都能看到一致的游戏状态,从而实现更好的用户体验和网络表现。
BenchmarkStinkySteak示例
提到这个示例,我们需要提到一个库 Unity 网络解决方案基准测试https://github.com/StinkySteak/unity-netcode-benchmark/
这个仓库,包含各种Unity网络框架项目对比基准测试的结果。目的就是公平的比较Unity的各个网络库的性能等差异。
网络库及版本包括如下:
Netcode | Version | Transport |
---|---|---|
Fusion | 1.1.8 F 725 | Realtime |
Fusion 2 | 2.0.0 RC 797 | Realtime |
Netick | Netick 2 Beta 0.8.8 | LiteNetLib |
NGO | 1.2.0 | Unity Transport |
Fishnet | 3.11.10 | Tugboat |
Mirror | 86.4.0 | KCP |
测试内容(2024年最新):
SineMoveYBehaviour:测试500个对象在Sine曲线Y周移动
SineMoveRandomBehaviour:测试500个对象在Sine曲线随机移动
WanderMoveBehaviour:测试500个对象随机漫游移动
测试结果如下:
Server Out (kBps)
Mirror | NGO | Mirage | Fishnet | Netick | Fusion | Fusion 2 | kBps | |
---|---|---|---|---|---|---|---|---|
Move Y | 267 | 265 | 137 | 62 | 27 | 26 | 86 | kBps |
Move All Axis | 307 | 306 | 124 | 103 | 87 | 81 | 79 | kBps |
Move Wander | 459 | 461 | 138 | 145 | 96 | 125 | 85 | kBps |
NetworkTransform (Test ID: 2)
NGO | Fusion 1 | Mirror | Fusion 2 | Fishnet | Netick | ||
---|---|---|---|---|---|---|---|
0 Client | 0.13 | 0.131 | 0.060 | 0.031 | 0.034 | 0.029 | ms |
6 Clients | 0.18 | 0.135 | 0.067 | 0.034 | 0.034 | 0.0295 | ms |
12 Clients | 0.30 | 0.139 | 0.071 | 0.038 | 0.035 | 0.0299 | ms |
24 Clients | ERROR | 0.147 | 0.079 | 0.044 | 0.036 | 0.030 | ms |
SyncVar/NetworkProperty (Test ID: 4)
NGO | Fusion 1 | Fusion 2 | Mirror | Netick | Fishnet | ||
---|---|---|---|---|---|---|---|
0 Client | 0.030 | 0.026 | 0.025 | 0.017 | 0.0150 | 0.0115 | ms |
6 Clients | 0.039 | 0.028 | 0.029 | 0.021 | 0.0158 | 0.0116 | ms |
12 Clients | 0.049 | 0.031 | 0.032 | 0.024 | 0.0159 | 0.0116 | ms |
24 Clients | 0.078 | 0.038 | 0.038 | 0.030 | 0.0163 | 0.0116 | ms |
大家通过测试结果可以客观的看到各个网络库的差异,然后决定自己使用哪一个。
当然,我们也不能完全看这个数据来选,因为我们如果用这个网络库做项目的话,还要考虑其他几个更重要的问题:
1.网络库的社群是否活跃,如果将来遇到什么问题,是否有人解决或者帮助你。
2.网络库的仓库是否及时维护,如果有什么bug,作者能否及时更新?
3.网络库的API是否好学,易懂,符合个人喜好等等。
4.网络库是否收费,如何收费?
5.网络库的示例代码是否足够,学习难度大不大?
等等
通过综合对比后,我简单聊一下我的选择和原因。
不难看出,我选了Mirror,要不就不会有这篇文章了。
1.Fusion,是Photon光子旗下的,要说好用和稳定,那是没话说,但是它收费,而且国内使用受限。所以pass
2.NGO,unity的亲儿子,但是它的更新速度太慢,再加上之前UNet的无疾而终,导致我们大家现在不敢相信它。
3.Netick,国外用的比较多,国内几乎没人用,用的人也不多
4.FishNet,它是作者用了Mirror后发现了一些问题,自研了FishNet,所以性能肯定比Mirror强,本来我准备用它,但是它示例太少,用的人也少,导致我研究了一周左右,还是摸不着头脑,语法结构也有点懵逼。最后还发现它是半收费的,就是有些高级功能你必须付费才能使用。所以也pass了。
最后就选择Mirror了,虽然它有一些小问题,但是它的社群丰富,示例丰富,学习起来很快,虽然它速度没有FishNet快,但是我们普通的游戏,根本不会用那么多的游戏对象,因为那些基准测试都是在极限条件下进行的测试。普通的网络游戏,Mirror用起来都是没有问题的。
另外最打动我的一点是,已经有太多的游戏通过Mirror开发并且上线,群众的眼睛是血量的,所以我也选择了Mirror。
好了,这篇文张就到这里,希望对你能有所帮助。
相关文章:

Unity-Mirror网络框架-从入门到精通之Benchmark示例
文章目录 前言什么是Benchmark?Benchmark 简要说明Benchmark示例BenchmarkNetworkManagerMonsterMovementPlayerMovementInterestManagement性能指标 BenchmarkIdle示例BenchmarkPrediction示例BenchmarkStinkySteak示例 前言 在现代游戏开发中,网络功能…...

毕业项目推荐:基于yolov8/yolov5的行人检测识别系统(python+卷积神经网络)
文章目录 概要一、整体资源介绍技术要点功能展示:功能1 支持单张图片识别功能2 支持遍历文件夹识别功能3 支持识别视频文件功能4 支持摄像头识别功能5 支持结果文件导出(xls格式)功能6 支持切换检测到的目标查看 二、数据集三、算法介绍1. YO…...

今日头条ip属地根据什么显示?不准确怎么办
在今日头条这样的社交媒体平台上,用户的IP属地信息对于维护网络环境的健康与秩序至关重要。然而,不少用户发现自己的IP属地显示与实际位置不符,这引发了广泛的关注和讨论。本文将深入探讨今日头条IP属地的显示依据,并提供解决IP属…...
FPGA设计-如何使用后端工具
目录 简介 布线布局 设计前期 布局布线策略 兼谈如何做第一次布局布线 正确看待map之后的资源占用报告 简介 本章节主要说明Xilinx的一些后端工具能为我们做什么在什么情况下我们考虑使用这些工具至于这些工具具体如何使用可以看Xilinx提供的相关文件 可从www.xilinx.com…...

苍穹外卖04——Redis初入门 在店铺打烊or营业状态管理功能中的使用
Redis入门 redis简介 它以键值对的形式存储数据在内存中,并且以极高的性能和灵活性而著称,通常用于缓存、消息代理以及持久化数据。 - 基于内存存储,读写性能高- 适合存储热点数据(热点商品、资讯、新闻)- 企业应用广泛Windows版下载地址:https://github.com/microsoft…...
【MySQL关于数据库和表结构的增删查改】
数据库和表结构的基本语法 数据库命令关于字符集语法 表操作语法创建表查看表结构修改表修改表名增加字段同时修改字段名和字段数据类型仅修改字段数据类型删除字段 删除表 备份和恢复备份恢复 mysql -h 127.0.0.1 -P 3306 -u root -p mysql -u root -h 和 -p 默认 进入MySQL程…...

JVM实战—11.OOM的原因和模拟以及案例
大纲 1.线上系统突然由于OOM内存溢出挂掉 2.什么是内存溢出及哪些区域会发生内存溢出 3.Metaspace如何因类太多而发生内存溢出 4.无限制调用方法如何让线程的栈内存溢出 5.对象太多导致堆内存实在放不下而内存溢出 6.模拟JVM Metaspace内存溢出的场景(动态生成268个类占1…...

LLM - 使用 LLaMA-Factory 部署大模型 HTTP 多模态服务 教程 (4)
欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/144881432 大模型的 HTTP 服务,通过网络接口,提供 AI 模型功能的服务,允许通过发送 HTTP 请求,交互大模型,通常基于云计算架构,无需在本地部署复杂的模型和硬件,…...
Clickhouse集群部署(3分片1副本)
Clickhouse集群部署 3台Linux服务器,搭建Clickhouse集群3分片1副本模式 1、安装Java、Clickhouse、Zookeeper dpkg -i clickhouse-client_23.2.6.34_amd64.deb dpkg -i clickhouse-common-static_23.2.6.34_amd64.deb dpkg -i clickhouse-server_23.2.6.34_amd64…...

刷服务器固件
猫眼淘票票 大麦 一 H3C通用IP 注:算力服务器不需要存储 二 刷服务器固件 1 登录固定IP地址 2 升级BMC版本 注 虽然IP不一致但是步骤是一致的 3 此时服务器会出现断网现象,若不断网等上三分钟ping一下 4 重新登录 5 断电拔电源线重新登录查看是否登录成功...

数据结构C语言描述9(图文结合)--二叉树和特殊书的概念,二叉树“最傻瓜式创建”与前中后序的“递归”与“非递归遍历”
前言 这个专栏将会用纯C实现常用的数据结构和简单的算法;有C基础即可跟着学习,代码均可运行;准备考研的也可跟着写,个人感觉,如果时间充裕,手写一遍比看书、刷题管用很多,这也是本人采用纯C语言…...

CSS——2.书写格式一
<!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title></head><body><!--css书写中:--><!--1.css 由属性名:属性值构成--><!--style"color: red;font-size: 20px;&quo…...

Elasticsearch 创建索引 Mapping映射属性 索引库操作 增删改查
Mapping Type映射属性 mapping是对索引库中文档的约束,有以下类型。 text:用于分析和全文搜索,通常适用于长文本字段。keyword:用于精确匹配,不会进行分析,适用于标签、ID 等精确匹配场景。integer、long…...

【NLP高频面题 - 分布式训练篇】ZeRO主要为了解决什么问题?
【NLP高频面题 - 分布式训练篇】ZeRO主要为了解决什么问题? 重要性:★★ 零冗余优化器技术由 DeepSpeed 代码库提出,主要用于解决数据并行中的模型冗余问题,即每张 GPU 均需要复制一份模型参数。 ZeRO的全称是Zero Redundancy …...
kubernetes-循序渐进了解coredns
文章目录 概要基础知识Kubernetes 集群中对对象名称的 DNS 流量解析 Kubernetes 集群外的名称的 DNS 流量CoreDNS 如何确定向哪个本地 DNS 请求解析?修改 CoreDNS 的配置 概要 CoreDNS 是 Kubernetes 的核心组件之一。只有在 Kubernetes 集群中安装了 容器网络接口…...
mysql8 从C++源码角度看 客户端发送的sql信息 mysql服务端从网络读取到buff缓存中
MySQL 8 版本中的客户端-服务器通信相关,特别是在接收和解析网络请求的数据包时。以下是对代码各个部分的详细解释,帮助您更好地理解这些代码的作用。 代码概述 这段代码主要负责从网络读取数据包,它包含了多个函数来处理网络数据的读取、缓…...

pygame飞机大战
飞机大战 1.main类2.配置类3.游戏主类4.游戏资源类5.资源下载6.游戏效果 1.main类 启动游戏。 from MainWindow import MainWindow if __name__ __main__:appMainWindow()app.run()2.配置类 该类主要存放游戏的各种设置参数。 #窗口尺寸 #窗口尺寸 import random import p…...

【Vim Masterclass 笔记08】第 6 章:Vim 中的文本变换及替换操作 + S06L20:文本的插入、变更、替换,以及合并操作
文章目录 Section 6:Transforming and Substituting TextS06L21 Inserting, Changing, Replacing, and Joining1 定位到行首非空字符,并启用插入模式2 在紧挨光标的下一个字符位置启动插入模式3 定位到一行末尾,并启用插入模式4 定位到光标的…...
Tailwind CSS 实战:动画效果设计与实现
在现代网页设计中,动画效果就像是一位优秀的舞者,通过流畅的动作为用户带来愉悦的视觉体验。记得在一个产品展示网站项目中,我们通过添加精心设计的动画效果,让用户的平均停留时间提升了 35%。今天,我想和大家分享如何使用 Tailwind CSS 打造优雅的动画效果。 设计理念 设计动…...

【动手学电机驱动】STM32-MBD(3)Simulink 状态机模型的部署
STM32-MBD(1)安装 Simulink STM32 硬件支持包 STM32-MBD(2)Simulink 模型部署入门 STM32-MBD(3)Simulink 状态机模型的部署 【动手学电机驱动】STM32-MBD(3)Simulink 状态机模型部署…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

VisualXML全新升级 | 新增数据库编辑功能
VisualXML是一个功能强大的网络总线设计工具,专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑(如DBC、LDF、ARXML、HEX等),并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...
Spring Boot + MyBatis 集成支付宝支付流程
Spring Boot MyBatis 集成支付宝支付流程 核心流程 商户系统生成订单调用支付宝创建预支付订单用户跳转支付宝完成支付支付宝异步通知支付结果商户处理支付结果更新订单状态支付宝同步跳转回商户页面 代码实现示例(电脑网站支付) 1. 添加依赖 <!…...

sshd代码修改banner
sshd服务连接之后会收到字符串: SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢? 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头,…...

02.运算符
目录 什么是运算符 算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符 关系运算符 逻辑运算符 &&:逻辑与 ||:逻辑或 !:逻辑非 短路求值 位运算符 按位与&: 按位或 | 按位取反~ …...

Vue3 PC端 UI组件库我更推荐Naive UI
一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用,前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率,还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库(Naive UI、Element …...
JS红宝书笔记 - 3.3 变量
要定义变量,可以使用var操作符,后跟变量名 ES实现变量初始化,因此可以同时定义变量并设置它的值 使用var操作符定义的变量会成为包含它的函数的局部变量。 在函数内定义变量时省略var操作符,可以创建一个全局变量 如果需要定义…...