嵌套就是指将一个对象嵌入另一个容器对象中,当我们移动容器对象的时候,嵌入的对象也跟着容器对象移动。确切的说,嵌套就是将对象打包在一起形成一个整体,通过控制容器对象来整体移动。
当使用PV3D实现太阳系运行效果时,嵌套能很好的解决他们之间旋转、旋转速度的问题。下面我们以Sun、Earth、Moon的运行讲解嵌套。
第一步,在场景中创建一个大小为60的Sun对象sun,它被加载进场景的(0,0,0)。
第二步,创建一个大小为20的Earth对象earth,很自然,我们也将它加载进场景,并设置位置为600。
(1) 使用rotationY(按照其父容器的Y旋转,即场景Y轴)让earth旋转。但是Earth并没有绕着场景原点旋转,而是像自转一样。
(2) 使用localRotationY(按照其自身Y轴旋转)让earth旋转。Earth也没有绕着场景原点旋转,而是自转(方向和rotationY相反)。
PV3D提供给我们的2种旋转都不能解决地球绕场景原点旋转。在这里需要注意的是:虽然rotation和localRotation旋转坐标系统不同,但是它们都是在其中点,依据坐标系统旋转的。因此我们只能看到earth绕自身旋转。
第三步,将earth加载进sun,而不是场景。此时sun和earth嵌套成一个整体,让容器对象sun进行rotation旋转,可以看到容器对象中的所有嵌入的对象都跟着旋转。下图虚线表示sun和earth嵌套成一个整体。
我们了解的太阳系是以太阳为参照的,因此太阳相对太阳系的其他行星是静止的。为了解决这个问题,可以使用容器类DisplayObject3D。
第四步,创建一个DisplayObject3D对象earthContainer,将其放入场景。然后将earth对象加载进earthContainer,并在刷屏方法中让earthContainer对象进行rotationY旋转。此时earthContainer和earth嵌套为一个整体,容器对象(earthContainer)旋转带动earth旋转,这样earth就能绕着场景原点旋转了。
现在要解决的是如何让月球绕地球旋转。在这里需要注意的是月球有2种运行:
Ø 一种是绕太阳(场景原点)旋转
Ø 一种是绕地球旋转
一种简单的解决方法是:将创建的Moon对象moon加载进earth中,并在刷屏方法中让earth自转(localRotation)。这样earth带动moon旋转,且earthContainer带动moon绕太阳旋转。
这种方法简单,但是月球绕地球旋转的速度和地球自转一样,因为月球的旋转是由地球自转带动的。为了跟好的模拟太阳系运行效果,我们可以设置一个DisplayObject3D容器在earth的中心,让容器带动moon的旋转。
第五步,创建一个容器moonContainer,并将其加载进earthContainer容器中(earthContainer绕场景原点旋转),设置moonContainer的位置和earth一样。此时将moon加载进moonContainer中。并在刷屏方法中设置moonContainer的旋转速度。
下面在FlashDevelop中运行的代码:
package { import flash.events.Event; import mx.core.BitmapAsset; import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.materials.ColorMaterial; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.objects.primitives.Cylinder; import org.papervision3d.objects.primitives.Sphere; import org.papervision3d.view.BasicView; /** * ... * @author yl */ [SWF(width = "800", height = "600", backgroundColor = "0x000000")] public class SunSys extends BasicView { private var sun:Sphere; private var earth:Sphere; private var moon:Sphere; private var earthContain:DisplayObject3D; private var water:Sphere; private var waterContain:DisplayObject3D; private var moonContain:DisplayObject3D; [Embed(source = "../assets/earth.jpg")] private var Earth:Class; [Embed(source = "../assets/moon.jpg")] private var Moon:Class; [Embed(source = "../assets/sun.jpg")] private var Sun:Class; [Embed(source = "../assets/water.jpg")] private var Water:Class; public function SunSys() { earthContain = new DisplayObject3D(); waterContain = new DisplayObject3D(); moonContain = new DisplayObject3D(); scene.addChild(waterContain); scene.addChild(moonContain); scene.addChild(earthContain); var sunAsset:BitmapAsset = new Sun() as BitmapAsset; var sunMaterial:BitmapMaterial = new BitmapMaterial(sunAsset.bitmapData); sun = new Sphere(sunMaterial, 60, 20, 20); scene.addChild(sun); var waterAsset:BitmapAsset = new Water() as BitmapAsset; var waterMaterial:BitmapMaterial = new BitmapMaterial(waterAsset.bitmapData); water = new Sphere(waterMaterial, 15, 20, 20); water.x = 200; waterContain.addChild(water); var earthAsset:BitmapAsset = new Earth() as BitmapAsset; var earthMaterial:BitmapMaterial = new BitmapMaterial(earthAsset.bitmapData); earth = new Sphere(earthMaterial, 20, 20, 20); earth.x = 600; earthContain.addChild(earth); moonContain.position = earth.position; earthContain.addChild(moonContain); var moonAsset:BitmapAsset = new Moon() as BitmapAsset; var moonMaterial:BitmapMaterial = new BitmapMaterial(moonAsset.bitmapData); moon = new Sphere(moonMaterial, 10, 20, 20); moon.x = 100; moonContain.addChild(moon); camera.y = 1000; camera.z = -1000; startRendering(); } override protected function onRenderTick(e:Event = null):void { super.onRenderTick(); earthContain.rotationY++; earth.localRotationY += 30; moonContain.rotationY += 5; waterContain.rotationY += 3; } } }