`
zhc0822
  • 浏览: 228445 次
  • 性别: Icon_minigender_1
  • 来自: 宝仔的奇幻城堡
社区版块
存档分类
最新评论

Torque X 官方文档中文译稿(1)——组件系统

阅读更多

 

组件系统

Torque X提供了组件系统来继承游戏对象。如果你查看内置的入门包的话,你会发现大多数游戏功能都是在组件中实现的。这份文档描述了组件系统以及它是如何工作的。

 

继承游戏对象

在深入研究组件到底是什么之前,先来考虑一个基本的实现问题。

在大多数面向对象语言中,包括C#,给一个对象添加新方法的主要内置方法是继承该类并修改子类。例如,假设你需要为一个特殊的游戏对象添加物理和碰撞功能,这个类叫做Player。你实现了Player类的逻辑。然后,你发现你还有一个类叫做Monster,它也需要同样的功能。你可以从Player类中复制粘贴代码到Monster类——这显然是个坏主意,因为除了在Monster类中集成新代码会出现很多bug,更倒霉的是你现在有两份重复的代码需要维护。当然你也可以创造一个包含物理和碰撞功能的代码的新类,然后让Player类和Monster类同时继承这个新类。这听上去是合理的,所以你新建了BaseActor类,然后将物理和碰撞的代码放到这个类中去。

不久之后你希望你的Player类能够拾取游戏对象。所以你向Player类中添加了拾取的代码。但是如果Monster类也需要拾取的代码,这时拾取的代码就要提升至父类BaseActor。同时,你添加了一个NPC类,继承于BaseActor。当然你肯定不希望NPC也能够拾取物品。但是现在你陷入了困境,因为NPCBaseActor继承了拾取的功能,并且没有方法来禁用这个功能。但是!你修正了这个bug:通过向BaseActor添加一个“启用拾取功能”的标志量,然后添加一些if语句。现在NPC继承于BaseActor,但是它的拾取功能被禁用了。

你也许会认为BaseActor中的代码此时一定非常丑陋。是的!你是对的,但是事实是,这仅仅是一个噩梦的开始。不久之后你的代码就会变得臃肿、复杂、晦涩并且难以维护。

上面的这个例子描述了滥用继承带来的问题。继承是很强大的工具,但是必须正确地使用。我要告诉你的是,还有其他的办法能够更好地解决这个问题。一个替代继承的方法是聚合。(省略聚合的定义若干字,不知道的回家打屁股)。最后,你所有的对象会变成一个包裹,诸如拾取、碰撞这些工具你只需要放入包裹或者从包裹中移除就可以了。

 

使用Torque X组件来解决问题

Torque X组件系统是一个聚合模型的实现。一个组件是一个能够向一个集合对象添加功能的小对象。现在使用组件系统来解决我们上面提到的例子,我们可以先创建一个BaseActor类,该类继承于TorqueObject类。这样BaseActor就拥有了包含组件的能力。接着我们定义一些组件:PhysicsComponentCollisionComponentPickupComponent并且让它们继承于TorqueComponent 一切组件的基类)。我们甚至不需要为PlayerMonsterNPC单独创建子类。我们只需要使用一个命名的对象或对象类型遮罩(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

 

聪明的你可以会意识到上面给出的组件示例有一点不和谐的地方。在原始的继承模型中,我们为游戏对象定义了新类,比如PlayerMonsterNPC。在组件模型中,我们没有定义任何新类,取而代之的使我们创建了玩家、怪物和游戏人物的实例。设想我们想拥有许多怪物?在继承模型之中,我们仅仅需要简单的几步就可以做到:

 

// 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的拥有者的一个变量。

我们可以将这个组件添加给任何对象,当它离开屏幕的时候,这个对象会自动注销自己。这是一个很简单的例子,但是我们要知道一个复杂的物体其实是由很多歌简单的组件组成的。一个更复杂的组件的例子是T2DphysicsComponentT2DCollisionComponent, 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)。

 

分享到:
评论
1 楼 andyjackson 2010-12-17  
FZ牛逼啊,受教~~~~ 

相关推荐

    torque4官方文档

    Torque集群是由一个管理点和多个计算节点组成。管理节点运行pbs_server进程,计算节点运行pbs_mom进程。用于提交和管理作业的客户端命令可以安装在任何主机上(包括不运行pbsserver或pbsmom的主机)。

    Torque 引擎文档

    有关游戏引擎Torque引擎,比较有用的文档,Torque 引擎的画面感很强 可以用来做游戏

    torque文档--torqueAdminGuide

    Torque ResourceManager Adminstrator Guide 9.0.1

    hpc作业调度 torque 6.1.2 (for Linux)

    torque 6.1.2 for Linux ,HPC作业调度软件,建议与maui配合使用。

    Torque3D fps 完整教程文档

    torque官网的自己动手做一个第一人称射击游戏的完整文档教程。 我兄弟花了好几个小时用工具编辑成比较舒适的电子书,大家放心下载。5个积分虽然多了点,但对得起他的辛苦。

    作业提交系统Torque个人安装总结

    PBS是功能最为齐全,历史最悠久,支持最... 其中OpenPBS是最早的PBS系统,目前已经没有太多后续开发,PBS pro是PBS的商业版本,功能最为丰富。Torque是Clustering公司接过了OpenPBS,并给与后续支持的一个开源版本。

    Torque3D fps 完整教程文档part2

    torque官网的自己动手做一个第一人称射击游戏的完整文档教程。 我兄弟花了好几个小时用工具编辑成比较舒适的电子书,大家放心下载。5个积分虽然多了点,但对得起他的辛苦。 这个是第二分卷,共两分卷

    集群管理工具,作业提交系统pbs,torque

    集群管理工具作业提交系统pbs,torque

    车况监控汉化版torque (1.5.50)

    Torque是android系统上一款非常优秀的OBD(车载自动诊断系统)软件。 您只要在淘宝上花几十块购买蓝牙设备ELM327(汽车检测仪),就可以使用安卓手机上这款杀手级应用直接读取车辆引擎最准确的所有信息。 您只要将...

    torque 6.1.2

    torque-6.1.2.tar.gz版本,用于torque作业提交系统下载应用

    torque相关资料大全part1

    收集了目前已知的所有与torque相关的中文帮助文档,以及编程规范,内容包括:3D游戏开发大全(含源码),Torque 游戏引擎简介.txt,Torque_3D游戏引擎规范及说明书.doc,Torque1.5中文教程.pdf,torque脚本读书笔记....

    torque-6.1.3.tar.gz

    torque-6.1.3 计算集群作业管理系统 for linux PBS的目前包括openPBS, PBS Pro和Torque三个主要分支. 其中OpenPBS是最早的PBS系统, 目前已经没有太多后续开发,PBS pro是PBS的商业版本, 功能最为丰富. Torque是...

    Torque_Pro v1.8.16繁体中文版

    Torque_Pro v1.8.16繁体中文版

    Torque2D 中文翻译版基础教程(收集)

    torque2D 引擎的基础教程翻译版本

    Torque快速入门教程

    Torque数据层框架快速入门 由于Torque是一个比较老的框架,外面资料不多,但在使用上还是相当方便简洁的,这是一份能让你快速掌握它的简易教程文档

    Torque教程初级篇(全)

    本教程以torque引擎1.52版本为例,结合3D游戏开发大全和网络上的一些脚本教程制作而成,仅供新手参考学习,不会用于任何商业用途. 希望大家通过学习该教程以后对于引擎能够有比较大概的了解. 美工爱好者可以加Torque...

    Characterizing the Torque Lookup Table of an IPM Machine for Automotive

    With the output torque to be the main control target, various control algorithms are developed that aim to achieve high torque accuracy while maximizing the machine energy efficiency. Most of such ...

    Torque3D规范及说明书

    Torque3D规范及说明书,刚刚开源了,估计很多人可以学习并需要文档.

    Torque3d 英文原版 pdf

    Torque 3d 是一款3D C++ 游戏引擎,现已开源。 本PDF 是Torque 3d 英文原版 详细介绍了 Torque 3d 脚本技术 如何游戏开发细节等等.

    Torque中文教程

    学习使用Torque 游戏引,介绍使用Torque 游戏引擎进行游戏开发的原理和方法,进而使用它制作出属 于自己的游戏。

Global site tag (gtag.js) - Google Analytics