Scraps v0.5.1.6 – A Beautiful Collision

I haven’t been making a post for each small update but I’ve made a few recently:

2015-08 – 0.5.1.5 and 0.5.1.6
– Revamped collision damage calculations.

2015-08 – 0.5.1.4
– XP earned now syncs with Steam Cloud.
– Edited a default vehicle and added a new one.
new_default

– More minor weapon balancing.
– Maybe a little fewer crazy physics events when colliding with laggy vehicles (there’s still SOME crazy physics, but I’ve been experimenting).
– Removed the old screenshot code – Steam supports this anyway with F12.

2015-08 – 0.5.1.3
– Multiplayer work that should make things generally smoother and improve “teleporting” laggy vehicles.
– Stopped vehicle damage sounds playing when setting initial health on vehicles when joining games.
– “Proximity firing” mode isn’t the default anymore, but made the keys to switch it show in the GUI.
– Sped up unlock progress a little again.
– Some minor weapon balancing.
– Updated “how to play” screens with the proximity firing mode key.

Expect new parts in the next serious update: Full turret versions of the MMG and Medium Cannon and a non-turret version of the Large Cannon.

I also haven’t done a technical post for a while, so let’s talk about collision damage since I’ve just changed the system. Writing it out always helps me think about it as well.

Collision Damage

Warning: This post is long and technical.

So, you probably want collision damage in your game to be based on the mass and velocity of the hit. I’ve tried a few different solutions during the life of Scraps.

Option 1: Change in your velocity * your mass

You can get an impulse force to a vehicle from the change in velocity from the hit, times its mass, i.e. Δv * m. That’s the ideal method. Imagine you hit the test map block for instance, in a 2000kg vehicle going 20m/s:

diagram1Force on vehicle: You almost stop, so force on the vehicle ≈ 18 * 2000 =  36,000
Force on block:
It’s heavy but only goes from 0 to like 2m/s. So force on block ≈ 2 * 17,500 = 35,000

Or whatever. The main thing is that you get a result that’s:

  • Basically equal on both.
  • Affected by both the velocity and mass of the hit.

Someone did suggest to me once that when you ram someone they should take most of the damage – but with vehicles driving all around everywhere, how would you determine who’s ramming who? The cowcatcher part does do 175% collision damage, so in that case you’re doing more damage to them because that multiplier is only one-way.

Anyway, this solution works great in single-player, but in multiplayer with lag and so on it’s not so great. Sometimes you hit someone and get thrown back while they barely move. Sometimes it’s the other way around. If damage is based on change in velocity, one of you ends up taking a bunch of extra damage. I want to make multiplayer physics a bit less crazy – that’s a different task altogether – but the least I could do for now is make collision damage a bit more fair, right?

Option 2: Difference in velocities * their mass

So you’re thinking about your options now and you think to yourself well, I shouldn’t have to rely on the change in velocity anyway. When I ram someone, if I’m doing like 20m/s and I weigh x amount – I should do that much damage to them! So “their velocity * their mass”.

Except wait a minute, if you’re doing 20m/s and the other vehicle’s doing 15 in the same direction, you’ll do 20*m damage when really you should have only done 5*m. So actually you want the relative velocity.

I’ve seen this method suggested in a fairly official capacity (the dot product part is also used on all of these – I’ll cover that later), and in many cases it works pretty well.

But in a few cases it works terribly. Imagine you hit the big test block again:

diagram2
Force on vehicle: 20 * 17,500 = 350,000
Force on block:
20 * 2000 = 40,000.

The block obliterates you just because it’s sitting there being heavy. Hitting a wall does more damage the heavier the wall is! Onward to new ideas…

Option 3: Actually several options

How about their pre-hit velocity * their mass?

diagram3Force on vehicle: 0 * 17,500 = 0
Force on block: 20 * 2000 = 40,000.

OK, that 0 should probably be 40,000, but if ramming stationary vehicles does way more damage to them than to you, that could be cool anyway? Oh wait, what about those vehicles colliding with one going 20 and the other going 15?

diagram4

Oh yeah, we need to use relative velocities! You’re both zooming along in the same direction, you tap each other and both explode…

Min(difference in velocities, their pre-hit velocity) * their mass?

Fixed it! Take the minimum of the two. The vehicles tapping each other use the relative velocity, hitting the stationary cube still works… wait, vehicles flying towards each other will use their individual velocities to calculate the hits instead of their combined velocity.

Just the difference in velocities? Who needs mass anyway?

Mass seems to be the big problem here. What if we make mass less of a factor (or not a factor at all)? Yes, that would actually work.

diagram5

But if mass is still a partial factor then heavier stationary objects are still going to damage you more for hitting them to some extent, and conversely, heavy vehicles are going to be punished as the extra weight slows them down but doesn’t compensate as much in damage dealt.

Option 4: We have the technology

WAIT.

We know the velocities and masses of the objects before they hit. We know the angle they hit at. Data going into a collision is actually relatively reliable even with network delays. We don’t have to use the results from the physics engine, we can work out what the change in velocity should be ourselves, and use that. For direct hits:

vfinal = ((mus – mthem) / (mus + mthem)) * vus + (2mthem / (mus + mthem)) * vthem

Force = (vfinal – vinitial) * vfinal

OK not the simplest formula, but super simple for a computer.  This assumes a perfect “elastic” collision, but that’s fine: We don’t need to do real physics here, just get an idea of hit force.

Force on block:
vfinal = ((17,500 – 2000) / (17,500 + 2000)) * 0 + (4000 / (17,500 + 2000)) *20 = 4.1
(4.1 – 0) * 17,500 = 71,750
Force on vehicle: ((2000 – 17,500) / (2000 + 17,500)) * 20 + (35000 / (2000 + 17,500)) * 0 = -15.9
(-15.9 – 20) * 2000 = -71,800

Another example. 1000kg vehicle going 20m/s hitting 2000kg vehicle going 15m/s, both in the same direction (5m/s relative velocity):
V1000:
vfinal = ((1000 – 2000) / (1000 + 2000)) * 20 + (4000 / (1000 + 2000)) * 15 = 13.3
(13.3 – 20) * 1000 = -6,700
V2000:
vfinal = ((2000 – 1000) / (2000 + 1000)) * 15 + (2000/ (2000 + 1000)) * 20 = 18.3
(18.3 – 15) * 2000 = 6,600

1000kg vehicle going 20m/s hitting 2000kg vehicle going 15m/s, towards each other (35m/s relative velocity):
V1000:
vfinal = ((1000 – 2000) / (1000 + 2000)) * 20 + (4000 / (1000 + 2000)) * -15 = -26.7
(-26.7 – 20) * 1000 = -46,700
V2000:
vfinal = ((2000 – 1000) / (2000 + 1000)) * -15 + (2000/ (2000 + 1000)) * 20 = 8.3
(8.3 + 15) * 2000 = 46,600

This is the way Scraps is calculating collision damage force as of version 0.5.1.6

Directional conversion

One more thing. The above equations apply to objects heading driving on the same vector – one-dimensional collision. But real vehicles are driving all around in three dimensions.

There are some super long formulas that will give you the new velocities in three dimensions after the collision, but we only need to get an idea of collision forces here – we don’t actually care about the direction.

So I do a bit of a cheat here. Unity gives you the normal vector of a collision, so we can use this to get just the component of the velocity that’s in the direction of the collision, and use that.

The simplest way to do this is to take the dot product of the collision normal and the velocity vector of each vehicle, which gives you their speeds in just the direction of the collision. In C# code:

Vector3.Dot(collisionContactPoint.normal, velocityVector)

Then I use those values in my equation and get nice collision force values as a result.

BIG DISCLAIMER: I learnt this stuff just from searching around on the Internet and testing stuff out. Although it works well for my purposes, there may well be better ways of doing this, or cases I haven’t come across yet where the system breaks down. Scraps is still evolving, so all this may change again.

Bookmark the permalink. Both comments and trackbacks are currently closed.