Hey guys, here is the second edition of the Hurtworld development update:
Hey guys, here is the second edition of the Hurtworld development update:
For those not signed up for the alpha, here is the update sent out this week via twitter + email.
Vehicles are something I’ve been itching to do since day one. I put them off thinking it would be too much work and we would just patch them in later.
This week I got sick of waiting and I’m pretty glad I did.
The first problem we had to figure out is how to network them.
This post assumes you already understand basic networking concepts. If you are new to multiplayer programming Glenn Fielder has an amazing series of in depth articles on everything you need here.
Most moveable objects in the HurtWorld universe are synchronized at a variable update rate with a combination of interpolation and extrapolation. They often run a decent amount of time behind the server because the player doesn’t ever drive them directly. The only thing up until now that the player can control is the player, which runs client side prediction and a few other latency hiding tricks to ensure that input has zero latency.
Vehicles get a little trickier… In Unity we use a character controller combined with a series of raycasts to move the player around. This allows us to simulate multiple movements between frames without having to wait for the physics solver. This is crucial for being able to rewind and replay for client side prediction.
A vehicle will need to rotate (character controllers can’t), simulate wheels and different physics constraints that would be a pain-in-the-ass to re-implement manually. So for now we are constrained to using the PhysX solver.
Here we run into a limitation of the Unity implementation of PhysX. The solver goes in one direction, at a fixed speed in a single universe. We can’t rewind, we can’t create an isolated island of physical objects and simulate it at will, there is one physx simulation, and it progresses with the entire Unity scene.
Fingers crossed in the future unity change this so we have access to the PhysX API directly or at least some unity specific rewind capability, not holding my breath.
This leaves us with one option, run the PhysX sim on the server, and tell the client about it the best we can.
First thing we need to do is get our input to the server. The client runs no simulation, just sends an RPC fairly often with the input controls we need.
Then we simply setup a rigid body on the server with four wheel colliders attached, modify the slip, torque and suspension settings as if it was a single player game.
Once we are happy with the simulation, we need to figure out what the client needs to know to simulate the car visually with minimal data.
Lets start with the body of the car. This can be synchronized like any other moving object in the world. In our case, it uses the exact same code as the rigidbody crates that spawn when you drop an item.
There are many ways to do this, ours is a simple interpolation of known states with an update frequency of 20 times per second of position and rotation. If we do nothing else, the car still simulates like a car because the server is doing all the work for us. The client knows nothing about wheels at this point, but it still looks like a car.
So we are pretty close with only a Vector3 and Quaternion per update, and these can be compressed later.
One thing to note is we have a round trip delay on any input now, as the server simulates our vehicle. Because of this we need to make sure the handling isn’t too sluggish otherwise the delay will make it feel unbearable. To compensate we made the our vehicles handle almost too well, so with an added delay they feel right.
We could ask the server what the state of the suspension is per tick, but this would get expensive quickly and is mostly a waste.
Its pretty easy to assume where the wheel will be based on the chassis position. To do this we can just do a raycast at each wheel on the client up to some maximum value, then move the wheel to either touch the ground, or sit at max extension of the suspension without any data.
First thing we want to do is steer the wheels, how accurately you do this is up to you. I only needed three states (left middle and right). Then, I just smooth them out on the other end. This only uses 2 bits of a byte which we can use later on.
Secondly we need to rolling rotation, this one is a bit trickier to assume, even if we tracked relative ground movement and just rotated them, we would miss out on things like burn outs or handbrake turns because the wheels would always track with the ground. We don’t need a massive amount of precision on these values as mostly the wheels will be a blur. You can compress the RPM value into a single byte using something like the following:
private byte QuantizeRPM(float rpm, float maxRpm = 200)
return (byte)(((Mathf.Clamp(rpm, -maxRpm, maxRpm) + maxRpm) / (maxRpm * 2f)) * 254f);
private float UnQuantizeRPM(byte rpm, float max = 200f)
return ((float)rpm / 254f) * (max * 2f) – max;
We also don’t need separate speeds for left and right, with the exception of if you have a differential simulation and what to see it, we just send one RPM for the rear and one for the front.
This part isn’t 100% necessary unless you have gears and you want to hear the revs increase and drop. This can also be packed into a single byte using the method above, make sure you get the max range correct though, you won’t notice wheels hitting the RPM ceiling, but you will with engine RPM sounds.
If you’ve ever tried to implement audio for a car engine, you’ll know just pitch bending a single rev loop sound terrible, even with fading of volume. There is a great example in the Unity 5 standard assets for a 4 channel car audio system that blends between the following:
The idea is that when the throttle is down, the engine sounds vastly different from when your foot isn’t on the accelerator. To do this we need to know when we are throttling.
I tried just checking if the engine RPMs were rising, this sorta worked, but caused it to sound like you were running out of petrol while cornering or going up hill so we added one more bit for throttle state.
Remember above we had some spare room inside the steering byte, we can pack it into there for free
Lastly we want to show smoke and play screech sounds when the wheels are losing traction, again we could try to assume whats going on based on movement of the point on the rigidbody vs. wheel RPM, that would work fine as long as our RPM values are accurate enough. As we have a few free bits leftover inside the steering byte we can throw this one in for simplicity.
Currently I send a bool for front and back axles and treat them equally, if you are making a racing game you would probably want more precision.
For a fairly quick implementation without too much optimizing we have a decent looking car for around 640 bytes per second. I’m sure better can be done, but this will do us for now.
Here is the final result in our 4WD test terrain
The art style we are currently chasing is a somewhat chunky, slightly stylized modelling aesthetic, with fairly strong diffuse texturing. Not being overly concerned with blending of colours and fairly subtle specularity. Pretty much the two main reasons for going down this path, is for the shortened time/resources it takes to make such assets………..and we like it!!!
Below are a couple of the animals that inhabit Hurt World.
The first is a Bor who tends to stick to forested regions, foraging through the undergrowth and being pretty disgruntled by any player who wanders too close.
The second is a Tokar, who is a nimble predatory animal that attacks its prey from a distance before closing for the kill.
Lastly is a Shigi. They were the first animal made for Hurt World and are small passive animals who wander peacefully around grazing and seeking out any Owrongs ( plantable and harvestable fruit ).
We hope to have male, female and baby versions of a lot of the Hurt World animals in an attempt to breathe some life into the world, rather than just having wandering beasts who fulfill x task of a game mechanic.
I just love making up stupid jokes, puns and the like. Which has all our cohorts here at Bankroll groaning/smirking, or grirking/smoaning.
These are a few of the fake brands that we are keen to put into the game, with a few more to come. Most of these have a level of subtle humour and research depth uncommon to my particular brand of amusing.
I decided to get out the thesaurus and Google translate, to help me craft these logo’s. I had some genuine belly laughs while I flipped words like broken, smashed, dodgy, dicey & exploding around in translate. The variations that came out of the various languages, produced some barely pronounceable but funny letter combinations.
While we want humour in the game we don’t want to force it down peoples throats. We want our players to create their own laughs out of the tool set we have provided. That being said, there will be more amusing tidbits added as more content fills the game.
One of the central systems in Hurt World is dynamic vegetation – the ability to plant things, watch them grow, and chop them down. Most importantly, it needs to be fast.
There were a few solutions we considered. The first, and most optimistic one was for all vegetation to be their own Gameobjects. This would simplify everything from collisions to growth to dynamic behaviour like catching alight or dying of dehydration. Unfortunately Gameobjects in Unity are no where near free. Even with heavy LODing, there’s just too much overhead per Gameobject and per Monobehaviour to make it a viable option. You don’t really want more than a thousand in your level, and considering we wanted something in the realm of 100k+ trees, it became pretty clear, pretty fast that Gameobjects weren’t a viable solution.
Of course, Unity offers an in-built solution to vegetation rendering in the Terrain system. We figured, surely, the foundation was sound and it was just a matter of being careful with our implementation, and creating high quality assets. But after 4 months of this, and hitting dead end after dead end with the system, we finally had to admit that the Tree System wasn’t getting either the results or the performance we wanted, and any kind of ideas we had about interactivity looked like fairly unachievable pipe-dreams. Our FPS was 50 at best, with not a huge amount of trees on screen, and we were getting plagued with bugs we couldn’t do anything about due to the closed-source nature of the system. You can see how it looked below, and… eh.
We eventually made the decision to try and implement our own system. After some seriously creative brainstorming, the in-house name for this system is the Tree System. It had to be fast, flexible, and most of all interactive. You had to be able to chop down trees! The first step was getting everything a bit lower level, and stripping out as much Unity dead weight as we could. Trees are rendered actually quite similar to how the Terrain system does it.
Every tree is a lightweight non-Monobehaviour class that contains properties like position, rotation, color, or anything unique to that instance. It also contains a reference to a Tree Blueprint. Blueprints contain properties that are common amongst types of trees, such as what material to use or what mesh to draw at a given LOD level. Every Tree Blueprint can have a different configuration of LODs, saying what distance to be what LOD at, if there should be instance limits, and so on and so forth.
Tree instances are held in Collections, which are in turn held in Segments. Each tree type will have a separate Collection, and a Segment is just a bunch of geographically similar Collections. Finally, everything reports to a Tree Manager, which broadcasts events like when the player moves and LODs need to be updated.
So how does the general rendering work? We want to render trees in two distinct ways, as meshes and billboards. The foundation of the mesh drawing is a great little function called Graphics.DrawMesh(), which allows us to directly render meshes into a scene without requiring the overhead of a mesh renderer or mesh object. It also allows us to pass in a MaterialProperyBlock structure which means we can allow trees to set shader parameters on a per-instance basis. Now in docs, they say to only use one MaterialPropertyBlock and clear/populate it as needed. However I found that just storing the relatively small structure per tree, while using more memory, to be a faster solution. Just remember to pass by reference!
One of the main tasks on the client is to sort the trees into the necessary LOD groups. We do this whenever the player moves. It’s pretty flexible how the system does this, and almost entirely up to the tree blueprint. So you could have a range of trees, all LODing differently, all in the same instance. For instance, below, we have the constraints:
You can see the high LODs (red), the medium LODs (blue) and the low LODs (green) as they change. We do a bunch of stuff to make this sorting process quicker, such as presorting Collections in a different thread asynchronously, so when we need to update the LODs for say, a few thousand trees, we’re sorting an already partially sorted list into our LOD buckets, which speeds things up a lot.
There’s a lot we haven’t covered in the system – for instance, dynamic vegetation over a network, or how we handle collisions without destroying the framerate or going over the Unity 4.6 collider limit. But all that might have to wait for another post!
We’ve learnt a lot from implementing our own system. On paper, it’s a relatively simple concept – I mean it’s just sorting and rendering a bunch of meshes, when it all comes down to it. The tricky thing is creating something usable, extensible, and efficient. There’s a bit to go on it, and definitely some more features I’d like to implement, but the results so far have been more than promising. Eventually we might be able release our vegetation engine to the public, and we can all stop using the proprietary Unity Tree engine. Wooh!
We are initially working with a male player character that can be customized as you advance with different clothing and equipment. Gav has modeled the character and a number of different clothing items that can be fabricated in the world using collected resources. Different items will have different properties, allowing you to survive in some of the harsher biomes present in hurtworld. These are all compiled into the rig/prefab. Animation wise, we are going for a ‘stylized realism’ – Not toony but also not mocapped photorealism. Rather than risk TL;DR, here are some vids for your enjoyment