小黄油框架轻量版,采用工厂模式与事件驱动。
继承自BaseObject的游戏对象类可以被ObjectFactory工厂进行全生命周期管理,开发者不需要对其进行额外维护。
例如,我们定义游戏的角色类Character:
public class Character : BaseObject<Character>
{
// 确保构造函数为public
public Character(uint id)
: base(id)
{
}
}
我们便可以通过ObjectFactory实例化Character类的对象:
var character = ObjectFactory.Instance.Create<Character>();
变量character是指向Character对象的ObjectReference,通过其Object属性可访问Character对象:
var characterObject = character.Object;
注意,继承自BaseObject的类无法通过new关键字实例化:
var characterObject = new Character(newID); // 将抛出InvalidOperationException
使用ObjectReference作为参数,可以删除工厂中的对象:
var character = ObjectFactory.Instance.Create<Character>();
ObjectFactory.Instance.Remove(character); // 删除character指向的对象
删除对象后,ObjectReference.IsValid将返回False,且ObjectReference.Object将返回null。
我们定义以下游戏对象类:
// 角色抽象基类
public abstract class Character : BaseObject<Character>
{
public Character(uint id)
: base(id)
{
}
public abstract string Name { get; }
}
// 英雄类,继承自角色基类
public class Hero : Character
{
public Hero(uint id)
: base(id)
{
}
public override string Name => "Hero";
}
// 怪物类,继承自角色基类
public class Monster : Character
{
public Monster(uint id, int attack)
: base(id)
{
Attack = attack;
}
public override string Name => "Monster";
public int Attack { get; }
}
可以通过ObjectFactory的Query方法进行对象查询,示例如下:
// 实例化游戏对象
ObjectFactory.Instance.Create<Hero>();
ObjectFactory.Instance.Create<Monster>(1);
ObjectFactory.Instance.Create<Monster>(3);
// 按类型查询
var allObjects = ObjectFactory.Instance.Query<IBaseObject>(); // 返回工厂中所有对象实例的ObjectReference,注意这里需要使用接口IBaseObject进行查询
var allCharacters = ObjectFactory.Instance.Query<Character>(); // 返回工厂中所有角色类实例的ObjectReference,在这个例子中查询结果与上一条查询相同
var allHeros = ObjectFactory.Instance.Query<Hero>(); // 返回工厂中所有英雄类实例的ObjectReference
// 条件查询
var validMonsters = ObjectFactory.Instance.Query<Monster>(monster => monster.Attack >= 3); // 返回工厂中所有攻击力大于等于3的怪物类实例的ObjectReference
使用EventManager的RaiseEvent方法抛出事件,示例如下:
EventManager.Instance.RaiseEvent("TestEventName");
所有事件均通过字符串进行唯一标识,可以使用字符串常量存储事件标识,确保不会因拼写错误导致事件处理出错。
抛出事件时可以提供参数,示例如下:
EventManager.Instance.RaiseEvent("TestEventName", 1, "abc", new[] { 0, 1 });
参数数量及类型均没有限制。对于同一个事件,也可以在多次抛出时传入不同数量及类型的参数。
使用EventManager的AddHandler方法注册事件Handler,示例如下:
EventManager.Instance.AddHandler("TestEventName", (args) =>
{
// Handle event
});
上例中使用lambda表达式定义了Handler方法,也可以使用带有可变参数的方法,示例如下:
EventManager.Instance.AddHandler("TestEventName", HandleEvent);
void HandleEvent(params object[] args)
{
// Handle event
}
在事件Handler中,可以使用args参数获取事件数据,但开发者必须确保访问args参数时不会造成下标越界。
如果事件Handler不会使用args参数,可以使用"_"忽略事件参数。
EventManager.Instance.AddHandler("TestEventName", (_) =>
{
// Handle event without parameters
});
另外,不仅在框架提供的BaseObject子类中可以注册事件Handler,在一般C#类中也可以注册事件Handler,即框架的事件模块可以单独使用。
不过需要注意的是,如果要在一般C#类中注册事件Handler,该类必须实现IEventHandler接口。
访问ObjectReference的任何成员都不会分配堆内存。
但目前访问ObjectFactory及EventManager的成员仍会产生堆内存垃圾,导致GC耗时增加。 因此不建议在画面绘制及逻辑更新等频繁调用的代码中(例如GameObject.Update())使用ObjectFactory进行数据查询,或使用EventManager抛出事件。
推荐的做法是仅在游戏数据发生改变时抛出事件,通知绘制代码进行画面状态更新。
后续计划进一步优化框架性能,减少ObjectFactory及EventManager的堆内存分配。