我想探讨一下在Unity中设计游戏架构时的一些方面,尤其是在不使用现成的基于OOP(面向对象编程)的GameObject技术时。虽然这种方式与传统的Unity方式不同,但我认为从两种方式中都可以学到一些东西。目前我正在开发的游戏的目标之一是非常系统化和模块化的,并且特别依赖于系统设计和“经济”进展。由于我之前大量使用Google表格(我还使用它们进行了第一次混合原型),所以我有很多数据对象,计划将它们导入Unity并解析。为了使Unity的“业务”源代码与“值”的数据对象属性“独立”,我的整个代码都将原子模型数据与字符串包装在结构中。例如:
public readonly struct WeaponId : IEquatable<WeaponId>
{
public readonly string Value;
public readonly int Hash;
public WeaponId(string value)
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("WeaponId cannot be null or empty.", nameof(value));
Value = value;
Hash = Animator.StringToHash(value);
}
public static WeaponId From(string value) => new(value);
public bool Equals(WeaponId other)
{
if (Hash != other.Hash) return false;
return string.Equals(Value, other.Value, StringComparison.Ordinal);
}
}
这种设计很好,因为我可以将“枚举”类型的验证移动到表格中,源代码不需要知道这些标识符可以分配的值。另外,一些类型的谓词函数可以在表格中定义,并在使用NCalc时执行:https://ncalc.github.io/ncalc/articles/index.html 或将它们解析为域函数,假如它们真的必要。然而,对于游戏逻辑架构(与系统数据、实体属性或游戏谓词/修饰符无关的部分),我担心会被enum(这次是直接嵌入源代码中的)和复合标识符淹没。可能是因为我尝试了更多的数据导向编程,所以我没有使用继承,结果是架构的 boilerplate 增加了,并且从这个架构中定义未来实现的概率也增加了人类错误的可能性。有人曾经设计过类似的项目吗?我被迫使用一些源码生成来强制结构并减少从这个架构中实现所需的代码(另一个解决方案可能是使用游戏特性的中间件UI工厂来强制结构?我并不擅长Unity UI API,即使我知道我应该学习新的UI Toolkit)。一个使用大量枚举和复合的架构代码的例子是游戏主体的度量系统。度量系统的中心元素是MetricCacheKey:
public readonly struct MetricPersistentCacheKey : IEquatable<MetricPersistentCacheKey>
{
public readonly SubjectRef Subject;
public readonly MetricId MetricId;
public readonly MetricEvaluationCacheDomain CacheDomain;
// ...
}
(为了澄清一些编码惯例:任何以“Id”结尾的东西都是在设计时验证过的,所以源代码不需要知道它可以分配的值,任何以“Ref”结尾的东西都是游戏逻辑中的主要实体,可以是复合的,任何以“Key”结尾的东西都是用于架构目的的复合类型,例如在字典或集合中进行索引)。这个关键用于确定要保存和检索的特定读模型值(在CQRS类似设计模式中)的哪个主题和哪个分支(域)。SubjectRef是带有枚举“kind”的标签联合,它根据枚举“kind”指定如何使用或必须使用它的客户端:
public readonly struct SubjectRef : IEquatable<SubjectRef>
{
public SubjectKind Kind { get; }
public ActorId Actor { get; }
public PopulationId Population { get; }
}
我对这个设计的担忧是,当变异的数量很少时,它是可以接受的,因为随着时间的推移,使用标签联合时很容易错误地使用它,因为没有在多态中强制的规则。
评论 (0)