利用Flare3D和Stage3D创建3D
Flare3D 是一款功能强大的引擎,它使得 Flash 中的 3D 内容管理变得更为简便。 它的设计宗旨是提供一个完美的开发工作流程,以便你能够获得事半功倍的效果。
本教程侧重讨论在 Flash 中创建 3D 游戏所需的 ActionScript 代码;但本范例项目不包括创建和导出游戏组件文件的过程。
在完成本教程之后,你将了解在Flash中开发一个完整 3D 游戏所需的基本概念。 在本范例中,你将使用 Stage3D 和 Flare3D 处理用于创建一个名称为 Yellow Planet 的游戏的文件(参见图1)。
你可以从页面 http://www.flare3d.com/demos/yellowplanet/ 下载和播放该演示视频。
本教程分为10个步骤,这些步骤描述了用于制作和控制一个较为精致和复杂系统的基本 3D 概念。 下载在 Requirements 部分提供的范例文件并且将未压缩的存档文件夹保存到你的桌面。 在你按照每个步骤进行操作时,你可以在范例文件中查阅相应的文件夹以便核查在每个教程章节中描述的类。 下面涉及的步骤是以接连的方式基于前面的话题开发相应的游戏版本,因此你能够以渐进地方式学习它们。
在编写本教程时,Flash Builder (和其它编辑器) 仍然不能被配置为 Flash Player 11 发布 SWF 内容。为了实现这一功能,你必须使用 Flash Player 11的一个特定版本,并且调整若干设置以便输出 Flash Player 11 的文件。你需要的文件已经在 Requirements 部分列出。 在下载相应文件之后,按照下列指南设置 Flash Builder 以便发布 Stage3D 和 Flare3D 内容。
利用 Flash Builder 导入项目
在下载和解压范例文件的文件夹之后,打开 flare3D 文件夹然后找到名称为 yellowplanet 的源代码。
- 复制 yellowplanet 文件夹,然后将它保存到你的硬盘驱动器上的期望位置。
- 启动 Flash Builder ,然后选中 File > Import Flash Builder project 以便导入项目。 在选择 Project folder 选项时,单击 Browse 按钮并且浏览选中 Project folder。 在设置 project folder 之后,单击 Finish(参见图2)。
设立 Stage3D 和 Flare3D 库
如需编译在 Flash Player 11 中播放的 SWF 文件,你需要更新下列项目属性:
- 指定正确的 Flex SDK 版本
- 添加 Flare3D 库
- 添加 playerglobal.swc 库的最新版本。
如需进行这些更改,你必须访问项目属性,然后更新这些设置。
- 选中 Project > Properties。 在左边的面板中,选中 ActionScript Build Path 选项。 使用提供的界面将项目设置为使用 Flex SDK 的正确版本(4.5.1 build 21328)。
- 如果以前你没有使用过 Flare3D 组件,则现在你可以添加它。 单击 Add SWC… 按钮,然后浏览选中你从 Requirements 部分下载的最新版本的 Flare3D 组件。 SWC 文件位于 Flare3D distribution 文件的 lib 文件夹中。 在完成这些更改之后,你项目的属性将与图3中的设置类似。
添加编译器参数
为了发布 Flash Player 11 文件,你需要将 SWF publish setting 设置为版本 13。此外,你还需要将项目设置为使用 Flex SDK 版本4.5.1。
- 在项目属性的左栏中,单击 ActionScript Compiler 选项。
在编写本教程时,SWF 文件的版本13选项仍然不是 ActionScript Compiler 的一个可用设置,因此,你必须人工添加它。
- 在 Additional compiler arguments 字段,输入如图4所示的代码行:
-swf-version 13
- 在 ActionScript Compiler 屏幕的上方,配置 Flex SDK 以便使用版本 4.5.1,如上所示。
更新 HTML 设置以满足 Stage3D 兼容性要求
在发布 Flash Player 11 文件时,必须将 wmode 参数设置为 direct。 你必须将这一参数添加到驻留应用程序的 HTML 文件中。 Flash Builder 将使用一个名称为 index.template.html 的模板文件自动创建一个 HTML 文件。
- 在 project 文件夹中,找到该html-模板文件,然后在配置玩家的每个位置上添加下面代码,如下所示:
params.wmode = "direct";
下面是更新之后的代码的一个范例:
params.quality = "high"; params.bgcolor = "${bgcolor}"; params.wmode = "direct"; params.allowscriptaccess = "sameDomain"; …
项目资源
本项目包含一些项目文件,它们放置于 assets 文件夹之中。 必要时,你可以打开这些文件,然后查看用于创建游戏应用程序的不同部件:
- 3D Max文件:所有用于本游戏的模型均使用 3D Max 进行建模。你可以根据需要编辑这些文件以及修改这些模型。 模型文件均有描述性的名称,以便它们能够方便地与相应的游戏组件一一对应。
- 纹理:如果你查看 assets 文件夹,你可以验证用于游戏模型的各种纹理。
- Assets.fla:该 Flash Professional 文件包含所有用于游戏用户界面的 2D 元素。 名称为 Assets.swc 的文件是一个由该文件生成的组件。
- Flare3D files: 文件 astronaut.f3d、planet.f3d 和 shadow.f3d 是使用 Flare3 插件从 3D Max 直接导入的。这些文件放置于 bin 文件夹中。
在你了解 asset 文件并且完成 Flash Builder 的配置之后,你可以进入下一章节并且能够开始研究如何进行游戏开发。
YellowPlanet_01.as 包含本节描述的代码。
与 Stage 可以用作 2D 内容的容器一样,你需要创建一个保存 3D 对象的内容。 该容器被称为 “scene”。
创建一个新的 scene 的过程非常简单,因为你可以输入一个单行即可:
var scene:Scene3D = new Scene3D(container);
在上面的代码中,容器参数可以引用任何被添加到 Stage 中的空 sprite 或影片剪辑实例。
注:有一个名称为 Viewer3D 的 Scene3D 版本非常有用。 该对象通过将场景旋转(scene rotation)功能和使用鼠标的缩放功能添加到viewer的功能中扩展 Scene3D 功能。
为了将 3D 模型加载到项目中,你应该使用下列代码:
var astronaut:Pivot3D = scene.addChildFromFile( "astronaut.f3d" );
Pivot3D 是 Flare3D 中最基本的对象。 它与空影片剪辑相似,因为你可以对它进行移动、旋转和转换以及将其它 3D 对象添加到其中。
如果现在你对项目进行编译,则你将看到一个小宇航员(astronaut)飞来飞去。
正如 addChildFromFile
方法的名称暗指的那样,它从一个外部资源将一个新的 child 添加到 scene,并且将该对象作为一个 Pivot3D 容器返回。 在本范例中,该对象被称为宇航员(astronaut)。
每个文件可以包含许多对象,其中包括几何图形、相机、灯光、等等。 因此,返回的对象本身不是 mesh,而是整个文件的一个容器。
下面的代码可以建立相应的 Stage,创建相应的 scene 以及加载包含 3D 模型的 Flare3D 文件:
// stage setup. stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; // In this first steps, just change the scene by creating a Viewer3D // to be able to look around the scene. scene = new Viewer3D(this); // Loads the external files and stores the references into planet // and astronaut objects. planet = scene.addChildFromFile( "planet.f3d" ); astronaut = scene.addChildFromFile( "astronaut.f3d" ); //You can listen PROGRESS and COMPLETE events to control scene loading scene.addEventListener( Scene3D.PROGRESS_EVENT, progressEvent ); scene.addEventListener( Scene3D.COMPLETE_EVENT, completeEvent );
YellowPlanet_02.as 包含本节描述的代码。
private function setupScene( pivot:Pivot3D )
:至此,你已经创建了相应的 scene 并且加载了相应的 3D 对象。 在本节中,你将学习如何在相应的 scene 中寻找对象以及如何将特定的行为应用于它们。
寻找特定对象
一旦 scene 完成加载,你可以访问所有包含的对象。 你可以使用方法 getChildByName("object_name")
获取里面所有的对象。 该方法能够在层级中以递归的方式进行搜寻,直到它找到需要的对象。 如果没有找到任何对象,则方法返回 null。
例如,你可以通过使用下行代码从天空(sky)对象中获取一个引用:
sky = planet.getChildByName( "sky" );
如果你希望修改一个特定对象的所有子对象,则你可以使用 forEach
方法。 forEach 方法将为特定对象的每个子对象调用一个函数。
例如,查看一下下面代码:
planet.forEach( setupScene );
在上面的代码行中,setupScene 是一个将每个子对象作为参数接收的方法。
查看一下下面的代码范例:
private function setupScene( pivot:Pivot3D ):void { if ( pivot.name == "fan" ) { // All Pivot3D objects has a userData property // you can use to store your own data. pivot.userData = new Object(); pivot.userData.speed = Math.random() * 5 + 5; // Adds to fan Vector. fan.push( pivot ); } }
更新 scene
你可以通过使用 Update 事件控制 scene 的更新时间。 该事件是在渲染之前下发的,因此你可以在每次调用该方法时修改 scene。 updateEvent 函数是该游戏中的主循环。
在本范例中,updateEvent
将控制风扇(fan)和天空(sky)的旋转方式:
private function updateEvent(e:Event):void { // Update the world. for each ( var f:Pivot3D in fan ) { f.rotateY( f.userData.speed ); //Rotate fan blades } sky.rotateX(0.1); //Rotate sky in the background }
- 在范例文件中,找到名称为 YellowPlanet_02.as 的文件。 双击该文件以便在 Flash Professional 中打开它,然后测试相应的影片剪辑。
在影片剪辑播放过程中,注意一下你可以利用你的鼠标移动控制行星的旋转。 在你观看风扇旋转过程中,思考一下 updateEvent 函数以及如何在行星内部修改它的对象。 在你完成查看范例项目之后,请继续阅读下一节内容。
YellowPlanet_03.as 包含本节描述的代码。
下面,你将研究如何利用 Flare3D 控制相机。 此外,你还将学习一些基本的相机操作理念。 目前,你可以使用许多不同的策略来控制相机的位置。 本节将介绍两个能够简化这一过程的用于 3D 游戏的函数。
查看宇航员(Astronaut)容器
如果你检查 YellowPlanet_03.as 中的代码,你将会注意到 Astronaut 被包裹于一个 Pivot3D 对象中。
目前,这一步骤看起来似乎没有必要。 但在将来,它在必要时能够便于更好地控制 astronaut。
查看下列代码,以便了解如何更好地控制 astronaut 的显示:
// This creates an empty 3D container and adds the astronaut to it. container = new Pivot3D(); container.addChild( astronaut ); //Adds the container to the scene too, otherwise //the objects will not be present on the scene. scene.addChild( container );
使用 setPositionWithReference 设置相机位置
在本范例项目游戏中,宇航员可以在行星上行走。 因此,相机必须能够跟踪他。 通过引用宇航员的当前位置可以现实这一相机行为。 你可以使用 setPositionWithReference
来完成这一任务。 这一方法包含于Pivot3DUtils
之中,而且它允许你确定一个对象与用作引用的另一个对象之间的相对位置。 这意味着你可以对任一与其它对象相关的对象进行转换。
在本游戏中,相机总是位于宇航员之后。 它可以保持与宇航员的相对位置。 相机(与其它在 scene 中的对象一样)具有一个定义其位置和方向的坐标轴。 坐标轴的方向如图 5 所示。
+Z 指向宇航员的方向,而 +Y 指向正上方。 这些矢量被分别称为 Up、Dir 和 Right(参见图6)。
假定每个对象具有其自己的坐标轴,下面的代码行将相机相对于宇航员的位置设置为 up (+Y) 80 单位 away (-Z) 20 单位:
Pivot3DUtils.setPositionWithReference( scene.camera, 0, 80, -20, container, 0.1 );
正如你看到的那样,通过使用 Flare3D 和一个单行代码即可解决一个复杂的任务。
使用 lookAtWithReference 设置相机的方位
在相机处于正确的位置之后,镜头需要对准正确的方向。 使用与通过参考点设置对象的位置相同的策略,你也可以使用参考点寻找游戏中的特定点。
这一方法具有一个名称为 \’up\’ 的附加和可选参数,它是本游戏必需的参数。
通常,Up 矢量总是指向正上方。 在许多情形下,传递一个 null 值即可使得 Flare3D 为你计算该矢量的值(参见图7)。
然而,设想下面的范例:如果你抬起你的手并且利用你的食指指向一个对象,就像一把手一样,则你的拇指将自动指向正上方,这是我们期望的结果。 此时已经定义了 Dir 和 Up 矢量。
现在,在你仍然将食指指向相同对象的同时,试图旋转你的手。 在它旋转的过程中,Up 手指(你的拇指)开始改变其方向。 注意,即使你总是保持你的手指向相同方向,但在你旋转你的手的过程中,Up 矢量将会移动。 为了对相机进行编程,以便它能够知道哪个方向是正上方,你需要使用 up 参数。
在本范例游戏中,相机位于宇航员的上方。 相机必须设置为指向下方,以便能够朝向宇航员。 相机的 Up 矢量应该指向宇航员的 Dir 矢量(参见图8)。
Pivot3DUtils.lookAtWithReference( scene.camera, 0, 0, 0, container, container.getDir(), 0.05 );
YellowPlanet_04.as 包含本节描述的代码。
在本节中,你将学习一些用于将互动性引入到游戏的概念。
首先,让我们探讨一下游戏如何管理用户的输入。 Flare3D 包含一些你可以使用的工具,它们能够在游戏播放过程中快速地管理键盘和鼠标输入信息。
在该类中,Input3D.keyDown()
方法可以用于检测按下的键。 该方法在按下一个键时将返回一个 true 值。
此外,该工具还包含 Input3D.keyHit()
方法,它类似于 keyDown,但只返回一次 true 值。
本范例项目中的另一个值得研究的有趣概念是光线的使用。
光线是虚拟的并且长度无限,它可以从任一点开始沿着某个方向无限延伸。
在无限延伸之前,光线能够测试游戏中的一个对象是否相交。 如果测试的结果是可靠的,则光线将提供关于碰撞的附加信息,其中包括 mesh、碰撞的确切点、被碰撞面部的法线等信息。
本范例项目将使用光线效果,因为宇航员在保持与球形地面对齐的同时需要不断地在球形地面上奔跑。 光线用于完成两个任务(参见图9)。
在本例中,环境的 stage 基本上是一个球体。 如有必要,你可以使用其它方法让宇航员保持与该球体对齐,但光线的作用很大,因为它们能够用于不均匀的表面。
使用 new RayCollision()
方法创建光线
使用下行代码创建一个新的 RayCollision:
ray = new RayCollision();
使用 ray.addCollisionWith( object )
方法添加对象以便对光线进行测试
下面代码将添加行星的地面以便利用光线测试碰撞效果:
ray.addCollisionWith( planet.getChildByName( "floor" ), false );
第二个参数(”false”)表示只能利用地面检测碰撞(丢弃地面的 child)。
使用 ray.test( from, direction )
方法测试光线碰撞
通过在光线起始的位置设置一个点开始测试。 这一位置应该不会与宇航员的位置完全一致,因为它与地面如此靠近,以致光线无法与任何物体相交。 相反,设置一个位置稍高的点,例如 (0,100,0),然后使用 localToGlobal 将该点转换为世界坐标(world coordinate)。
在定义光线起始点之后,下一个任务是为它提供方向。 该方向应该向下指向宇航员的方向。 你可以使用 Pivot3D 的 getDown() 方法获取该矢量。
//define ray’s start point var from:Vector3D = container.localToGlobal( new Vector3D( 0, 100, 0 ) ); //define ray’s direction var dir:Vector3D = container.getDown(); // Test the ray. if ( ray.test( from, dir ) ) { // ray collision is true. …
使用收到的碰撞信息
在上节中,你已经了解如何测试何时光线碰撞返回 true 值。 光线碰撞信息已经可用并且你可以将它用于游戏逻辑。 该信息是以特定的结构储存的。 你可以使用 CollisionInfo
对象来解析收到的信息。 在下面代码中,注意一下你如何才能获取位置 (info.point.x、info.point.y、info.point.z) 和标准方位 (info.normal) 信息。
// Test the ray. if ( ray.test( from, dir ) ) { // Get the info of the first collision. var info:CollisionInfo = ray.data[0]; // Set the astronaut container at the collision point. container.setPosition( info.point.x, info.point.y, info.point.z ); // Align the astronaut container to the collision normal. container.setNormalOrientation( info.normal, 0.05 );
YellowPlanet_05.as 包含本节描述的代码。
SphereCollision 是一款用于处理复杂几何图形碰撞的工具。 它本质上是一个与一个或多个 mesh 对象碰撞和相互作用的虚拟球体(参见图10)。
本范例游戏使用 SphereCollisions 以便使得宇航员能够与行星周围的墙和障碍物碰撞。 通过使用 SphereCollision,你可以将宇航员包裹于虚拟球体之后以便与游戏环境碰撞。
此外,该 SphereCollision 工具在碰撞发生时还能够提供一段平滑的位移,这一情形被称为滑块(slider)。
下面步骤描述如何使用 SphereCollision 类:
- 创建一个 SphereCollision 实例,然后将球体目标和一个半径值传递给它。
- 使用 collisions.addCollisionWith( object ) 方法添加对象以便对球体进行测试。
- 通过调用 collisions.slider()、collisions.fixed() 或 collisions.intersect() 方法更新碰撞。
- 在碰撞发生之后,你可以根据需要访问和使用相应的碰撞信息。
注: RayCollision 和 SphereCollision 均是计算强度很大的数学算法,因此最好尽可能与较少多边形对象一起使用。 由于这些工具需要完成如此复杂的数学公式计算,所以你必须注意到在以调试模式或发布模式下运行项目时存在的显著性能差异。 此外,SphereCollision 不能处理缩放的对象,因此在游戏中处理它们之前,必须对你的碰撞对象进行标准化处理。
使用 SphereCollision (source, radius, offset) 方法创建球体碰撞效果
首先,必须声明相应的碰撞对象,如下所示:
// The instance for the SphereCollision. private var collisions:SphereCollision;
其次,你可以通过将宇航员用作一个半径为 3 个单位的目标创建相应的 SphereCollision
。 记住,在步骤 3 中,宇航员被放置于一个名称为 “container” 的 Pivot3D
对象中(参见图11)。
在默认情形下,虚拟球体将位于坐标轴的中心。 在本范例中,宇航员的坐标是在他的脚上,因此你需要为虚拟球体提供一个偏移量,它是位于 (0,3,0) 的第三个 Vector3D
参数,如图11所示。
collisions = new SphereCollision( container, 3, new Vector3D( 0, 3, 0 ) );
如果你记得步骤 3 的”查看宇航员(Astronaut)容器”,你知道宇航员角色是包裹于一个容器之中的。 现在,该是说明原因的时候了。 宇航员具有跳跃的能力。 因此,在某些情形下,该角色能够跳过风扇以避免一次必然的死亡。 但宇航员不一定能够跳过大楼等障碍物。 此时,你可以检测到宇航员和障碍物之间的碰撞。 如果宇航员跳过障碍物,则绝不会下发碰撞事件。 因此,SphereCollision 将被添加到容器中,而不是宇航员。 之后,当宇航员跳起时,球体仍然处于底部并且能够检测到碰撞事件(参见图12)。
添加与障碍物的碰撞
一旦创建相应的碰撞,你必须指定需要核查的对象。 在本范例游戏中,这些对象(Pivot3D 对象)的每个对象均被称为 “obstacle”,因此你需要为每个对象添加一个碰撞事件。
if ( pivot.name == "obstacle" ) { // If the object is an obstacle, add it to the collisions list. collisions.addCollisionWith( pivot, false ); }
一旦你移动宇航员,所有的碰撞事件均被测试。 updateEvent() 函数包含下面代码行:
collisions.slider();
如果宇航员与一个障碍物发生碰撞,则该方法将产生一段位移以便确定相应的交点并且将宇航员重新定位到 scene 的正确位置。 这样可以避免出现导致宇航员被粘连在一个单一位置以及看似被冻结在 scene 中的问题。
YellowPlanet_06.as 包含本节描述的代码。
现在是添加一些好看的特殊效果的时候了(参见图13)。
粒子允许你同时快速地绘制大量数据。 当你创建效果时,你可以将所有粒子进行分组并且在一次绘制调用中将它们全部绘制出来,以避免单独绘制每个粒子。 正如你能够想象的那样,这一策略能够提升绘图速度,因为粒子与其它 3D 对象的工作方式有点不同,并且粒子通常面向相机。
按照下列步骤在 3D Flash 游戏中使用粒子:
- 创建一个 particle 类,它扩展
Particle3D
类的功能以便用作一个 particle 模板。 - 覆盖方法 init,更新并且克隆
Particle3D
类以便将期望的行为添加到相应的粒子中。 - 创建一个 ParticleMaterial3D 类以便设置与粒子兼容的纹理、颜色或其它过滤器。
- 使用材料和粒子模板创建一个
ParticleEmiter3D
类。 - 使用
emitParticlesPerFrame
、particlesLife
和decrementPerFram
属性配置相应的发射器(emitter)。
在本范例中,新的发射器(emitter)是在由 ParticleEmiter3D 类扩展而来的独立类中创建的,以便代码更加简洁并且易于更新。
设立粒子元素
首先,设置用于粒子的变量。 注意 smoke 和 fire 均是 Texture3D
对象。
private var smoke:Texture3D; private var fire:Texture3D; private var fireEmiter:FireEmiter;
然后,将纹理作为外部文件进行加载。
smoke = scene.addTextureFromFile( "smoke.png" ); fire = scene.addTextureFromFile( "particle.png" );
使用 FireEmiter
你可以创建火焰发射器并且将它添加给宇航员。 FireEmiter 类扩展了 ParticleEmiter3D 功能,并且你可以在目录src\objects 下的 sample files 文件夹中找到该类。
将 FireEmiter 的 parent 设置为 astronaut。 这将确保粒子能够从 astronaut 模型中发射出来。
fireEmiter = new FireEmiter( fire ); fireEmiter.parent = astronaut;
使用 SmokeEmiter
你可以按照使用 FireEmiter 的相同方式使用 SmokeEmiter。 在本范例游戏中,SmokeEmiter 与位于行星上的每个风扇是对应的。
注意 SmokeEmiter 类将风扇方向用作每个发射器的方向:
// Creates a new smoke emiter and adds them to the scene. var particles:SmokeEmiter = new SmokeEmiter( smoke ); particles.copyTransformFrom( pivot ); particles.parent = scene;
YellowPlanet_07.as 包含本节描述的代码。
本节描述的概念与开发任何其它 Flash 游戏使用的概念类似。 在本步骤中,我们的目的是让宇航员能够跳过 Y 轴。
由于宇航员位于容器对象的内部,所以无论容器的方位如何变化均无关紧要。 宇航员总是沿着其自己的 Y 轴上下移动。
在完成这一步骤之后,你需要更新游戏的逻辑。 为了管理游戏播放的流程,添加一些状态变量非常必要。 此外,你必须添加函数 gameLogics 以便在播放游戏过程中更新所有游戏状态。
管理状态和游戏变量
添加下列给出的代码以便实现一些游戏逻辑。 为了控制宇航员角色的行为,你需要保存角色的状态。 此外,为了控制宇航员的动量,你必须添加一个名称为 jumpValue 的新变量。
// Game logics variables. private var state:String = "run"; private var jumpValue:Number = 0;
使用 gameLogics 方法
在每次 Update 重复操作时均调用 gamesLogic 方法。 gamesLogic 方法能够核查宇航员的状态。 如有必要,你可以修改游戏行为以改变其工作方式。 例如,如果用户按下 Space 键,则它将激活 FireEmiter 并且更新角色的动画效果。 下面是一个范例:
private function gameLogics():void { switch( state ) { case "run": if ( Input3D.keyHit( Input3D.SPACE ) ) { jumpValue = 4; fireEmiter.emitParticlesPerFrame = 25; state = "jump"; container.gotoAndPlay( "jump", 3 ); } break; case "jump": if ( astronaut.y == 0 ) { state = "run"; container.gotoAndPlay( "run", 3 ); } break; } }
YellowPlanet_08.as 包含本节描述的代码。
现在,游戏的确变得赏心悦目。 你已经进入了添加有趣部件的阶段以便使得游戏变得更加引人入胜。 在本节中,你将添加能够允许宇航员角色杀死敌人、死亡和飞行的代码。
这些均是能够为游戏带来生气的冒险和诱惑理念。 你将会继续使用相应的游戏逻辑不断地将新的功能融合到游戏中。
此刻也是将控制风扇和地雷的行为移植到一个名称为 gameObjects 的新函数的时候了。
管理状态和游戏变量
在游戏逻辑变量中创建一个新游戏变量,将其命名为 shakeFactor:
// Game logics variables. private var state:String = "run"; private var jumpValue:Number = 0; private var shakeFactor:Number;
在宇航员死亡时,你将使用 shakeFactor 变量摇晃相机并且生成一种特殊效果。
了解 gameObjects
gameObjects 的行为与 gameLogics 类似,因为在每次 Update 重复操作时均调用 gameObjects 方法。 该函数通过比较宇航员角色和 scene 中其它元素之间的距离获取宇航员的当前位置并且更新游戏状态。
处理风扇碰撞
如果宇航员与风扇之间的距离小于风扇的半径,则游戏的状态被更改为 “fan” 并且在下次调用 gameLogics 方法时宇航员将被发射到太空(参见图14)。
// changes the state to "fan" if ( Vector3D.distance( f.getPosition(), position ) < radius ) state = "fan";
处理地雷碰撞
如果宇航员与地雷之间的距离小于10个单位,则游戏逻辑使用状态变量的值来设置一个具有下面两种可能的结果:
- State = “jump” – 结果是宇航员跃过地雷并且地雷被摧毁。
m.visible = false; shakeFactor = 2;
- State = “run” – 结果是宇航员与地雷相撞并且宇航员死亡(参见图15)。
container.visible = false; shakeFactor = 15; state = "die";
重新激活销毁的地雷
每次地雷被摧毁时,它将被重新安置到行星的另一侧。 之后,必须重新激活它。
为了检测地雷是否位于行星的另一侧,你可以将每个地雷的 Up 矢量与宇航员的当前Up矢量进行比较。 如果你使用 dotProduct 将这两个矢量相乘,并且它们均指向相同的方向,则结果是一个正的数值,这表示宇航员和地雷均位于行星的同一侧。 如果比较之后计算出两个矢量指向不同方向,则结果是一个负值,因为地雷的位置与宇航员的位置相距很远(参见图16)。
//This line gets the mine Up vector var mineUp:Vector3D = m.getUp(); //This line gets astronaut Up vector var contUp:Vector3D = container.getUp(); //Here compare the vector and re-activate the mine if result is great than zero. if ( mineUp.dotProduct( contUp ) < 0 ) m.visible = true;
YellowPlanet_09.as 包含本节描述的代码。
在本节中,你将使用 Flash Professional 将声音和 2D 内容集成到 3D Flash 游戏中。
声音和 2D 对象的添加为游戏增添了引人入胜的新外观,因此使得游戏玩起来更赏心悦目。
你可以使用许多不同的方法将 Flash 内容带入游戏。 在本范例中,为了保持简洁,你可以通过将你的游戏部件作为组件导出(SWC 文件)。
按照下列步骤将一个 FLA 文件作为一个组件导出:
- 打开一个 Flash 文件,然后使用 Property inspector 将链接名称设置为你希望导出的对象和影片。
- 在 Library 面板上右击需要的元素。 在显示的 context 菜单中,选中选项:Export SWC File。
在导出 SWC 文件之后,你可以将相应的库添加到你的项目中,然后使用其中包含的组件创建它们新的实例。
这部分过程与处理基本影片剪辑符号和声音非常相似。
建立 2D 界面
范例游戏的 2D 界面包含于 assets.swc 文件。 你可以使用下列代码为该对象创建一个实例:
// MovieClip and Sound objects. private var loading:Loading = new Loading(); private var gui:GUI = new GUI(); private var sndMusic:MusicSound = new MusicSound(); private var sndCoin:CoinSound = new CoinSound(); private var sndDead:DeadSound = new DeadSound(); … // Adds the MovieClips to the stage. addChild( gui ); addChild( loading );
与图形用户界面元素互动
在用户玩游戏的过程中,需要跟踪三种不同的分数。 用户的个人游戏点数(game points)、对象收集的能量数(number of energy)和总体最高分数(overall highest score)均可以计算和跟踪,以便于在游戏界面上显示出来。 相应的分数信息可以在 gameGUI()
函数中更新。 该函数能够修改 2D UI 元素中的文本字符以便显示当前游戏信息(参见图17)。
// Updates the interface of the game. private function gameGUI():void { // Gets the best score. if ( score > bestScore ) bestScore = score; gui.points.score.text = score.toString(); gui.best.score.text = bestScore.toString(); gui.energy.content.count.text = energyCount.toString(); gui.total.score.text = score.toString(); }
啪、轰、砰!
游戏包含当宇航员死亡或摧毁地雷时显示的信息。 这些信息也是由 2D 图形元素构成的(参见图18)。
核查一下下面的代码,看看信息是如何放置于 scene 的正确位置,在此我们使用宇航员的位置作为参考位置。
private function newPop():void { var pos:Vector3D = container.getScreenCoords(); var pop:Pop = new Pop(); pop.x = pos.x; pop.y = pos.y; addChild( pop ); }
方法 getScreenCoords()
将 3D 容器的坐标转换为 2D 坐标(以便返回一个 Point 对象)。
YellowPlanet_10.as 包含本节描述的代码。
本游戏的目标是在宇航员在行星上行走时收集宇航员发现的能量条目。 当用户收集到 3 个能量条目时,它们将被提升到游戏的下一级别并且宇航员的速度将会增大(参见图19)。
能量条目是存储于 energy.f3d 文件中的一个外部 3D 模型。 为了将能量条目添加到游戏中,你应该加载 F3D 文件并且将它添加到 scene 中,就像前面添加其它 Flare3D 元素一样。
放置能量条目
每次宇航员发现一个能量条目之后,将生成一个全新的能量条目并且放置于行星的一个随机位置。 新的能量条目的位置是通过随机计算得出的,如下所示:
// Pick a random position. var currIndex:int = energyIndex; while ( currIndex == energyIndex ) energyIndex = Math.random() * points.length; //energy is moved to the new position energy.copyTransformFrom( points[energyIndex] );
更多能量 = 更高速度
每次宇航员发现一个新的能量条目之后,他的速度将会提升:
speedCounter++; speed += 0.2; gui.speed.visible = true; gui.speed.content.value.text = speedCounter.toString(); gui.speed.gotoAndPlay(1); energyCount = 0; resetCounter = 120; state = "energy";
当宇航员连续获取 3 个能量条目时,他将在一小段时间内暂时不能移动并且游戏将显示 “Next Level” 告警提示信息。 在此之后,游戏的状态将返回到 “run”。 这一类型的附加细节信息将为游戏增添兴奋场面,并且提供更为赏心悦目的用户体验。
if ( Input3D.keyDown( Input3D.RIGHT ) ) container.rotateY( 5 ); if ( Input3D.keyDown( Input3D.LEFT ) ) container.rotateY( -5 ); resetCounter--; if ( resetCounter < 0 ) state = "run";
下一步阅读方向
名称为 Yellow Planet 的 3D Flash 游戏将作为教程游戏包含于正式的 Flare3D 版本中。 你可以使用该范例项目组件来研究 Flare3D 的运行机制。 在本教程中,你已经学习了与开发游戏相关的主要任务。
希望这一简介已经激发你使用 Flare3D 技术开发你自己的 Flash 游戏并且充分利用 Flash Player 11 引入的新功能。
如需获取关于使用 Flare3D 的更多信息,请访问 Flare3D 网站。
此外,请务必查看 Stage 3D Developer Center 寻找各种有用的文章、教程和范例项目以便让你能够迅速地展开工作。