Posts by Aymeric
Alef is out!
4Hey there,
It has been a long time since I wrote my first game, Kinessia, with the Citrus Engine (and start contributing), one and a half year. Less than one year ago, I made an other one, Tribus, using a Citrus Engine’s port in Objective-C. That was my first project in Objective-C so the source code of the port shall not be very clean, that’s why I’ve never released it officially.
We all know the problem making a framework / library and never use in real context project. That’s why I started my carreer as a freelancer 4 months ago, to be able to make applications / games for companies and show what can be done with the Citrus Engine. And now I’m proud to add a new reference to the engine: Alef.
Alef
Alef is a free app that teaches young children the letters of the alphabet and the construction of simple words in 5 different languages. The app encourages learning through simple interaction and is adapted for young kids. Children are taught to identify letters by dragging and dropping colorful puzzle pieces together. Parents can select a language including English, German, French, Arabic or Spanish and challenge their kids to construct words using the puzzle pieces! The app also provides word categories, for example animals, which give children visual clues about the meaning of the word they are trying to spell. Parents can customize the words themselves, opening the door to a world of possibilities: names, places, household items, etc. When the word has been put together correctly, a jingle will celebrate the child’s success. Hooray!
The beginning
Alef is similar to Spell Cubes in its first approach. Drag & drop letters at the right place with letter’s sound, like many other games did. When we start working on Alef, we considerate it as an internal project: AJ Consulting is one of the company that I enjoy to work with. They have tons of fresh ideas. They were looking for a Flash framework which would help them to make edutainment project, so I convinced them to use the Citrus Engine and it was a blast! In two days, we already had a working prototype! Then we’ve started to experiment: how to enhance user experience? Children love animals, they enjoy funny sounds too. Both has been added in this first official release. And we have many more awesome features coming! We’ve released the app now for having lots of feedback.
Technologies
We use the Citrus Engine library including Starling, Feathers and Nape.
Why did we use the CE for this app? The obvious answer is for the physics engine support, then comes very useful class to work with Starling: setting a Starling object very quickly and manage its z-sorting easily. And finally for the state transition between each part of the app. The CE makes them easy to clean and create!
We have also added Parse to store data in the cloud. If we want to add new animals, we just need to add it in our Parse database, send a push notification via Parse, people will update the list via the update button and they will get the last animals!
Also I’ve wrote an algorithm for the arabic alphabet which is a bit complex to manage due to the different states of a letter: isolated, initial, medial and final according where the letter is situated in the word. And obviously there are exceptions
Since I’d no knowledge in arabic that was very challenging!
And finally, it was funny to replicate the iOS keyboard behavior managing accents!
CitrusAJ*
As you may have noticed, it’s mentioned the name of CitrusAJ* in the information of the application. It’s a private (at the moment) fork of the Citrus Engine (which has a MIT licence) including stuff which aren’t directly requested in a game engine. It has some helper for Parse, languages, SQL database, etc.
It’s always hard to determine what should or should not be included in the CE.
CitrusAJ* Builder
There isn’t official level editor / app builder for the CE. We use existing tools (Flash Pro, Gleed2D, Tiled Map Editor), it works great but we’re not able to manage them as we would. On the other side, writing a level editor takes lots of time and I don’t have enough time to do it. The CitrusAJ* Builder will be a commercial product developed by AJ Consulting to make an app without writing code. It would be a mix of a game level editor and the storyboard mode in Xcode.
Citrus Engine
No worries, the CE will always be free, open-source and strongly supported. Since we don’t have any kind of financial support, it’s important to take opportunities to work with companies on future product using the engine. This way we make sure it will stay the best AS3 game (and app!) engine!
I made my first conference!
5Yesterday I was in Paris to make my first conference! The subject was: Using Flash for video game development.
As you thought, I spoke of the Citrus Engine
As a first experience it was awesome, and the best was certainly to meet some of you!
You can find my slides there.
Hope to see you again soon!
AIR and iOS app options (cutsom url scheme, hidden)
3Each day we discover new options and features giving the developer life more exciting!
Today I’m sharing some options for iOS development. Let say you have a main application including links to several games. You don’t want to include all this games into the main application because there are more like independant mini-games, or you don’t want to clean everything as it should when you leave your game (the lazy way), or there are several developers involved in this different games and they don’t use the same technology!
Anyway, you want to create a main application able to navigate to an other one. In other terms: communicate with other apps (Apple references check Communicating with Other Apps part and below).
In your *-app.xml document, add your url scheme for your main app inside
<iPhone><InfoAdditions><![CDATA[ |
<array> <dict> <key>CFBundleURLSchemes</key> <array> <string>aymeric</string> </array> <key>CFBundleURLName</key> <string>games</string> </dict> </array> |
From any other games on your iOS device, you can call this main application this way:
navigateToURL(new URLRequest("aymeric:games")); |
Now let say we have a pirate game. Defines its url scheme:
<array> <dict> <key>CFBundleURLSchemes</key> <array> <string>pirate</string> </array> </dict> </array> |
You will be able to call it fropm your main app:
navigateToURL(new URLRequest("pirate:")); |
Why not using aymeric:pirate url scheme? Well it seems there is an issue… it didn’t work on my side.
For more informations on custom url scheme, take a look there and there.
It’s pretty cool to be able to open an app from an other one, but I already see them on the iOS “desktop”. If you have one hundred of mini-games, it may be embarassing… So let’s hide them from the “desktop” and the “dock”!
<key>SBAppTags</key> <array> <string>hidden</string> </array> <key>SBIsRevealable</key> <false /> |
Now you won’t see them anymore! Also if you’re launching games from a main application, it sounds more logical to exit them when you go back to this application, if you go back on them they will be relaunched from start.
<key>UIApplicationExitsOnSuspend</key> <true /> |
That’s it!
Unity and its asset store
0This last weeks, I played a lot with Unity. Thanks to its Editor it’s very easy to set up a scene in no time!
Also one of its strongest features is its Asset Store. You can find excellent stuff (assets, code, utilities…), however you’ll have probably to pay for that.
Unity has succeeded to create a market around its product and make its own product even better. Imagine if Greensock/TweenMax or even the Citrus Engine were able to be included into your project in one click. People looking for game engine will see the different ones and reviews. By promoting its asset store, Unity promotes its technology and people which are using it. Developers are highlighted and they gain some profits for their hard work (Unity too it’s a 70% – 30% deal). How many Flash developers earn money thanks their libraries? Certainly less than Unity developers and there are more (were?) Flash developers.
It seems that Unity users don’t hesitate to give money for what they want, whereas Flash users pay more attention. The Citrus Engine wouldn’t be popular and used if it wasn’t free.
Well I could write a long blog post, comparing Unity and Flash frameworks/libraries management but that’s definitely not my goal. I just would like to say that if you pay $15 for this awesome component, you will be able to make a FPS in no time.
Like I did in 15 minutes. Demo.
I wish I could make this in Flash in less than 15 minutes, and I don’t care about the price (if it worths it and is reasonable).
Android 4.2.2. resolve problem with AIR mobile deployment
2Google has recently pushed a new Android version: 4.2.2. Concerning the new features, this update brings about a new security feature in regards to USB debugging: now there is a gatekeeper on your phone! You will have to accept the RSA key before being able to deploy on your device. More information concerning this update there.
If you have made this update on your device, you won’t be able to deploy your application, even if you’ve accepted the RSA key. Your IDE won’t success to connect to the device and push on it.
When I tried to resolve the problem, it quickly appears it wasn’t related to the IDE but directly from the AIR SDK. Then I tried to deploy on my tablet with a Unity project: same problem. Using Unity, you work directly with the Android SDK. I just needed to download its latest version and replace the previous one, and it works!
Concerning AIR, we also use the Android SDK but not with its original structure. You need to replace some files on this folder: YourSDK/lib/android/bin. Replace those files: aapt, adb and dx.jar with files coming from the latest Android SDK: sdk/platform-tools aapt, adb and its lib folder with the dx.jar file. Using Windows you will also need to replace the dll files.
That was easy! I hope Adobe will quickly deploy an AIR SDK update for everyone.
First month as a freelancer completed!
0Hey guys,
This month was simply awesome! I’ve worked on some cool exciting projects using mostly AS3 with Starling, and the new ones in the pipeline are even better
I wish I could clone myself to work on all of them! I’d time to improve the Citrus Engine, give a first try to Cocos2D (loved to redo some Objective-C, but I’ll stay on Sparrow, a project using it is coming!) and started to learn Unity3D (you can make quickly some 2D games using Orthello framework). Learning Unity is very exciting, it’s definitely one of future tool I will mostly used in the future.
During this month there was the Global Game Jam. With some great guys, we made a game which won the local jury award using the Citrus Engine:
First achievements are unlocked
See you later!
Take photo with AIR on iOS
3Hey guys,
Recently, I’ve worked on a mobile project and camera use drives me crazy about performances. On iOS, after taking a photo and validate it, it freezes the application during one minute to be able to encode it.
I’ve made lots of tests with several libraries, using Alchemy ones, and finally the fastest way (less than 5 seconds) is to use the new BitmapData encode method, available since Flash Player 11.3 and AIR 3.3.
Here is the code (lots of code come from this excellent post) :
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 | if (CameraUI.isSupported) { viewsAC.addItem({label:"Take Photo", icon:cameraIcon}); myCam = new CameraUI(); myCam.addEventListener(MediaEvent.COMPLETE, onCameraComplete); } protected function onCameraComplete(evt:MediaEvent):void { sqlStatement = new SQLStatement(); sqlStatement.sqlConnection = model2.connection; sqlStatement.text = "INSERT INTO albumItems (album, photoFile, dateAdded) " + "VALUES (:album, :photoFile, :dateAdded)"; sqlStatement.parameters[":album"] = model2.selectedAlbum; var mediaPromise:MediaPromise = evt.data; if (mediaPromise.file == null) { // For iOS we need to load with a Loader first _loader = new Loader(); _loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageloader_complete, false, 0, true); _loader.loadFilePromise(mediaPromise); return; } else { // Android & BlackBerry registerPhoto(mediaPromise.file.url) } } private function imageloader_complete(event:Event):void { _loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, imageloader_complete); var loaderInfo:LoaderInfo = event.target as LoaderInfo; if (CameraRoll.supportsAddBitmapData) { var bitmapData:BitmapData = new BitmapData(loaderInfo.width, loaderInfo.height); bitmapData.draw(loaderInfo.loader); var file:File = File.applicationStorageDirectory.resolvePath("image" + new Date().time + ".jpg"); var stream:FileStream = new FileStream() stream.open(file, FileMode.WRITE); var bytes:ByteArray = bitmapData.encode(bitmapData.rect, new flash.display.JPEGEncoderOptions()); stream.writeBytes(bytes, 0, bytes.bytesAvailable); stream.close(); } registerPhoto(file.url) } private function registerPhoto(url:String):void { sqlStatement.parameters[":photoFile"] = url; sqlStatement.parameters[":dateAdded"] = new Date(); sqlStatement.execute(); viewsList.selectedIndex = -1; } |
Becoming a freelancer
2Hi! Since the new Citrus Engine website, this blog was in stand-by during the last two months. This year I worked really hard, using my personnal time, on the Citrus Engine which is the most advanced Open-Source & free AS3 game engine. This work and its support aren’t finished.
Now I’m happy to share with you some exciting news: in January, I will start my career as a freelancer! Thanks to this status, I will be able to work with companies on strong games using the Citrus Engine and make it even more professional. Take a look on its support page.
Will I only make games using this engine? No, diversity is stimulating. I’ve just bought an iMac to be able to re-do some Objective-C, and develop on iOS using Haxe NME.
To sum-up, I will focus on games, mobile applications, and maybe some websites using AS3, Haxe, Objective-C, HTML5 and PHP programming languages. In a long run I hope to improve my Unity3D knowledge, and in a short period I will give Cocos2D a try.
Also I will try to keep this blog alive, making one article by month which won’t be related to the Citrus Engine.
Future is exciting. Feel free to contact me!
Citrus Engine V3 and a new website!
2Hey there,
I’m glad to announce the V3 release and a new website for the Citrus Engine! It has been a long and hard work. Most of the new tutorials and news will be displayed on this new website. Don’t know how I will manage this blog on Citrus Engine subject due to the new website, I will certainly replicate some contents.
Anyway I let you explore the new website
Don’t hesitate to give some feedback.
Playing with Cadet Editor 3D and AwayPhysics
6The 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!