Transcript from the "Game Manager" Lesson
>> So we've written a couple of scripts we have kind of this physics sandbox, where we can move the player around. But now we need to start turning this into an actual game with scoring and timers, and a game loop, and we need to be able to restart the game.
[00:00:19] There's a lot of stuff to do there, and a lot of times, even in large games, you'll have this class called a game manager. It's not something that you have to have, it's not something that you have to call this class. It's just kind of a de facto architectural approach to making games in unity, where there's a script component called game manager, and it's used to control high level aspects of the games loop.
[00:00:50] So, this might include things like initializing a level, along with other state changes, such as game over or restarting the game. So a game manager can do lot of things, but generally, it keeps track of the business logic in the game. So it might also keep track of just important game wide numbers, like scores or a round timer.
[00:01:18] It's pretty flexible in its definition, but it's a thing that you will find a lot of unity developers doing. They'll create this class called a GameManager and do a lot of their business logic there for the game. So, because this class is doing a lot of stuff, you can imagine that there's actually a lot in here.
[00:01:40] This is not the full class, this is split over a couple of slides, and we'll go through this one piece at a time, and go back and forth between Unity as we code this. So first, we need to set up variables, where we'll keep track of a couple of different values in the game.
[00:02:01] And includes the players score, the opponents score, the amount of time for each round, the remaining time in each round, a boolean to keep track of the game state, and then global values for use by a couple of other scripts. So you'll see here that these variables are called player score and opponent score, that's not the actual number for the score.
[00:02:30] The score, remember is in the score components, so this is referencing that component and we'll drop those into the inspector after we create the script. At the start of the game, the timescale will be set to zero, so you can modify the speed at which time moves in unity, it's by default moving at a speed of one.
[00:02:55] But if you wanted to do like a slow-mo effect, you could set it to 0.5, and all of the physics updates would move at half speed. If you set it all the way to zero, that's how you pause the game, time stops. And so at the start of the game we're going to set the timescale to zero to pause the game, we're going to set the value for the game clock.
[00:03:21] Which is the amount of time the game should take in seconds, which is a float value of 60 here for one minute. And then we'll update the timer for the first time calling this method update timer, which we'll write later. So there's a lot to do here, so let's get started.
[00:03:41] I'm going to switch to Unity, I'm going to create a new game object here called Game Manager. And this is just the last values that were we were using on the transform, but we wanna reset this to zero, not because it really matters. But it's just good practice to reset your transforms to the origin, just to indicate that in the case of like this game manager, they don't actually need to be at that particular position.
[00:04:17] So you can always reset the transform by going to this little three dotted menu here next to transform, and choose reset. So I have a GameManager GameObject, I need a GameManager script. So I'll create a new C SharpScript, and we'll call it GameManager without a space. And then finally.
[00:04:47] I'll drag that script component to the Game Manager Game Object, and now it's attached. And increase the size here, you'll notice that unity has actually given that script a special icon. It's not because it's different than any of these other scripts, it's just that game manager is so common as a name for this type of a class, that unity just includes an icon for it so that you can check it out.
[00:05:18] So I'm going to go ahead and open the GameManager here, and the first thing that we need to do, is create our variables. So I'm gonna remove these namespaces we don't need, and then we need a serialized field for the score, this will be the player score. We need a serialized field with a score that will be the opponent's score.
[00:05:56] Now we need another serialized field that will be a float value for the game time in seconds. Seconds, spell correctly, and that will be a default value of 60. You don't have to create this as a serialized field if you don't want to, you could just leave that off, and always have the game set to 60.
[00:06:18] But again, when you're creating games in unity, it's nice to expose values like this, so that you can try out different values as you're tuning the game. Maybe around time of a minute is too short or too long, and you want to easily adjust that, and so you can change it right in the inspector if you wanna.
[00:06:37] So we need another float value that will be the timer, and even though we have the game time in seconds that's soaring the amount of time that each round should take. We still need another float value, that's the actual timer that will be keeping track of time. We have a boolean value called m_gameIsPlaying, and by default we'll set that to false, because when the game first starts up, the game is not playing.
[00:07:14] We have to hit the spacer to start the game. And then finally, we'll have one more variable here, that will be a public static float value. And this is actually going to be a property, and it's a property because I'm capitalizing it. And we'll call this RespawnHeight, and we'll set to a value of 0.25.
[00:07:47] We'll use this later on, this is going to be the y height, at which a player should respond. So if the player falls off the edge of the board, this is the height at which we should trigger a Respawn. And we'll other later on to I think the rolling controller, sorry, the rolling movement script, but for now, we're just leaving this accessible in the game manager.
[00:08:17] There's a couple of other ways we could have done that, we could have put it directly on the rolling movement script, but this is a good opportunity to see an a global value that you might want to access other places. So, in the start method, get rid of this comment, we want to set the timescale to zero, to pause the game at the start.
[00:08:48] So to do that, we'll access the time class, and we'll set time scale, equal to a floating point value of zero. Then we wanna set to the game timer to the number of seconds. In a round, so we have that timer ,and at the start we wanna set the timer to the game time and second, so we'll start the clock at 60 seconds.
[00:09:28] And then we want to update the timer text, and that's a method we'll write later. And I'm actually just going to comment out that method call for now, so that it's not creating errors, but we do wanna remember that we need to call there. All right, so we have the start, of our game manager here, there's a lot more to do but I'll save that file, and let's switch back to the slides, and see what's next.
[00:10:11] So, every physics update, we need to actually update the timer, and then if the timer runs out, we should end the game. So this is another good example of something that it's a better idea to do it in fixed update if you can, you could do it in update and say multiply by delta time.
[00:10:34] But just keeping track of it in a fixed update that you always know, is gonna run at the same time. Usually better for actual game timers, if you want a little bit better predictability and precision. So, we have our fixed update here, and it's going to subtract from the timer.
[00:10:58] If the timer hits zero, we're gonna enter the game overstate, where game is playing is set to fix, and then the timescale once again, is set back to zero. So let's switch over to our code, and let's write that up. So we need FixedUpdate, right? And then if the game isn't playing.
[00:11:34] We should stop here, and that's where we're using that Boolean value. So if game is playing is not true, then just return. So even though fixed update has returned type of void, we can just return early. And if the game is not playing, so if game is playing as false, we wanna just stop, because we don't wanna update the timer.
[00:12:00] We don't want to do anything else that's in fixed update, so it's just an early exit. So next we want to subtract the fixed delta time from the timer. So this is interesting, we have the timer variable that we created, and then we want to Decrease it by Time.fixedDeltaTime.
[00:12:28] So, update and fixed update and the time class have a measurement of delta time. Delta time is how much time has elapsed since the last update. So in update, you can actually use time.Deltatime, and that's going to give you how much time has elapsed in seconds, since the previous frame was rendered.
[00:13:01] In this case, we're in fixed update, we want the amount of time that's happened since the last fixed update. And even though fixed update is running at a uniform rate, so maybe we know that exact value, maybe it's one divided by 50. If we know that it's running at a rate of 50 times a second, really, because this can change elsewhere in unity.
[00:13:29] It's good to just use fixedDeltaTime, so that we have that exact measurement. So every fixed update, its going to subtract the amount of time that has elapsed since the last fixed update, and that's how we're gonna decrement the timer. At a consistent rate, the same rate at which time is actually passing inside a fixed update.
[00:13:53] So, if the timer hits zero. Then we need to enter the game overstate. So if timer is less than or equal to zero, because it could potentially be less than zero. Remember it's just subtracting fixedDeltaTime, so it could go below zero. So if it's less than or equal to zero, then we want to call a GameOver method, all right?
[00:14:30] And we wanna set the timer to zero, and that's just not totally necessary in this case. But it's good habit to set your timers to zero, once they've actually hit the value. That becomes really important in areas like animation, where you might have something running along an s curve.
[00:14:53] You don't want to overshoot its value, you wanna make sure that once something has hit zero, less than or equal to zero, then you're actually setting it to zero. So that any subsequent code that's using that timer, is behaving the way it should be. So we need to write that GameOver method, so we'll save void GameOver.
[00:15:19] And in GameOver, we wanna say game IsPlaying = false, and then Pause the game by setting the time scale to zero. So again, we'll say Time.timeScale. Is zero. We could have potentially put these two methods sorry, these two statements right here where Game Over is. But sometimes using methods like this is nice just for the sake of clarity, so that we know exactly what's happening.
[00:15:58] Where the timer hits zero, it's calling GameOver.