第 1 章 Unity C# Refresher:Unity 语境下的 C# 复习

覆盖脚本创建与实例化、变量、条件、数组、循环、函数、事件、类、继承、多态、属性、可见性和消息派发。

问题:为什么高级脚本书仍用一整章复习 C# 基础

先预测:一个 .cs 文件已经通过编译,是否意味着场景里已经存在可运行对象?不是。原章把语言结构放回 Unity 的资产与组件生命周期:文件定义类,类被编译,脚本挂到 GameObject 后才形成组件实例。变量、条件、数组、循环和函数不是语法清单,而是后续调试、AI、编辑器和资源案例共同使用的最小表达工具。

原书边界与官方小节

官方目录从 Why C#?、Creating script files、Instantiating scripts 开始,依次覆盖 Variables、Conditional statements、Arrays、Loops、Functions、Events;随后进入 Classes and object-oriented programming、inheritance、polymorphism、C# properties、Commenting、Variable visibility、The ? operator,以及 SendMessage and BroadcastMessage。它是复习章,不是完整 C# 教科书。

  • Why C#?;Creating script files;Instantiating scripts
  • Variables;Conditional statements;Arrays;Loops;Functions;Events
  • Classes and object-oriented programming;Classes and inheritance;Classes and polymorphism
  • C# properties;Commenting;Variable visibility;The ? operator
  • SendMessage and BroadcastMessage

这些小节按官方目录唯一归属。本站重新组织解释、代码和实验,不逐字复制原文;现代补充必须放在迁移段,不能替换或重复计算原始单元。

从脚本资产到组件实例

C# 文件是 Project 中的源码资产,主类名需要与文件名匹配;编译成功只产生类型,拖到 GameObject 或 AddComponent 才产生实例。实例字段保存对象状态,方法表达行为,事件向外发布变化,继承与多态允许消费者面向契约工作。属性可约束赋值,却不会自动显示在旧版 Inspector;可见性与序列化是两套规则,public 不是唯一暴露数据的方法。

五个核心术语与责任边界

必须放回本章责任链:指出输入来自哪里、状态由谁拥有、结果由谁消费,以及错误时先观察哪一个信号。

必须放回本章责任链:指出输入来自哪里、状态由谁拥有、结果由谁消费,以及错误时先观察哪一个信号。

必须放回本章责任链:指出输入来自哪里、状态由谁拥有、结果由谁消费,以及错误时先观察哪一个信号。

必须放回本章责任链:指出输入来自哪里、状态由谁拥有、结果由谁消费,以及错误时先观察哪一个信号。

必须放回本章责任链:指出输入来自哪里、状态由谁拥有、结果由谁消费,以及错误时先观察哪一个信号。

术语若不能落到具体对象、状态和观测点,就仍停留在记忆层。读者应能为每个术语写出生产者、变换规则、消费者、正常值、边界值和失败信号。

关键对象与数据流

最小实验从文件名和类名开始,创建两个 GameObject 挂同一脚本,证明它们共享类型但不共享实例字段。再用基类或接口统一调用不同行为,使用事件发布状态变化。最后故意拼错 SendMessage 名称,与编译期接口调用对照,观察字符串派发为何难以重构和验证。

排查时从最早可控输入开始,逐段检查中间状态和最终消费者。最终结果正确但中间链不符合预测,也不能立即签发,因为它可能依赖未记录的缓存、时序或 Editor 状态。

用同一需求比较五种协作方式

设定一个固定需求:角色受伤后扣减生命值,同时更新界面并通知音效。直接引用最容易追踪,但要求调用方知道具体组件;接口保留编译期检查并允许替换实现;C# event 让多个消费者解耦,却要求严格管理订阅;UnityEvent 便于 Inspector 配置,但重命名和资源引用必须进入场景验收;SendMessage 不需要静态引用,却把方法名和参数检查推迟到运行时。比较时应固定输入和消费者,只改变派发方式,记录缺失接收者、重命名和销毁对象后的行为。

组件实例实验还要区分四类状态:普通实例字段由每个组件独立持有,static 字段由类型共享,序列化字段进入场景或预制体数据,属性只定义代码访问规则。复制 GameObject、重新进入 Play Mode、关闭 Domain Reload 或重新加载场景时,这四类状态的重置方式并不相同。若实验没有记录对象 instance ID、场景身份和播放选项,就无法判断观察到的是语言语义还是 Editor 配置。

代码审查时逐项追问

  • 文件名与 MonoBehaviour 主类型名是否一致,类型是否真的被挂载或动态创建。
  • 字段是实例状态、全局状态还是需要持久化的数据,所有者是否唯一。
  • public 是为了 API,还是仅为了 Inspector;后者应优先序列化私有字段。
  • 继承是否表达真正的可替换关系,还是可以用组件组合和接口降低层级耦合。
  • 事件和消息的消费者何时注册、何时注销,销毁后是否仍能被调用。

最终证据不应只有一段“能运行”的脚本。至少保存两个实例的不同状态、一次接口替换、一次事件有无订阅者的对照、一次属性拒绝非法值,以及一次故意拼错字符串消息的失败日志。这样才能分别证明类型检查、实例隔离、封装约束和动态派发边界。

正常、边界与失败样本

每章至少保存一个正常样本、一个极端边界和一个故意破坏配置。正常样本证明主路径,边界样本说明适用范围,失败样本证明诊断方法真的能抓到错误。

从 2015 年到现代 Unity

UnityScript 与 Boo 已退出现代 Unity,C# 成为统一脚本语言;MonoDevelop 通常被 Visual Studio 或 Rider 替代。语言不变量仍是类型、实例、状态、控制流、函数、事件与对象模型。现代项目优先接口、直接引用、UnityEvent 或 C# event,SendMessage 只作为历史机制与特殊动态边界保留。

迁移记录采用五列:原书载体、稳定不变量、现代实现、不可等价处、目标平台证据。允许替换工具和 API,不允许抹去原始问题或倒写历史。

验收证据

验收包含两个组件实例状态隔离、一次多态调用、一次属性验证、一次事件通知和一次故意失败的字符串消息。日志要能指出文件、类型、实例与消费者,证明读者知道编译成功、挂载成功和行为成功是三道不同门。

证据包同时记录场景路径、代码版本、参数、复现步骤、预期和实际结果。交给另一位读者后不需要猜测隐藏状态即可重放,才算掌握而不只是读完。

小结

  • 脚本文件定义类型,挂载后才形成拥有独立状态的组件实例
  • 基础语言结构必须落到可观察的游戏状态转换
  • 继承、多态、属性和可见性解决不同边界,不能混用
  • SendMessage 是历史动态派发工具,现代代码应优先可检查契约

讨论

评论区加载中…