Vehicle Based Character Controller
Create a physics based character controller for a vehicle based arena combat game.
I decided to do it physics based for a few reasons. Once is that it is more responsive to outside forces. This way it is easy to handle forces coming in from explosions, and other outside effects in the game. Another is that it creates a more fast paced environment, where everything is a reaction from inputs, both from the player and from the game. It also helps ground it in a sense of reality, but the goal was never to make it realistic, just as easy as possible to control so there are a lot of helping functions to keep the car moving.
In order to make the car accelerate I started with a very basic just adding force in the direction of the car’s forward. This is simply just adding a force to the car, strengthening the force by how much they are accelerating and just setting the direction to the way the car is facing. This leads to some issues where if the car is leaning the force will be applied to the forward of the car which is either up or down, and this can compound on itself which makes it tip more until the car flips over.
Instead I take the average normal of the ground the car is on. Normal is a direction 90 degrees to the plane of the ground the car is on. I then project the direction on to this plane and set the length to what it was originally. This ensures that the force we are applying to the car is always parallel to the ground we are on and fixes any issues of tipping. This causes some issues while on walls, but we will cover this later.
I do some changes behind the scenes with the acceleration values of the car. If the car is changing directions from forward to backward, or vice-versa I increase the acceleration. This way if the car is speeding up from a stop, or coming to a hard stop, they can do this quicker which increase responsiveness and keeps the car feeling fast paced instead of sluggish.
To make the car feel a bit better while accelerating or decelerating, I also change the position of where I apply the force on the car. When we are accelerating, I change the position of the car in order to tip the forward of the car up slightly when it first starts accelerating to add a little bit of “realism” to how it feels to drive. This only needs to be done a little bit because the momentum of it might tip it too much. Projecting the force in the direction of the car helps it a lot, because as the point you are applying the force to become in line with the center of the car and the force being applied to it, it will stop tipping. This will typically even out the car as the force is being applied straight forward and since by this point the extra force we add for first acceleration will no longer be applied.
To turn the car I apply a force at the front of the car, to apply torque and turn it. This by itself will just rotate the car around the center point. For this I check the speed of the car before applying any turning forces to make sure they move forward at least a bit while moving. Since it is the same amount of force being applied, the faster you go your turning radius naturally gets larger the faster you move. To further enhance this I increase the force applied to turn the car the slower the player moves in order to help them move more precisely at slower speeds. I also cube any turning input I get since I receive input from -1 for left to 1 for right. So cubing it makes small inputs even smaller, and an added bonus is that inputs stay negative and positive. This helps the player make small adjustments as they drive instead of swerving the car every time they touch the stick.
The position that I add the force on the car in order to move it near the front of the car, but otherwise centered. If the y-value is not exactly in the center of the car, the car will start to roll when turning. While the physics of the car turning while moving will naturally slightly turn it, it is not ideal and can cause the car to tip over onto its side. Instead I have the model of the car seperate from the physics of the car, and as it is turning I tilt the model to fit with the turning. This really exaggerates how much the car is turning, especially at the higher speeds. I do give it some degree of freedom when not moving so you can tilt the car just a tiny bit while turning.
This is more related to acceleration, but the problem is caused by turning so I will cover it here since accelerating is already complicated enough. When you turn the forward of the car moves, and this is what causes the car to move in another direction. The current velocity of the car does not change with this though. So as is so far, whenever you turn, your velocity keeps going with your last forward, with no friction, and it takes you a little bit to actually start heading in the new direction you are turning as you velocity slowly starts to transform towards the new forward. It feels much like a boat at this point.
In order to fix this I project the velocity of the car along a sideways vector of the car. This gives me the part of the velocity that is not in the new forward direction. Since it runs every frame, this amount is not a lot when you turn, since in a frame you actually turn a small amount. I take this velocity and just cancel it out of the velocity of the car so it is no longer taken in account, just the velocity in the direction of the car’s forward. This reduces the speed of the car overall because you are just taking away velocity from the car. This is not that big of a deal since the amounts per frame are really small, but you can check if it is too large of an amount to cancel out and add that velocity back to the forward of the car to keep up speed. I did not have this issue especially with the small turning forces I was working with. This loss of speed or “energy” is somewhat realistic as when real cars turn they lose some energy to friction of the tires to have enough friction to keep real cars from behaving similarly. Otherwise they feel like vehicles that do not have any ways to apply friction such as boats or spaceships.
The most important part of the physics based car is keeping the car off the ground. We do this by using a simple suspension system one on each corner. All I do it use a physics raycast downward on each corner. I use this to see if the car’s “wheel” is down on the ground. If it is down, I push up on that corner of the car to keep it up. This way on a bumpy surface you can see the car react. I typically do not push straight up on the car, but from the normal of the ground that I hit, to try to push away from it as much as possible. An average of both would be best. The force I push up with is dependent on many variables, such as the strength of gravity being endured, how close it is to the ground, how heavy the car is, and how many “wheels” are currently on the ground. I end up taking all of these into consideration to try to keep the ride of the car as smooth as possible. If one corner is too strong or weak compared to the others it can wobble, and in extreme cases bottom out or launch the car into the air.
For the “compression” of the suspension check I use a custom curve that I use to determine what percentage of strength to push with based off of how far away the car is from the max check being performed. As a rule of thumb the max distance should be as large as it can be so the car can clear as many obstacles as it can. This may not always be the case, since often we have breakables and debris/litter we want the car to smash into and push around instead of driving over, but we also have ramps we want to be able to take smoothly. With ramps as well I have a special consideration to increase the strength of the suspension check to try to keep the front of the car from colliding with the ramp.
The check is not actually straight down, it’s slightly off to an angle, to push the car back towards the middle. This keeps it more stable when taking turns at high speeds, or across bumps. The car could start to roll, but since it’s off at an angle it is still preventing the car from tipping. Along with this, after checking all the suspensions themselves, in the handler I check which ones are on the ground and which ones are not. If any two checks on one side are not on the ground I push down on that side to try to keep the car as parallel to the ground as possible to prevent it from tipping. This helps keep the driving as smooth as possible.
To help keep the game running as smoothly as possible I only do two checks per frame. I check opposite corners on a frame and use the data from the previous frame for the other two corners to keep the forces the same. I tried a few different ways of checking, but this was the best I could do and keep the car feeling the same. Having the car only check one corner per frame wobbled it a little bit, but that was before I started applying old data I think. It is certainly possible that there is room for improvement here, but I unfortunately did not have the time to pursue all the options that I wanted to.
I wanted to add drifting to the game in order to allow for better corning and to make driving more interesting with some extra choices. This was mostly turning off the mechanic of cancelling out any sideways velocity when turning in order to have the car slide around some. I also change where I apply the forces on the car back to the center point of the car. This allows the backend to swing out more instead of just following the front of the car. This value could be played around with some, to allow for more or less turning when the player is drifting. This could also be a unique value for each car if you wanted one to drift more than the other, but I wanted most of the mechanics and general stuff to feel more or less the same.
Changes for Variable Gravity:
Our game had different gravity walls and platforms that were their own source of gravity. A large challenge for the game was trying to adjust for these effects, especially with the character controller. For accelerating, I would check the normals of the ground they hit, and check against the source of gravity to make sure the cars could not drive up walls, but they could drive on approved walls that had gravity effects on them. It also brings up the fact that cars could be under the influence of multiple sources of gravity and the suspension had to account for this extra strength in order to counteract everything. Another challenge was the self righting in air. If the car was launched or jumped into the effect of a gravity source, I had to change the direction of the ground for the player so that the car oriented itself to land on it’s wheels and not on it’s side or back and be stuck. It is helpful to orient everything in terms of the effect of gravity instead of just down so that it could be changed at any moment. Anything that would be checked against the downwards force, like the car’s speed not counting the constant effect of gravity, has to cancel out the ever present force.
Drafting And Other Boosts:
Since driving is such a big part of the game I wanted to make it as smooth as possible. This meant eliminating as many slow downs in the game as possible, but as well as accounting for things in game and responding accordingly to keep everything flowing well. One thing I did in this script is include drafting on other cars. This helps cars that are chasing other cars keep up. This prevents one player from just constantly staying away from other characters, along with arrows that point to each other. Every few frames, I send a box collider out in front of the car to detect any collisions with other players in front of them. If it hits a player I give the player a small speed boost to set their max speed to a higher level. This boost should raise the car with the lowest speed up to the level of the car with the highest speed so they could chase down the fastest enemy. I continue to check to make sure the player is still in front of them.
Another solve of the game slowing down the player is collisions. I gave everything in the level no friction so any player that hits a wall just slides off the wall and it allows them to keep going. This is also applied to the ground so flipped cars keep sliding. It also bounces the car away to prevent them from just sticking to the wall and keep going where they want to. It should be enough to slow down the player, but not actually stop them. I also give all the walls collider that detect players when they get close and provide a correcting force to prevent the player from colliding with the wall and keep them parallel to it. It also gives the player a speed boost when they are close to the wall to promote “dangerous” driving and make things more exciting.
Within the systems of the car itself I give the player boosts under two conditions. They are both at the end of events that can lower the players overall speed. One is when the player stops drifting. Suddenly all of their sideways velocity is cancelled out and they only keep their forwards momentum, which if they were completely sideways would be zero. So at the end of a drift I apply a small boost to increase their speed and transfer all their sideways momentum into their actual speed. Another place the player can lose speed is upon landing. When we had jumping in the game this was more common, but now they only do this when coming off of jumps. Their momentum would be downwards, and their max speed would cap their speed so when they landed they would lose a lot of speed. To prevent this, when they land I detect how much speed they would lose upon landing and apply a boost to the player to have them keep up their speed. The goal of all of these changes it to subtle enough to prevent the player from noticing it even happening, but have enough of an effect to keep the player moving easily through the level.
Jumping and Air Controls:
We had previously had jumping in the game so the cars could jump in the air and take alternate paths, but this provided additional design challenges and introduced new bugs into the game. It eventually just made the most sense to remove these features from the game and do more work on all of the other features.
The jump would take input from the jump button and apply a force up until the maximum height. It would calculate the speed of the car and stop applying force when it’s velocity was enough to carry it to its maximum height. It would then wait for a new source of input and add a jump to the queue for when it landed. All of it’s force would be relative to the gravity effecting the car, but it could jump out of an effect of gravity and be under the force of the default gravity.
Air controls would allow the player to rotate their car mid air. This was eventually cut because it was hard to make them feel really good. I started by making it so the player could do a full rotation along each axis in a single jump. This would mean that any accidental input while they were in the air would cause them to spin very rapidly. I did have an acceleration to the rotation so that any small inputs would not cause any rotation, but holding the inputs in a direction would speed up to allow them to do a full rotation. Doing a front or back flip would mean that they would have to flip super fast in order to do a full flip because of the extra energy needed to flip along the longer axis. This was one of many reasons we had to cut these features.