创建一个新的等距应用可以被分解为以下几个步骤:
外部应用:
1 创建将要在你的scenes中使用的各种定义。
2 使用上一步的各种定义,创建xml形式的scene。
内部应用:
3 加载一个scene到你的engine(xml形式)
4 展示scene
5 控制scene
定义文件(声明文件/用于定义的文件 Definition files)
定义文件是xml文件,包含了建造ffilmation 应用的scenes的各种资源的定义。加载一个scene需要加载所有它所牵涉到的定义文件和在这些文件中定义的所有外部的未被加载进来的swf文件。这对对于你是可见的,你可以看到一个“scene loading” 的过程。单独一个xml可以包含的定义的数量是不受限制的,但是将相近的elements分组放入不同的独立文件是一个好的习惯,比如trees.xml, stonewalls.xml or furniture.xml.
将scens和他们本身的定义分离是有一堆理由的,在大项目中理由会更充分。
1 你可以在scenes之间共享element。当你改变一个元素的时候,这个改变会应用到所有使用它的scenes中。引擎已经足够聪明来识别先前加载的资源和定义,所以你不用担心重复。
2 你可以平衡加载时间。在一个大项目里,从一开始就加载进所有的东西是不可能的。想象一下一个游戏有10级,你需要给一个从来都没有超过1级的用户加载10级的元素和资料吗?答案当然是否定的。如果你将你的各种定义聪明的分组,并且每个scene只涉及它需要的定义,在并且每个定义文件只涉及它需要的swf文件,你的加载时间会是最佳的。
你可能会想把应用中一些常用的元素(类库)放进你的主swf中去,因为他们在所有的地方,所有的scenes中都会用到。一个典型的例子就是你游戏中的英雄。不论怎样,如果你创建了一个scene,这个scene不只是使用了它自己的xml中所特有的涉及到的资源,你就不能再其他的ffilmation 应用中加载这个scene。FFilmation 编辑器中失败的例子: it will try to create an instance of a Bitmap or a Class that will not be there, and will give an error.
通常,定义是可以嵌套的。如果你创建了一个定义文件并且想在另一个文件中使用一个定义,简单引用到第二个文件。你不需要复制它的内容:engine会处理这些引用。
为一个FFilmation 应用的scene创建定义
<temp>
有一天你可以使用编辑器来创建定义文件而不需要去面对繁琐的xml结构,这是很有希望的。但是在此期间这些文件还是需要靠手工来创建了。这个连接指向了一个例子和标签的描述 (http://www.ffilmation.org/website/documentation/2-usage/xml-how-to/)
</temp>
定义文件包括 media引用,元素引用, object引用。
media引用就是需要在这个文件中加载的资源的路径(包含classes、mc或者bitmaps的外部swf)。
Object定义提供了objects类型的通用信息,比如绘画资源的使用和大小。你可以定义实例化这些定义的多个实例,每个实例都会共享这个通用的定义。object的基础属性有:
name: 实例对象连接到定义时使用
solid: solid object可以进行碰撞,没有solid的object不能。
receiveLights: 是否受灯光影响
castShadows: 在其他展示元素上投射影子
receiveShadows: 从其他展示元素接收影子
虽然每个实例都可以重写上面所有的属性(除了name),但是你可以在object定义中提供默认值。
一个object定义包括一个现实模型,一个碰撞模型和一个影子模型。现实模型是由一个或者多个sprite组成的,每个sprite都对应一个角度。当object被转向某个角度(0-360度),engine会自动展示具有最接近角度的sprite。sprite的属性有:
angle: sprite所展示的角度
src: sprite的库链接
shadowsrc: sprite的影子的链接,假如你不想使用同一个sprite的话可以设置这个。
请点击这个按钮查看如何创建你的object定义使用的资源。
碰撞模型要用来计算碰撞的几何属性。如果engine不能推断一个sprite的有效几何碰撞,你就需要定义它。目前支持2种碰撞模型:圆柱体碰撞模型和box碰撞模型。
圆柱体碰撞模型的属性有:
radius: 假想中围绕object的圆柱体的半径(像素)
height: 假想中围绕object的圆柱体的高(像素)
box碰撞模型的属性有:
width, depth, height w围绕object的box的x、y和z轴的长
阴影模型目前只支持sprite类型,不过有一天你是可以给box和圆柱体来定义精确的阴影的。
素材定义主要在墙壁和地面中使用。engine导入了几种标准的素材类型。
点击这里查看目前支持哪几种素材类型以及他们的xml格式。
如果提供的素材不够你使用,你可以写你自己的。一个素材最终会变成一给类。有一个程序的素材类型可以将任何实现了fEngineMaterial 接口的类当做素材使用。去engine的api可以看到详细怎样实现一个新的素材类的说明。
Scene 文件
engine使用xml来定义一个scene。 一般情况下engine会从外部文件读取xml,但是你也可以写一个类来生成一个随机的scene,动态的返回xml给engine。现在我们来使用最常用的用法。一个scene的xml可以被大致分为3部分:定义一些普通的变量,定义我们需要的定义文件的引用和放置这些定义的实例的精确的坐标。
使用FFilmation 编辑器,你不需要了解全部的xml的格式,因为你将会可视化的创建你的scene,可以随意拖动摆放物品。目前在编辑器里,并不是所有的标签都能有效的被使用到,所以,你可以连接到 这里 查看一个完整的xml说明书。
当你创建一个scene的时候,你将要做的第一件事就是设定这个scene的格子的大小。虽然格子大小是可以被随时设置和更改的,但是它在scene的表现上会有非常多的牵连,所以你最好在做任何其他事情之前考虑好格子的大小。有一点关于格子的说明:每个scene在内部表现上是一个个的3d格子。想想一下你的等容引擎,由小正方体组成的棋盘,但是有3个面。结构元素(scene中的墙壁和地面)被强制沿着格子的边,那就是说,一面墙只会在单元格的左边或者右边而不会穿过单元格。当你把墙和地面放进你的scene中时,他们的位置和大小应该是正好适合格子的。例如:你有一个50px的格子,添加了一个长140px的墙。这面墙将会被成比例放大到150px,这样它就能正好占3个格子,墙的高度处理也是一样的。编辑器会自动处理这些。
格子会被许多内部的算法和计算中使用,主要的3个方面如下:
Lighting: 展现一个灯光需要2布:A 确定在灯光范围内有哪些元素以及另外的那些元素会在他们上面投射阴影。B 设计绘制灯光阴影。A步会比B耗费更多的cpu资源,因此我们尽可能的简化它。使用格子,我们可以认为任何灯源在A步的结果是一样的,因此,那些计算需要在每个单元的第一时间执行然后缓存在内部。
Collisions: 格子中每一个存放墙和object的单元可以随意的触碰。所以当一个角色移动到那个单元里,我们尝试和那些元素碰撞而不是尝试和场景里所有的元素碰撞。
zSorting: 当一个场景被处理后,所有的墙壁,地面和单元都被沿z轴排序,并且每一个单元都会被分配一个z轴的编号(zIndex)。当一个object进入一个单元时,他会被分配给这个单元的zIndex来避免复杂的计算。这意味着在同一个单元里的所有objects拥有同一个zIndex,因此,如果两个object在同一个单元里时会有一个小的瑕疵。
这就是说在engine里的一些物品会是单元级别的精确度而不是像素级别。较小的格子变长就意味着更多的资源和单元,太大的量会导致计算失败。通常情况下,一个合适的边长就是能大致能包围一个角色的大小。默认大小是64px。
加载一个scene到engine
一旦你的scene确定后,你就可以创建你的FFilmation 应用并将scene加载进去。你需要做的第一件事是创建一个fEngine 类的实例。我会对所有的构造函数和方法使用最常用的语法。请到api查看更多的扩展功能。
我们现在使用给定的sprite创建了一个engine。FFilmation 的每一个东西都会被绘制到这个容器中。你可以自由的添加它到你的显示列表的任一地方。现在我们加载一个scene。
engine的createScene 方法接收的对象有用xml初始化的loader(一般情况下,一个类要加载外部的xml)和这个常见的大小(超出的内容会被剪掉)。当scene被创建,它就开始初始化和加载它自己。在它初始化完成前你不能做任何事情。我们使用fScene.LOADCOMPLETE事件来通知我们它的结束。你也可是使用fScene.LOADPROGRESS事件来为场景的加载显示一个进度条。
显示scene
当scene准备好时(接收到fScene.LOADCOMPLETE 事件),通常你就会想要展现它来看看了。当你准备显示一个scene时,你需要先为它声明一个camera。scene的camera允许你指定scene的那一部分被显示。
注意,camera有3个运动方向因为它在3d中运动。camera的坐标会在内部转换成2d的值,接着转换成适当的滚动框的值。你可以为一个scene定义多个camera(多次调用scene.createCamera() )并且可以随意的在他们之间切换。
最后:
这句话会显示你的scene。engine同一时间只能显示一个scene,所以如果另一个显示了它就要被隐藏掉。你可以手动的调用 engine.hideScene() 隐藏scene。
在这里有一点我很提倡的建议:显示/隐藏scene只影响scene所使用的DisplayObject,并不影响scene本身以及它里面的element。你可以处理scene中的elements(移动人物,添加移除灯光)不论他们显示还是没有显示。scene会保持它的状态并且当scene再次显示的时候,在scene隐藏时所作的修改已经应用了。
另一方面,scene中的DisplayObject会在scene隐藏的时候销毁,在scene显示的时候会再一次被创建。这样做主要是为了减少资源的使用从而允许更大程序拥有更多的scene。虽然scene会在内部处理好一切但是你也需要知道这些。例如,你为一个特殊的element的显示对象注册了鼠标监听,然后隐藏了scene,这时显示对象会被销毁因此事件监听就丢失了。如果你再一次显示scene你就需要为element分配一个新的显示对象然后重新注册监听。
操作scene
你的scene现在在屏幕上,你可以看到你激活的camera所对应的区域。让我们来操作scene中的element。通常的,你要做的第一件事是创建一些到element的引用这样你可以调用他们内部的方法。如果你知道你的element的id(在编辑器中设定的)你可以这样做:
现在你就有权利访问api中每个element类型的所有方法了。你也可以动态的增加和删除element。不是每个element类型都可以从scene中增加或删除。例如你不能增加或删除墙和地面。我知道这是一个缺点,不过这是有原因的。墙和地面定义了scene的基础的几何结构并且是灯光和z排序运算的基础。engine中大多数的优化计算都是通过预先计算好一些假定不变的关系来实现的。因此墙和地面不能被创建和销毁(但是你可以显示/隐藏他们,这会多花费95%的时间)。
engine中动态的element有灯光和人物。灯光应该不必在说了。人物是主要的需要被移动、创建和移除的对象。我做了对比,静态对象效率会好一点。fCharacter 类结成自fObject 类。让我们来创建一个灯光和一个人物:
现在我们对scene中所有的element都有访问的权利了,包括已经在它里面声明的element以及动态创建的element。你会想要做的第一件事就是移动他们:
character会更新它的位置。对于object和character来说,你同样也可以改变它的朝向。当object被确定了朝向之后(0-360°),engine会自动显示最接近它的朝向的sprite。记住这个:一个element的有效的sprites是被定义在关于element类型的定义文件中的。
移动一个character可能导致它和scene中的其他element发生碰撞。engine会处理这个碰撞(取消移动),但是你可能也会想自己做点什么。如果做的话,你可以监听fCharacter.COLLIDE 事件:
所有的element有一个solid的Boolean属性来决定他们是否接受碰撞。你可以在任何事件改变它的值。当你移动一个non-solid的character时,它不会和任何东西发生碰撞。但是当一个solid的character触碰到了一个non-solid的element,虽然运动不会停止,但是会生成另一个事件。这就是fCharacter.WALKOVER事件,它用来获取走路时经过的元素是很重要的,例如:
让你的对象动起来
在典型的应用里你会有一些characters在你的scene周围走动。在周围走动意味着2件事情:character更新它的位置和一些走动的动画。engine的设计理念是设计者可以用于任何项目而不需要知道任何关于actionscript和engine事怎样运行的。所以简单的动画循环能立即显示在flash的时间轴上是很重要的。假定是这样,character就有权利来访问它的时间轴这样你就可以通过时间轴来播放它。简单的:
它会将character的时间轴跳转到指定的帧上开始播放。别弄错:fCharacter 不是一个MovieClip,只是方法的名称为了通俗易懂而弄了个一样的。当你调用fCharacter 的gotoAndPlay方法,fCharacter 类会调用真正的gotoAndPlay方法,从而真正的嵌套在engine某处的mc会开始播放(阴影也是这样)。
engine假定一个object的所有的sprite使用相同的帧数目帧来播放相同的动画。为什么这样呢?想象一个character跑动,它的循环导致sprite 0 被使用,这个sprite当前在32帧,现在你将对象转向另一方,这就需要一个新的sprite(我们叫它sprite 3),engine存储32作为当前帧,从舞台上移除sprite 0 ,添加sprite 3并且从第32帧开始播放sprite 3,它会播放和另一个转向相同的帧。
现在我们知道如何移动一个character和如何让它播放期望动画循环。但是仍然会有一些特殊的逻辑会产生控制人物的移动。我们会在后面的高级教程中看到。
Holes(洞)
在FFilmation engine中的平面支持洞。那么洞就是:你可以透过它看东西,光线可以穿过它并且你可以射击穿它。通常你会用洞来创建门和窗户。目前engine只支持矩形的洞,但是更多复杂的几何形状会被应用进来。
一个平面的洞由它的素材定义。我知道这可能会有争议,一个洞的属性,和它作为显示体来比较更靠近的是它是一个结构体。无论如何,我是非常着迷于让设计者使用这个engine,同时对我来说能直接在素材上绘制一个窗口并且让engine能够识别这个洞是一个很重要的特征。
如果你使用“MovieClip”素材, 点击这里 查看如何定义一个洞。如果你使用engine制作的素材类型,当你应用素材的时候洞就已经创建好了。一些洞可以被打开和关闭。如果一个洞可以“closeable”,那么它必须提供一个洞的障碍物。洞的障碍物是在洞关闭的时候显示的一个简单的DisplayObject。典型的,门。如果你想写你自己的素材类型,写一个实现fEngienMaterial 接口的类并且当被请求时提供必须的洞障碍物。
可以关闭的洞默认是关闭的,所有其他的是打开的。让我们来看一个例子。scene包含一个id为wallWithDoor的墙。我们知道那有一个门,所以墙上会有一个洞来表现这个门。
这是打开门的代码。你会看到门消失(播放列表中的洞障碍物被移除)。你就可以走过和射穿它。灯光和阴影也会影响它。
子弹