Relearning Some High School Math
April 4, 2020
Previously, we've talked about the fight camera and how the player can Lock-On to their opponent in battle. Read Free Camera & Lock-On for more about switching between the control schemes associated with those camera states. In this post, I'll talk about a problem created by the Lock-On camera controls and how I fixed it. The problem was rooted in a subject that I am dreadfully under-practiced in: high school mathematics.
The issue we're tackling today had to do with the change in character control invoked by changing between Free and Lock-On camera modes. While in Free mode the camera will follow the player as they freely traverse the battlefield, in Lock-On mode the camera looks at the opponent and player movement happens in relation to the opponent's position rather than the camera position. While locked on, the player faces their opponent, and the forward/backward axis allows the player to step in or back away from the target. The left/right axis allows the player to circle around their opponent.
Technically we can do this, but the problem is we walk at the same speed whether we in close to the opponent walking a small circle, or several meters away from the opponent walking a larger circle. If you wanted to run a circle with a diameter of 10 meters it would take you a lot longer than running a circle with a diameter of just one meter. The early version of Lock-On character control doesn't differentiate! We need to fix this.
Let's take a look at a snippet of the FightControl code.
In this code for movement in the Lock-On state, we move the player around the target. We have the target's Transform, we have the player's controller input, and we have the mech's walking speed. We use Transform.RotateAround
to move the player around the target's position with regards to the "up" axis (Vector3.up
), and we stipulate an amount to move by multiplying our input, our walking speed, and of course Time.deltaTime
to calculate frame-by-frame. Maybe you can already see what's causing the problem. It's the third parameter that we're feeding to Transform.RotateAround
. This formula for our movement per frame, -movement.x * moveSpeed * Time.deltaTime
, works for the Free camera control scheme, but not for Lock-On control. This is because RotateAround
moves a Transform by an angle, that is, a certain number of degrees, but we're feeding it a value meant to express a distance in meters. More importantly, we're not adjusting that value in accordance with our distance from the target, or, the radius of our circle. If we change the way we handle both these points, we can solve this issue and have realistic circling around the enemy.
Now a mech's walking speed is determined by its stats, totaled from its Parts. The speed that a mech walks in Free camera mode should remain the same when Lock-On takes over. We just need to take that formula, -movement.x * moveSpeed * Time.deltaTime
, and convert it from meters into degrees to make it compatible with Transform.RotateAround
. Logically, we know that we also need to account for the length of the radius connecting the player and their target, and once bring this into the picture, we actually have everything we need to solve our problem using just a little bit of high school-level mathematics. Trigonometry?
So let's talk about circles. We want to move our player around the circumference of a circle where the Lock-On target is the center of that circle. The imaginary line connecting the player and target is the radius of that circle. The amount of distance we can walk in one frame is currently a measurement in meters around the perimeter of the circle; we call this length an arc length. So let's convert our arc length from meters to degrees.
Besides degrees, there is another way to talk about a portion of a circle's perimeter. It's a unit called a radian. The length of a radian is the length of a circle's radius, but curved to follow the border of the circle. Now obviously, all circles are very similar, so they share aspects in common. All circles total up to a certain number of radians: 2π radians. 360 degrees of any circle always equals 2π radians. Knowing this, we know we can easily convert a number of radians into degrees, or vice versa, but first we need to convert our arc length into radians. Remember, an arc length uses a lateral unit of measure like an inch, or in this case, a meter. A radian is a different unit of measure.
There's a simple formula describing the relationship, in all circles, between an arc length, the circle's radius, and the arc's size in radians.
s = rθ
In this formula, we write arc length as s. We write the circle's radius as r, and theta θ is the number of radians representing our arc length. For our script, let's invert this formula to account for the parts of the equation we already know.
θ = s/r
This equation is the basis for a new function we add to our script called GetRadians
. We also add a function called GetDegrees
that runs the standard conversion from radians into degrees.
Now we've set the stage for our revised Lock-On character control.
We have our calculated distance that the player can walk in a frame, and we calculate the current distance between the player and the target. Our variable moveStep
represents our arc length, and our variable distance
represents our radius. We can use these values to convert the arc length to theta, a number of radians. Now we've got our bridging theta value which we can quickly convert to a number of degrees. Finally, we can rewrite our Transform.RotateAround
call to move the player by degrees, and not by meters.
And it works! This post began with the "before," and here's the "after":
I always struggled with math is school, and circling back around to these techniques in game development always makes me wish I'd retained them the first time around. I don't know whether having more concrete and more entertaining use-case examples in class would've made the subject matter more interseting to me, or maybe I just needed things explained in a certain (different) way than I got. When mathematics can solve these seemingly opaque and oblique issues I really have to step back and marvel at how this world works. It's not like humans invented geometry after all. Thanks for reading! For more updates on this game's development follow @avknights_dev on Twitter.