SoC:002 - Polygons and Power Drives
February 27, 2018
In the second State of Carbon, I ramble a bit about 3D modeling, trying to drive a mech with Unity physics, and what sorts of goodies to expect in the newest build.
After working on the weapons system a bunch I decided to throw some time at a couple different (but equally important) parts of the game: engines and models.
Shooting stuff is cool and all, but gets boring quick if you can't move around. So next steps for me was to start building a method of moving the mech around the environment. This proved a little more difficult than initially planned, and I still don't know if I'm totally satisfied with the solution I have in place, but it moves and that's what counts.
Forces, Velocities, and Picking One To Mess With
My first implementation had me directly modifying the velocity values on the mech rigidbody. I figured if I want to keep it pretty arcade-y, why bother making it realistic? It worked to a degree, but some ugly side-effects started to show pretty early on.
- Jittery collision resolution: When running into walls, especially at high speeds, there was some really bad shaking taking place due to the resolution of the inevitable overlap of the mech's capsule collider and the wall's box collider.
- Whatever, other forces: Writing directly to the velocity of a rigidbody is pretty direct. So much so that it overwrites any precalc'd velocities from the physics engine with whatever you throw at it, so if you expect other forces to move the object around you're in for a world of disappointment.
After messing around with this a bit I decided it might be better to take the forces approach. This too had some peculiarities at first, but I think I've worked out most of the kinks. The one issue I do want to touch on is how I ended up actually applying the forces, which first requires a quick explanation of movement in MechWarrior 2.
From the start, I've wanted to keep Carbon feeling like you're driving a vehicle of some kind. The player has a sort of indirect method of controlling the mech, rather than the very immediate control a player has in a typical first person shooter, and this is something I think MechWarrior 2 does very well. In your standard FPS, the player interacts with the game by directly moving within the space presented, usually using the W/A/S/D keys to move forward/backward and strafe left/right and the mouse to pivot and look up/down. In contrast, MW2 provides the player with a method of controlling the throttle of the mech as a means to move it forward/backward at a particular speed. This disconnect goes a long way to make you feel like you're a pilot rather than the mech itself (this is a big part of why I have trouble calling Hawken a mech sim).
So I set out to replicate this by applying forces to the mech in Unity. Initially I just took a maximum force and applied a percentage of it based on where the throttle was, but the result was no movement at low throttles shifting very quickly to extreme movement in high throttles. I needed a way to modify how the throttle was translated to a force in a non-linear fashion, and at this point I learned about the magic of animation curves.
Unity gives you (the developer) a lot of really cool things for free. Animation curves definitely fall in this category of goodies that are far more useful than the name implies. The short version is they let you draw a curve in the editor by plotting verticies on a 2D grid, and then providing you with tweened values via the Evaluate() method. This was a great solution that took a fair amount of finesse, as I wanted to have a linear acceleration for the first engine I built. Here’s what I did:
- I started by settling on a maximum force that gave my rigidbody the top speed I desired. This was very much a "tweak it until it feels right" process.
- Once I had the speed I wanted, I put it in a spreadsheet (exciting!) and got each in-between speed by multiplying it by the throttle percentages applied using the MW2 keyboard style input (number keys 1 to 0, equal to a range of 0.0 to 1.0).
- With all the in-between speeds worked out, I set up a very long and narrow test level the mech could "run" down, and added a vertex for each of the 10 throttle positions at a point at which the mech moved at the speed defined in the spreadsheet. </ol> This was a relatively long and frustrating process (doubly so since I did the whole edit-in-play-mode thing you shouldn't do), but it got me a solid result that I'm fairly happy with.</p>
On the other side of things, I started messing around in Blender, trying to model the Carbon mech. Blender and I go way back, but my experience with hard surface modeling is pretty limited. This was one of the first times I tried doing a deep dive into making something with some real direction, and I think after doing a bit of learning I actually gained a few new skills.
For starters, the flow of topology is something I haven't always paid attention to that much. Doing low poly experiments never really required it, but with the high res subsurf work I wanted to do it was critical. Normally when I try to do this I kinda give up after twenty minutes of frustration, but this time around I had two new bits of data that made it so, so much easier: a concept sketch and the shrinkwrap modifier.
This probably goes without saying, but it's super handy to have a reference when you're creating, well, anything. This is extra true for 3D modeling in my opinion, as the medium itself is already pretty abstract. Having a doodle to work off of helps ground things immensely, so that's exactly what I did. These are from my notebook, just photographed with my phone and distortion corrected in photoshop.
Blender lets you import images and display them in the 3D view as "Background Images". You can have them visible from any angle or limit them to specific axis. By drawing a front and side view and locking those sketches to the X and Z (Y in Blender) axis, I was able to block out the rough shape of the mech.
Once I had the basic shapes laid out, I used the incredibly powerful "Shrinkwrap" modifier to create the new topology. Shrinkwrap effectively does what it says: it takes the mesh it's applied to and snaps it's vertices to a target mesh. Using this, combined with a subsurf modifier, I was able to quickly build a much cleaner mesh on top of the rough blocks I laid out using the sketch I imported. I was also able to create face loops around the important parts of the sketch, giving the mesh good definition.
I close this section with the positive note that I'm scrapping the whole mesh I made, lol. I decided after seeing it in 3D that I didn't really like the concept I came up with on paper and want to take it back to the drawing board. Still, it was a really good exercise and I improved my workflow a lot by doing it. So not a total loss by any means.
In case you didn't read to the bottom of the last SoC, I'm pushing builds with each post to itch.io where you can check out what I've implemented so far. This new build has a few new goodies in it for you to check out:
- The new mech movement/physics system (increase/decrease the throttle using W/S or directly select with the top number keys; toggle reverse with Backspace)
- Four different hitscan weapons to play with (switch between them using the mousewheel)
- Some miscellaneous UI inclusions to help with physics debugging
As previously stated, this is all very buggy/limited stuff. It's super early in development, so expect things to be broken and/or missing.
That's it for this post. If you do happen to read this and play with the demo please send all questions/comments/concerns to me via Twitter, or swing by BGS at one of the bi-weekly game dev meetups and chat with me in person. Catch you again in a couple weeks!