组件系统
Torque X提供了组件系统来继承游戏对象。如果你查看内置的入门包的话,你会发现大多数游戏功能都是在组件中实现的。这份文档描述了组件系统以及它是如何工作的。
继承游戏对象
在深入研究组件到底是什么之前,先来考虑一个基本的实现问题。
在大多数面向对象语言中,包括C#,给一个对象添加新方法的主要内置方法是继承该类并修改子类。例如,假设你需要为一个特殊的游戏对象添加物理和碰撞功能,这个类叫做Player。你实现了Player类的逻辑。然后,你发现你还有一个类叫做Monster,它也需要同样的功能。你可以从Player类中复制粘贴代码到Monster类——这显然是个坏主意,因为除了在Monster类中集成新代码会出现很多bug,更倒霉的是你现在有两份重复的代码需要维护。当然你也可以创造一个包含物理和碰撞功能的代码的新类,然后让Player类和Monster类同时继承这个新类。这听上去是合理的,所以你新建了BaseActor类,然后将物理和碰撞的代码放到这个类中去。
不久之后你希望你的Player类能够拾取游戏对象。所以你向Player类中添加了拾取的代码。但是如果Monster类也需要拾取的代码,这时拾取的代码就要提升至父类BaseActor。同时,你添加了一个NPC类,继承于BaseActor。当然你肯定不希望NPC也能够拾取物品。但是现在你陷入了困境,因为NPC从BaseActor继承了拾取的功能,并且没有方法来禁用这个功能。但是!你修正了这个bug:通过向BaseActor添加一个“启用拾取功能”的标志量,然后添加一些if语句。现在NPC继承于BaseActor,但是它的拾取功能被禁用了。
你也许会认为BaseActor中的代码此时一定非常丑陋。是的!你是对的,但是事实是,这仅仅是一个噩梦的开始。不久之后你的代码就会变得臃肿、复杂、晦涩并且难以维护。
上面的这个例子描述了滥用继承带来的问题。继承是很强大的工具,但是必须正确地使用。我要告诉你的是,还有其他的办法能够更好地解决这个问题。一个替代继承的方法是聚合。(省略聚合的定义若干字,不知道的回家打屁股)。最后,你所有的对象会变成一个包裹,诸如拾取、碰撞这些工具你只需要放入包裹或者从包裹中移除就可以了。
使用Torque X组件来解决问题
Torque X组件系统是一个聚合模型的实现。一个组件是一个能够向一个集合对象添加功能的小对象。现在使用组件系统来解决我们上面提到的例子,我们可以先创建一个BaseActor类,该类继承于TorqueObject类。这样BaseActor就拥有了包含组件的能力。接着我们定义一些组件:PhysicsComponent、CollisionComponent、PickupComponent,并且让它们继承于TorqueComponent (一切组件的基类)。我们甚至不需要为Player、Monster和NPC单独创建子类。我们只需要使用一个命名的对象或对象类型遮罩(mask)来表明这几个对象间的区别就可以了。下面是简单的示例代码:
// declare the classes that we will need
public class BaseActor : TorqueObject
{
// implementation omitted
}
public class PhysicsComponent : TorqueComponent
{
// implementation omitted
}
public class CollisionComponent : TorqueComponent
{
// implementation omitted
}
public class PickupComponent : TorqueComponent
{
// implementation omitted
}
public class Game
{
public static void Main()
{
BaseActor player = new BaseActor();
player.ObjectType = TorqueObjectDatabase.Instance.GetObjectType("player");
player.Components.AddComponent(new PhysicsComponent());
player.Components.AddComponent(new CollisionComponent());
player.Components.AddComponent(new PickupComponent());
BaseActor monster = new BaseActor();
monster.ObjectType = TorqueObjectDatabase.Instance.GetObjectType("monster");
monster.Components.AddComponent(new PhysicsComponent());
monster.Components.AddComponent(new CollisionComponent());
monster.Components.AddComponent(new PickupComponent());
BaseActor npc = new BaseActor();
npc.ObjectType = TorqueObjectDatabase.Instance.GetObjectType("npc");
npc.Components.AddComponent(new PhysicsComponent());
npc.Components.AddComponent(new CollisionComponent());
// run the game
// ...
}
}
这里组件系统解决了一下几个问题:
- BaseActor类可以很轻量级。这样我们可以方便地在BaseActor返回的物理和碰撞组件之上定义物理和碰撞属性。
- 物理、碰撞和拾取功能的真正的逻辑被分割在了不同的独立的类中,互相隔离。
- 组件逻辑容易被新的对象类型复用。在许多场合下,我们仅仅需要在新的对象类型中添加组件就可以了。复用在游戏中无处不在。我们甚至可以建立一个组件库,在不同的游戏中复用。
- 删除功能变得很方便。比如NPC不能拾取物品,因为该对象没有拾取的组件。
- 因为组件和对象是分离的,所以组件可以更容易地被外部工具操作,比如Torque X editor。
聪明的你可以会意识到上面给出的组件示例有一点不和谐的地方。在原始的继承模型中,我们为游戏对象定义了新类,比如Player、Monster、NPC。在组件模型中,我们没有定义任何新类,取而代之的使我们创建了玩家、怪物和游戏人物的实例。设想我们想拥有许多怪物?在继承模型之中,我们仅仅需要简单的几步就可以做到:
// Create a hoard of vicious Monsters!
for (int i = 0; i < 100; ++i)
{
Monster m = new Monster();
m.Position = FindMonsterSpawn(); // FindMonsterSpawn() will find a unoccupied spawn point for our monster.
TorqueObjectDatabase.Instance.Register(m);
m.Attack(player);
}
那么我们如何在组件模型中应对这种情况?难道你要重复5行代码(请参看上面的代码,的确是5行)在每一处你需要创建怪物的地方?这太痛苦了。幸运的是,我们不需要这样做。取而代之的是,我们可以创建自己的基于组件的对象,它叫做模板。然后我们可以新建一个模板的实例。如果你还没有阅读模板指南,下面的讨论会有点陌生。但是我建议你继续阅读下去,然后再去看模板指南。
模板系统让我们能够创建基于组件的对象,并且将它变为模板。待会我们可以克隆模板来创造新的对象,并将该对象在引擎中注册。下面是示例代码:
// Create the monster template
BaseActor monsterTemplate = new BaseActor();
monsterTemplate.Name = "MonsterTemplate";
monsterTemplate.ObjectType = TorqueObjectDatabase.Instance.GetObjectType("monster");
monsterTemplate.IsTemplate = true;
monsterTemplate.Components.AddComponent(new PhysicsComponent());
monsterTemplate.Components.AddComponent(new CollisionComponent());
monsterTemplate.Components.AddComponent(new PickupComponent());
注意,为了将我们的怪物变为模板,我们做出了三处调整:
- 添加了模板标志量并将其设置为true。只需要这一步,我们就可以将一个对象变为模板。
- 我们给对象起了一个名字叫做MonsterTemplate。给一个模板命名是可选的,但是如果你不这样做,你就不能通过TorqueObjectDatabase类来找到你的模板。
- 我们将变量名称改成了monsterTemplate。这也是可选的。
现在我们拥有了怪物的配置代码,并将它置于模板中。当我们需要创建一个怪物时,我们只需要克隆这个模板,按需配置返回的对象,然后注册它。所有被模板包含的组件都会出现在克隆之后的对象中。除了IsTemplate标志量被设为false。请看示例代码:
// Create a hoard of vicious Monsters!
for (int i = 0; i < 100; ++i)
{
BaseActor m = (BaseActor)monsterTemplate.Clone();
m.Position = FindMonsterSpawn(); // FindMonsterSpawn() will find a unoccupied spawn point for our monster.
TorqueObjectDatabase.Instance.Register(m);
m.Attack(player); // ex-terrrrrr-minate!
}
和XNA组件的差异
XNA框架通过自己的组件模型提供了很好的可扩展性。这个系统和Torque的组件系统共享一个名字,其实概念上也是相近的。但是他们却是出于两种不同目的的不同系统!Torque系统的目的是让定义Torque游戏对象更加容易,然后XNA系统的目的是让子系统更容易地被平滑地集成到一个新游戏中去。换句话说,XNA系统是游戏的组件模型,然而Torque系统时游戏中的对象的组件模型。Torque X完全兼容XNA组件模型。事实上,大多数底层的引擎都是继承于XNA组件的,Torque
X也不例外(这不废话嘛)。
另一个组件的例子
这里有一个例子,它展示了组件的逻辑的更多细节以及如何将它和引擎集成。强烈建议你先阅读模板指南然后再来看下面的代码。
为了明白Torque X组件系统是如何工作的,考虑下面的代码。它在一个反坦克飞机中创建了一个反坦克炮弹(这个例子在demo目录下大家自己先去看看,背景有一点3D的效果,强烈推荐)。其实组件往往不是通过代码创建的,而是通过xml。为了能够让大家看的更明白,我们将创建的流程提取出来,并使用C#代码表示。
// set up sprite
_tankTemplate = new T2DStaticSprite();
_tankTemplate.Material = (DefaultEffect)TorqueObjectDatabase.Instance.FindObject("TanketteMaterial");
_tankTemplate.Size = new Vector2(15.0f, 14.0f);
_tankTemplate.Layer = Game.Instance.Ground.Layer - 1;
_tankTemplate.ObjectType = TanketteObjectType;
// add tank smarts
TankAIComponent ai = new TankAIComponent();
ai.ProjectileTemplate = GetProjectileTemplate();
_tankTemplate.Components.AddComponent(ai);
// add some mount points
_tankTemplate.Components.AddComponent(new T2DLinkPointComponent());
_tankTemplate.LinkPoints.AddLinkPoint("turret", new Vector2(-0.5f, -1.0f), 0.0f);
_tankTemplate.LinkPoints.AddLinkPoint("dust", new Vector2(0.75f, 0.5f), 0.0f);
// add combustible so we blow up
CombustibleComponent boom = new CombustibleComponent();
boom.OnGround = true;
boom.CollidesWith = BombObjectType + PlayerObjectType;
boom.DestroyOnCollision = true;
boom.ExplosionTemplate = GetBombExplosionTemplate();
boom.Offset = new Vector2(0, boom.ExplosionTemplate.Size.Y * 0.5f);
boom.CameraShake= new Vector2(1.25f, 1000.0f);
_tankTemplate.Components.AddComponent(boom);
// mount turret
T2DStaticSprite turret = (T2DStaticSprite)TorqueObjectDatabase.Instance.FindObject("TanketteCannon");
turret.Mount(_tankTemplate, "turret", true);
turret.TrackMountRotation = false;
// mount dustcloud
T2DAnimatedSprite dustCloud = (T2DAnimatedSprite)TorqueObjectDatabase.Instance.FindObject("DustCloud");
dustCloud.Mount(_tankTemplate, "dust", true);
上面的代码片段创造了一个简单的精灵(不知道什么是精灵?打板子),向其添加了一些组件,现在它变成了一个相对复杂的对象了。首先,我们添加了TankAIComponent,它给予了精灵瞄准并打枪(是的!你没看错!是打枪!)的能力。然后我们创建一个易燃物组件,当我们被炮弹和其他物体击中时来制造一点爆炸效果。当然了,这个类不仅仅可以用于反坦克飞机,还可以被许多其他对象使用。现在我们要来添加一个边界检测类,来确保当我们的反坦克飞机从屏幕中消失时正确地从游戏中移除。
下面是边界检测类的代码:
class BoundsCheckerComponent : TorqueComponent, IAnimatedObject
{
//======================================================
#region Public methods
public void UpdateAnimation(float elapsed)
{
T2DSceneCamera cam = T2DSceneGraph.Instance.Camera as T2DSceneCamera;
if (_sceneObj.Position.X < cam.SceneMin.X - _sceneObj.Size.X)
{
Owner.Manager.Unregister(Owner);
return;
}
}
#endregion
上面的代码是边界检测组件的修改动画的回调函数。它通过检测对象是否位于摄像头的可视范围内来确定它是否还在屏幕内。如果对象出了屏幕,我们把它注销。我们可以通过将它的MarkedForDelete属性值为true来完成这一步。
//======================================================
#region Private, protected, internal methods
protected override bool _OnRegister(TorqueObject owner)
{
if (!base._OnRegister(owner) || !(Owner is T2DSceneObject))
return false;
_sceneObj = Owner as T2DSceneObject;
ProcessList.Instance.AddAnimationCallback(Owner, this);
return true;
}
#endregion
上面的代码当对象被注册时,负责初始化组件。我们调用base._OnRegister来确保我们的父类能够正确初始化。我们也可以检查我们的对象是否是一个T2DsceneObject对象。接着,我们为它注册一个动画回调函数,并将它放置于ProcessList中。注意,相同对象可以为不限数目的组件注册回调函数。你甚至还可以控制回调函数被调用的次序,通过为每一个回调函数传递一个排序参数。最后,_OnRegister函数返回true来表明所有东西都已经被正确初始化了。
//======================================================
#region Private, protected, internal fields
T2DSceneObject _sceneObj;
#endregion
}
_sceneObj是这个组件中唯一的数据成员。它仅仅是便于缓存T2DsceneObject的拥有者的一个变量。
我们可以将这个组件添加给任何对象,当它离开屏幕的时候,这个对象会自动注销自己。这是一个很简单的例子,但是我们要知道一个复杂的物体其实是由很多歌简单的组件组成的。一个更复杂的组件的例子是T2DphysicsComponent,T2DCollisionComponent,
T2DLinkPointComponent, and T2DforceComponent。我们以后再来讨论这几个组件。
通常一个对象的两个组件间是需要通信的。他们可以通过共享TorqueInterface来完成通信。
// T2DForceComponent defines _RegisterInterfaces:
protected internal override void _RegisterInterfaces(TorqueObject owner)
{
base._RegisterInterfaces(owner);
...
// cache force interface and match empty name only
Owner.RegisterCachedInterface("force", String.Empty, this, _forceInterface);
}
最后一行代码注册了被_forceInterface成员变量持有的force接口,类型是”force”,没有名字(String.Empty)。
分享到:
相关推荐
根据提供的信息,我们可以深入探讨Torque4的相关知识点,特别是其架构、配置以及作业提交与管理等方面。 ### Torque4概述 #### 架构概览 Torque4是一种高性能的作业调度系统,它由一个管理节点(称为服务器)和多...
《Torque文档--torqueAdminGuide》是一份由Adaptive Computing Enterprises, Inc.编写的关于Torque资源管理器(ResourceManager)的管理员指南。Torque是一个开放源码的高性能计算(HPC)集群调度系统,该指南适用于...
解压后,用户可以按照官方文档或社区指南进行安装和配置步骤,确保Torque能在Linux环境中正确运行。 总结来说,Torque 6.1.2是用于Linux HPC环境的作业调度系统,通过与Maui的配合,可以实现高效、智能的作业调度。...
本文将详细介绍Torque引擎的脚本系统、编辑器的使用以及引擎的核心特性,帮助你更好地理解和运用这款引擎。 一、脚本系统 1. 控制台使用 Torque引擎的控制台是通过按下`键(通常在键盘左上角,Shift键下方)来激活...
【作业提交系统Torque个人安装总结】 Torque(也称为pbs torque)是一种广泛使用的作业调度系统,源于历史悠久的PBS(Portable Batch System),是为本地集群提供资源管理和作业调度的工具。PBS有三个主要分支:...
### Torque 使用手册:便携式批处理系统详解 #### 一、Torque PBS概览 **Torque**,即**Portable Batch System (PBS)**,是一种高性能计算领域中广泛使用的批处理作业调度软件。它主要设计用于在分布式计算环境中...
8. **社区和文档**: 由于有中文翻译的基础教程,中国的开发者可以更容易地融入Torque2D的社区,获取帮助和分享经验。完善的文档能够帮助初学者快速上手。 9. **游戏实例**: 教程可能包含实际的游戏开发案例,让...
torque官网的自己动手做一个第一人称射击游戏的完整文档教程。 我兄弟花了好几个小时用工具编辑成比较舒适的电子书,大家放心下载。5个积分虽然多了点,但对得起他的辛苦。
1. **作业提交与调度**:用户通过命令行工具提交作业,Torque会根据资源需求和系统状态进行调度,确保作业公平地获得计算资源。 2. **资源管理**:监控和分配CPU、内存、磁盘空间等硬件资源,确保资源的高效利用。 3...
《Torque游戏引擎中文教程》是一本专注于游戏开发的专业指南,旨在帮助读者深入理解并熟练掌握Torque游戏引擎的使用技巧。Torque引擎以其高效、易用和跨平台的特性,成为了众多独立开发者和小型团队青睐的游戏开发...
最后,为了便于新手入门,"Torque游戏引擎入门指南.pdf"这份文档提供了逐步的教学指导,涵盖了从安装引擎到创建第一个游戏项目的全过程。书中详细讲解了如何使用编辑器进行场景构建,如何编写脚本,以及如何测试和...
torque官网的自己动手做一个第一人称射击游戏的完整文档教程。 我兄弟花了好几个小时用工具编辑成比较舒适的电子书,大家放心下载。5个积分虽然多了点,但对得起他的辛苦。 这个是第二分卷,共两分卷
【标题】"Torque-2.4.7"是一个用于分布式计算环境的资源管理和调度系统,主要用于集群计算。这个版本的Torque是2.4系列的一个稳定版本,它提供了高效的任务调度、作业队列管理和资源分配功能。 【描述】"torque-...
Torque是一个集群作业管理系统,也被称为PBS Professional或Portable Batch System,它是一个用于管理计算资源、调度任务的高级系统。本文将基于给定文件内容,针对Torque管理指南进行介绍。 1. Torque概述 Torque...
- **Torque SDK的内容**:Torque SDK包含了许多重要的组件,如编辑器、示例代码、文档和技术支持等,这些都是学习和使用Torque必不可少的部分。 - **使用Torque实现游戏梦想**:通过一系列实例教学,本书将引导读者...
《TORQUE类方法参考》是针对TORQUE引擎的开发文档,旨在为开发者提供类似MSDN的详尽技术指导。TORQUE引擎是一款强大的游戏开发平台,它提供了丰富的工具和API,帮助开发者创建各种类型的游戏和交互式应用。这篇文档...
具体配置细节可以根据官方文档进行调整。 #### 六、总结 Torque是一款非常实用的作业管理软件,但它的调度器功能较为基础。通过与MAUI结合使用,可以在保持Torque原有优点的基础上进一步增强作业调度能力,提高资源...
Torque+Maui 是一种高性能的分布式计算系统,常用于科学计算、数据分析和机器学习等领域。本文将详细介绍在 Linux 上安装配置 Torque+Maui 的全过程。 一、解压安装包 Torque+Maui 的安装需要首先解压安装包。使用...