跳转至

2025

Odin Toolkits 的编码规范智能体提示词参考

角色定位

你是一位深耕 Unity 开发与 C# 编程的资深规范专家,精通行业通用标准(如 Microsoft C# 命名规范、Unity 官方编码指南)及大型项目实践经验,同时也是 Rider IDE 使用者,一切以 Rider 使用为前提。你的核心职责是:为开发者提供符合工程化标准的代码规范建议、识别潜在的规范问题、优化代码结构,并结合 Unity 引擎特性(如组件生命周期、资源管理、性能优化)给出针对性指导。

语气风格

  • 专业严谨:基于明确的规范依据(如引用具体条款),避免模糊表述;
  • 务实易懂:用开发者熟悉的术语(如 “MonoBehaviour 生命周期”“协程”)解释规范,而非纯理论;
  • 建设性:指出问题时同步提供修改方案,例如 “此处建议使用 PascalCase 命名类,改为 PlayerController 更符合规范”;

工作流程

  1. 需求接收:明确开发者的诉求(如 “检查这段代码的规范问题”“制定团队 Unity 代码规范”“优化 MonoBehaviour 脚本结构”);

  2. 场景分析:结合上下文判断代码用途(如运行时逻辑 / 编辑器工具 / 数据模型),因场景调整规范侧重(例如编辑器脚本允许更多 static 方法,而运行时脚本需严格管理生命周期);

  3. 规范校验:从以下维度逐层检查(优先级递减):

  4. 基础规范:命名规则(类 / 方法 / 变量的大小写)、代码格式(缩进、空行)、注释完整性(XML 注释);

  5. 遵守 SOLID 原则
  6. Unity 特性规范:组件设计(单一职责原则)、生命周期调用(如 Awake/Start 的分工)、资源释放(OnDestroy 清理、避免空引用);
  7. 性能与安全性:避免闭包导致的内存泄漏、合理使用 [SerializeField] 而非 public 字段、减少 Find 系列方法滥用;
  8. 可扩展性:接口 / 抽象类的合理使用、避免硬编码(使用 ScriptableObject 存储配置);

  9. 建议输出:按 “问题描述 + 规范依据 + 修改示例” 的结构呈现,例如:

问题:私有字段 playerSpeed 使用了 camelCase 但未加前缀依据:Unity 团队规范中,私有字段建议添加 _ 前缀(如 _playerSpeed)以区分局部变量修改示例:private float _playerSpeed = 5f;

  1. 补充说明:对有争议的规范(如 “是否必须使用 var”),说明不同方案的适用场景(小型项目可灵活使用 var 简化代码,大型项目建议显式声明类型以提升可读性)。

核心规则与规范

1. C# 基础规范

  • 命名

  • 类 / 结构体 / 枚举:PascalCase(如 PlayerData EnemyState);

  • 方法 / 属性:PascalCase(如 MoveToTarget() IsGrounded);
  • 局部变量 / 参数:camelCase(如 float damageValue);
  • 私有字段:_camelCase(如 private int _health),常量:UPPER_SNAKE_CASE(如 const float MAX_SPEED = 10f);
  • 序列化命名:序列化的私有字段,采用 camelCase(如 float damageValue
  • 格式:缩进使用 4 个空格(而非 Tab),语句块 braces 另起一行(避免行内 braces);
  • 注释:类 / 公共方法必须添加 XML 注释,复杂逻辑(如算法、状态切换)需添加行内注释。

2. Unity 特有规范

  • 组件设计:一个 MonoBehaviour 只负责单一功能(如 PlayerMovement 仅处理移动,PlayerHealth 处理生命值);
  • 生命周期管理
  • Awake:初始化变量(不依赖其他对象),Start:处理依赖初始化(如获取其他组件引用);
  • 避免在 Update 中执行 heavy 操作,高频逻辑使用 FixedUpdate
  • 字段序列化:非公开字段需序列化时使用 [SerializeField],而非将字段设为 public;
  • 资源与引用:通过 [RequireComponent] 声明依赖组件,使用 GetComponent 缓存引用(而非每帧获取)。

3. XML 注释添加规范

  • 默认只添加 summary 部分
  • 如果没有特别要求,不要添加 remark 和 example

Try-Catch 实践指南:在 Unity C# 中优雅处理错误

引言

在 Unity 开发中,错误处理至关重要。一个健壮的游戏应该能够优雅地处理意外情况,而不是直接崩溃或产生难以调试的行为。Try-Catch 作为 C# 中的核心异常处理机制,配合 Odin Inspector 插件,可以帮助我们构建更稳定、更易于调试的 Unity 项目。

一、Try-Catch 基础用法

基本语法

try
{
    // 可能发生错误的代码
    riskyOperation();
}
catch (SpecificException ex)
{
    // 处理特定类型的异常
    Debug.LogError($"发生特定错误: {ex.Message}");
}
catch (Exception ex)
{
    // 处理其他所有异常
    Debug.LogError($"发生错误: {ex.Message}");
}
finally
{
    // 无论是否发生错误都会执行的代码
    cleanupResources();
}

在 Unity 中的基本实践步骤

  1. 识别风险代码块 - 确定哪些操作可能失败:
  2. 资源加载(Resources.LoadAssetBundle.Load
  3. 文件操作(File.ReadAllText
  4. 网络请求
  5. 反射操作
  6. 解析数据(JSON、XML)
  7. 捕获具体异常 - 始终优先捕获具体异常类型:
try
{
    var prefab = Resources.Load<GameObject>("MyPrefab");
    Instantiate(prefab);
}
catch (ResourceLoadException ex)
{
    Debug.LogError($"资源加载失败: {ex.Message}");
}
catch (ArgumentNullException ex)
{
    Debug.LogError($"实例化失败,预制体为空: {ex.Message}");
}
  1. 适当处理或传递异常 - 根据情况决定是在当前位置处理异常还是向上传递:
// 向上传递异常让调用者处理
public GameObject LoadAndInstantiate(string resourcePath)
{
    try
    {
        var prefab = Resources.Load<GameObject>(resourcePath);
        if (prefab == null)
            throw new Exception($"未找到资源: {resourcePath}");

        return Instantiate(prefab);
    }
    catch (Exception ex)
    {
        // 添加额外上下文信息后重新抛出
        throw new Exception($"加载并实例化资源失败: {resourcePath}", ex);
    }
}

二、Unity 中的常见应用场景

1. 资源加载

public T LoadResource<T>(string path) where T : UnityEngine.Object
{
    try
    {
        T resource = Resources.Load<T>(path);
        if (resource == null)
        {
            throw new System.Exception($"资源加载失败: {path},类型: {typeof(T).Name}");
        }
        return resource;
    }
    catch (System.Exception ex)
    {
        Debug.LogError($"[ResourceLoader] 加载资源时出错: {ex.Message}");
        return null;
    }
}

2. 协程中的错误处理

IEnumerator LoadDataCoroutine(string url)
{
    UnityWebRequest request = UnityWebRequest.Get(url);

    try
    {
        yield return request.SendWebRequest();

        if (request.result != UnityWebRequest.Result.Success)
        {
            throw new System.Exception($"网络请求失败: {request.error}");
        }

        ProcessData(request.downloadHandler.text);
    }
    catch (System.Exception ex)
    {
        Debug.LogError($"[DataLoader] 加载数据失败: {ex.Message}");
        ShowErrorMessageToPlayer("数据加载失败,请稍后重试");
    }
    finally
    {
        request.Dispose();
    }
}

3. 数据解析

public T ParseJson<T>(string jsonString)
{
    try
    {
        return JsonUtility.FromJson<T>(jsonString);
    }
    catch (System.ArgumentException ex)
    {
        Debug.LogError($"[JsonParser] JSON 解析失败: {ex.Message}");
        Debug.LogError($"无效的 JSON: {jsonString}");
        return default(T);
    }
}

三、结合 Odin Inspector 增强错误处理

Odin Inspector 可以帮助我们在编辑器中更好地处理和展示错误信息,提升开发效率。

1. 在 Inspector 中显示错误信息

using Sirenix.OdinInspector;
using UnityEngine;

public class ErrorHandlingExample : MonoBehaviour
{
    [SerializeField] private string resourcePath;

    [ShowInInspector, ReadOnly] private string lastError;

    [Button("加载资源")]
    public void LoadResource()
    {
        try
        {
            lastError = string.Empty; // 清除之前的错误
            var resource = Resources.Load<GameObject>(resourcePath);

            if (resource == null)
                throw new System.Exception("资源为空或不存在");

            Instantiate(resource, transform);
        }
        catch (System.Exception ex)
        {
            lastError = ex.Message; // 在 Inspector 中显示错误
            Debug.LogError($"加载失败: {ex.Message}");
        }
    }
}

2. 条件验证与错误提示

using Sirenix.OdinInspector;
using UnityEngine;

public class DataProcessor : MonoBehaviour
{
    [SerializeField, Required("数据文件路径不能为空!")]
    private string dataFilePath;

    [Button("处理数据")]
    [GUIColor(0.8f, 0.2f, 0.2f)]
    public void ProcessData()
    {
        try
        {
            // 使用 Odin 的验证确保路径有效
            if (string.IsNullOrEmpty(dataFilePath))
                throw new System.Exception("数据文件路径不能为空");

            string data = System.IO.File.ReadAllText(dataFilePath);
            // 处理数据...

            Debug.Log("数据处理成功");
        }
        catch (System.Exception ex)
        {
            // 在编辑器中显示错误弹窗
            Sirenix.Utilities.Editor.EditorUtility.ShowDialog(
                "处理失败", 
                ex.Message, 
                "确定");
        }
    }
}

3. 错误日志与重试机制

using Sirenix.OdinInspector;
using System;
using UnityEngine;

public class NetworkManager : MonoBehaviour
{
    [SerializeField] private string apiUrl;

    [ShowInInspector, FoldoutGroup("错误日志")]
    private string lastErrorLog;

    [Button("发送请求")]
    public void SendRequest()
    {
        StartCoroutine(TrySendRequest());
    }

    private System.Collections.IEnumerator TrySendRequest()
    {
        int retryCount = 0;
        const int maxRetries = 3;

        while (retryCount < maxRetries)
        {
            try
            {
                using (var request = UnityWebRequest.Get(apiUrl))
                {
                    yield return request.SendWebRequest();

                    if (request.result != UnityWebRequest.Result.Success)
                    {
                        throw new Exception($"请求失败: {request.error}");
                    }

                    Debug.Log("请求成功: " + request.downloadHandler.text);
                    lastErrorLog = "请求成功";
                    yield break;
                }
            }
            catch (Exception ex)
            {
                retryCount++;
                lastErrorLog = $"尝试 {retryCount}/{maxRetries} 失败: {ex.Message}";
                Debug.LogWarning(lastErrorLog);

                if (retryCount >= maxRetries)
                {
                    lastErrorLog = $"最终失败: {ex.Message}";
                    ShowErrorPopup(lastErrorLog);
                }
                else
                {
                    // 等待后重试
                    yield return new WaitForSeconds(1f);
                }
            }
        }
    }

    [Button("显示错误弹窗"), DisableIf("IsLastErrorEmpty")]
    private void ShowErrorPopup(string message)
    {
        Sirenix.Utilities.Editor.EditorUtility.ShowDialog(
            "网络错误", 
            message, 
            "确定");
    }

    private bool IsLastErrorEmpty()
    {
        return string.IsNullOrEmpty(lastErrorLog) || lastErrorLog.Contains("成功");
    }
}

四、Try-Catch 的强大之处

1. 防止游戏崩溃,提升用户体验

Unity 游戏中未处理的异常可能导致:

  • 编辑器崩溃
  • 游戏在某些设备上闪退
  • 玩家进度丢失

Try-Catch 可以捕获这些异常,让游戏有机会优雅地恢复或提示用户。

2. 精确定位问题,加速调试

通过捕获异常并记录详细信息(包括堆栈跟踪),可以快速定位问题:

try
{
    // 可能出错的代码
}
catch (Exception ex)
{
    Debug.LogError($"错误: {ex.Message}\n堆栈跟踪: {ex.StackTrace}");
    // 可以将错误信息发送到服务器进行分析
}

3. 分离正常逻辑与错误处理

没有 Try-Catch 时,代码可能充斥着各种检查:

// 没有 Try-Catch 的代码
var prefab = Resources.Load<GameObject>(path);
if (prefab == null)
{
    Debug.LogError("资源为空");
    return;
}

var instance = Instantiate(prefab);
if (instance == null)
{
    Debug.LogError("实例化失败");
    return;
}

var component = instance.GetComponent<MyComponent>();
if (component == null)
{
    Debug.LogError("组件缺失");
    Destroy(instance);
    return;
}

使用 Try-Catch 后,代码更清晰:

// 使用 Try-Catch 的代码
try
{
    var prefab = Resources.Load<GameObject>(path) ?? 
        throw new Exception("资源为空");

    var instance = Instantiate(prefab) ?? 
        throw new Exception("实例化失败");

    var component = instance.GetComponent<MyComponent>() ?? 
        throw new Exception("组件缺失");

    // 正常逻辑...
}
catch (Exception ex)
{
    Debug.LogError($"操作失败: {ex.Message}");
    // 清理工作
}

4. 实现复杂的错误恢复策略

Try-Catch 使实现重试、回退等复杂错误处理策略变得简单:

// 带重试机制的资源加载
public T LoadWithRetry<T>(string path, int maxRetries = 3) where T : UnityEngine.Object
{
    int attempts = 0;

    while (attempts < maxRetries)
    {
        try
        {
            attempts++;
            var resource = Resources.Load<T>(path);

            if (resource == null)
                throw new Exception("资源加载返回空");

            return resource;
        }
        catch (Exception ex)
        {
            Debug.LogWarning($"尝试 {attempts} 加载失败: {ex.Message}");

            if (attempts >= maxRetries)
            {
                Debug.LogError("达到最大重试次数,加载失败");
                return null;
            }

            // 等待一小段时间再重试
            System.Threading.Thread.Sleep(100);
        }
    }

    return null;
}

五、最佳实践与注意事项

  1. 不要过度使用 Try-Catch - 只在可能发生不可预测错误的地方使用,不要用它替代正常的条件检查。
  2. 避免捕获所有异常 - 不要使用空的 catch {} 或捕获 Exception 而不做处理,这会隐藏真正的问题。
  3. 注意性能影响 - Try-Catch 会带来轻微的性能开销,避免在每帧执行的代码(如 Update)中过度使用。
  4. 清理资源 - 始终在 finally 块中释放资源(如文件流、网络请求)。
  5. 提供有用的错误信息 - 错误信息应包含足够的上下文,帮助调试。
  6. 区分开发和生产环境 - 在开发环境中可以抛出详细错误,在生产环境中则应显示友好提示并记录错误。
try
{
    // 风险操作
}
catch (Exception ex)
{
    // 开发环境 - 显示详细错误
#if UNITY_EDITOR
    Debug.LogError($"详细错误: {ex}");
    Sirenix.Utilities.Editor.EditorUtility.ShowDialog("错误", ex.Message, "确定");
#else
    // 生产环境 - 显示友好信息
    Debug.LogError($"错误: {ex.Message}");
    ShowPlayerFriendlyError("发生错误,请重试或联系支持");
#endif
}

结语

Try-Catch 是 Unity 开发中处理错误的强大工具,当与 Odin Inspector 结合使用时,不仅能让你的游戏更健壮,还能显著提升开发效率和调试体验。通过合理使用异常处理,你可以构建出更稳定、更专业的 Unity 游戏。

记住,良好的错误处理不是事后弥补,而是应该在开发初期就纳入设计考量的重要部分。

C# 编码建议

C# 是一种非常灵活且功能丰富的语言,它提供了属性、方法、继承、多态等丰富的特性,使得开发者可以根据具体需求选择不同的方式来构建高效、可维护的代码。本文提供一些编码建议,帮助开发者形成统一的编码风格。

C# SOLID 原则完整指南:从理论到 Unity 实战

在 Unity 游戏开发中,随着项目规模的扩大,代码的可维护性、可扩展性和可测试性变得至关重要。SOLID 原则作为面向对象设计的五大基石,为我们提供了构建高质量代码的指导方针。本文将结合 Unity 开发实践和 Odin Inspector 工具,深入探讨如何在 C# 项目中应用这些原则。