zoukankan      html  css  js  c++  java
  • Alternativa 3D Series – Tutorial 1 – Getting Started

    Alternativa 是基于flash的3d引擎。使用Alternativa我们可以直接在web页面嵌入3d应用。在链接互联网的pc上,flashplayer的安装率超过了90%。因此使用alternative3d制作的应用,不必考虑麻烦用户安装多余的插件。在接下来的一系列文章中,我将向你展示如何使用alternative3d和flex制作简单的3D应用。

      

    本系列教程内容虽然不是“Hello world”,但是他同样很简单。在文章开始部分,我们会着手制作一个后续文章方便扩展的框架。本着这个目标,我们首先会将逻辑关系和引擎的管理部分进行分离。逻辑管理部分仅用来描述程序自身的一些特性。

    代码
    1 package
    2 {
    3 import flash.display.StageAlign;
    4 import flash.display.StageScaleMode;
    5 import flash.events.Event;
    6 import mx.collections.ArrayCollection;
    7 import mx.core.Application;
    8 import mx.core.UIComponent;
    9 import alternativa.engine3d.controllers.CameraController;
    10 import alternativa.engine3d.core.Camera3D;
    11 import alternativa.engine3d.core.Object3D;
    12 import alternativa.engine3d.core.Scene3D;
    13 import alternativa.engine3d.display.View;
    14 import alternativa.utils.FPS;
    15
    16 /**
    17 * The EngineManager holds all of the code related to maintaining the Alternativa 3D engine.
    18 */
    19 public class EngineManager extends UIComponent
    20 {
    21 public var scene:Scene3D;
    22 public var view:View;
    23 public var camera:Camera3D;
    24 public var cameraController:CameraController;
    25
    26 // a collection of the BaseObjects
    27   protected var baseObjects:ArrayCollection = new ArrayCollection();
    28 // a collection where new BaseObjects are placed, to avoid adding items
    29 // to baseObjects while in the baseObjects collection while it is in a loop
    30   protected var newBaseObjects:ArrayCollection = new ArrayCollection();
    31 // a collection where removed BaseObjects are placed, to avoid removing items
    32 // to baseObjects while in the baseObjects collection while it is in a loop
    33 protected var removedBaseObjects:ArrayCollection = new ArrayCollection();
    34 // the last frame time
    35 protected var lastFrame:Date;
    36
    37 public function EngineManager()
    38 {
    39 super();
    40 addEventListener(Event.ADDED_TO_STAGE, init);
    41 }
    42
    43 public function init(e:Event): void
    44 {
    45 stage.scaleMode = StageScaleMode.NO_SCALE;
    46 stage.align = StageAlign.TOP_LEFT;
    47
    48 // Creating scene
    49 scene = new Scene3D();
    50 scene.root = new Object3D();
    51
    52 // Adding camera and view
    53 camera = new Camera3D();
    54 camera.x = 100;
    55 camera.y = -150;
    56 camera.z = 100;
    57 scene.root.addChild(camera);
    58
    59 view = new View();
    60 addChild(view);
    61 view.camera = camera;
    62
    63 // Connecting camera controller
    64 cameraController = new CameraController(stage);
    65 cameraController.camera = camera;
    66 cameraController.setDefaultBindings();
    67 cameraController.checkCollisions = true;
    68 cameraController.collisionRadius = 20;
    69 cameraController.controlsEnabled = true;
    70
    71 // FPS display launch
    72 FPS.init(stage);
    73
    74 stage.addEventListener(Event.RESIZE, onResize);
    75 stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
    76 onResize(null);
    77
    78 // set the initial frame time
    79 lastFrame = new Date();
    80
    81 // start the application
    82 ApplicationManager.Instance.startupApplicationManager();
    83 }
    84
    85 private function onResize(e:Event):void
    86 {
    87 view.width = stage.stageWidth;
    88 view.height = stage.stageHeight;
    89 Application.application.width = stage.stageWidth;
    90 Application.application.height = stage.stageHeight;
    91 }
    92
    93 protected function onEnterFrame(event:Event):void
    94 {
    95 // Calculate the time since the last frame
    96 var thisFrame:Date = new Date();
    97 var seconds:Number = (thisFrame.getTime() - lastFrame.getTime())/1000.0;
    98 lastFrame = thisFrame;
    99
    100 // sync the baseObjects collection with any BaseObjects created or removed during the
    101 // render loop
    102 removeDeletedBaseObjects();
    103 insertNewBaseObjects();
    104
    105 // allow each BaseObject to update itself
    106 for each (var baseObject:BaseObject in baseObjects)
    107 baseObject.enterFrame(seconds);
    108
    109 // User input processing
    110 cameraController.processInput();
    111 // Scene calculating
    112 scene.calculate();
    113 }
    114
    115 public function addBaseObject(baseObject:BaseObject):void
    116 {
    117 newBaseObjects.addItem(baseObject);
    118 }
    119
    120 public function removeBaseObject(baseObject:BaseObject):void
    121 {
    122 removedBaseObjects.addItem(baseObject);
    123 }
    124
    125 protected function shutdownAll():void
    126 {
    127 // don't dispose objects twice
    128 for each (var baseObject:BaseObject in baseObjects)
    129 {
    130 var found:Boolean = false;
    131 for each (var removedObject:BaseObject in removedBaseObjects)
    132 {
    133 if (removedObject == baseObject)
    134 {
    135 found = true;
    136 break;
    137 }
    138 }
    139
    140 if (!found)
    141 baseObject.shutdown();
    142 }
    143 }
    144
    145 protected function insertNewBaseObjects():void
    146 {
    147 for each (var baseObject:BaseObject in newBaseObjects)
    148 baseObjects.addItem(baseObject);
    149
    150 newBaseObjects.removeAll();
    151 }
    152
    153 protected function removeDeletedBaseObjects():void
    154 {
    155 for each (var removedObject:BaseObject in removedBaseObjects)
    156 {
    157 var i:int = 0;
    158 for (i = 0; i < baseObjects.length; ++i)
    159 {
    160 if (baseObjects.getItemAt(i) == removedObject)
    161 {
    162 baseObjects.removeItemAt(i);
    163 break;
    164 }
    165 }
    166
    167 }
    168
    169 removedBaseObjects.removeAll();
    170 }
    171 }
    172 }

    我们从EngineManager类讲起。这个类用来完成3d引擎的初始化。它继承自UIComponent,这样我们就能像使用其他的flex组件一样,直接把它放到Application标签中。一会我们就可以在Alternativa1.mxml中看到他。

    在EngineManager的构造函数中,我们添加了一个事件监听器。它可以在EngineManager放到显示列表时,完成一些初始化操作。有一点我们不必担心,该事件一旦发生,stage属性将不为空。

    在init函数中,我们做了一些Alternativa初始化操作。我们创建了4个很重要的东西:Scene3D,Camera3D,CameraController和View。Scene3D本质上是一个容纳其他物体的容器(功能同sprite)。Camera3D摄像机,从名字我们就能知道他的作用,他就像3d世界中的眼睛。CameraController为我们提供了鼠标和键盘控制camera,同时做一些简单的物体碰撞检测。使用CameraController我们只需要六行代码,就可以移动和控制3d世界,这真是一件很棒的事情。最后还有一个View,他用来把3d的影像转化成2d图片显示在显示器上。


    你可能注意到,我们使用了一个FPS.init(stage).方法。这个方法用来在舞台上显示每秒的内存使用和帧率。对于程序调试,他是一个很有用的工具,但是在最终的发布版本,这个方法是可以注释掉的。

    一旦Alternativa 引擎初始化,我们需要增加两个事件监听。一个是用来响应窗口大小改变(stage.addEventListener(Event.RESIZE, onResize),另一个用来不停渲染( stage.addEventListener(Event.ENTER_FRAME, onEnterFrame)。我们捕捉窗口大小变化事件,以便让View 做出相应大小改变。帧频渲染可以不停渲染当前3d场景。


    帧频渲染是3D程序运行的基本概念。渲染包括两个部分,第一部分(游戏逻辑)是程序自身的更新:游戏中的运动和游戏逻辑处理。第二部分(屏幕渲染)就是3d engine的逐帧屏幕渲染,可以显示3d世界的变化。

    第一部分的更新是由BaseObject 这个类完成的。你会在onEnterFrame 函数看到,我们用从BaseObjects取出单个BaseObject,并调用它的enterFrame 方法。enterFrame 方法是BaseObject 里重量级方法,他允许继承自BaseObject 的子类方便的进行自我渲染更新。在MeshObject 和RotatingBox 类中会看到他。


     

    最后在init方法中我们调用了ApplicationManager.Instance.startupApplicationManager().在讲解这个以前,我们先把程序的逻辑和engine逻辑做分离。EngineManager的重要作用就是管理Alternativa引擎和不停渲染。

    下面我们接着做另一件事,创建ApplicationManager 类。

     

    代码
    1 package
    2 {
    3
    4 import mx.core.Application;
    5
    6 /**
    7 * The ApplicationManager holds all program related logic.
    8 */
    9 public class ApplicationManager
    10 {
    11 protected static var instance:ApplicationManager = null;
    12
    13 public static function get Instance():ApplicationManager
    14 {
    15 if (instance == null)
    16 instance = new ApplicationManager();
    17 return instance;
    18 }
    19
    20 public function ApplicationManager()
    21 {
    22
    23 }
    24
    25 public function startupApplicationManager():ApplicationManager
    26 {
    27 var rotatingBox:RotatingBox = new RotatingBox().startupRotatingBox();
    28 Application.application.engineManager.cameraController.lookAt(rotatingBox.model.coords);
    29
    30 return this;
    31 }
    32
    33 }
    34 }

     

    ApplicationManager类是个单例类(不明白的自己百度)。该类中仅有一个名为startupApplicationManager的方法,它会返回类本身的唯一实例,在这个方法中我们还创建了一个RotatingBox(旋转多面体),并把它指向给摄像机。建立一个类,就在里面写了两行代码,似乎有些多余,但是对于一个复杂的程序来说,这样做有助于将程序逻辑和引擎逻辑分离。

     

     

    代码
    1 package
    2 {
    3 import mx.core.Application;
    4
    5 /**
    6 * The BaseObject class allows extending classes to update themselves during the render loop.
    7 */
    8 public class BaseObject
    9 {
    10 public function BaseObject()
    11 {
    12
    13 }
    14
    15 /**
    16 * Must be called by all extending classes when being created. Adds this object to the list of BaseObjects maintained
    17 * by the EngineManager.
    18 */
    19 public function startupBaseObject():void
    20 {
    21 Application.application.engineManager.addBaseObject(this);
    22 }
    23
    24 /**
    25 * Must be called by all extending classes when being destroyed. Removes this object to the list of BaseObjects maintained
    26 * by the EngineManager.
    27 */
    28 public function shutdown():void
    29 {
    30 Application.application.engineManager.removeBaseObject(this);
    31 }
    32
    33 /**
    34 * This function is called once per frame before the scene is rendered.
    35 *
    36 * @param dt The time in seconds since the last frame was rendered.
    37 */
    38 public function enterFrame(dt:Number):void
    39 {
    40
    41 }
    42 }
    43 }

     

     

    代码
    1 package
    2 {
    3 import alternativa.engine3d.core.Object3D;
    4 import alternativa.engine3d.materials.SurfaceMaterial;
    5
    6 import mx.core.Application;
    7
    8 public class MeshObject extends BaseObject
    9 {
    10 public var model:Object3D = null;
    11
    12 public function MeshObject()
    13 {
    14 super();
    15 }
    16
    17 override public function shutdown():void
    18 {
    19 super.shutdown();
    20 Application.application.engineManager.scene.root.removeChild(model);
    21 model = null;
    22 }
    23
    24 public function startupModelObject(object:Object3D):void
    25 {
    26 model = object;
    27 Application.application.engineManager.scene.root.addChild(model);
    28 super.startupBaseObject();
    29 }
    30 }
    31 }

    RotatingBox继承自MeshObject,而MeshObject继承自BaseObject,MeshObject比BaseObject多了一个叫Object3D的属性。Object3D(3D物体的基类)可以在屏幕上创建带3D网格的物体。

    看起来在一个类中只增加了一个属性,还要多次继承来实现,有点多余。但是,你可以想象,不管是在游戏还是在程序中,我们需要很多3D的物体,并且邪王某些类虽然不包含3D物体但是它也需要每帧做更新,我们这样的类层次划分就很合理。这也是我们把MeshObject和BaseObject分离成两个类的原因,继承自BaseObject 的类就可以做自我更新,继承自MeshObject 的类不光可以自我更新,还多了一个3D实物的属性——像RotatingBox

     

     

    代码
    1 package
    2 {
    3 import alternativa.engine3d.materials.WireMaterial;
    4 import alternativa.engine3d.primitives.Box;
    5
    6 public class RotatingBox extends MeshObject
    7 {
    8 protected static const ROTATION_SPEED:Number = 1;
    9
    10 public function RotatingBox()
    11 {
    12 super();
    13 }
    14
    15 public function startupRotatingBox():RotatingBox
    16 {
    17 var box:Box = new Box(100, 100, 100, 3, 3, 3);
    18 box.cloneMaterialToAllSurfaces(new WireMaterial(1, 0x000000));
    19 super.startupModelObject(box);
    20 return this;
    21 }
    22
    23 public override function enterFrame(dt:Number):void
    24 {
    25 model.rotationX += dt * ROTATION_SPEED;
    26 model.rotationY += dt * ROTATION_SPEED;
    27 model.rotationZ += dt * ROTATION_SPEED;
    28 }
    29 }
    30 }

    让我们来研究一下RotatingBox 类,它有两个重要的方法:startupRotatingBox 和enterFrame。startupRotatingBox 方法创建一个带材质的3D物体,本例中我们创建了一个原始的四方体,并给四方体使用了一个叫WireMaterial(线形)的材质。enterFrame 对继承自BaseObject的方法做了重写。在这里,每次调用该方法都会让box旋转一定的角度。

     

    代码
    1 <?xml version="1.0" encoding="utf-8"?>
    2 <mx:Application
    3 xmlns:mx="http://www.adobe.com/2006/mxml"
    4 layout="absolute"
    5 xmlns:ns1="*"
    6 width="640"
    7 height="480" color="#FFFFFF"
    8 backgroundGradientAlphas="[1.0, 1.0]"
    9 backgroundGradientColors="[#FFFFFF, #C0C0C0]">
    10
    11 <ns1:EngineManager id="engineManager" x="0" y="0" width="100%" height="100%"/>
    12
    13 </mx:Application>

    我们将所有东西集合到Alternativa1.mxml文件中,这是程序的入口文档。前文提到的让EngineManager 继承自UIComponent 就是为了,能让它放到主的Application类中。如你所见,我们在主文档中仅需像添加一个文本标签一样简单的添加EngineManager 标签,

    就可以完成一个3D控制器。

    总结:在EngineManager用来初始化并管理3D引擎,本类在以后几章中将很少变化。ApplicationManager 分离出了程序自身的逻辑,本类将会在后续章节中相应改变。BaseObject 和MeshObject 基类可以让我们很方便的创建一个物体,并实现物体的自我更新。这两个类

    以后不会变动。最后RotatingBox 继承自BaseObject 和MeshObject ,创建了一个现实的3D物体。

  • 相关阅读:
    马赛克算法及iOS代码实现
    iOS制作Static Library(静态库),实现多工程的连编
    iOS由ImageIO.framework实现gif的系统解码
    KVC和KVO实现监听容器类(数组等)的变化
    Dynamicaly Typed(动态定型), Objective-C Runtime Programming
    Mac OSX下修改hosts文件
    MAC配置SVN服务器
    Encoding非常用编码转换
    Block作为参数使用
    UITextField关闭系统自动联想和首字母大写功能
  • 原文地址:https://www.cnblogs.com/crkay/p/1765852.html
Copyright © 2011-2022 走看看