Tank Fire Emitter Effect with Flare3D Part2

昨天暂停在代码实现之前的部分,为了让坦克主体能够分别表现炮击瞬间的后坐力的效果,我对整个模型的结构做了一下层级调整,关于如何让一个模型成为另外一个模型的子部分,是直接拖拽放置即可实现的。

层级如下:

[![](https://archive.writeitdown.site/%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202014-01-07%20%E4%B8%8B%E5%8D%882.41.10.png)](https://archive.writeitdown.site/%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202014-01-07%20%E4%B8%8B%E5%8D%882.41.10.png)
这样一来,在代码加载模型后,就可以很方便地引用模型对应的各个部分, 下面上代码:
 package  
 {  
      import flash.display.Sprite;  
      import flash.events.Event;  
      import flash.events.KeyboardEvent;  
      import flash.events.ProgressEvent;  
      import flash.text.TextField;  
      import flash.text.TextFormat;  
      import flash.ui.Keyboard;  
      import caurina.transitions.Tweener;  
      import flare.basic.Scene3D;  
      import flare.basic.Viewer3D;  
      import flare.core.Camera3D;  
      import flare.core.Particles3D;  
      import flare.core.Pivot3D;  
      public class TankEmitterDemo extends Sprite  
      {  
           private var scene:Scene3D;  
           private var loadingLabel:TextField;  
           public function TankEmitterDemo()  
           {  
                scene = new Viewer3D( this );  
                scene.camera = new Camera3D();  
                scene.autoResize = true;  
                scene.castShadows = true;  
                scene.showLogo = false;  
                scene.camera.x = 500;  
                scene.camera.y = 500;  
                scene.camera.z = -300;  
                scene.camera.lookAt(0,0,0);  
                createTexts();  
                scene.addChildFromFile("t90.zf3d");  
                scene.addEventListener(Event.COMPLETE,onComplete);  
                scene.addEventListener(ProgressEvent.PROGRESS,onProgress);  
                stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);  
           }  
           private function createTexts():void  
           {  
                var label:TextField = new TextField();  
                label.defaultTextFormat = new TextFormat("Helvitica",15,0xffffff);  
                label.multiline = true;  
                label.width = 400;  
                label.height = 400;  
                label.text = "1 Hold left mouse button to rotate view\n2 Press space bar to fire.";  
                label.x = stage.stageWidth - 400;  
                label.y = stage.stageHeight - 50;  
                this.addChild(label);  
                loadingLabel = new TextField();  
                loadingLabel.defaultTextFormat = new TextFormat("Helvitica",30,0xffffff);  
                loadingLabel.text = "LOADING MODEL 0%";  
                loadingLabel.width = 400;  
                loadingLabel.x = (stage.stageWidth - 400)/2;  
                loadingLabel.y = stage.stageHeight/2;  
                this.addChild(loadingLabel);  
           }  
           private var parSmoke:Particles3D;  
           private var parSmokeFire:Particles3D;  
           private var tank:Pivot3D;  
           private var body:Pivot3D;  
           private var tower:Pivot3D;  
           private var subTower:Pivot3D;  
           private var rolling:Boolean = false;  
           private var rollingStopTimer:uint;  
           public var towerRotationX:Number = 0;  
           protected function onComplete(event:Event):void  
           {  
                this.removeChild(loadingLabel);  
                var v3d:Viewer3D = Viewer3D(event.currentTarget);  
                tank = v3d.getChildByName("tank");  
                body = tank.getChildByName("body");  
                tower = tank.getChildByName("tower");  
                subTower = tower.getChildByName("subTower");  
                parSmoke = tower.getChildByName("smoke") as Particles3D;  
                parSmokeFire = tower.getChildByName("fire") as Particles3D;  
                parSmoke.addEventListener("animationComplete",onSmokeComplete);  
                parSmokeFire.addEventListener("animationComplete",onSmokeFireComplete);  
                parSmoke.visible = false;  
                parSmokeFire.visible = false;  
           }  
           protected function onProgress(event:ProgressEvent):void  
           {  
                loadingLabel.text = "LOADING MODEL "+int(event.bytesLoaded/event.bytesTotal)+"%";  
           }  
           protected function onKeyDown(event:KeyboardEvent):void  
           {  
                if(event.keyCode == Keyboard.SPACE)  
                {  
                     fire();  
                }  
           }  
           protected function loop(event:Event):void  
           {  
                if(tower != null && rolling)  
                {  
                     tower.rotateY(1);  
                }  
           }  
           protected function onSmokeFireComplete(event:Event):void  
           {  
                parSmoke.visible = false;  
           }  
           protected function onSmokeComplete(event:Event):void  
           {  
                parSmokeFire.visible = false;  
           }  
           private function fire():void  
           {  
                parSmoke.visible = true;  
                parSmokeFire.visible = true;  
                parSmoke.start();  
                parSmokeFire.start();  
                subTower.rotateX(-10);  
                towerRotationX = -10;  
                Tweener.addTween(this,{time:2,transition:"easeOutElastic",towerRotationX:0,onUpdate:onFireMove});  
                Tweener.addTween(tank,{time:1,transition:"easeOutExpo",z:tank.z - 10});  
           }  
           private function onFireMove():void  
           {  
                subTower.setRotation(towerRotationX,0,0);  
           }  
      }  
 }  

简单说明一下,其中加载模型完毕后,onComplete事件触发的函数中,按照上图描述的层级,分别解析出了不同的对应部分。

用于开火的fire函数,做法是先让粒子visible为true,然后调用start函数播放,在结束的瞬间做了隐藏。
至于后坐力表现的部分则是炮塔做瞬间仰角后再以震荡曲线回复到原始状态,效果在下面,我觉得还挺像的。

照例奉上相关资源:

Source

Flare3D