The 3D part is a bit in stand-by this last weeks on the Citrus Engine, because I’m polishing the engine for the V3 which should be out this week!
I’ve never clearly introduced AwayPhysics in the Citrus Engine, so it’s the day! The idea is to have a similar pre-built platformer objects that we already have with Box2D and Nape but with AwayPhysics this time for 3D stuff! This work and Away3D support will evolve during the V3.
Now that 3D views and physics are supported, it’s time to give a look on which tool we can use as a 3D game Level Editor. At first, I thought to Prefab. This is the best tool to create a scene with Away3D, importing assets, add lights… But too complex for a simple level design, I mean : hey the Citrus Engine is not a concurrent to Unity3D (which I started to learn thanks to this awesome tutorial) 😀 Its 3D physics part is just here to create basic 3D game / puzzle. Also the level editor has to support object creation (physics object), this isn’t obvious with Prefab.
Then I gave a look to Cadet Editor, the 3D editor is very easy to handle. You can create quickly sphere, cube and plane objects, add lights… That’s the right tool to see what can be done!
I really enjoy the design of Cadet Editor, however it should manage right click with camera rotation instead of displaying a simple menu with a link to Away3D.
In Cadet3D project are saved into a format close to MXML. See our examples and its code :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | <cadet:CadetScene x:id="0" framerate="30" name="Component" timeScale="1" xmlns:cadet="www.unwrong.com/cadet/1.0.0" xmlns:x="org.bonesframework.core.serialization.Serializer" xmlns:bones="www.bonesframwork.org/bones/1.0.0" xmlns:cadetAway3D4="www.unwrong.com/cadetAway3D4/1.0.0" xmlns:ns0="cadetAway3D4.components.cameras" xmlns:ns1="cadetAway3D4.components.materials" xmlns:ns2="cadetAway3D4.components.geom" xmlns:ns3="cadetAway3D4.components.lights"> <bones:ArrayCollection x:name="children" x:id="1"> <cadetAway3D4:RendererAway3D x:name="0" x:id="2" name="Away3D 4 Renderer"> <ns0:CameraComponent x:name="cameraComponent" x:id="3" transform="0.9833181500434875,-8.754431490842762e-8,-0.18189382553100586,0,0.12057413905858994,0.7487242221832275,0.6518235206604004,0,0.13618825376033783,-0.66288161277771,0.7362341284751892,0,-191.17271423339844,590.8668212890625,-854.3065185546875,1" name="Camera"> <bones:ArrayCollection x:name="children" x:id="4"/> </ns0:CameraComponent> </cadetAway3D4:RendererAway3D> <cadetAway3D4:MeshComponent x:name="1" x:id="5" transform="1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1" name="Plane"> <ns1:ColorMaterialComponent x:name="materialComponent" x:id="6" gloss="50" specularStrength="1" color="333722" name="Blue Material" depthCompareMode="less"/> <bones:ArrayCollection x:name="children" x:id="7"> <ns2:PlaneGeometryComponent x:name="0" x:id="8" height="1000" segmentsH="1" segmentsW="1" name="a platform" width="1000"/> </bones:ArrayCollection> <x:Ref x:name="geometryComponent" x:id="8"/> </cadetAway3D4:MeshComponent> <ns3:DirectionalLightComponent x:name="2" x:id="9" color="16777215" specular="1" ambient="0.1" transform="0.9063077569007874,0,-0.4226183295249939,0,0.3830223083496094,0.42261824011802673,0.8213937878608704,0,0.17860621213912964,-0.9063078165054321,0.3830221891403198,0,0,0,0,1" ambientColor="16777215" diffuse="1" name="Directional Light"> <bones:ArrayCollection x:name="children" x:id="10"/> </ns3:DirectionalLightComponent> <x:Ref x:name="3" x:id="3"/> <cadetAway3D4:MeshComponent x:name="4" x:id="11" transform="1,0,0,0,0,1,0,0,0,0,1,0,-58.91594696044922,146.62379455566406,-32.241188049316406,1" name="Cube"> <ns1:ColorMaterialComponent x:name="materialComponent" x:id="12" gloss="50" specularStrength="1" color="489736" name="Green Material" depthCompareMode="less"/> <bones:ArrayCollection x:name="children" x:id="13"> <ns2:CubeGeometryComponent x:name="0" x:id="14" height="180.85" segmentsH="1" tile6="1" depth="180.85" segmentsW="1" segmentsD="1" name="a cube" width="180.85"/> </bones:ArrayCollection> <x:Ref x:name="geometryComponent" x:id="14"/> </cadetAway3D4:MeshComponent> <cadetAway3D4:MeshComponent x:name="5" x:id="15" transform="1,0,0,0,0,1,0,0,0,0,1,0,-140.8660888671875,340.4476318359375,-54.04615783691406,1" name="Sphere"> <ns1:ColorMaterialComponent x:name="materialComponent" x:id="16" gloss="50" specularStrength="1" color="12320768" name="Red Material" depthCompareMode="less"/> <bones:ArrayCollection x:name="children" x:id="17"> <ns2:SphereGeometryComponent x:name="0" x:id="18" segmentsH="12" segmentsW="16" radius="84.13898408877864" name="a sphere"/> </bones:ArrayCollection> <x:Ref x:name="geometryComponent" x:id="18"/> </cadetAway3D4:MeshComponent> <cadetAway3D4:MeshComponent x:name="6" x:id="19" transform="1,0,0,0,0,1,0,0,0,0,1,0,151.02638244628906,146.50900268554688,127.5979995727539,1" name="Sphere"> <x:Ref x:name="materialComponent" x:id="16"/> <bones:ArrayCollection x:name="children" x:id="20"> <ns2:SphereGeometryComponent x:name="0" x:id="21" segmentsH="12" segmentsW="16" radius="52.35" name="Sphere Geometry"/> </bones:ArrayCollection> <x:Ref x:name="geometryComponent" x:id="21"/> </cadetAway3D4:MeshComponent> <cadetAway3D4:MeshComponent x:name="7" x:id="22" transform="1,0,0,0,0,1,0,0,0,0,1,0,164.13088989257813,348.98486328125,102.31562805175781,1" name="Cube"> <x:Ref x:name="materialComponent" x:id="12"/> <bones:ArrayCollection x:name="children" x:id="23"> <ns2:CubeGeometryComponent x:name="0" x:id="24" height="76.87" segmentsH="1" tile6="1" depth="76.87" segmentsW="1" segmentsD="1" name="Cube Geometry" width="76.87064711138314"/> </bones:ArrayCollection> <x:Ref x:name="geometryComponent" x:id="24"/> </cadetAway3D4:MeshComponent> <cadetAway3D4:MeshComponent x:name="8" x:id="25" transform="1,0,0,0,0,1,0,0,0,0,1,0,-161.5980987548828,37.646751403808594,291.46575927734375,1" name="Cube"> <x:Ref x:name="materialComponent" x:id="12"/> <bones:ArrayCollection x:name="children" x:id="26"> <ns2:CubeGeometryComponent x:name="0" x:id="27" height="75.29" segmentsH="1" tile6="1" depth="75.29" segmentsW="1" segmentsD="1" name="Cube Geometry" width="75.29350445797198"/> </bones:ArrayCollection> <x:Ref x:name="geometryComponent" x:id="27"/> </cadetAway3D4:MeshComponent> <cadetAway3D4:MeshComponent x:name="9" x:id="28" transform="1,0,0,0,0,1,0,0,0,0,1,0,-10.505144119262695,181.75933837890625,69.79844665527344,1" name="Cube"> <x:Ref x:name="materialComponent" x:id="12"/> <bones:ArrayCollection x:name="children" x:id="29"> <ns2:CubeGeometryComponent x:name="0" x:id="30" height="66.29" segmentsH="1" tile6="1" depth="66.29" segmentsW="1" segmentsD="1" name="Cube Geometry" width="66.29740645095558"/> </bones:ArrayCollection> <x:Ref x:name="geometryComponent" x:id="30"/> </cadetAway3D4:MeshComponent> <cadetAway3D4:MeshComponent x:name="10" x:id="31" transform="1,0,0,0,0,1,0,0,0,0,1,0,-393.7727966308594,113.91818237304688,-41.75886917114258,1" name="Cube"> <x:Ref x:name="materialComponent" x:id="12"/> <bones:ArrayCollection x:name="children" x:id="32"> <ns2:CubeGeometryComponent x:name="0" x:id="33" height="84.85" segmentsH="1" tile6="1" depth="84.85" segmentsW="1" segmentsD="1" name="Cube Geometry" width="84.85060333420417"/> </bones:ArrayCollection> <x:Ref x:name="geometryComponent" x:id="33"/> </cadetAway3D4:MeshComponent> <cadetAway3D4:MeshComponent x:name="11" x:id="34" transform="1,0,0,0,0,1,0,0,0,0,1,0,110.92161560058594,25.127405166625977,230.1426239013672,1" name="Cube"> <x:Ref x:name="materialComponent" x:id="12"/> <bones:ArrayCollection x:name="children" x:id="35"> <ns2:CubeGeometryComponent x:name="0" x:id="36" height="50.25" segmentsH="1" tile6="1" depth="50.25" segmentsW="1" segmentsD="1" name="Cube Geometry" width="50.25481013821924"/> </bones:ArrayCollection> <x:Ref x:name="geometryComponent" x:id="36"/> </cadetAway3D4:MeshComponent> <cadetAway3D4:MeshComponent x:name="12" x:id="37" transform="1,0,0,0,0,1,0,0,0,0,1,0,156.6885223388672,99.52238464355469,51.9654655456543,1" name="Cube"> <x:Ref x:name="materialComponent" x:id="12"/> <bones:ArrayCollection x:name="children" x:id="38"> <ns2:CubeGeometryComponent x:name="0" x:id="39" height="49.76" segmentsH="1" tile6="1" depth="49.76" segmentsW="1" segmentsD="1" name="Cube Geometry" width="49.76054139091827"/> </bones:ArrayCollection> <x:Ref x:name="geometryComponent" x:id="39"/> </cadetAway3D4:MeshComponent> <cadetAway3D4:MeshComponent x:name="13" x:id="40" transform="1,0,0,0,0,1,0,0,0,0,1,0,-241.41665649414063,106.39049530029297,-15.305731773376465,1" name="Sphere"> <x:Ref x:name="materialComponent" x:id="16"/> <bones:ArrayCollection x:name="children" x:id="41"> <ns2:SphereGeometryComponent x:name="0" x:id="42" segmentsH="12" segmentsW="16" radius="81.08778158664182" name="Sphere Geometry"/> </bones:ArrayCollection> <x:Ref x:name="geometryComponent" x:id="42"/> </cadetAway3D4:MeshComponent> <cadetAway3D4:MeshComponent x:name="14" x:id="43" transform="1,0,0,0,0,1,0,0,0,0,1,0,240.3887176513672,66.32697296142578,28.3767147064209,1" name="Sphere"> <x:Ref x:name="materialComponent" x:id="16"/> <bones:ArrayCollection x:name="children" x:id="44"> <ns2:SphereGeometryComponent x:name="0" x:id="45" segmentsH="12" segmentsW="16" radius="42.73389288130251" name="Sphere Geometry"/> </bones:ArrayCollection> <x:Ref x:name="geometryComponent" x:id="45"/> </cadetAway3D4:MeshComponent> <cadetAway3D4:MeshComponent x:name="15" x:id="46" transform="1,0,0,0,0,1,0,0,0,0,1,0,77.10282897949219,82.25804901123047,-173.88815307617188,1" name="Sphere"> <x:Ref x:name="materialComponent" x:id="16"/> <bones:ArrayCollection x:name="children" x:id="47"> <ns2:SphereGeometryComponent x:name="0" x:id="48" segmentsH="12" segmentsW="16" radius="44.53863475379832" name="Sphere Geometry"/> </bones:ArrayCollection> <x:Ref x:name="geometryComponent" x:id="48"/> </cadetAway3D4:MeshComponent> <cadetAway3D4:MeshComponent x:name="16" x:id="49" transform="1,0,0,0,0,1,0,0,0,0,1,0,-8.887575149536133,77.42305755615234,325.1551513671875,1" name="Sphere"> <x:Ref x:name="materialComponent" x:id="16"/> <bones:ArrayCollection x:name="children" x:id="50"> <ns2:SphereGeometryComponent x:name="0" x:id="51" segmentsH="12" segmentsW="16" radius="26.87288819730761" name="Sphere Geometry"/> </bones:ArrayCollection> <x:Ref x:name="geometryComponent" x:id="51"/> </cadetAway3D4:MeshComponent> <cadetAway3D4:MeshComponent x:name="17" x:id="52" transform="1,0,0,0,0,1,0,0,0,0,1,0,-111.85294342041016,150.3166961669922,314.55419921875,1" name="Sphere"> <x:Ref x:name="materialComponent" x:id="16"/> <bones:ArrayCollection x:name="children" x:id="53"> <ns2:SphereGeometryComponent x:name="0" x:id="54" segmentsH="12" segmentsW="16" radius="36.59617847826625" name="Sphere Geometry"/> </bones:ArrayCollection> <x:Ref x:name="geometryComponent" x:id="54"/> </cadetAway3D4:MeshComponent> <cadetAway3D4:MeshComponent x:name="18" x:id="55" transform="1,0,0,0,0,1,0,0,0,0,1,0,194.72483825683594,66.20823669433594,249.91229248046875,1" name="Sphere"> <x:Ref x:name="materialComponent" x:id="16"/> <bones:ArrayCollection x:name="children" x:id="56"> <ns2:SphereGeometryComponent x:name="0" x:id="57" segmentsH="12" segmentsW="16" radius="34.06102262376309" name="Sphere Geometry"/> </bones:ArrayCollection> <x:Ref x:name="geometryComponent" x:id="57"/> </cadetAway3D4:MeshComponent> <cadetAway3D4:MeshComponent x:name="19" x:id="58" transform="1,0,0,0,0,1,0,0,0,0,1,0,153.58782958984375,59.29662322998047,139.8775634765625,1" name="Sphere"> <x:Ref x:name="materialComponent" x:id="16"/> <bones:ArrayCollection x:name="children" x:id="59"> <ns2:SphereGeometryComponent x:name="0" x:id="60" segmentsH="12" segmentsW="16" radius="27.704758141615848" name="Sphere Geometry"/> </bones:ArrayCollection> <x:Ref x:name="geometryComponent" x:id="60"/> </cadetAway3D4:MeshComponent> <x:Ref x:name="20" x:id="16"/> <x:Ref x:name="21" x:id="6"/> <x:Ref x:name="22" x:id="12"/> </bones:ArrayCollection> <Object x:name="userData" x:id="61"/> <bones:DependencyManager x:name="dependencyManager" x:id="62"> <bones:ArrayCollection x:name="dependencyNodes" x:id="63"/> </bones:DependencyManager> </cadet:CadetScene> |
This format isn’t easy to handle! Cadet should really export to XML or JSON format.
Anyway this what I’ve done : demo. You can remove the AwayPhysics debug view via the console :
set awayPhysics visible false
As usual all the source code is available on the GitHub.
This is the GameState :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | package awayphysics.cadet3d { import away3d.controllers.HoverController; import away3d.debug.AwayStats; import com.citrusengine.core.State; import com.citrusengine.physics.awayphysics.AwayPhysics; import com.citrusengine.utils.ObjectMaker3D; import com.citrusengine.view.CitrusView; import com.citrusengine.view.away3dview.Away3DView; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Vector3D; /** * @author Aymeric */ public class Cadet3DGameState extends State { [Embed(source="/../embed/3D/simpletest.away3d4", mimeType="application/octet-stream")] private const _CADET_LEVEL:Class; // navigation variables private var _cameraController:HoverController; private var _move:Boolean = false; private var _lastPanAngle:Number; private var _lastTiltAngle:Number; private var _lastMouseX:Number; private var _lastMouseY:Number; private var _lookAtPosition:Vector3D = new Vector3D(); public function Cadet3DGameState() { super(); } override public function initialize():void { super.initialize(); addChild(new AwayStats((view as Away3DView).viewRoot)); var awayPhysics:AwayPhysics = new AwayPhysics("awayPhysics"); awayPhysics.visible = true; add(awayPhysics); ObjectMaker3D.FromCadetEditor3D(XML(new _CADET_LEVEL())); _cameraController = new HoverController((view as Away3DView).viewRoot.camera, null, 175, 20, 1000); stage.addEventListener(MouseEvent.MOUSE_DOWN, _onMouseDown); stage.addEventListener(MouseEvent.MOUSE_UP, _onMouseUp); stage.addEventListener(Event.MOUSE_LEAVE, _onMouseUp); stage.addEventListener(MouseEvent.MOUSE_WHEEL, _onMouseWheel); } // Make sure and call this override to specify Away3D view. override protected function createView():CitrusView { return new Away3DView(this); } override public function destroy():void { stage.removeEventListener(MouseEvent.MOUSE_DOWN, _onMouseDown); stage.removeEventListener(MouseEvent.MOUSE_UP, _onMouseUp); stage.removeEventListener(Event.MOUSE_LEAVE, _onMouseUp); stage.removeEventListener(MouseEvent.MOUSE_WHEEL, _onMouseWheel); super.destroy(); } override public function update(timeDelta:Number):void { super.update(timeDelta); if (_move) { _cameraController.panAngle = 0.3 * (stage.mouseX - _lastMouseX) + _lastPanAngle; _cameraController.tiltAngle = 0.3 * (stage.mouseY - _lastMouseY) + _lastTiltAngle; } _cameraController.lookAtPosition = _lookAtPosition; } private function _onMouseDown(mEvt:MouseEvent):void { _lastPanAngle = _cameraController.panAngle; _lastTiltAngle = _cameraController.tiltAngle; _lastMouseX = stage.mouseX; _lastMouseY = stage.mouseY; _move = true; } private function _onMouseUp(evt:Event):void { _move = false; } private function _onMouseWheel(mEvt:MouseEvent):void { _cameraController.distance -= mEvt.delta * 5; if (_cameraController.distance < 100) _cameraController.distance = 100; else if (_cameraController.distance > 2000) _cameraController.distance = 2000; } } } |
Like Flash as Level Editor or Tiled Map Editor, we use the ObjectMaker class. This is the template for Cadet Editor 3D :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | public static function FromCadetEditor3D(levelData:XML, addToCurrentState:Boolean = true):Array { var ce:CitrusEngine = CitrusEngine.getInstance(); var params:Object; var objects:Array = []; var type:String; var radius:Number; var object:AwayPhysicsObject; for each (var root:XML in levelData.children()) { for each (var parent:XML in root.children()) { type = parent.@name; if (type == "Cube" || type == "Plane" || type == "Sphere") { var transform:Array = parent.@transform.split(","); params = {}; params.x = transform[12]; params.y = transform[13]; params.z = transform[14]; for each (var child:XML in parent.children()) { for each (var finalElement:XML in child.children()) { params.width = finalElement.@width; params.height = finalElement.@height; params.depth = finalElement.@depth; radius = finalElement.@radius; if (radius) params.radius = finalElement.@radius; if (type == "Plane") { // the plane seems to use the height as the depth params.depth = params.height; params.height = 0; params.view = new Mesh(new CubeGeometry(params.width, params.height, params.depth), new ColorMaterial(0xFF0000)); object = new Platform("plane", params); } else { if (params.radius) { params.view = new Mesh(new SphereGeometry(params.radius), new ColorMaterial(0x00FF00)); object = new AwayPhysicsObject("sphere", params); } else { params.view = new Mesh(new CubeGeometry(params.width, params.height, params.depth), new ColorMaterial(0x0000FF)); object = new AwayPhysicsObject("cube", params); } } objects.push(object); } } } } } if (addToCurrentState) for each (object in objects) ce.state.add(object); return objects; } |
As you can notice, the object maker uses CE prebuilt objects for AwayPhysics but not in a dynamic way.
This is some points that Cadet Editor should allow to be an excellent level editor :
– quickly most of the points are in Tiled Map Editor, checkout Citrus Engine support there.
– an easy to parse exporter format (xml or json).
– adding personal properties so we can precize type, gravity, view for example directly in the level editor. A must have!
I will continue my experiments and improve Cadet 3D support. I really hope that some of my requests will be added to Cadet Editor and then it will be an awesome 3D level editor for the CE!
Hi Aymeric,
Thanks for the feedback and this great post! 🙂
It’s exciting for us to see other developers showing an interest in CadetEditor and integrating it with their own systems. It’s a shame we can’t provide you with the AwayPhysics editor just yet, you may have already seen that this is in development.
The serialized scenes are just XML, though they use namespaces like MXML. The reason for this is that the namespaces (e.g. xmlns:cadet=”www.unwrong.com/cadet/1.0.0″) help to map the serialized objects to their AS3 equivalents. If we were to try and simplify the xml, we would lose information that is critical to this process in the general case. We are however considering introducing custom exporters for specific builds, allowing games (for instance) to deal in a much simplified format that’s quicker for their implementation to load/parse, so this might help you down the line.
We’re actually currently working on a *big* update which should make your life easier in a number of ways… Looking forward to getting it out there, but there’s still a reasonable amount of work to do..
If you’d like to discuss anything in more detail, please feel free to ask any questions on the new CadetEditor Google Group! https://groups.google.com/forum/?fromgroups#!forum/cadeteditor
cheers
rob
Hey Rob,
I understand the use of the namespaces used by Cadet. It just should export in other format for developers, that’s in your plan so it’s great!
So I’m waiting for the big update 😉
Continue the good work!
Cadet2D support would be awesome aswell.
Indeed, I really love how you can design a Box2D scene in Cadet2D. However the save format file isn’t easy to parse. I hope that Cadet Editor will provide a simple xml or json formats. From what Rob said, that’s on the roadmap 😉
I hadn’t thought of using ActionScript for 3D game development (due to the lack of developer friendly tools). But Citrus + Cadet are clearly proving that Flash will continue to evolve as a legitimate game development toolset.
Great work Rob and Aymeric!
nice, looking forward to news about this cadet + CE team up
cheers
sir, about download link for cadet3d editor, “http://www.cadeteditor.com/” something wrong, a japanese hairstyle advertisement, any tutorial for configuration of cadet3d editor ?
Apparently it moves to http://cadeteditor.github.io/