As described in a previous post, Omeganaut has transitioned from using internal collision boxes in ZGameEditor to using collision boxes managed by the external Bullet library. This is great, except that it's complicated, or even impossible, to visualize these new collision boxes in the editor or even in the game.

So, it works better, but adding a collision box takes a lot of time, and it's easy to make a mistake, which might not be immediately apparent. Not great, right? Therefore, for this new version of Omeganaut, I've created a little tool: a moulinette! And I'm going to explain my process for converting a visible 3D model into an unreadable line of code, because creating tools is just as fun as using them.

It's a shooting game, not a collision simulation. We're not dealing with every single triangle, so I've decided to only use collision primitives, namely:

  • Sphere: A simple sphere defined by a radius.
  • Scalable-Sphere: A sphere that can be stretched along all three axes.
  • Box: A basic cube that can be stretched along all three axes to create nice rectangular blocks.
  • Cone: An ice cream cone that can be adjusted in height and radius.
  • Cylinder: Similarly adjustable in height and radius.
  • Compound: This isn't a primitive, but a group that can contain the aforementioned primitives while preserving their rotation and position.

I started with a Ruby script for SketchUp. I wasn't familiar with this language, which resembles Python, but with the help of ChatGPT, I quickly got the hang of it. With a simple click, the script retrieves primitives from the selected group and converts them into lines of text:

##### Just copy/paste that in Google Sheets #####<br><br>2_SCASPHERE S_SHPERE_133x100x441 zbtCreateScalableSphereShape(1.0); zbtSetShapeLocalScaling(S_SHPERE_133x100x441,0.665,0.5,2.205);<br>3_BOX S_BOX_60x43x296 zbtCreateBoxShape(0.3,0.215,1.48);<br>9_COMPOUND S_SHIP zbtCreateCompoundShape();zbtAddChildShape(S_SHIP,S_BOX_60x43x296,-0.98,-0.17,1.01,0,-0.086,0);zbtAddChildShape(S_SHIP,S_SHPERE_133x100x441,0,-0.1,-0.24,-0.016,0,0);zbtAddChildShape(S_SHIP,S_BOX_60x43x296,0.98,-0.17,1.01,0,0.086,0);

In this example, the group consists of a Scalable-Sphere, a single Box used twice, and a Compound that holds their position and rotation relative to the group's center. The primitives are automatically named based on their shape and the transformations applied to them. The Compound takes on the group's name. This way, there are no issues with duplicates later on. All I have to do is paste this result at the end of the Shapes tab in the Google Sheet containing the game's data. Since the elements are separated by tabs, they automatically populate the correct columns in the table:

Then I copy/paste the desired Name into the Models tab. In our example, the model M_SHIP will use the Compound shape S_SHIP. Next comes Google Apps Script, which resembles JavaScript, also with the assistance of ChatGPT:

  • Clean Shapes: This function helps remove duplicates and unused shapes, then organizes them alphabetically. This organization is crucial as the name of primitives must be declared before being used in a Compound.
  • Export Shapes: This function generates the code for me to copy/paste into ZGameEditor: a long line that I wouldn't need to double-check. For this example, I've only exported the processed data mentioned above:

xptr S_SHPERE_133x100x441,S_BOX_60x43x296,S_SHIP;void initBulletShapes(){S_SHPERE_133x100x441=zbtCreateScalableSphereShape(1.0);zbtSetShapeLocalScaling(S_SHPERE_133x100x441,0.665,0.5,2.205);S_BOX_60x43x296=zbtCreateBoxShape(0.3,0.215,1.48);S_SHIP=zbtCreateCompoundShape();zbtAddChildShape(S_SHIP,S_BOX_60x43x296,-0.98,-0.17,1.01,0,-0.086,0);zbtAddChildShape(S_SHIP,S_SHPERE_133x100x441,0,-0.1,-0.24,-0.016,0,0);zbtAddChildShape(S_SHIP,S_BOX_60x43x296,0.98,-0.17,1.01,0,0.086,0);}xptr GetBodyShape(int type){switch(type){default:trace("ERROR: No body for " + ObjectNames[type]);return Shape_Sphere_100;case M_SHIP:return S_SHIP;}}

The script first declares the pointers with their unique names: S_SPHERE_133x100x441, S_BOX_60x43x296, and S_SHIP. Then, it defines the initBulletShapes() function, which initializes the corresponding primitive using the Shapes pointer and applies the necessary rotation and scale transformations. Finally, it declares the GetBodyShape(int type) function that associates the shape with the model. Here, when an M_SHIP model appears, this function assigns it the S_SHIP shape. All of this is concatenated into a single line. It might not be great for readability, but it's convenient for quick copy-pasting with a triple mouse click.

In addition to this tool, I've fixed small bugs, optimized all the functions, and added a few surprises. You can find the details in the devlog.
Now, all that's left is for me to create some content!

Edit of 20/08/2023

Little bug fixes and revert to frame rate style "SyncedWithMonitor" instead of "Free", because the high framerate was generating a lot of little bugs that I couldn't detect, since I don't have a monitor fast enough to test it. It will be for another time :)

Files 1 MB
Aug 18, 2023 1 MB
Aug 18, 2023
Omeganaut_Android_230818.apk 1 MB
Aug 18, 2023


Buy Now$5.00 USD or more

Leave a comment

Log in with to leave a comment.