Game Development with Unity for Web Developers Opponent Follow Target
Transcript from the "Opponent Follow Target" Lesson
>> So I mentioned that Unity has a component-based architecture, and we've used that a little bit, but what we can do with components is we can compose them. So Unity's component-based architecture leans into an idea called composition. So rather than other approaches to code diversification, such as object-oriented inheritance, we can combine components to get more emergent behavior.
[00:00:30] So while C# does allow for inheritance and inheritance is very useful in Unity projects, thinking in terms of GameObjects that use several different components, tends to lead toward more variety and emergent behavior. And we've actually already done this. So similar to the PlayerMovement script, we can actually create a FollowTarget script that automatically drives the existing RollingMovement script, and it kind of creates the appearance of a simple opponent AI.
[00:01:07] So because the opponent isn't going to have the benefit of player input, we need another way to drive that RollingMovement script. So we're still reusing the RollingMovement script, but we're going to just move it in different way. So we've got this FollowTarget script, and I'm going to switch over to Unity.
[00:01:36] And in the scripts folder, I'm going to create a new C# script and we'll call this FollowTarget. And we want to attach this script to the opponent. So we'll add this to the opponent, and I'll click on the opponent to make sure that, yes, it does indeed now have a FollowTarget component.
[00:02:08] And let's get rid of these namespaces we don't need. And we are going to use Start, and we are going to use FixedUpdate. So we'll change that. All right, so we need a couple of variables here. We need a SerializeField, which will be a Transform, and this is going to be the target that the opponent is trying to follow.
[00:02:40] So the target, in this case, is going to be the collectible, it's trying to grab the collectible. So it's transform of the collectible. So we're gonna create another SerializeField. It's gonna be a float value. And we're gonna call this retargetingSpeed. And, this is going to be how fast the FollowTarget or the opponent is able to find a new target.
[00:03:10] So we're gonna give it some time to kind of meander a little bit before it actually is moving in the right direction, because that's how a real player would be playing the game. Otherwise it makes it way too difficult, because the opponent can just kind of go immediately in the direction of the collectible as it appears.
[00:03:31] So in the start method, we're going to gather components. The only one we really need is the RollingMovement, which, oops, I actually did need one more variable there, I apologize. We need RollingMovement. Okay, so we have the RollingMovement component. And of course, we need to get that component from this GameObject, so we'll say GetComponent<RollingMovement>.
[00:04:02] And now we've got it. So in FixedUpdate, how are we gonna do this? Well, first, we need to point the target direction vector toward the target. So we're gonna do a little bit of vector math here. We want to create a new Vector3, which we're gonna use in subsequent calculations.
[00:04:28] We're gonna call this TargetDirection. And this is going to be equal to the target's position. Okay, remember the target is the collectible. So we are looking for the position of the collectible minus our own Transform position. So, what that's going to do is it's going to create a vector that points in the direction from our current position to the collectible.
[00:05:07] So if the sphere is close, or the collectible is close, set the retarget speed to be high. Otherwise, use the typical retargeting speed. Okay, So we're gonna calculate another value here. We're gonna call this retargetSpeed. And this is another slightly confusing line, so I'll type this out and then explain it.
[00:05:52] We want to type Vector3.SqrMagnitude, Of the targetDirection And then if that is less than 0.1 float, I'll type a question mark here, Then a float of 1,000, a colon, and the retargeting speed variable. So, There's other ways I could have written this. This is basically an if else statement in one line, but there's a couple things going on here.
[00:06:40] So first, here's kind of what's inside our if statement. So we're saying if the squared magnitude of this target direction is less than 0.1, what does that mean? Well, the squared magnitude, if you kind of remember back to the Pythagorean theorem, we could take the magnitude of the vector and actually get its exact distance by calculating the hypotenuse of that triangle.
[00:07:14] We don't need the exact distance, we just need to know kind of relative distance. So by taking the squared magnitude, we're avoiding a square root calculation, which is really expensive in any application of computers. [LAUGH] So especially when you're running fixed update and you're doing it every physics update, you can use square roots, it's not a big deal, but if square roots can be avoided, they should be.
[00:07:46] So, we're taking the squared magnitude. So this is a distance, right? So we're saying, if the distance to the collectible is less than 0.1, or the approximate distance is less than 0.1, then we're gonna do this. We're going to set the retarget speed to be really high, because if it's right there, we just wanna kinda move immediately.
[00:08:14] We don't want to spend so much time kinda retargeting to finally get there. So it's kind of a cheat or a hack, basically, for the computer. Otherwise, if that's not true, if we're not close to a collectible, if it's further away than that, we're just gonna use this normal retargeting speed.
[00:08:38] So we've got these values here, we've calculated a target direction, we've calculated the speed at which we want to retarget. Now, we need to actually do those things. So we're going to do that with what's called a linear interpolation, which will allow us to slowly do something over time, or quickly, if we want to.
[00:09:05] So I'll type out a comment here. We are going to linearly interpolate the movement vector from the current direction to the target, Where T (time) is fixed delta time multiplied by the retargeting speed. Okay, so that's where we're going to use that. So in rollingMovement.movementDirection, This is where we're going to actually set that vector, the direction that it should be rolling in, just like we did with player input and other scripts.
[00:09:59] So to do this, on the Vector3 class, there is a method called lerp. And you will use lerps all the time as a game developer. A lerp is a linear interpolation, and that's where you're going from one value to another value over time, and you're updating this every fixed update.
[00:10:25] So which values do we wanna go between? Well, we're retargeting, right? So we want to take the current movement direction of the RollingMovement script. And that's going to be your starting point. So our starting point is constantly going to be changing, because we're reassigning it. But the next value that we assign is going to be slightly closer, To the target direction that we calculated.
[00:11:06] And we do need to do, again, a little vector math thing here. We need to normalize this vector, so that it has a magnitude of 1, because the MovementDirection also has a magnitude of 1. Remember, that's being multiplied by speed in the RollingMovement script. So we don't want to multiply that any further.
[00:11:33] So what is time? So we're moving from the MovementDirection to the TargetDirection. So we're slowly retargeting where this thing is. How do we wanna do that over time? Well, we want to say Time.fixedDeltaTime. So we at least want to calculate that consistently over every fixed update. But then we wanna be able to adjust the speed of that retargeting.
[00:11:59] So just like how we adjust the speed of rolling, we can also adjust that by multiplying by the retargetSpeed. So that's a lot of code. Hopefully the explanation and the comments help. I imagine there might be a few questions. But let's switch back to Unity. And on the opponent, Remember we created a few serialized fields here.
[00:12:33] So what do we want to target? Well, we want to target, The collectible. And so now, if I save the scene and hit Play, We hit Space to start, the opponent is gonna start moving toward that sphere. And of course, it's a little difficult to see which player is which.
[00:13:01] I know which it is, because I'm the one playing. But, yeah, now we have kind of the appearance of AI with just a little bit of vector math, just pretty neat.