Unity基础:GameObject与Component——Unity核心架构思想彻底理解
Unity基础GameObject与Component——Unity核心架构思想彻底理解大家好欢迎回到Unity教程系列。前面十篇文章中我们已经反复接触了GameObject和Component这两个概念。今天这篇文章我要把它们之间的关系彻底讲透。GameObject与Component的关系是Unity整个引擎设计的基石。很多新手用Unity写了一两年代码但对这个核心架构仍然理解模糊——GameObject不就是场景里的东西吗Component不就是游戏物体上的组件吗这个理解没错但太表面了。今天我要带你深入到设计哲学的层面真正理解Unity为什么会这样设计以及这个设计对你的日常开发意味着什么。一、GameObject——场景中的空容器1.1 GameObject的本质 让我用一句话说出GameObject的本质GameObject是一个没有任何功能的空容器。是的你没听错。一个纯净的GameObject不带任何额外组件只有默认的Transform除了占据场景中的某个位置外什么也做不了。它不能显示任何图形、不能产生碰撞、不能播放声音、不能执行任何逻辑。创建一个空GameObject来验证这一点在Hierarchy中右键 → Create Empty。选中这个空GameObject看Scene视图——你什么都看不到。看Game视图——也看不到。看Inspector——只有一个Transform组件。这个空不是缺陷而是Unity设计的精妙之处。GameObject就像一块空白的画布你通过在上面画画添加Component来赋予它功能和意义。1.2 GameObject是一个组件容器从数据结构的角度来看GameObject本质上是一个Component的集合容器。它内部维护着一个Component列表并提供了添加、获取、移除Component的方法。// GameObject的核心方法群gameObject.AddComponentRigidbody();// 添加组件gameObject.GetComponentMeshRenderer();// 获取组件gameObject.GetComponentsCollider();// 获取某类型的所有组件Destroy(gameObject.GetComponentAudioSource());// 移除组件GameObject本身除了管理Component之外几乎不包含任何游戏逻辑。它的行为完全由挂载在上面的Component定义。1.3 GameObject的静态工厂方法除了从Hierarchy中创建GameObject外你还可以通过代码创建// 创建空GameObject最灵活的方式GameObjectobjnewGameObject(MyObject);// 创建基础3D几何体自带MeshFilter和MeshRendererGameObjectcubeGameObject.CreatePrimitive(PrimitiveType.Cube);GameObjectsphereGameObject.CreatePrimitive(PrimitiveType.Sphere);GameObjectcapsuleGameObject.CreatePrimitive(PrimitiveType.Capsule);GameObjectcylinderGameObject.CreatePrimitive(PrimitiveType.Cylinder);GameObjectplaneGameObject.CreatePrimitive(PrimitiveType.Plane);GameObjectquadGameObject.CreatePrimitive(PrimitiveType.Quad);// 实例化预制体最常用的动态创建方式GameObjectinstanceInstantiate(prefab); CreatePrimitive主要用于快速创建测试物体或需要在运行时动态生成简单几何体的情况。正式的游戏内容应该使用预制体。二、Component——功能的载体2.1 Component是什么Component是附加在GameObject上的功能模块。每一个Component都提供了一种特定的能力MeshRenderer让物体可见Collider让物体能检测碰撞Rigidbody让物体受物理影响AudioSource让物体能播放声音Animator让物体能播放动画自定义脚本继承MonoBehaviour让物体执行你定义的逻辑2.2 组件式架构的设计哲学Unity的组件式架构体现了一个重要的设计原则组合优于继承Composition over Inheritance。在传统的面向对象游戏开发中你可能会设计这样的继承链GameObject → Entity → Character → Enemy → FlyingEnemy这种设计在简单的游戏中可以工作但随着游戏变得复杂继承链会变得极其冗长且难以维护。如果你需要让FlyingEnemy同时具有飞行能力和射击能力而这些能力在继承链的不同分支上你就会陷入困境。Unity的组件式架构优雅地解决了这个问题GameObject FlyingEnemy ├── Transform ├── MeshRenderer显示外观 ├── Collider检测碰撞 ├── Rigidbody物理模拟 ├── FlyingBehavior飞行脚本 ├── ShootingBehavior射击脚本 ├── HealthComponent生命值脚本 └── AIControllerAI控制脚本想要飞行能力添加FlyingBehavior组件。想要射击能力添加ShootingBehavior组件。想要飞行加射击两个都加上就行了。组合的方式带来了极大的灵活性。2.3 内置Component分类Unity内置了大量的Component我将其分为几个大类渲染相关MeshFilter定义网格形状MeshRenderer渲染网格SkinnedMeshRenderer渲染带骨骼动画的网格SpriteRenderer渲染2D精灵LineRenderer渲染线段TrailRenderer渲染拖尾ParticleSystem粒子系统物理相关Rigidbody / Rigidbody2D刚体BoxCollider / BoxCollider2D盒子碰撞器SphereCollider球形碰撞器CapsuleCollider胶囊碰撞器MeshCollider网格碰撞器音频相关AudioSource音频播放源AudioListener音频监听器通常在Main Camera上动画相关AnimatorMecanim动画控制器Animation旧版动画组件UI相关Canvas画布CanvasRendererCanvas渲染器Image、Text、Button等UI控件组件其他Camera相机Light光源NavMeshAgent导航代理自定义脚本继承MonoBehaviour的类都是Component2.4 Transform——特殊的ComponentTransform是GameObject唯一必需的Component。它无法被移除创建GameObject时自动添加。你可以把它看作GameObject的身份标识——一个GameObject之所以是场景中的物体就是因为它有Transform占据了空间中的某个位置。三、GameObject与Component的交互3.1 GetComponent——获取组件的核心方法 GetComponent是Unity中使用频率最高的方法之一。它从GameObject上查找并返回指定类型的Component// 获取当前GameObject上的组件RigidbodyrbGetComponentRigidbody();MeshRendererrendererGetComponentMeshRenderer();// 获取父物体上的组件RigidbodyparentRbGetComponentInParentRigidbody();// 获取子物体上的组件包括自身AnimatoranimatorGetComponentInChildrenAnimator();⚠️ 性能注意事项GetComponent有一定性能开销。不要在Update中每帧都调用GetComponent——应该将组件引用缓存在Awake或Start中// ❌ 不好的做法每帧都调用GetComponentvoidUpdate(){RigidbodyrbGetComponentRigidbody();rb.AddForce(Vector3.up*10f);}// ✅ 好的做法启动时缓存引用privateRigidbodyrb;voidAwake(){rbGetComponentRigidbody();}voidUpdate(){rb.AddForce(Vector3.up*10f);}3.2 TryGetComponentUnity 2019及之后的版本提供了TryGetComponent方法它在组件不存在时不会分配内存GetComponent在找不到组件时会产生少量GCif(TryGetComponentRigidbody(outRigidbodyrb)){rb.AddForce(Vector3.up);}else{// 没有Rigidbody组件的处理逻辑}3.3 AddComponent——运行时动态添加组件你可以在运行时为GameObject动态添加组件// 给物体添加刚体让它受物理影响RigidbodyrbgameObject.AddComponentRigidbody();rb.mass2f;rb.useGravitytrue;// 添加碰撞器BoxCollidercolgameObject.AddComponentBoxCollider();col.sizenewVector3(1,2,1);动态添加组件是组件式架构灵活性的重要体现——你可以在运行时改变一个物体的能力。3.4 GetComponents——获取同一类型的所有组件一个GameObject上可以挂载多个同类型的组件比如多个Collider、多个自定义脚本// 获取当前物体上所有的ColliderCollider[]collidersGetComponentsCollider();// 包括子物体Collider[]allCollidersGetComponentsInChildrenCollider();// 包括父物体Collider[]parentCollidersGetComponentsInParentCollider();3.5 组件之间的通信在一个GameObject上不同的组件需要互相通信。常见的通信方式直接引用Direct ReferencepublicclassPlayerController:MonoBehaviour{privateRigidbodyrb;privateAnimatoranimator;voidAwake(){// 在同一个GameObject上获取其他组件rbGetComponentRigidbody();animatorGetComponentAnimator();}voidUpdate(){if(Input.GetKeyDown(KeyCode.Space)){rb.AddForce(Vector3.up*10f,ForceMode.Impulse);animator.SetTrigger(Jump);}}}通过Inspector拖拽赋值序列化引用publicclassHealthBar:MonoBehaviour{// 在Inspector中拖拽赋值可以是不同GameObject上的组件publicSliderhealthSlider;publicTexthealthText;publicPlayerHealthplayerHealth;// 其他GameObject上的脚本引用voidUpdate(){healthSlider.valueplayerHealth.currentHealth/playerHealth.maxHealth;healthText.text${playerHealth.currentHealth}/{playerHealth.maxHealth};}}四、理解组合优于继承4.1 为什么组合更好让我用一个真实开发场景来说明组合优于继承的价值。假设你在做一个RPG游戏。你有一个基础角色类BaseCharacter玩家Player和敌人Enemy都继承自它。如果使用继承BaseCharacter ├── Player │ ├── Warrior战士——使用近战武器、重甲 │ ├── Mage法师——使用魔法、轻甲 │ └── Archer弓箭手——使用弓箭、中甲 └── Enemy ├── Goblin哥布林——近战、低血量 ├── Dragon龙——飞行、喷火、高血量 └── Skeleton骷髅——近战、免疫毒素现在需求来了需要一个会隐身的龙、一个会魔法的哥布林、一个可以骑乘的骷髅。在继承体系下这些组合变异会让继承树爆炸式增长。如果使用组件式架构GameObject InvisibleDragon ├── MeshRenderer显示龙的外观 ├── HealthComponent高血量 ├── FlyingComponent飞行能力 ├── FireBreathComponent喷火能力 ├── StealthComponent隐身能力 └── AIControllerAI控制 GameObject MageGoblin ├── MeshRenderer显示哥布林外观 ├── HealthComponent低血量 ├── MeleeAttackComponent近战能力 ├── MagicCastComponent魔法施放能力 └── AIController GameObject RideableSkeleton ├── MeshRenderer显示骷髅外观 ├── HealthComponent中等血量 ├── MeleeAttackComponent近战能力 ├── RideableComponent可骑乘能力 └── PoisonImmuneComponent毒素免疫能力每个能力都是一个独立的Component。想给任何角色添加任何能力AddComponent就完事了。不需要修改类继承结构不需要重复代码。4.2 组件式架构的开发指南基于组件式架构我建议遵循以下开发原则单一职责每个脚本组件只负责一个明确的功能。如果发现一个脚本越来越大超过几百行应该考虑拆分成多个组件。组件解耦组件之间尽量减少直接的硬引用。使用事件、接口或UnityEvent来解耦组件间的通信。可组合的行为将游戏行为拆分为小的、可复用的组件。例如MoveComponent移动、JumpComponent跳跃、AttackComponent攻击、HealthComponent生命值——每个都是独立的组件任意组合。配置与逻辑分离将可配置的数据放在ScriptableObject中将逻辑放在MonoBehaviour组件中。这样同一个逻辑组件可以用不同的数据配置出不同的行为。五、代码实例——组件式架构实战5.1 一个简单的角色系统让我用一个完整的例子来演示组件式架构的威力// 1. 移动组件——只负责移动publicclassMovementComponent:MonoBehaviour{[SerializeField]privatefloatspeed5f;privateRigidbodyrb;voidAwake(){rbGetComponentRigidbody();}publicvoidMove(Vector3direction){Vector3velocitydirection*speed;velocity.yrb.velocity.y;// 保持当前的垂直速度rb.velocityvelocity;}}// 2. 跳跃组件——只负责跳跃publicclassJumpComponent:MonoBehaviour{[SerializeField]privatefloatjumpForce10f;[SerializeField]privateLayerMaskgroundLayer;[SerializeField]privatefloatgroundCheckDistance0.2f;privateRigidbodyrb;voidAwake(){rbGetComponentRigidbody();}publicboolTryJump(){if(IsGrounded()){rb.AddForce(Vector3.up*jumpForce,ForceMode.Impulse);returntrue;}returnfalse;}privateboolIsGrounded(){returnPhysics.Raycast(transform.position,Vector3.down,groundCheckDistance,groundLayer);}}// 3. 生命值组件——只负责管理生命值publicclassHealthComponent:MonoBehaviour{[SerializeField]privateintmaxHealth100;privateintcurrentHealth;publicSystem.ActionOnDeath;publicSystem.Actionint,intOnHealthChanged;// (current, max)voidAwake(){currentHealthmaxHealth;}publicvoidTakeDamage(intdamage){currentHealthMathf.Max(0,currentHealth-damage);OnHealthChanged?.Invoke(currentHealth,maxHealth);if(currentHealth0){OnDeath?.Invoke();}}publicvoidHeal(intamount){currentHealthMathf.Min(maxHealth,currentHealthamount);OnHealthChanged?.Invoke(currentHealth,maxHealth);}}// 4. 输入组件——只负责处理玩家输入publicclassPlayerInputComponent:MonoBehaviour{privateMovementComponentmovement;privateJumpComponentjump;voidAwake(){movementGetComponentMovementComponent();jumpGetComponentJumpComponent();}voidUpdate(){floathorizontalInput.GetAxis(Horizontal);floatverticalInput.GetAxis(Vertical);Vector3directionnewVector3(horizontal,0,vertical);movement.Move(direction);if(Input.GetButtonDown(Jump)){jump.TryJump();}}}5.2 组件的灵活组合有了这些独立组件你可以创建多种不同类型的角色而不需要写重复代码// 玩家角色 —— 有移动、跳跃、输入控制和生命值 Player ├── Rigidbody CapsuleCollider ├── MovementComponent ├── JumpComponent ├── HealthComponent └── PlayerInputComponent // 追踪型敌人 —— 有移动、生命值和AI追踪不能跳跃 ChaserEnemy ├── Rigidbody CapsuleCollider ├── MovementComponent ├── HealthComponent └── ChaseAIComponent // 固定炮塔 —— 只有生命值不能移动 Turret ├── BoxCollider ├── HealthComponent └── TurretAIComponent✅ 看到没有每个角色定制自己需要的能力——需要什么就AddComponent什么不需要就不加。继承体系根本做不到这么灵活。六、常见误区与最佳实践6.1 不要把所有逻辑写在一个脚本里❌ 新手常见错误创建一个Player脚本里面塞入移动、跳跃、攻击、生命值、UI更新、音效播放……几千行代码一个脚本管一切。✅ 正确做法将不同的功能拆分为独立的组件。让每个组件保持小而聚焦。6.2 组件引用缓存// ❌ 每帧调用GetComponentvoidUpdate(){GetComponentRigidbody().AddForce(Vector3.up);}// ✅ 启动时缓存privateRigidbodyrb;voidAwake(){rbGetComponentRigidbody();}voidUpdate(){rb.AddForce(Vector3.up);}6.3 善用[RequireComponent]如果你写的组件依赖于其他组件存在使用[RequireComponent]特性[RequireComponent(typeof(Rigidbody))][RequireComponent(typeof(Collider))]publicclassProjectile:MonoBehaviour{privateRigidbodyrb;voidAwake(){rbGetComponentRigidbody();}publicvoidLaunch(Vector3direction,floatspeed){rb.velocitydirection*speed;}}这样当脚本被挂载到GameObject上时Unity会自动添加Rigidbody和Collider组件。七、本篇总结✅ 本文核心知识点回顾GameObject是一个空容器本身不包含任何功能Component是功能的载体通过挂载不同的Component来赋予GameObject能力Unity的组件式架构体现了组合优于继承的设计哲学GetComponent系列方法是获取组件引用的核心API组件之间通过直接引用、Inspector拖拽、事件/接口等方式通信每个脚本组件应该保持单一职责小而聚焦[RequireComponent]特性声明组件依赖关系自动添加必要组件 理解了GameObject与Component的关系你就能理解为什么在Unity中一切皆GameObject一切功能皆Component。这个架构贯穿了Unity开发的始终从最简单的场景搭建到最复杂的系统设计。动手任务① 创建一个包含至少5个不同Component的GameObject② 按单一职责原则将功能拆分到不同的组件中③ 用TryGetComponent替换项目中的GetComponent调用④ 尝试用[RequireComponent]声明组件依赖关于作者拥有多年Unity开发经验的游戏开发者专注Unity系统化教学与最佳实践分享。欢迎在评论区交流讨论

相关新闻

DRF序列化器:RESTful API开发的核心技术解析

DRF序列化器:RESTful API开发的核心技术解析

1. RESTful规范与DRF基础概念 在Web开发领域,RESTful API设计已经成为构建服务的标准方式。Django REST Framework(简称DRF)作为Django生态中最强大的REST框架,为开发者提供了一套完整的工具集。我第一次接触DRF时,最让…

2026/7/20 23:33:38阅读更多 →
Unity UGUI核心原理与性能优化实战指南

Unity UGUI核心原理与性能优化实战指南

1. 项目概述:为什么UGUI是Unity开发者的必修课如果你正在用Unity做项目,无论是手游、PC游戏还是工具应用,几乎都绕不开一个东西:用户界面。Unity自带的UGUI系统,就是那个你每天都要打交道,但又可能对其内部…

2026/7/20 23:33:38阅读更多 →
航班乘客满意度分析:数据建模与业务优化实践

航班乘客满意度分析:数据建模与业务优化实践

1. 项目背景与核心价值航班乘客满意度分析是航空业提升服务质量的关键抓手。去年某国际航空协会数据显示,乘客满意度每提升1个百分点,航空公司年营收平均增长2.3%。这个项目要做的,就是通过数据建模找出影响满意度的关键因素,并给…

2026/7/20 23:33:38阅读更多 →
终极指南:如何快速打造个人哔咔漫画离线图书馆

终极指南:如何快速打造个人哔咔漫画离线图书馆

终极指南:如何快速打造个人哔咔漫画离线图书馆 还在为网络卡顿、加载缓慢而无法畅快阅读哔咔漫画而烦恼吗?想要随时随地享受心爱漫画,不受网络限制?今天,我将为你介绍一款专业级哔咔漫画下载器——picacomic-download…

2026/7/21 23:49:13阅读更多 →
FLI发布AI安全指数,全球模型没有超过C+

FLI发布AI安全指数,全球模型没有超过C+

nthropic、OpenAI、Google DeepMind、Meta,这4家曾经都承诺过:一旦自家系统接近特定风险阈值,就单方面暂停开发。现在,4家全都弱化或作废了这些承诺。有的把暂停条件改成了"看竞争对手怎么做再说"。Future of Life Inst…

2026/7/21 23:49:13阅读更多 →
作文自动批改系统:语法、逻辑、文采的分层评价体系

作文自动批改系统:语法、逻辑、文采的分层评价体系

作文自动批改系统:语法、逻辑、文采的分层评价体系 一、个性化深度引言 批改一篇 800 字的作文,人类老师大约需要 5-10 分钟。这对于同时教两个班共 100 名学生的语文老师来说,每次批改作业就是 8-16 个小时的工作量。而这还没算上给出有针对…

2026/7/21 23:49:13阅读更多 →
知识追踪模型训练:学生行为序列的建模与评估

知识追踪模型训练:学生行为序列的建模与评估

知识追踪模型训练:学生行为序列的建模与评估 一、个性化深度引言 传统考试能告诉你"某个学生在某个时刻答对了几道题",但无法回答"这个学生掌握了哪些知识点,未来遇到类似题目有多大把握做对"。 知识追踪(Kno…

2026/7/21 23:49:13阅读更多 →
零基础入门 UDS 诊断|一文吃透 0x11 ECU 复位服务(报文格式 + 核心测试点全解析)

零基础入门 UDS 诊断|一文吃透 0x11 ECU 复位服务(报文格式 + 核心测试点全解析)

很多刚接触 ISO 14229 UDS 协议的小伙伴,人生中第一条调试的诊断服务,大概率都是 0x11 ECU 复位。毕竟 “不行就重启” 是刻在所有工程师 DNA 里的万能操作。但如果你以为 0x11 服务就只是 “发个指令让 ECU 重启” 这么简单,那可就错过它的核…

2026/7/21 23:49:13阅读更多 →
YOLOv13改进策略【Conv和Transformer】| CVPR2025 CATANet LRSA局部区域自注意力 重叠分块交互 + 共享多头权重,精细复原图像纹理细节

YOLOv13改进策略【Conv和Transformer】| CVPR2025 CATANet LRSA局部区域自注意力 重叠分块交互 + 共享多头权重,精细复原图像纹理细节

一、本文介绍 本文记录的是利用LRSA局部区域自注意力优化YOLOv13的目标检测网络模型。 LRSA(Local-Region Self-Attention)通过重叠滑动窗口分块、全局共享多头注意力与通道卷积增强结合,实现图像邻域像素精细上下文交互,弥补长程聚合模块缺失的局部纹理信息。本文利用LR…

2026/7/21 23:47:12阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/21 0:51:49阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/21 0:51:49阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/21 0:51:49阅读更多 →
Windows+macOS 通用 OpenClaw 部署流程,内置依赖一键启动智能桌面助手

Windows+macOS 通用 OpenClaw 部署流程,内置依赖一键启动智能桌面助手

📌教程适配:OpenClaw v2.7.9 | 兼容 Windows10/11、macOS 双系统 📖前言 当下各类本地 AI 工具层出不穷,多数产品仅能完成文字问答交互,很难直接操控电脑执行实际操作。OpenClaw,业内常称小龙虾 AI&#…

2026/7/21 0:01:46阅读更多 →
Codex 接入后 Bug 反增?复盘从个人演示到团队协作的“流程陷阱”

Codex 接入后 Bug 反增?复盘从个人演示到团队协作的“流程陷阱”

聊《一次Codex项目复盘,问题最后出在流程而不是模型》之前,先说一句实在的:别急着背概念,先看它在真实项目里到底解决什么问题。摘要先把这篇文章的目标说清楚:看完之后,你应该能判断这件事值不值得做&…

2026/7/21 0:01:46阅读更多 →
手把手搓一个五子棋游戏,零代码也能当“游戏开发者”

手把手搓一个五子棋游戏,零代码也能当“游戏开发者”

大家好,还是我。前几期带大家做了心情日记本和可视化大屏,后台有朋友留言:“能不能教点好玩的?我想做游戏,但一行代码都不会。”行,这期就安排。今天的目标:从零做一个五子棋游戏。 带AI对战、三…

2026/7/21 0:03:46阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/21 22:53:50阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/21 18:53:30阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/21 18:53:30阅读更多 →