《Unity & Steam 终极开发圣经:虚空到宇宙》

The Ultimate Codex: From Void to Cosmos

作者: GDM-100 (您的专属架构师)
受众: 云笥阁下及其麾下所有开发者 (零基础 -> 首席架构师)
当前版本: Vol.0 (起源:核心哲学与代码艺术重制版)


🔮 第一卷:Unity 的灵魂 (The Philosophy of Creation)

云笥阁下,很多计算机科班出身的人学 Unity 都会极其痛苦,因为他们习惯了“继承”,而 Unity 的核心是“组合”。

1.1 实体与组件 (Entity & Component) —— 组装的艺术

在 Unity 里,你不是在“写程序”,你是在“装配零件”。

大师心法:组合优于继承 (Composition over Inheritance)

1.2 空间的基石:Transform (变换)

每一个 GameObject 天生必定带有一个 Transform 组件。你删不掉它。

1.3 预制体 (Prefab) —— DNA 克隆


🎻 第二卷:C# 交互艺术 (The Art of Interaction)

代码写得好不好,不是看算法有多复杂,是看解耦 (Decoupling) 做得彻不彻底。
宗旨: 脚本之间应该像陌生人一样,不要互相打电话,要发广播。

2.1 观察者模式 (Delegate & Event) —— 别互相引用了!

场景: 玩家死了 (Player)。此时:

  1. UI 要显示“游戏结束” (UIManager)。
  2. 怪物要停止攻击 (EnemyManager)。
  3. 音效要播放悲伤音乐 (AudioManager)。
  4. 成就系统要记录死亡次数 (AchievementSystem)。

🔴 菜鸟写法 (强耦合):
Player 脚本里写满了引用:

public class Player : MonoBehaviour {
    public UIManager ui;       // 还要在编辑器里一个个拖进去,累不累?
    public EnemyManager enemy;
    public AudioManager audio;
    
    void Die() {
        ui.ShowGameOver();
        enemy.StopAll();
        audio.PlaySadMusic();
        // 假如明天加了个成就系统,你还得回来改 Player 的代码,再加个变量。
        // 改着改着 Player 就崩了。
    }
}

🟢 大师写法 (事件驱动):
Player 只需要大喊一声:“老子挂了!”。谁爱听谁听。

// Player.cs
public class Player : MonoBehaviour {
    // 1. 定义事件:这是一个广播频道
    public event Action OnPlayerDeath; 

    void Die() {
        Debug.Log("我死了!");
        // 2. 发送广播:?.Invoke 意思是“如果有人在听,就喊一声”
        OnPlayerDeath?.Invoke(); 
    }
}

// UIManager.cs
public class UIManager : MonoBehaviour {
    public Player player; // 只需要 UI 知道 Player,Player 不需要知道 UI

    void Start() {
        // 3. 订阅:听到广播就执行 ShowGameOver
        player.OnPlayerDeath += ShowGameOver;
    }

    void OnDestroy() {
        // 4. 【必背】反订阅:为了防止内存泄漏,临死前必须取消关注
        player.OnPlayerDeath -= ShowGameOver;
    }

    void ShowGameOver() {
         text.text = "WASTED";
    }
}

优势: 哪怕把 UI、音效、怪物全删了,Player 代码一行都不用改,照样能跑。

2.2 接口 (Interface) —— 通用协议

场景: 你的枪能打箱子(碎掉)、打人(流血)、打玻璃(炸裂)。

🔴 菜鸟写法:

void OnTriggerEnter(Collider other) {
    if (other.GetComponent<Box>()) {
        other.GetComponent<Box>().Break();
    } else if (other.GetComponent<Human>()) {
        other.GetComponent<Human>().Bleed();
    }
    // 写一万个 else if 吗?
}

🟢 大师写法:
不管你是谁,只要你实现了 IDamageable 接口,我就能打你。

// 1. 定义协议
public interface IDamageable {
    void OnHit(int damage);
}

// 2. 各种物体去实现协议
public class Box : MonoBehaviour, IDamageable {
    public void OnHit(int damage) { 
        PlaySound("WoodCrack"); 
        Destroy(gameObject);
    }
}

public class Human : MonoBehaviour, IDamageable {
    public void OnHit(int damage) { 
        hp -= damage;
        SpawnBlood();
    }
}

// 3. 子弹逻辑
void OnTriggerEnter(Collider other) {
    // 4. 尝试获取接口
    if (other.TryGetComponent(out IDamageable target)) {
        // 我根本不知道你是箱子还是人,我只管打
        target.OnHit(10);
    }
}

2.3 单例模式 (Singleton) —— 必要之恶

有些东西全游戏只能有一个,比如 GameManagerNetworkManager。你要在任何地方都能访问它。

public class GameManager : MonoBehaviour {
    // 静态实例,全宇宙唯一
    public static GameManager Instance { get; private set; }

    void Awake() {
        // 确保只有一个王
        if (Instance != null && Instance != this) {
            Destroy(gameObject);
            return;
        }
        Instance = this;
        // 切换场景时不销毁
        DontDestroyOnLoad(gameObject);
    }

    public void AddScore(int score) { ... }
}

// 其他地方调用:
// 甚至不需要 GetComponent,直接调
GameManager.Instance.AddScore(100);

🔗 第三卷:Unity 核心生命周期 (The Loop) - 补充细节

云笥阁下,您之前让我枚举,这里我把为什么要死记顺序的逻辑补全。

3.1 为什么 Awake 和 Start 要分开?

这是一个经典的死锁问题。

3.2 物理帧 (FixedUpdate) vs 渲染帧 (Update)


《Unity & Mirror 终极开发圣经:从虚空到宇宙》

The Ultimate Codex: From Void to Cosmos

当前版本: Vol.1 (基础架构与网络理论篇)


序章:Unity 的解剖学 (The Anatomy of Engine)

小白总以为 Unity 是个黑盒,我们现在把它拆开。

1.1 生命周期的绝对真理 (The Absolute Lifecycle)

脚本不是随便乱跑的。Unity 的每一帧都在执行一个严格的循环 (Loop) 。如果你不背下这个表,你就永远是菜鸟。

我们将生命周期分为五个阶段:

A. 诞生阶段 (Initialization)

这些函数在脚本刚“活”过来时调用。仅调用一次。

  1. Awake()
    • 何时调用: 脚本实例被加载时。哪怕脚本组件没勾选 (enabled = false) 也会运行! 只要所在的 GameObject 是激活的。
    • 绝对用途: 变量初始化、单例赋值 (Instance = this)、获取自身组件 (GetComponent).
    • 禁忌: 严禁去访问其他物体的脚本(因为别人可能还没醒)。
  2. OnEnable()
    • 何时调用: 每次脚本从“关”变成“开”时。物体被激活时也会调。
    • 绝对用途: 订阅事件 (Event += Func)、重置状态(比如怪物复活)。
  3. Start()
    • 何时调用: 第一帧逻辑更新之前。
    • 绝对用途: 获取其他物体的引用 (GameObject.Find)、初始化逻辑数值。此时所有人的 Awake 都跑完了,访问别人是安全的。

B. 物理阶段 (Physics Loop)

这是独立于帧率的,哪怕游戏卡成 PPT,这里也依然平滑。

  1. FixedUpdate()
    • 频率: 默认 0.02秒 (50Hz)。
    • 绝对用途: 所有对 Rigidbody 的操作必须写在这! 加力、改速度。写在 Update 里会导致物理抖动。
  2. OnTriggerEnter/Stay/Exit (触发器检测)
    • 用途: 踩雷、进毒圈、吃金币。
  3. OnCollisionEnter/Stay/Exit (实体碰撞检测)
    • 用途: 车撞墙、子弹打铁。

C. 逻辑与渲染阶段 (Game Loop)

  1. Update()
    • 频率: 随显卡性能波动 (60FPS = 0.016s/帧)。
    • 绝对用途: 非物理移动、输入检测 (Input.GetKdye)、计时器倒数。
  2. LateUpdate()
    • 何时调用: 所有人的 Update 跑完之后。
    • 绝对用途: 摄像机跟随。如果不放在这,主角在 Update 里动,摄像机也在 Update 里动,执行顺序一旦错乱,画面就会抖动。

D. 渲染与调试 (Rendering & Debug)

  1. OnDrawGizmos()
    • 用途: 在 Scene 窗口画线、画圈,方便调试 AI 视线或刷怪范围。只有编辑器里能看到。
  2. OnGUI()
    • 警告: 这是上古遗留的 UI 代码,除了写调试按钮,严禁用于正式游戏 UI! 极度消耗性能。

E. 死亡阶段 (Decommission)

  1. OnDisable()
    • 绝对用途: 注销事件 (Event -= Func)。如果你不注销,物体销毁后事件还会试图调用它,导致报错。
  2. OnDestroy()
    • 用途: 彻底释放非托管资源(如手动读取的文件流)。

1.2 军火库清单:原生组件全枚举 (Native Components)

不要自己造轮子,Unity 已经给了你全套零件。小白必须认识以下所有组件:

【形体与渲染 (Visuals)】

【物理世界 (Physics)】

【声音 (Audio)】

【用户界面 (UGUI)】


第二章:Mirror 网络库的宏观理论 (The Mirror Theory)

云笥阁下,这部分是最抽象的。请想象您的公司:您是服务器,员工是客户端。

2.1 网络拓扑结构 (Architecture)

Mirror 是 Server-Authoritative (服务器权威) 架构。

  1. Dedicated Server (专用服务器):
    • 纯粹的上帝。它没有显卡,没有画面,只跑逻辑和数据。Linux 黑框框。
    • 场景: 比如《魔兽世界》或《CS:GO》排位服。
  2. Host (主机):
    • 二象性。他是服务器,同时也是一个玩家 (Client)。
    • 风险: 如果 Host 拔网线,所有人都掉线。Host 的电脑既要算逻辑又要渲染画面,压力大。
    • 场景: 局域网联机,《求生之路》。
  3. Client (客户端):
    • 哑巴终端。只负责显示画面,发送键盘鼠标指令。

2.2 网络身份 (NetworkIdentity)

这是 Mirror 的身份证


第三章:网络生命周期与行为 (Network Behaviour)

普通的 C# 脚本继承 MonoBehaviour
联机脚本必须继承 NetworkBehaviour

3.1 扩展的生命周期 (The New Loop)

继承 NetworkBehaviour 后,您获得了新的神力:

  1. OnStartServer()
    • 只在服务器运行。当物体在服务器端生成时调用。
    • 用途: 初始化怪物的 AI、加载 NPC 的掉落列表。
  2. OnStopServer()
    • 服务器停止或物体被销毁时调用。
  3. OnStartClient()
    • 在所有客户端运行。当物体在玩家电脑上被创建时调用。
    • 用途: 播放“传送进场”的特效,或者注册 UI 血条。
  4. OnStartLocalPlayer()
    • 关键!只在“我”的电脑上,针对“我”的角色调用。
    • 用途: 开启摄像机、开启输入监听、点亮主角光环。这是区分“我”和“队友”的最佳地点。

3.2 权限 (Authority) —— 谁说了算?

这是小白最容易晕的地方。代码里怎么判断“我是谁”?


《Unity & Mirror 终极开发圣经:从虚空到宇宙》

The Ultimate Codex: From Void to Cosmos

当前版本: Vol.2 (核心通信机制与数据流篇)


第四章:四大天王——网络数据流 (The Four Pillars of Data Flow)

云笥阁下,这是整个 Mirror 架构的心脏。如果小白搞不懂这四个修饰符(Attributes)的区别,他们就只能写出充满 Bug 的单机游戏。

4.1 [SyncVar] —— 状态的绝对同步

方向: Server -> All Clients
频率: 只要变量变了,下一帧自动同步。

这是最常用的。你想同步血量、魔法值、武器类型、皮肤颜色,就用它。

// 【代码范例:带 Hook 的同步变量】

[SyncVar(hook = nameof(OnHealthChanged))] 
public int currentHealth = 100;

// Hook 函数必须有两个参数:旧值和新值
void OnHealthChanged(int oldVal, int newVal) {
    // 只有这里涉及到 UI 更新或者特效
    // 为什么?因为服务器没有 UI,跑这些逻辑浪费性能。
    // 但如果有 hook,客户端就会自动执行这里。
    healthBarSlider.value = newVal;
    
    // 进阶技巧:如果你想做伤害飘字,可以在这里算差值
    int damage = oldVal - newVal;
    if (damage > 0) ShowDamagePopup(damage);
}

// 正确的修改方式(只在服务器调用)
[Server]
public void TakeDamage(int amount) {
    currentHealth -= amount; // 修改这行代码瞬间,同步就开始了
}

4.2 [Command] —— 凡人的祈祷

方向: Client (拥有权限者) -> Server
命名规范: 函数名必须以 Cmd 开头。

这是客户端唯一能对服务器说话的机会。

// 【代码范例:请求开枪】

void Update() {
    if (!isLocalPlayer) return; // 不是我别动
    if (Input.GetButtonDown("Fire1")) {
        // 客户端只是发送请求,不要在这里扣子弹!
        CmdFire(); 
    }
}

[Command]
void CmdFire() {
    // --- 这里已经是服务器环境了 ---
    
    // 1. 验证逻辑 (防作弊)
    if (ammoCount <= 0) return;
    if (isDead) return;

    // 2. 执行逻辑
    ammoCount--;
    
    // 3. 广播表现 (告诉所有人这里开枪了)
    RpcPlayMuzzleFlash();
}

4.3 [ClientRpc] —— 神的广播

方向: Server -> All Clients
命名规范: 函数名必须以 Rpc 开头。

它和 SyncVar 的区别:

// 【代码范例:全服广播特效】

[ClientRpc]
void RpcPlayMuzzleFlash() {
    // 这行代码会在所有连接的玩家电脑上运行
    particleSystem.Play();
    audioSource.PlayOneShot(shootSound);
}

4.4 [TargetRpc] —— 这里的私语

方向: Server -> Specific Client (Target)
命名规范: 函数名必须以 Target 开头。
参数: 第一个参数必须是 NetworkConnection

用于私密信息。

// 【代码范例:给特定玩家发消息】

[Server]
void GiveLoot(NetworkConnection target, string itemName) {
    // 只有这个玩家的屏幕上会弹出窗口
    TargetShowLootPopup(target, itemName);
}

[TargetRpc]
void TargetShowLootPopup(NetworkConnection target, string itemName) {
    UIManager.Instance.ShowMessage($"你获得了神器: {itemName}");
}

第五章:同步策略进阶 (Advanced Strategy)

教完基础,我要教点能让游戏不卡的东西。

5.1 状态同步 (State Sync) vs. 帧同步 (Lockstep)

市面上的误区很大,作为云笥阁下的员工必须分清。

5.2 只要结果是对的,过程可以是假的

这是 Mirror 开发的核心哲学。

问题: 服务器 1 秒钟只发 20 次包 (TickRate = 20)。如果客户端直接硬设置坐标,玩家看起来就是一顿一顿的(每 0.05 秒跳一次)。

解决方案: Snapshot Interpolation (快照插值)
Mirror 的 NetworkTransform 组件已经内置了这个功能,但你需要理解它:

  1. Buffer: 客户端收到服务器的数据包,不马上用。先存进一个缓冲队列。
  2. Delay: 故意延迟 2-3 个包的时间(约 100ms)。
  3. Lerp: 客户端永远是在“过去”的两个数据包之间做线性插值。
    • 结果: 丝般顺滑的移动,代价是你看别人永远有 100ms 的延迟。这就是 FPS 游戏里“我明明躲开了还是被打死”的原因(Lag Compensation 没做好的话)。

第六章:实战演练 —— 你的第一个联网交互物体

别光看,把这段代码复制进项目,挂在一个 Cube 上,加上 NetworkIdentity

任务目标: 一个多人点击变色的方块。谁点的,方块就变成谁的颜色。

using Mirror;
using UnityEngine;

public class MagicCube : NetworkBehaviour {
    
    // [SyncVar] 无论谁改变了这个颜色,全服同步
    // Hook 保证了材质球会跟着变
    [SyncVar(hook = nameof(OnColorChanged))]
    private Color blockColor = Color.white;

    private MeshRenderer _meshRenderer;

    void Awake() {
        _meshRenderer = GetComponent<MeshRenderer>();
    }

    // 只有本地玩家把鼠标移上去并点击时触发
    void OnMouseDown() {
        // 1. 判断:我必须是本地玩家才能发指令
        // 注意:这里需要配合 NetworkIdentity 的 "Require Authority" 设置
        // 或者我们使用更通用的方式:找玩家控制器去发令
        // 但为了简单演示,假设这个 Cube 拥有 Client Authority (不推荐,但适合教学)
        
        // 更常见做法:
        var player = NetworkClient.connection.identity.GetComponent<MyPlayerScript>();
        player.CmdChangeCubeColor(this.gameObject);
    }

    // Hook: 视觉更新
    void OnColorChanged(Color oldCol, Color newCol) {
        if (_meshRenderer != null)
            _meshRenderer.material.color = newCol;
    }

    // Server 方法供外部调用
    [Server]
    public void ServerSetColor(Color newCol) {
        blockColor = newCol; // 修改这个变量,Mirror 自动做剩下的事
    }
}

// ------------------------------------------
// 配合使用的玩家脚本 (挂在 Player Prefab 上)
// ------------------------------------------
public class MyPlayerScript : NetworkBehaviour {
    
    [Command]
    public void CmdChangeCubeColor(GameObject targetCube) {
        // 安全检查:距离够不够?
        if (Vector3.Distance(transform.position, targetCube.transform.position) > 5f) return;

        MagicCube cube = targetCube.GetComponent<MagicCube>();
        if (cube != null) {
            // 生成一个随机颜色
            Color newCol = new Color(Random.value, Random.value, Random.value);
            cube.ServerSetColor(newCol);
            
            // 顺便广播个声音
            RpcPlayClickSound(targetCube.transform.position);
        }
    }

    [ClientRpc]
    void RpcPlayClickSound(Vector3 pos) {
        // 在指定位置播放音效
        AudioSource.PlayClipAtPoint(someAudioClip, pos);
    }
}

《Unity & Mirror 终极开发圣经:从虚空到宇宙》

The Ultimate Codex: From Void to Cosmos

当前版本: Vol.3 (高级网络优化与世界构建篇)


第七章:视野管理 (Interest Management) —— 谁有资格看见谁?

小白最常犯的错误:全图广播
如果你在地图左下角放了个屁(生成特效),地图右上角的人根本不需要知道。如果服务器要把所有人的数据发给所有人,带宽呈指数级爆炸 (N2N^2)。

7.1 核心概念:Observer (观察者)

在 Mirror 里,每个联网物体都有一个 NetworkIdentity

7.2 解决方案:Spatial Hashing (空间哈希)

不用自己写算法,Mirror 自带了。

大师警告: 如果你用了 Interest Management,千万别依赖全局搜索 FindObjectsOfType<Player>()。因为远处的玩家对你来说是不存在的!


第八章:射击游戏的噩梦 —— 延迟补偿 (Lag Compensation)

这是 FPS 游戏的圣杯。
场景: 敌人正在向右跑。A 玩家瞄准敌人的头,开枪。

8.1 为什么会这样?

客户端看到的是“过去”的影子(插值后的画面)。

8.2 解决方案:服务器回滚 (Server-side Rewind / Rollback)

这是高级技术,原理如下:

  1. 记录历史: 服务器不仅存当前位置,还用一个环形缓冲区 (RingBuffer) 存过去 1 秒内每一帧所有玩家的位置。
  2. 时间戳: A 玩家发 CmdFire 时,带上自己的时间戳(Client Time)。
  3. 时光倒流: 服务器收到请求,算出 A 开枪时是 100ms 前。
  4. 回滚: 服务器把那个敌人的 Hitbox 瞬间移回到 100ms 前的位置。
  5. 判定: 做射线检测 (Physics.Raycast)。
  6. 还原: 检测完瞬间把人移回去,好像无事发生。

[代码概念演示]

// 这不是完整代码,是逻辑伪代码
[Command]
void CmdShoot(Vector3 direction, double clientNetworkTime) {
    // 1. 算出延迟了多久
    double lag = NetworkTime.time - clientNetworkTime;
    
    // 2. 把所有 Hitbox 回滚到那个时间点
    LagCompensationSystem.Rollback(lag);
    
    // 3. 判定
    if (Physics.Raycast(transform.position, direction, out RaycastHit hit)) {
        ApplyDamage(hit.transform);
    }
    
    // 4. 还原现场
    LagCompensationSystem.Restore();
}

第九章:世界切换 (Scene Architecture) —— 也是网络同步的一部分

在单机里你用 SceneManager.LoadScene。在 Mirror 里,这会导致断线。

9.1 全局切换 (Server Change Scene)

当你要把所有玩家从“大厅”拉进“游戏地图”。

// 只能在服务器调用
NetworkManager.singleton.ServerChangeScene("GameMap_Level1");

9.2 增量加载 (Additive Scenes) —— 开放世界

像《原神》或《魔兽世界》那样无缝跑图。


第十章:传输层 (Transports) —— 数据的管道

Mirror 是顶层逻辑,底层怎么发包?这是 Transport 组件管的。

10.1 TCP vs UDP

10.2 KCP Transport (Mirror 默认)

这是目前最强的基于 UDP 的可靠传输协议。

10.3 端口转发 (Port Forwarding)

这是新手在家测试最崩溃的地方。


第十一章:实战作业 —— 制作一个简单的聊天室与计分板

网络游戏不只有打打杀杀,还有 UI 同步。

11.1 结构体同步 (SyncList / SyncStruct)

不要一个个同步变量,要同步结构体

public struct PlayerScore {
    public string playerName;
    public int kills;
    public int deaths;
}

// 必须继承 SyncList
public class SyncListPlayerScore : SyncList<PlayerScore> {}

public class GameManager : NetworkBehaviour {
    // 只要这个列表变了,客户端自动收到通知
    public readonly SyncListPlayerScore scores = new SyncListPlayerScore();

    public override void OnStartClient() {
        // 客户端监听列表变化,刷新 UI
        scores.Callback += OnScoreUpdated;
    }

    void OnScoreUpdated(SyncList<PlayerScore>.Operation op, int index, PlayerScore oldItem, PlayerScore newItem) {
        // 更新 UI 逻辑...
        Debug.Log($"排行榜更新: {op}");
    }
}

第十二章:大坑预警 (Common Pitfalls)

为了让您的新员工少掉几根头发,请让他们背诵:

  1. 不要同步 Transform 的 Scale: NetworkTransform 默认不同步缩放。想变大变小需要自己写 SyncVar。
  2. CharacterController vs NetworkTransform: 这两个甚至会打架。最好在非 LocalPlayer 的物体上禁用 CharacterController,只由 NetworkTransform 插值移动。
  3. 不要在 Awake 里用 NetworkIdentity.connectionToClient: 那时候还没连上网呢!去 OnStartServer 里用。
  4. 随机数同步: 所有的随机种子 (Random Seed) 必须由服务器生成并下发。不然 A 暴击了,B 没暴击,数据就不同步了。

《Unity & Steam 终极开发圣经:虚空到宇宙》

The Ultimate Codex: From Void to Cosmos

当前版本: Vol.4 (Steam 架构、URP 视觉与原生组件补完篇)


第十三章:Steamworks 交互 (The Steam Bridge)

既然项目用了 FizzySteamworks,意味着我们不再用 IP 直连,而是用 Steam ID (P2P) 进行打洞连接。
Fizzy 只是 Mirror 的底层管道 (Transport),它负责传数据。但业务逻辑(比如创建房间、获取头像)需要我们直接调用 Steamworks.NET 的 API。

13.1 核心身份 (Identity)

Steam 的一切都基于 CSteamID (一个 64 位的 ulong 整数)。

13.2 大厅系统 (Lobby Matchmaking)

这是 FizzySteam 联机的核心。Mirror 不管怎么进房间,只管进房间后的同步。怎么进房间是 Steam 的事。


第十四章:原生组件补完计划 (The Missing Parts)

上一卷只列了最基础的。要想完全掌控 Unity,这些组件必须刻在脑子里。

14.1 物理系进阶 (Advanced Physics)

14.2 渲染系进阶 (Advanced Rendering)

14.3 音频系进阶 (Advanced Audio)


第十五章:UGUI 圣经 (The True UI)

云笥阁下英明,XML UI (UI Toolkit) 是给做编辑器的程序员用的,不是给做游戏的艺术家用的。UGUI (Canvas) 才是正统。

15.1 核心架构:Canvas Scaler

这是所有 UI 的根基。

15.2 布局系统 (Auto Layout)

别手动算坐标,用这三个神组件:

  1. Horizontal / Vertical Layout Group:
    • 让子物体自动排成一列。
    • Child Force Expand: 勾选后强行把子物体拉伸填满。
  2. Grid Layout Group: 背包格子专用。
  3. Content Size Fitter:
    • 作用: 这种 UI 容器的大小,由它的内容决定。
    • 场景: 聊天气泡。字越多,气泡背景图越大。

15.3 交互心脏:EventSystem

场景里必须有一个 EventSystem 物体。

15.4 文字的神:TextMeshPro (TMP)

别用那个叫 Text 的组件了,那是垃圾。用 TextMeshPro - UGUI


第十六章:URP 通用渲染管线 (Visual Revolution)

这是您项目的画质核心。Standard Pipeline 已经入土了,HDRP 太重,URP 是唯一的王。

16.1 配置文件 (URP Asset)

URP 的核心不是 Camera,而是那个配置文件 (UniversalRenderPipelineAsset)。

16.2 Shader Graph (着色器图)

别写 HLSL 代码了(除非你是图形学博士)。用连连看。

16.3 VFX Graph (视觉特效图)

抛弃老旧的 Particle System (Shuriken) 吧。

16.4 后处理 (Post-Processing)

这是把“游戏画面”变成“电影”的最后一步。
在 Camera 上勾选 Post Processing,然后挂个 Volume 组件。


第十七章:实战作业 —— 蒸汽朋克风格的联网大厅 UI

任务: 结合 FizzySteam、URP 和 UGUI。

  1. 场景: 搭建一个 URP 场景,放一点 Bloom 泛光。
  2. UI:
    • 使用 UGUI + Layout Group 做一个好友列表。
    • 使用 TextMeshPro 显示好友名字。
    • 点击好友,使用 SteamFriends.InviteUserToGame 发送邀请。
  3. 特效:
    • 背景里用 Shader Graph 做一个旋转的、边缘发光的能量核心。
    • 用 VFX Graph 做一点漂浮的尘埃粒子。

《Unity & Steam 终极开发圣经:虚空到宇宙》

The Ultimate Codex: From Void to Cosmos

当前版本: Vol.5 (核能运算与驾驶舱指南·终章)


第十八章:核能武器 —— Compute Shader (概念篇)

小白以前写的代码都在 CPU 上跑。CPU 像是一个数学博士,算复杂的逻辑很快,但他只有几个脑子(核心)。
Compute Shader 是让代码在 GPU 上跑。GPU 像是一万个小学生

18.1 它是干什么的?

18.2 核心概念

  1. Kernel (核函数): GPU 上运行的那个函数入口。
  2. Dispatch (调度): CPU 命令 GPU:“嘿,启动 1000 个线程组去跑这个函数!”
  3. RWStructuredBuffer: 读写缓冲区。这是 CPU 和 GPU 共享内存的桥梁。CPU 往里填数据,GPU 改数据,CPU 再读回来。

小白要知道: 当你想做草海交互(几万根草被风吹倒)、流体模拟、或者万人同屏时,去搜 “Unity Compute Shader”。


第十九章:驾驶舱指南 —— Unity 编辑器生态 (The Cockpit)

这是小白最容易晕的地方。这几个设置窗口看起来都一样,但改错一个,游戏就废了。

19.1 新输入系统 (New Input System)

位置: Package Manager 安装 -> Project Settings -> Player -> Active Input Handling (选 Both 或 Input System Package)。

为什么要换?

19.2 Project Settings (项目设置) —— 游戏的 DNA

位置: Edit -> Project Settings
这些设置是跟着项目走的。你把项目发给别人,这些设置也会过去。

19.3 Preferences (偏好设置) —— 你的个人习惯

位置: Edit -> Preferences (Mac 在 Unity -> Settings)
这些设置是存在你电脑里的。你把项目发给别人,这些设置不会跟过去。

19.4 Build Settings (构建设置) —— 出炉的烤箱

位置: File -> Build Settings

  1. Scenes In Build (菜单):
    • 极其重要! 只有拖进这个列表的场景,才能被打包。
    • 列表里的 Index 0 是游戏启动后第一个进入的场景(通常是 Login 或 Logo 界面)。
  2. Platform (平台):
    • Windows / Android / iOS / WebGL。
    • Switch Platform: 点击切换。这会花很久时间重新转换资源格式(比如把贴图从 PC 格式转成安卓格式)。
  3. Development Build:
    • 勾选后,打包出来的游戏可以连接 Profiler 性能分析器,屏幕右下角会有 “Development Build” 水印。正式发布一定要去掉!