Posted August 18, 2023 by Txori
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:
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:
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 :)