Thursday, February 7, 2013

Conjoining objects

Being that the major mechanic in Rustbot is putting objects together to build structures and/or contraptions, I'd obviously better make sure that it is water-tight. Making this work on a dynamic runtime level while allowing for near-infinite combinations is really not as easy as it sounds. Here I'll be describing what I want in my model, and the different roads I took (and finally settled on). Be forewarned, this will get technical.

One of the concepts involved in Rustbot's genesis was the idea of building not just structures, but contraptions with movable parts. In order to do that, any method of sticking one object to another has to be absolutely solid and unwavering, while retaining the ability to move under defined joint constraints. Basically, some parts should be "welded" together, and others should have the ability to be connected via hinges, springs or pistons.

The obvious approach is to use your physics engine of choice's built-in joint system. When I was still working on my own game engine and using Bullet Physics, I tried this method, but found an unfortunate problem with it - the joints bounced. The joints that I had chosen because I imagined they'd be solid acted, in fact, like springs. You can see this action in a demo video that I posted almost two (!!!!!) years ago. At the time, my reasoning was such that I would deal with it despite not liking it, because it was native and I didn't feel like doing anything extravagant.

Well, that changed when I found out about Bullet Physics' Compound Shape class, wherein you can add multiple primitives together to form one cohesive, solid shape. I made some rudimentary progress with this but ultimately wound up having to unwrite and rewrite a lot of base class code, thereby causing my interest to wane. Subsequently I moved onto terrain LOD and eventually went AWOL altogether.

Fast forward a year and a half and you have my return with Unity. In my research into Unity before deciding to use it, I saw that it also made use of Compound Colliders within a game object, and so that sealed the deal for me. However, in what would seem a fit of selective amnesia, I first tried PhysX's (the physics engine within Unity) FixedJoint class to join two objects. This yielded exactly the same results as Bullet Physics's joints; objects still bounced around each other. Once again, I contemplated just going with it because it had some fun side effects (they were breakable with enough strength, and I could foresee some additional gameplay mechanics for bridgebuilding and whatnot). But when it came time to grab an object on screen, they wouldn't stay still and would jitter crazily in front of the camera, driving me mad.

So I had to find something else. I briefly contemplated using the Compound Colliders that had originally swayed me to Unity but, when I thought about it, it didn't strike me as a good fit. Adding an additional collider to an object is one thing - changing the mesh is another. Feeling like I'd be opening a Pandora's Box were I to choose this route, I tried another.

This time I tried using Unity's incredibly easy-to-use hierarchy system. When I wanted to join one object to another, I simply made it a child of the other and removed its rigidbody, thereby causing it to move with the parent as one object of complex shape. They were solid and rigid and perfect. Well, almost perfect.

The objects in Rustbot are all variations on primitives, all dynamically added to and removed from the game as needed. They can have varying dimensions. I can create a brick by instantiating a cubic box and altering its X, Y and Z local scale factors. This is the only way I can achieve what I want in-game without making one hundred thousand different meshes of varying sizes and, so, it must stay.

This becomes very problematic in my hierarchical method of joining objects, though. If I make one object a child of another, and the parent has scaling that is not 1, then the child object's scaling automatically is adjusted and is skewed to the point of ridiculousness. Obviously, this is not good. Objects need to retain their shapes when conjoined.

I looked into this problem online and noticed a few other people had similar issues. The response they all received was the same - it cannot be helped, it's part of Unity. My experience with 3d programming (OpenGL specifically) has given me insight into why this is. When the parent object is rendered, the transformation matrix is modified to match the object's positional, rotational and scale properties. If it has to then move on to a child object, it does not reset the transformation matrix but instead further modifies it according to the child's positional, rotational and scale properties. So what you end up with is a skewed child, graphically. So what to do?

Well, when I want to join the objects, I still work with the hierarchy but, instead of making one the child of the other, I instantiate an empty prefab and make both objects children of that. I give a rigidbody to the prefab, add each child's mass to the prefab's, and finally delete both children's rigidbodies. They both maintain their shapes and move together, rigidly, exactly as they should. This can be done ad infinitum.

So instead of

Object 1
  +--Object 2

We have
Empty Prefab
  +--Object 1
  +--Object 2

This behaves exactly as I want it to.

With this method I can attach any loose objects to other loose objects, or loose objects to existing structures, or existing structures to loose objects, or existing structures to existing structures, all with simple testing of whether either object in question has a pre-existing Empty Prefab parent. Additionally, I can add hinges, springs or pistons between objects by creating them between the parent Empty Prefab objects instead of individual children, thereby making everything kosher and cool.

I recorded a short video last night of this mechanic, but the conversion tool I have (which takes the video from 540MB to 5MB) was flaking out and omitting large chunks of video. Grumble grumble.

Anyways, this progress is, in my eyes, fantastic because it really solidifies the groundwork for the game mechanic and, once it's complete (or really close), then some actual other design can occur and we might begin to see something like a game instead of a tool.

That's all for now!

No comments:

Post a Comment