ECS架构实战:从理论到2D游戏开发的完整实现

news2026/3/15 4:38:13
1. 为什么你的游戏代码总是一团乱麻试试ECS吧如果你写过游戏尤其是那种有很多角色、怪物、道具在屏幕上跑来跑去的2D游戏你肯定有过这种体验一开始代码结构还挺清晰但随着功能越加越多比如要给角色加个“中毒”状态给怪物加个“分裂”能力代码就开始变得像意大利面条一样纠缠不清。一个Player类里塞满了移动、攻击、动画、音效、状态判断……改一处而动全身调试起来简直让人头大。这就是传统面向对象编程OOP在应对复杂、动态的游戏实体时常见的困境。类继承的“是-a”关系在游戏世界里往往很僵硬——一个“会飞的、能喷火的、死了会爆炸的石头人”它到底该继承“飞行单位”还是“地面单位”多重继承更是灾难。这时候一种叫做ECSEntity Component System实体-组件-系统的架构模式就像一把锋利的手术刀能帮你把这一团乱麻理得清清楚楚。我最早接触ECS是在做一个有上百个单位同屏对战的2D小游戏时OOP的架构让我在添加一个新技能时痛苦不堪。直到改用ECS我才发现原来游戏逻辑可以组织得如此优雅和高效。简单来说ECS的核心思想就一句话数据与行为彻底分离。它把游戏中的一切看成三种东西实体Entity它什么都不是只是一个唯一的ID像一张空白的身份证。组件Component它是实体的“形容词”是纯粹的数据。比如PositionComponent位置、VelocityComponent速度、RenderableComponent可渲染。系统System它是游戏的“动词”是纯粹的行为逻辑。它只关心数据不关心是谁的数据。比如MovementSystem移动系统只做一件事遍历所有同时拥有Position和Velocity组件的实体然后根据速度更新位置。这样一来一个“红色小球”就不再是一个RedBall类的实例而是一个实体比如ID1它身上挂着Position在坐标(0,0)、Velocity速度为0、Sprite红色圆形贴图这几个组件。控制它移动的是另一个独立的PlayerControlSystem。如果你想让它还能被敌人攻击只需要给它再挂上一个Health生命值组件然后CombatSystem就会自动把它纳入处理范围。这种“乐高积木”式的组合方式让游戏的扩展性变得无比灵活。2. 庖丁解牛亲手搭建一个迷你ECS框架光说理论太抽象咱们直接动手用C配合一点简单的OpenGL渲染从零搭建一个支持2D游戏的迷你ECS框架。我们会实现一个经典案例一个用键盘WASD控制的红色小球以及一堆从屏幕顶部落下的其他物体。通过这个完整过程你会彻底理解ECS的每一块“积木”是怎么拼起来的。2.1 核心积木定义实体、组件与系统首先我们定义最基础的“原材料”。实体就是一个ID我们用无符号整数表示。// Entity.hpp using Entity std::uint32_t; // 实体就是一个ID constexpr std::size_t MAX_ENTITIES 5000; // 假设我们最多有5000个实体组件是纯粹的数据结构。注意它没有任何成员函数方法。// Components.hpp #include glm/glm.hpp // 使用glm数学库处理向量 struct TransformComponent { glm::vec2 position {0.0f, 0.0f}; // 位置 glm::vec2 scale {1.0f, 1.0f}; // 缩放 float rotation 0.0f; // 旋转 }; struct VelocityComponent { glm::vec2 speed {0.0f, 0.0f}; // 速度向量 }; struct SpriteComponent { glm::vec4 color {1.0f, 0.0f, 0.0f, 1.0f}; // RGBA颜色默认红色 // 在实际项目中这里可能还有纹理ID、UV坐标等 }; struct PlayerTagComponent { // 这是一个“标签”组件里面没有数据仅用于标记“玩家实体” // 系统可以通过检查是否存在此组件来识别玩家 };系统是行为的执行者。我们先定义一个所有系统的基类接口。// System.hpp class System { public: virtual ~System() default; virtual void update(float deltaTime) 0; // deltaTime是上一帧到这一帧的时间差 // 系统需要知道自己关心哪些实体拥有哪些组件组合这个我们稍后通过“签名”机制实现 };2.2 高效管理的秘密组件数组与实体签名如果每次系统更新都要遍历所有实体再检查它有没有某个组件效率就太低了。ECS的高性能秘诀之一在于对数据的紧密排列和快速查询。我们引入两个核心管理器ComponentManager和EntityManager。ComponentManager的核心是为每种组件类型维护一个紧密排列的数组。所有TransformComponent都连续存储在内存的一块区域这非常有利于CPU缓存预取当MovementSystem遍历所有位置和速度数据时速度会极快。// ComponentManager.hpp (简化版) templatetypename T class ComponentArray { private: std::arrayT, MAX_ENTITIES componentData; // 紧密排列的组件数据 std::unordered_mapEntity, size_t entityToIndex; // 实体ID - 数组索引 std::unordered_mapsize_t, Entity indexToEntity; // 数组索引 - 实体ID size_t size 0; public: void insertData(Entity entity, T component) { // 将组件数据放入数组末尾并建立索引映射 size_t newIndex size; entityToIndex[entity] newIndex; indexToEntity[newIndex] entity; componentData[newIndex] component; size; } T getData(Entity entity) { // 通过映射快速找到组件数据 return componentData[entityToIndex[entity]]; } // ... 还有移除数据、实体销毁等函数 };EntityManager负责生成和回收实体ID更重要的是它为每个实体维护一个“组件签名Signature”。签名是一个位集合bitset每一位代表一种组件类型。如果实体拥有该组件对应的位就设为1。// EntityManager.hpp (简化版) using Signature std::bitsetMAX_COMPONENT_TYPES; // 假设我们最多有64种组件 class EntityManager { std::queueEntity availableEntities; // 可重用的实体ID池 std::arraySignature, MAX_ENTITIES signatures; // 每个实体的组件签名 public: Entity createEntity() { Entity id availableEntities.front(); availableEntities.pop(); signatures[id].reset(); // 清空签名 return id; } void setSignature(Entity entity, Signature signature) { signatures[entity] signature; } Signature getSignature(Entity entity) const { return signatures[entity]; } };2.3 系统的智能筛选如何找到该管的实体系统不需要知道所有实体它只处理拥有特定组件组合的实体。我们通过签名匹配来实现这个“智能筛选”。每个系统在创建时也会定义一个自己关心的“系统签名”。例如MovementSystem的签名必须有Transform和Velocity。RenderSystem的签名必须有Transform和Sprite。在游戏主循环的每一帧SystemManager系统管理器会做这样一件事遍历所有实体将实体的签名与每个系统的签名进行“按位与”操作。如果结果等于系统签名说明该实体拥有系统所需的全部组件那么这个实体就被加入到该系统的处理列表中。// SystemManager 内部逻辑伪代码 void SystemManager::entitySignatureChanged(Entity entity, Signature entitySignature) { for (auto [systemType, system] : systems) { Signature systemSig system-getSignature(); if ((entitySignature systemSig) systemSig) { // 实体签名包含系统所需的所有组件 system-registerEntity(entity); // 将该实体加入系统的处理列表 } else { system-unregisterEntity(entity); // 否则移除 } } }这样MovementSystem的内部update函数就变得非常干净高效void MovementSystem::update(float deltaTime) { for (Entity entity : registeredEntities) { // 只遍历注册给自己的实体 auto transform componentManager-getComponentTransformComponent(entity); const auto velocity componentManager-getComponentVelocityComponent(entity); // 纯粹的数据操作 transform.position.x velocity.speed.x * deltaTime; transform.position.y velocity.speed.y * deltaTime; } }3. 从积木到城堡实现2D游戏Demo框架搭好了现在让我们用它来构建文章开头提到的那个2D Demo一个键盘控制的红球和一堆自由下落的物体。3.1 组装世界注册组件与系统首先我们需要告诉我们的ECS世界有哪些类型的“积木”组件和“工人”系统。// Game.cpp - 初始化部分 void Game::initECS() { // 1. 注册所有组件类型 world.componentManager.registerComponentTransformComponent(); world.componentManager.registerComponentVelocityComponent(); world.componentManager.registerComponentSpriteComponent(); world.componentManager.registerComponentPlayerTagComponent(); // 2. 注册系统并定义其关心的组件签名 auto movementSys world.systemManager.registerSystemMovementSystem(); Signature moveSig; moveSig.set(world.componentManager.getComponentTypeTransformComponent()); moveSig.set(world.componentManager.getComponentTypeVelocityComponent()); world.systemManager.setSignatureMovementSystem(moveSig); auto renderSys world.systemManager.registerSystemRenderSystem(); Signature renderSig; renderSig.set(world.componentManager.getComponentTypeTransformComponent()); renderSig.set(world.componentManager.getComponentTypeSpriteComponent()); world.systemManager.setSignatureRenderSystem(renderSig); // 3. 创建玩家实体 Entity player world.entityManager.createEntity(); world.componentManager.addComponent(player, TransformComponent{ .position {0.0f, 0.0f} }); world.componentManager.addComponent(player, SpriteComponent{ .color {1.0f, 0.0f, 0.0f, 1.0f} }); // 红色 world.componentManager.addComponent(player, PlayerTagComponent{}); // 玩家初始速度为零由键盘控制系统来设置Velocity组件 // 4. 创建一堆下落物体实体 for (int i 0; i 50; i) { Entity fallingObj world.entityManager.createEntity(); float x (rand() / (float)RAND_MAX) * 2.0f - 1.0f; // 随机x坐标 float y (rand() / (float)RAND_MAX) 1.0f; // 起始y坐标在屏幕上方 world.componentManager.addComponent(fallingObj, TransformComponent{ .position {x, y} }); world.componentManager.addComponent(fallingObj, VelocityComponent{ .speed {0.0f, -1.0f} }); // 向下落 world.componentManager.addComponent(fallingObj, SpriteComponent{ .color {(rand() / (float)RAND_MAX), (rand() / (float)RAND_MAX), (rand() / (float)RAND_MAX), 1.0f} }); // 随机颜色 } }3.2 注入灵魂编写系统逻辑现在让我们给系统注入具体的游戏逻辑。键盘控制系统这个系统比较特殊它不直接遍历实体而是响应外部输入如GLFW的键盘回调找到玩家实体并修改其速度组件。// PlayerControlSystem.cpp void PlayerControlSystem::processInput(GLFWwindow* window) { // 这里我们需要一个方法来快速找到“玩家实体”。 // 一种简单做法是在创建玩家时记录其ID或者让系统在初始化时遍历一次所有实体找到带有PlayerTag的实体。 if (!playerEntity.has_value()) { // 初始化时查找玩家实体仅一次 auto view world.componentManager.viewPlayerTagComponent(); for (Entity e : view) { playerEntity e; break; } } if (!playerEntity) return; auto velocity world.componentManager.getComponentVelocityComponent(*playerEntity); velocity.speed {0.0f, 0.0f}; // 每帧先清零 if (glfwGetKey(window, GLFW_KEY_W) GLFW_PRESS) velocity.speed.y 1.0f; // 向上 if (glfwGetKey(window, GLFW_KEY_S) GLFW_PRESS) velocity.speed.y -1.0f; // 向下 if (glfwGetKey(window, GLFW_KEY_A) GLFW_PRESS) velocity.speed.x -1.0f; // 向左 if (glfwGetKey(window, GLFW_KEY_D) GLFW_PRESS) velocity.speed.x 1.0f; // 向右 }下落物体循环系统这个系统负责处理所有下落物体当它们落出屏幕底部时将其重置到顶部。// FallingObjectSystem.cpp void FallingObjectSystem::update(float deltaTime) { // 这个系统处理所有有Transform和Velocity但没有PlayerTag的实体即下落物体 // 我们需要一个“视图View”来高效地获取这类实体。 auto view world.componentManager.viewTransformComponent, VelocityComponent(); for (Entity entity : view) { // 排除玩家 if (world.componentManager.hasComponentPlayerTagComponent(entity)) continue; auto transform world.componentManager.getComponentTransformComponent(entity); // 检查是否落出屏幕底部假设屏幕底部y坐标为-1.0 if (transform.position.y -1.2f) { transform.position.y 1.2f; // 重置到屏幕顶部 transform.position.x (rand() / (float)RAND_MAX) * 2.0f - 1.0f; // 随机x位置 } } }3.3 让世界运转起来游戏主循环最后将所有系统串联在游戏主循环中。// Game.cpp - 主循环 void Game::run() { initECS(); while (!glfwWindowShouldClose(window)) { float currentFrame glfwGetTime(); float deltaTime currentFrame - lastFrame; lastFrame currentFrame; // 1. 处理输入 playerControlSystem.processInput(window); // 2. 更新所有ECS系统顺序很重要 movementSystem.update(deltaTime); // 先根据速度更新位置 fallingObjectSystem.update(deltaTime); // 再处理下落物体的边界循环 // 3. 渲染 glClear(GL_COLOR_BUFFER_BIT); renderSystem.update(deltaTime); // 渲染系统根据最新的位置和颜色数据绘制 glfwSwapBuffers(window); glfwPollEvents(); } }当你运行这个程序就会看到一个用WASD控制的红色小球以及无数彩色方块从屏幕顶部落下触底后循环回顶部。整个代码结构清晰移动逻辑、下落逻辑、渲染逻辑完全分离添加新功能比如让小球发射子弹或让方块之间碰撞只需要定义新组件和新系统几乎不用修改现有代码。4. ECS vs OOP一场思维模式的较量通过上面的实战你应该能切身感受到ECS和传统OOP在组织代码思维上的根本不同。我们来做个详细的对比看看在游戏开发这个具体场景下它们各自的优劣。传统OOP面向对象的思路是“名词思维”。我们首先想到的是“玩家”、“敌人”、“子弹”这些对象。我们会创建一个GameObject基类然后派生出Player : public GameObject在Player类里会有position、health等成员变量以及move()、shoot()、takeDamage()等成员方法。这种方式的优点是直观符合我们对现实世界的初级抽象。但缺点也很明显钻石问题与僵化继承如果一个FlyingEnemy既要会飞有fly()方法又要会射击有shoot()方法而你的基类设计里FlyingUnit和ShootingUnit是平行的你就陷入了多重继承的泥潭。即使使用接口组合关系也会变得复杂。代码复用性差move()方法写在Player里如果Enemy也需要相同的移动逻辑你可能需要复制代码或者提升到基类但这又可能污染基类。性能优化困难对象在内存中分散存储。当你需要更新所有对象的物理状态时你是在一个std::vectorGameObject*里遍历这些指针指向的内存地址可能天各一方导致CPU缓存命中率极低这就是所谓的“缓存不友好”。ECS的思维是“属性与行为分离”。它不关心“是什么”只关心“有什么”和“做什么”。“有什么”就是组件位置、速度、生命值、精灵图、玩家标签……这些都是数据。“做什么”就是系统移动系统、渲染系统、碰撞系统、AI系统……这些都是行为。这种架构带来的优势恰恰击中了OOP的痛点无与伦比的组合灵活性想让一个实体“会飞”给它加个WingsComponent。想让这个会飞的实体还能“喷火”再加个FlamethrowerComponent。一个MovementSystem可以同时驱动玩家、敌人、飞行道具的移动只要它们都有Transform和Velocity组件。添加新功能就像搭积木。数据与行为解耦易于测试系统是纯函数它接收组件数据输出修改后的组件数据。你可以很容易地为系统编写单元测试而无需构造复杂的游戏对象。极致的数据局部性与性能这是ECS在大型游戏如《守望先锋》中备受推崇的核心原因。组件按类型连续存储TransformComponent数组、VelocityComponent数组。MovementSystem工作时是在两个大数组上顺序遍历这种内存访问模式对CPU缓存超级友好可以轻松实现SIMD指令优化性能提升是数量级的。天然的并行化不同的系统之间通常没有数据依赖MovementSystem和RenderSystem处理的是上一帧和这一帧的数据。游戏引擎可以很容易地将不同系统分配到不同的CPU核心上并行执行。当然ECS并非银弹它也有自己的“坑”学习曲线陡峭需要开发者扭转OOP的思维定式适应这种数据驱动的模式。调试相对困难一个实体的状态分散在多个组件数组中当出现一个诡异的Bug时你需要跨多个系统去追踪数据流。不适合所有场景对于逻辑极其复杂、状态机庞大的单个实体比如一个具有几十种技能和复杂行为树的Boss用ECS来建模可能会显得繁琐传统的状态模式或行为树在单个实体内组织逻辑可能更清晰。框架复杂度你需要自己实现或引入一个ECS框架如EnTT、Flecs等这增加了项目初期的复杂度。对于非常小型的游戏可能有点“杀鸡用牛刀”。5. 进阶与避坑让ECS在项目中真正落地当你决定在下一个2D游戏项目中尝试ECS时下面这些实战经验和避坑指南可能会帮到你。首先关于框架选择。除非是为了学习否则我强烈建议不要从头造轮子。成熟的ECS库如EnTTC或FlecsC有C绑定经过了大量项目的检验提供了极其高效和丰富的功能比如查询Query、观察者Observer、事件Event、快照Snapshot等。它们的内存布局和迭代器优化做到了极致比自己实现的玩具框架强大得多。其次理解并善用“查询Query”。这是ECS框架的核心API。它允许你以声明式的方式查找拥有特定组件组合的实体。例如在EnTT中你可以这样写auto view registry.viewTransformComponent, VelocityComponent, SpriteComponent(); for (auto [entity, transform, velocity, sprite] : view.each()) { // 这个循环会自动、高效地遍历所有同时拥有这三个组件的实体 transform.position velocity.speed * dt; // 渲染sprite... }查询可以包含排除条件excludePlayerTag也可以指定组件的读写权限const非常灵活。第三注意系统的执行顺序。ECS中系统是独立运行的但游戏逻辑往往有依赖关系。比如InputSystem输入必须在PlayerControlSystem玩家控制之前运行。PhysicsSystem物理必须在MovementSystem移动之后运行因为物理计算会产生新的速度。MovementSystem又必须在RenderSystem渲染之前运行否则画面会落后一帧。 你需要在架构层面定义一个清晰的系统执行顺序通常可以在系统注册时指定优先级或阶段Phase。第四谨慎处理组件间的依赖。有时一个系统需要修改另一个系统所依赖的组件。例如一个CollisionSystem碰撞系统检测到碰撞后可能需要修改实体的HealthComponent生命值而一个DeathSystem死亡系统会在每帧检查所有实体的生命值。如果CollisionSystem和DeathSystem在同一阶段运行可能会出现一帧内“碰撞-扣血-死亡”的预期逻辑也可能因为执行顺序问题导致意外。对于这种有强顺序要求的逻辑可以通过事件Event或命令Command队列来解耦让CollisionSystem发送一个TakeDamageEvent由专门的DamageResolutionSystem在后续阶段统一处理伤害和死亡。最后从一个小模块开始试点。不要试图一下子将整个项目重构为ECS。可以从游戏中的某个子系统开始比如粒子效果、道具系统或UI元素。这些部分通常数据驱动特性强逻辑相对独立非常适合用ECS来管理。当你和你的团队熟悉了这种开发模式后再逐步推广到游戏的核心逻辑上。我自己在项目中的体会是ECS带来的最大好处不是初期的开发速度而是中后期维护和扩展时的那种从容。当策划提出“我们想让这个怪物在血量低于30%时分裂成三个小怪物并且小怪物自带中毒效果”这种需求时在ECS架构下我可能只需要新建一个SplitableComponent、一个PoisonousComponent然后写一个SplitSystem和一个PoisonSystem再将它们组合到现有的怪物实体上整个过程清晰、可控对原有代码的冲击极小。这种模块化和数据驱动的开发体验一旦适应就很难再回去了。

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…