这篇按 Unity 写脚本时最常查的 C# 写法整理:字段怎么暴露、集合怎么查、事件怎么解绑、null 怎么防、哪些写法别放进 Update。
快速索引
按需求查写法
| 我想做什么 |
常用写法 |
| Inspector 可调但外部不能乱改 |
[SerializeField] private |
| 外部只读内部可改 |
public int Hp { get; private set; } |
| 常量和只读值 |
const、readonly |
| 安全访问 Dictionary |
TryGetValue |
| 遍历时删除 List 元素 |
倒序 for |
| 表示能力 |
interface |
| 表示有限状态 |
enum |
| 订阅事件 |
+= |
| 取消事件 |
-= |
| 安全触发委托 |
?.Invoke() |
| 判断组件是否存在 |
TryGetComponent |
| 判断 Unity 对象是否销毁 |
if (obj == null) |
| 低频筛选集合 |
LINQ |
| 高频筛选集合 |
手写 for |
0. 字段、属性、常量
1 2 3 4 5 6 7 8 9 10
| [SerializeField] private int maxHp = 100; [SerializeField] private Transform target;
private int currentHp;
public int CurrentHp => currentHp; public bool IsDead => currentHp <= 0;
private const int MaxLevel = 99; private readonly float spawnRadius = 5f;
|
1 2
| public int Score { get; private set; } public string PlayerName { get; set; }
|
不建议:
1 2
| public int hp; public bool isDead;
|
1. class / struct / interface / enum
1 2 3 4
| public class Inventory { private readonly List<ItemData> items = new(); }
|
1 2 3 4 5
| public readonly struct DamageInfo { public readonly int Amount; public readonly Vector3 HitPoint; }
|
1 2 3 4
| public interface IDamageable { void TakeDamage(int amount); }
|
1 2 3 4 5 6 7 8
| public enum GameState { Boot, MainMenu, Playing, Paused, GameOver }
|
2. List 常用操作
1 2 3 4 5 6 7 8 9 10
| private readonly List<GameObject> enemies = new();
enemies.Add(enemy); enemies.Remove(enemy); enemies.Clear();
if (enemies.Count > 0) { GameObject first = enemies[0]; }
|
遍历时删除:
1 2 3 4 5 6 7
| for (int i = enemies.Count - 1; i >= 0; i--) { if (enemies[i] == null) { enemies.RemoveAt(i); } }
|
3. Dictionary 常用操作
1 2 3 4 5 6 7 8 9 10 11 12 13
| private readonly Dictionary<int, ItemData> itemMap = new();
itemMap[item.Id] = item;
if (itemMap.TryGetValue(id, out ItemData data)) { Debug.Log(data.Name); }
if (itemMap.ContainsKey(id)) { itemMap.Remove(id); }
|
不建议:
1
| ItemData data = itemMap[id];
|
4. 委托、Action、事件
1 2 3
| public Action<int> OnHpChanged;
OnHpChanged?.Invoke(currentHp);
|
1 2 3 4 5 6 7
| public event Action<int> HpChanged;
private void SetHp(int value) { currentHp = value; HpChanged?.Invoke(currentHp); }
|
生命周期里订阅:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| private void OnEnable() { player.HpChanged += OnHpChanged; }
private void OnDisable() { player.HpChanged -= OnHpChanged; }
private void OnHpChanged(int hp) { hpText.text = hp.ToString(); }
|
少用:
1
| button.onClick.AddListener(() => DoSomething());
|
5. null 与 Unity 对象判断
1 2 3 4 5
| if (target == null) { Debug.LogWarning("target 未设置", this); return; }
|
1 2 3 4
| if (TryGetComponent(out Rigidbody rb)) { rb.AddForce(Vector3.up, ForceMode.Impulse); }
|
1 2
| string nameText = playerName ?? "Unknown"; player?.TakeDamage(10);
|
Unity 特别注意:
1 2 3 4 5 6
| UnityEngine.Object obj = target;
if (obj == null) { return; }
|
6. LINQ 使用边界
低频可用:
1 2 3
| var aliveEnemies = enemies .Where(e => e != null && e.activeInHierarchy) .ToList();
|
高频改手写:
1 2 3 4 5 6 7 8 9 10
| aliveEnemies.Clear();
for (int i = 0; i < enemies.Count; i++) { GameObject enemy = enemies[i]; if (enemy != null && enemy.activeInHierarchy) { aliveEnemies.Add(enemy); } }
|
7. 字符串与格式化
1
| Debug.Log($"hp={currentHp}, max={maxHp}");
|
热路径慎用:
1 2 3 4 5 6
| private readonly StringBuilder builder = new();
builder.Clear(); builder.Append("Score: "); builder.Append(score); scoreText.text = builder.ToString();
|
8. 异常处理
1 2 3 4 5 6 7 8
| try { LoadConfig(path); } catch (IOException ex) { Debug.LogError($"配置读取失败 path={path}\n{ex}"); }
|
不建议:
1 2 3 4 5 6 7 8
| try { LoadConfig(path); } catch { }
|
9. 命名速查
1 2 3 4 5 6 7
| PlayerHealth # 类名 PascalCase TakeDamage # 方法 PascalCase currentHp # 私有字段 camelCase maxHp # SerializeField 字段 camelCase CurrentHp # 属性 PascalCase IDamageable # 接口 I 开头 OnHpChanged # 事件处理方法 On + 事件名
|
10. Unity 脚本常用模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| using UnityEngine;
public sealed class PlayerMover : MonoBehaviour { [SerializeField] private Transform target; [SerializeField] private float moveSpeed = 5f;
private void Awake() { if (target == null) { Debug.LogWarning("target 未设置", this); } }
private void Update() { if (target == null) { return; }
transform.position = Vector3.MoveTowards( transform.position, target.position, moveSpeed * Time.deltaTime ); } }
|
11. 常见坑速查
1 2 3 4 5 6 7
| public 字段到处暴露 # 状态容易被外部乱改 Dictionary[key] 直接取 # key 不存在直接异常 foreach 里删除 List # 集合被修改会异常 OnEnable 订阅事件但不 OnDisable 取消 # 场景切换后旧对象还响应 Update 里 LINQ # 容易产生 GC catch 后不打印 ex # 报错线索被吞 Unity 对象 Destroy 后继续访问 # MissingReferenceException
|
系列导航