这篇文章来自 人选 Laugh。Laugh 专注游戏研发及管理 15 年,目前在巨人网络担任技术负责人、专家,负责 MMO 类型的架构设计、产线设计及团队建设。他在 Unity 官方开发者社区发布了“主程必杀技”系列文章,探索使用 Source Generator 和 Analyzer 在 Unity 中实现代码规范框架要求的方式,帮助主程更高效地进行技术管理。
点击阅读原文,可以访问 Laugh 在 Unity 官方开发者社区的个人主页,阅读更多技术干货。
在本系列的前三篇文章中,我们通过开发 Analyzer 尝试了 包括逻辑分支 (if...else...) 的写法,并实现了 、自动化执行 等要求,不符合要求的代码会直接报 Error 且无法编译,方便主程进行代码规范管理和框架要求,而不再需要花费大量时间 Review。
本文为大家带来 Laugh 的“主程必杀技”系列文章第四篇《 Unity C# 自动化执行代码规范-检查继承关系 》。更多技术分享,请访问 Laugh 的 Unity 官方开发者社区主页 ,持续关注学习。
https://developer.unity.cn/u/xiao-wei-58?tab=article
演示代码
获取链接:
https://gitee.com/palang/unity-sharp-code-regulator.git
C# Code Analyzer 入门系列文章
⦁ Analyzer 初体验:强制使用大括号包括逻辑分支 (if...else...)
⦁ 强制加注释(上)类注释 | 强制加注释(下)方法注释
⦁ 命名规范检查(一)命名空间和类名
⦁ 命名规范检查(二)public 和 private 方法名称规范检查
⦁ 命名规范检查(三)成员变量命名规范检查
⦁ 命名规范检查(四)临时变量命名检查
⦁ 检查继承关系
Unity C# 自动化执行代码规范
检查继承关系
Unity 中,很多类,我们不希望继承自 MonoBehaviour(主要考虑到性能和自己控制生命周期)。比如我们的 MVC 中的 Controller,或者战斗系统中的 SkillLogic,希望脱离 Unity 的 Monobehavior 及其生命周期的控制,通常会自定义帧驱动逻辑。
看本篇文章之前,请先阅读 ,环境准备工作也与前几篇保持一致。
实现目标
为了实现自己控制的帧驱动逻辑,我在项目定义了 IFrameDrivable 接口(其中有OnFrameUpdate 等方法,不是这里的重点,可以忽略)。
我希望实现 IFrameDrivable 的类,不能继承自 MonoBehaviour 类。
编写 Analyzer 分析器
首先在类 DianogsticIDs 中定义新的错误 ID:
public const string FORCE_NAMING_CONVENTIONS_ID= "FERR1004";然后在 AnalyzerReleases.Unshipped.md 文件添加该规则,如下:
定义分析器类
新建分析器类 ForbidInheritFromMonoBehavor.cs,内容如下(具体步骤请看代码中的注释)
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis;
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.Immutable;
using Analyzer;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Linq;
namespace CdeAnalyzer
{
/**
* Author: Laugh(笑微)
* https://developer.unity.cn/projects/65937455edbc2a001cbd8102
*/
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal class ForbidInheritFromMonoBehavor : DiagnosticAnalyzer
{
///
/// 错误描述
///
private static readonly DiagnosticDescriptor ForbidInheritDescriptor =
new DiagnosticDescriptor(
DianogsticIDs.FORCE_FORBID_INHERIT_MONOBEHAVIOR_ID, // ID
"实现了IFrameDrivable接口的类,不能继承自MonoBehavior", // Title
"实现了IFrameDrivable接口的类,不能继承自MonoBehavior", // Message format
DiagnosticCategories.Criterion, // Category
DiagnosticSeverity.Error, // Severity
isEnabledByDefault: true // Enabled by default
);
public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(ForbidInheritDescriptor);
///
/// 初始化分析方法
///
///
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxTreeAction(AnalyzeSymbol);
}
///
/// 检测方法
///
///
private static void AnalyzeSymbol(SyntaxTreeAnalysisContext context)
{
//找到文档的语法根树
var root = context.Tree.GetRoot(context.CancellationToken);
if (ConstraintDefinition.ExcludeAnalize(context.Tree.FilePath))
{//排除特殊目录
return;
}
var classList = root.DescendantNodes()?.OfType ();
foreach(var cls in classList)
{//遍历语法树中的所有类
var baseClsList = cls.BaseList?.ChildNodes()?.OfType ();
if (baseClsList == null)
{
break;
}
bool isMonoBehavior = false;
bool isFrameDrivable = false;
foreach (var bcls in baseClsList)
{
var idName = bcls.ChildNodes()?.OfType ()?.First();
var bname = idName.ToString();
if(bname == "IFrameDrivable")
{//检查是否实现了接口IFrameDrivable
isFrameDrivable = true;
}
else if (bname == "ArrayList")
{//检查是否继承自MonoBehavior
isMonoBehavior = true;
}
}
if (isFrameDrivable && isMonoBehavior)
{
//报错
var diagnostic = Diagnostic.Create(ForbidInheritDescriptor, cls.GetFirstToken().GetLocation());
context.ReportDiagnostic(diagnostic);
}
}
}
}
}Laugh 是 人选。Unity 价值专家(UVP)是通过原创作品启发国内创作者的 Unity 专业人员,欢迎提名/自荐。
https://unity.cn/uvp
阔别重逢,终于回来了!
7月23日-25日,Unite 将于上海隆重开启。1 场 Keynote 和多个专场,涵盖团结引擎、游戏生态、数字孪生、智能座舱等多重赛道,我们还准备了小而美的工作坊,覆盖小游戏、开源鸿蒙、Vision Pro三大主题。
走过路过别错过。
扫码购票处
Unity 官方微信
第一时间了解Unity引擎动向,学习进阶开发技能
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.