-
Phase 7 - Managers.
-
Okay, so in this particular section
-
we're going to create the game manager
-
and the idea is that by the end of it
-
we will have rounds with tanks
-
where you can drive around and fire
-
and destroy another tank and at the
-
end of it you have a UI that
-
explains that you have won that round,
-
and then after several rounds have been won
-
that you've won the game.
-
There is it turning red.
-
And you get this text at the end,
-
Player (number) wins the round!
-
And you'll notice also that we've colored
-
the UI to match the color
-
of the tank.
-
So we're going to start by creating
-
the spawn points for each tank.
-
So for that we just need a
-
position reference in the world,
-
so we're going to use an empty game object
-
for that, it's very simple, we're just
-
going to go to the hierarchy and we're going to
-
choose Create Empty.
-
I'm going to rename this one SpawnPoint1.
-
Then I'm going to duplicate it.
-
Control-D or Command-D
-
depending on your platform, and call it
-
SpawnPoint2.
-
And I'll give you some positions for those.
-
So SpawnPoint1 is (-3, 0, 30).
-
With a rotation of (0, 180, 0).
-
It's going to be a lot quicker to read it off the
-
slider because I've got both spawn points on at once.
-
So SpawnPoint1 position (-3, 0, 30)
-
Rotation 180 in the Y axis, 0 in the other two.
-
SpawnPoint2 (13, 0, -5)
-
ensuring a rotation of SpawnPoint2 is (0, 0, 0).
-
And naturally this is one of those things
-
that is not going to make a huge difference.
-
If you decide you want to move those spawn points somewhere
-
else in your game feel free to go ahead and do that.
-
Obviously you want to keep the Y axis
-
positioned at 0 at all times
-
because that's where you want the tanks to start.
-
Because we're dealing with these spawn points which are
-
effectively empty game objects,
-
something that just has a transform component
-
it's very difficult to see them in the scene.
-
So you have to select the translate tool to
-
be able to see their handles,
-
so SpawnPoint1 is here,
-
SpawnPoint2 is over here.
-
Now there's a quick way to rectify that
-
which is to select the SpawnPoint
-
and give it what we call a gizmo.
-
So a gizmo is a 2D representation
-
of something in the scene view.
-
So if you reselect SpawnPoint1.
-
And then at the top of the inspector
-
there's this little cube icon.
-
If you click down you can choose from a number of gizmos.
-
You can specify your own if you bring in
-
a texture in Unity, you can set that as the gizmo,
-
which is super handy.
-
But for this we're just going to select a blue background.
-
So that's going to then print the name
-
SpawnPoint1 just like that.
-
And then we'll do the same for SpawnPoint2
-
we're going to choose red.
-
So that's going to match the color of our tanks
-
that we're spawning as well, so SpawnPoint2 is red,
-
SpawnPoint1 is blue.
-
Okay, so that's where my two spawn points are.
-
So we just, design-wise, decided
-
to put them either side of a building so the
-
players have to at least drive around to start
-
shooting in the game.
-
So that's gizmos.
-
The next thing we need to do is create
-
a screen space canvas.
-
So when we first put in a UI
-
element in to the game, a slider for the health,
-
I mentioned that ordinarily our
-
UI is over the entire screen.
-
So that's what we're going to do now.
-
So in the hierarchy I'm just going to choose the
-
Create menu, I'm going to choose UI,
-
and Canvas.
-
And yet again it's going to appear huge,
-
and it should look like that if you've done it right.
-
And you can't really get it wrong because that's the default.
-
Then what we're going to do is rename this
-
MessageCanvas, so F2 on PC,
-
or Return on Mac,
-
just rename it MessageCanvas.
-
Now we want to work with this canvas a little
-
bit and add a text element that's going to be
-
the basis of our messaging at the end
-
of each round.
-
So what I'm going to do is,
-
first off I'm going to set the scene view to 2D mode.
-
So at the top there's a little button here
-
by the scene tab that says 2D, click on that.
-
It's going to constrain to a particular axis.
-
Then what I'm going to do is to click on
-
my MessageCanvas and you can
-
either hover over the scene view
-
and press F on the keyboard to frame,
-
or you can go to Edit - Frame Selected.
-
Then you can just use your scroll wheel
-
or gesture on your trackpad if that's what you use,
-
to kind of frame that in the scene view.
-
Then what we're going to do is actually add a
-
text field, so I'm going to right-click on my MessageCanvas,
-
so that I am immediately creating something
-
under that parent object.
-
And I'm going to choose UI Text
-
What you should see then is you have something called
-
New Text, it's a new text field,
-
it's left aligned and it's kind of sat in
-
the middle of our canvas.
-
It should look like that.
-
So basically with this thing we want it
-
to be nice huge text
-
and we want it to be stretched over most of the
-
screen except for a border around the edge.
-
So depending on what we put in to it we will use
-
Best Fit on the text which will scale it
-
if there's too much information on the screen,
-
and will ultimately fit it in to this canvas.
-
So on the Rect Transform for the text
-
the anchors for X and Y
-
should have a minimum of 0.1
-
and a maximum of 0.9.
-
So it should look like this.
-
So note that it's X, Y across,
-
and then X, Y across again.
-
So Min is the first two values, Max is the second,
-
so 0.1, 0.1, 0.9, 0.9.
-
Then what we're going to do is reset
-
all of these values.
-
So the anchors are basically where those
-
edges start from, so if I put those all to 0
-
and I say that X and Y should have 0.1
-
it's going to take that as a proportion
-
of the screen that we've got.
-
So you should now see that your screen looks
-
like this, it's got this border
-
around the outside and our text
-
is basically starting up in the upper left there.
-
Okay, so we've made a new
-
MessageCanvas, we've added a Text
-
new game object to it.
-
And on the rect transform the anchors for
-
X and Y have a minimum of 0.1
-
and a maximum of 0.9,
-
just creating this pad around the outside.
-
Obviously it varies depending on width and height
-
because of the aspect ratio
-
being wider than it is tall.
-
I'll reset all of those left, right, top, bottom to 0.
-
So then we'll actually work with the text component.
-
With that Text game object still selected
-
we're going to scroll down to our Text
-
component which is here.
-
So obviously the main characteristic of text
-
is what it actually says,
-
so I'm going to write in TANKS!
-
You can do that or I'm sure you can name the game
-
something hilariously different.
-
So feel free to do that.
-
And then the font that we've included for you guys,
-
which is a free for use font,
-
is one called BowlbyOne-Regular.
-
So next to the Font field just
-
use the circle select to change from the default
-
Arial to that one.
-
And then because this entire
-
block here is that text field
-
we want to set the alignment under the Paragraph section
-
to centre and middle.
-
So that'll place it right in the centre of the screen.
-
Then finally what we're going to do is
-
enable Best Fit.
-
And set the maximum size to 60, 60.
-
And the color to White.
-
So the font is BowlbyOne-Regular.
-
I've enabled Best Fit which will give me
-
a Min and Max size, I set the maximum to 60.
-
And then I set my color to White
-
just by dragging up in this color panel.
-
It should look like this if you've got it right.
-
Okay, so this might be tricky
-
to see so what we're going to do is just add
-
a drop shadow to it, nice and simple.
-
And for that we have a separate component.
-
So if you click the Add Component button
-
and just type in Shadow that will jump to that
-
very simply and you can hit Return.
-
And for the shadow we're going to
-
set the color, so first off we're going to choose
-
a kind of brown color to go with the sand.
-
So that is 114, 71, 40,
-
and the alpha remains the same,
-
it should be 128.
-
Then because that's only just poking out from
-
just behind the tank's actual text
-
I'm going to set the Effect Distance to -3 in X and Y.
-
It should look like that.
-
If you want to get artistic at this point, feel free.
-
And finally, once we've designed that
-
I'm just going to save my scene really quick
-
and then I'm going to disable 2D mode
-
because I don't want to look at this stuff any more
-
I want to go back to actually messing with my levels
-
so I'm going to uncheck 2D mode on the scene view
-
and then I'm going to select my level
-
art and I'm going to frame that
-
and then zoom back in to where I was.
-
So one more time, I was on my MessageCanvas
-
in 2D mode, framed,
-
and I'm just going to reselect my level art,
-
hover, press F to frame,
-
when not in 2D mode and zoom in.
-
We've added out text component
-
and we've set the text to say TANKS!
-
or something otherwise hilarious.
-
We have used circle select to choose our font.
-
That font is BowlbyOne-Regular.
-
It's a free font that we took from Google.
-
Thank you Google.
-
We've set the alignment to centre and middle.
-
So that it sits right in the centre of
-
where we've set it up.
-
We've enabled Best fit.
-
So basically Best Fit will allow that
-
to scale up to the maximum size.
-
So if we end up putting a lot of text in
-
to this text field then it will be between
-
60 and 10, which is the default
-
minimum if the amount of text forces it to
-
be smaller those are the things it'll be between
-
before it starts actually clipping over the
-
edge of the rectangle.
-
Then we set the color to White.
-
And we added a shadow component
-
to the text and we set the Effect color to
-
be brown and (-3, -3) for the Distance.
-
So that's back to the left and down a little.
-
And then we disabled 2D mode to go back
-
to the level itself.
-
Then we need to get back
-
to actually framing our tank and
-
worrying about how the camera will behave
-
when there's more than one tank.
-
So what I'm going to do is select my
-
CameraRig game object.
-
Now what you'll notice with the camera rig
-
is that we now have something missing.
-
So before we dragged out single tank
-
on and dropped it on to the targets array,
-
the thing at the bottom here, this thing.
-
Now it has a size of 1 because
-
we've already populated that field,
-
but this is missing because we've deleted
-
our tank so it can't find it any more.
-
But that's fine because we don't actually want this
-
to be there at all, we want the script to handle it for us.
-
So what I'm going to do is to
-
go to my CameraControl script,
-
I'm going to set my size back to 0,
-
so it doesn't exist any more
-
and then I'm going to double click on my CameraControl script
-
to go back to editing it.
-
So you may remember when we first came across
-
this there was that HideInInspector bit at the top?
-
And now comes the time to
-
uncomment that bit so that this Targets field
-
is no longer seen in the inspector.
-
So we're not deleting that part,
-
we're just removing the / and * either side of it.
-
So it should look like this.
-
So when you save your script and return to the editor
-
you'll notice that the camera control no longer shows that bit.
-
And compiled, there we go.
-
Okay, so your Targets array should disappear
-
it's still there, it's still accessible via the script,
-
it's just not visible for you to drag and drop stuff
-
on to, which is exactly what we needed.
-
Then we are ready to actually create
-
our game manager, so I'm going to save my scene
-
and I'm going to use the hierarchy Create button
-
to create an empty
-
and I'm going to name it GameManager.
-
So we're just using this as
-
a thing that can host our GameManager script,
-
we could really attach this to anything,
-
we could attach it to the level art or
-
something that we know is always going to be in
-
the scene but for our purposes it just makes sense
-
to have a dedicated game object we can
-
select and go back and set everything up on.
-
So our GameManager is going to be an independent object.
-
So in the Scripts Managers folder you will
-
find two files, one of them is the GameManager,
-
one of them is the TankManager, and we'll come on
-
to explain what the tank manager does shortly.
-
For now I'm going to just grab my
-
GameManager and drop it on to my GameManager object.
-
So grab the GameManager script,
-
drop it on to the GameManager object.
-
So let's actually do things the other way round
-
this time, we're going to populate these variables
-
and then we'll look at the script and see how it works
-
because there's a fair bit to it.
-
So we have a number of rounds to win
-
and as we said earlier there are 5 rounds to win.
-
There's a start and end delay to
-
each round which will allow the players to actually
-
read the text that's on the screen.
-
And then there is a reference to our CameraControl,
-
so remember we said that this script would
-
tell the camera where the tanks were and,
-
instantiate them and whatnot. So it needs
-
a reference, so that's the first thing we're going to setup.
-
So the CameraRig has that
-
cameraControl script attached to it,
-
so this is expecting something of type Camera Control.
-
So that component is attached to the camera rigs
-
so if I just drag and drop my CameraRig
-
on to that, then that will assign it.
-
Then, under Message Canvas,
-
I have my text game object.
-
that's my Message Text, I'm going to drag and
-
drop that on to assign it too.
-
And finally my Tank prefab,
-
let's zoom out a little,
-
is in my Prefabs folder, so I'm going to select Prefabs,
-
and I'm going to grab and drop my tank on there.
-
Then you will notice conspicuously
-
there is an array at the bottom called Tanks.
-
Which if you expand has size.
-
So as we know there are 2 tanks in our game.
-
So the size needs to be 2 so there
-
are 2 items in this array
-
Player1 Tank, Player2 Tank.
-
If you expand that once you've typed in
-
to it and hit return you will see there's Element 0 and Element 1.
-
So just a reminder, any array always starts
-
at 0, so that's why you're seeing
-
instead of 1 and 2, it's 0 and 1.
-
So the two things that we actually need here are
-
just the color that we want it to be,
-
so this color will apply to
-
the tank itself and
-
it'll also apply to the name
-
of the tank or the name of player1 and player2
-
in the UI itself.
-
So this color will get used for both of those things.
-
I'm going to setup red and blue for this.
-
So Element 0, I'm going to click on the color block.
-
So that big black square there, click on that
-
to bring up the color picker
-
and then the color I'm going to use there is
-
(40, 100, 178).
-
So shade of blue like that.
-
You'll notice that this conspicuously matches
-
the name tag gizmo that we gave Spawn Point 1.
-
Funny that.
-
So we will drag on SpawnPoint1
-
as the Spawn Point.
-
Then Element 1 will have a different color.
-
And that is a red which is 229,
-
I'm going to recap these in a moment.
-
(229, 46, 40).
-
(229, 46, 40) R, G and B.
-
And you guessed it, SpawnPoint2 is
-
the spawn point to drop on.
-
We've expanded our Tanks array so the
-
GameManager will create these tanks and color them
-
and also color the UI for us.
-
We've assigned all of our references,
-
like the text and the camera rig.
-
And then the colors we've used are
-
blue of (42, 100, 178).
-
That's for SpawnPoint1.
-
And then color of red (229, 46, 40).
-
for SpawnPoint2,
-
which should also be dragged on.
-
Okay, so let's talk about our GameManager.
-
The GameManager is in charge
-
of the game, weirdly enough,
-
the clue is in the name,
-
but the way that that works is pretty much what you're seeing here,
-
so it's in charge of initialising the game,
-
it needs to spawn as many tanks
-
as we've told it to, in this case 2.
-
And it needs to setup the camera targets,
-
so it needs to say 'hey camera control, these are
-
the tanks that I've spawned and you need to
-
focus in on them when the game starts'.
-
Then it needs to run the states of the game.
-
So we're going to look at the code in
-
the GameManager shortly and you'll see
-
that it's basically a simple sort of state machine that's running
-
round starting what happens during
-
round playing and what needs to be decided
-
when the round ends.
-
And all these things link in to the Tank Manager,
-
which is a separate script.
-
So the Tank Manager handles
-
shooting and movement for the tanks,
-
and also visual elements, I.E. the UI.
-
So what you need to understand is
-
that each tank gets assigned
-
it's own tank manager, and that
-
tank manager is then in charge of
-
turning off input, control, and shooting.
-
So every time that tank gets spawned in the world
-
it doesn't know what it's got to do but it has
-
it's tank manager there to sort it out.
-
So obviously this game is extendable
-
so we've put this kind of third one there to
-
kind of mean 'and so on'
-
as we populate that array with more tanks
-
it can do more things.
-
So shall we have a quick look at
-
the TankManager script?
-
Because that is the first one
-
that we need to understand.
-
Double click on the icons to open both those scripts.
-
And make sure you're looking at the TankManager.
-
So the TankManager is already complete
-
and we'll go through that briefly now
-
so you can understand what it's doing,
-
what it's role is and how it interacts
-
with the GameManager.
-
Most of the scripts that we're dealing with
-
today and in fact all of them other than
-
this one are mono behaviours.
-
So after it says public class and then the
-
name of the class you've got a colon and then MonoBehaviour.
-
CameraControl : MonoBehaviour.
-
So what that means is that
-
this script you can drop on to a game object
-
and it will have functions
-
that are called back such as Awake, Start,
-
Update, Fixed Update, those sorts of things.
-
That's all part of mono behaviour.
-
But this one does not have that.
-
It's not inheriting from anything before that.
-
So everything that you see in TankManager
-
it it's own thing.
-
We've also go this attribute at the top,
-
Serializable, so what that's doing is
-
saying to Unity
-
'when you have an instance of this
-
you can show it in the inspector'.
-
Most of the time you don't need that because
-
by default fields are serializable.
-
But when you've got a class that you make yourself
-
you need to mark it as serializable
-
so that it'll show up in the inspector.
-
Okay, so let's have a look at the public variables.
-
So we already know the first two,
-
we've got the color, which is the color
-
the player is going to be when it's spawned,
-
the tank and it's name.
-
And we've got a transform representing the
-
spawn point so that's obviously where
-
and in what direction it's going to be spawned.
-
So very crucially, what you're actually seeing
-
here is the result of what you just
-
filled in in the inspector.
-
So in the inspector we have this thing called Tanks
-
so each of these elements is
-
effectively those two things,
-
these are individual tank managers and those are the
-
only things that are down in the inspector.
-
That's because the Tanks array in the Game Manager
-
is an array of tank managers.
-
So it's showing those
-
two because we've told it to but the rest
-
we're saying 'hide those from the inspector'.
-
so if I jump over to Game Manager really quickly.
-
so there's an array of TankManagers
-
so these classes called Tanks,
-
so that's why you're seeing that in the inspector.
-
Okay, so the rest of these public
-
fields we've got HideInInspector to stop it from
-
showing up because we don't want people to
-
be able to adjust those.
-
So first is the PlayerNumber, so that's going to filter
-
through to the shooting Script and the Movement script
-
to tell each of those scripts
-
what input it needs, because you'll remember
-
they're based on the PlayerNumber.
-
Next we've got m_ColoredPlayerText.
-
So you'll notice that when we
-
showed that little video the text
-
that came up was in the color of the player.
-
So the way you do that is you use
-
HTML-like rich text
-
and we'll show you how to do that in a moment,
-
but that's just a variable to store that.
-
Next we've got a game object which is
-
storing the instance of the tank.
-
So if we need to
-
turn anything on or off we need to
-
get that referenced through the instance.
-
And the number of wins that the
-
tank currently has, so when it
-
gets enough wins it'll win the game.
-
Next we've got a few private references
-
so we've got references to the TankMovement and TankShooting scripts
-
so we can turn those on and off when we need to.
-
And a reference to the Canvas game object
-
so that we can turn the UI on and off.
-
Next we've got a public function called Setup.
-
So this is public because it's going to be called by
-
the GameManager when it first creates these tanks.
-
Seeing as the tank has been created
-
it's going to find the references to
-
the Movement and Shooting scripts
-
by saying that the Instance, get component from the instance.
-
So set the instance, get the components there.
-
Next, a little bit more obtuse,
-
we've got the Canvas game object
-
so what it's going is it's got the instance,
-
it's finding a component of type
-
Canvas in it's children,
-
because you'll remember all of the UI is
-
underneath a canvas, so we're finding that
-
canvas in the children,
-
and then we're getting the game object that that's on.
-
That's what that bit is doing there.
-
Next we're setting the PlayerNumber on the
-
Movement and Shooting scripts.
-
And then there's the HTML-like rich text.
-
So this looks a little bit scary and confusing,
-
but basically what we're doing is taking bits
-
of the text and then putting stuff in between them.
-
So for example,
-
if you saw in the video earlier we showed,
-
at the end it says
-
'Player 1 - X amount of wins'
-
'Player 2 - X number of wins'.
-
So what we're doing with this is we're just
-
adding bits to a string.
-
So this is a string here, in inverted commas,
-
and then we add to it something that's
-
converting a piece of code
-
to a particular color by using
-
this thing called ColorUtility
-
ToHtmlStringRGB, very long winded thing but
-
it basically takes in a color,
-
and will then color whatever
-
the text is after that, and the thing it's coloring is Player.
-
So if you've seen HTML before you'll know that
-
HTML tags fit inside angled brackets.
-
So it starts here, it then says
-
the color that I should color it is
-
this thing that takes in the PlayerColor,
-
so that will result in a color reference.
-
Now we finish that tag
-
and the piece of text its actually coloring
-
is the word Player.
-
Then we put in a space.
-
Then we put in the PlayerNumber
-
And then we finish coloring.
-
So all of that stuff is saying
-
'Player 1 in particular color
-
by using this rich text'.
-
It looks a bit lengthy but once you piece
-
through it it makes sense.
-
And like I said, have a look at the completed scripts,
-
it will have all the comments in there to tell you
-
exactly what we're doing.
-
So the next line is getting an array of mesh renderers.
-
So mesh renderers are the things
-
that actually show up your
-
3D objects in the scene.
-
So the tank is made up of
-
a series of mesh renderers, like one for the
-
tracks, one for the chassis, one for the turret, etcetera.
-
I'm just going to show that real quick.
-
So I've just dragged in my prefab
-
just to demonstrate but you'll see there's this mesh renderer
-
component and if I toggle things on and off
-
they disappear because they're not being rendered any more.
-
That's just what a mesh renderer is doing.
-
Okay we're making an array of mesh renderers
-
called Renderers and we're setting that to
-
all of the components in Children
-
that are mesh renderers.
-
So the instance, get the components in the children
-
that are mesh renderers and return them.
-
And then what we do is we loop through all of
-
those renderers and set their material's color
-
to the player's color.
-
So all that's doing is it's finding all the mesh renderers,
-
getting the color and changing it to the player color
-
that we chose.
-
Then we've got just a few more public functions
-
that are going to be called by the Game Manager.
-
We've got DisableControl which
-
turns off the script and
-
turns off the canvas and we've got
-
EnableControl that turns on the script
-
and turns on the canvas.
-
Finally we've got Reset.
-
So what Reset does is
-
it sets the
-
instance back to it's spawn point.
-
It turns it off and then
-
it turns it back on again and the reason it
-
does that is that all of
-
the tank's apart from the winner
-
will be off, but we need to
-
reset all of them so we need to turn
-
everything off first before
-
we can turn it back on again.
-
Okay, so that's all there is to TankManager.
-
Let's see about the GameManager.
-
So just to jump back to this slide real quick.
-
As we've said the Game Manager is
-
using an array of TankManagers,
-
so when we look at the GameManager in a second
-
we're going to see that at the start
-
it's going to use the TankManagers to spawn those.
-
So those were the instances that you just saw
-
referenced inside TankManager.
-
And then we're going to get in to actually
-
looping through things.
-
So let's take a look at that.
-
So if you switch over to the GameManager script you should
-
see the stuff that we're looking at right now.
-
Either that or you can look at the screen.
-
So there are three blocks of comments in this one.
-
There's one at lines 19 and 21.
-
I'm just going to remove those.
-
There is one at line 66 to line 74.
-
And then there's a really long one
-
which is line 108
-
all the way down to 152.
-
Cool, so apologies, we had to comment those
-
out otherwise you would have seen some warnings
-
and queries in the console that
-
kind of make it awkward for us to teach you stuff,
-
so we just commented those out.
-
Okay, so we've already gone over the
-
public variables, I won't bore you by doing it again.
-
The private variables,
-
so we've got an integer which stores the current round number.
-
So obviously as it counts up then you get
-
to display what the current round number
-
is when it starts.
-
Then we've got these two WaitForSeconds things.
-
So in coroutines you can put
-
a delay in your function course.
-
What is a coroutine James?
-
I think we'll cover that shortly.
-
Good idea.
-
But basically these WaitForSeconds things
-
are what a coroutine is looking for a
-
delay so we've got a start delay
-
and an end delay.
-
We'll change those in to these WaitForSeconds
-
classes so we can use them.
-
So basically we have a little pause within a function,
-
but they need to be converted to a type that is called
-
WaitForSeconds and you'll see why shortly when
-
we explain coroutines.
-
Okay then the last two things we've got,
-
two instances of TankManagers,
-
so they're referring to specific tanks
-
that are the RoundWinner and the GameWinner.
-
And we'll use those for the
-
message at the end of each round.
-
So next we've got this start function,
-
which is setting up those WaitForSeconds.
-
So we've got a StartDelay and an EndDelay,
-
which are just numbers in seconds
-
and then we're creating new WaitForSeconds
-
for the start and the end.
-
Then we've got SpawnAllTanks function
-
and SetCameraTargets function.
-
We'll cover those now and then come back
-
that StartCoroutine thing at the end.
-
So SpawnAllTanks.
-
All that's doing is it's going to loop through
-
the TankManagers.
-
For each TankManager it's going to
-
set the instance belonging to that TankManager
-
to an instantiated prefab,
-
so we're calling Instantiate(m_TankPrefab,
-
and then we're spawning it at the spawn point of the tanks.
-
With the same rotation.
-
So this may look confusing but it's basically a long
-
line that we've moved on to a new
-
line because it doesn't make any difference and it's easier
-
to fit on a projector.
-
So we're just instantiating
-
a tank per instance as required.
-
Okay, so now we've created the tank
-
we want to set it's player number,
-
and since this loop is going from 0
-
upwards that doesn't really
-
work as nicely as starting from
-
1 so we're just saying that the PlayerNumber is i + 1,
-
so it starts at 0 so
-
the PlayerNumber would start at 1,
-
and so on.
-
Otherwise when you start the game you'd get
-
'Player 01,
-
Player 1 failed', you don't want that.
-
so you need to just add 1
-
in a minimum of Player1, Player2 and so on.
-
And then finally for each tank we're
-
going to call that Setup function which is the first
-
function that we covered in TankManagers.
-
Just a quick reminder,
-
TankManagers - Setup.
-
So it's the thing that's in charge of
-
movement, shooting and coloring and whatnot.
-
SetCameraTargets, we're creating an array of
-
transforms called Targets
-
and we're setting it to be the same length as
-
the TankManagers array.
-
Then we're going to loop through this Targets array
-
and we're going to set
-
each target equal to
-
the TankManager's instances transform.
-
So each tank's instance
-
that transform.
-
And then finally we can set the
-
CameraControl's targets to
-
this Targets array that we just created.
-
so we're just looking at the TankManager
-
and saying 'here's all the positions
-
of the tanks you've just created,
-
and we'll just assign them to the CameraControl script'.
-
Okay, so the last thing that
-
the Start function did was
-
it did something called Start Coroutine
-
and then it had a function within
-
it's parameters, which is a bit weird.
-
So let's find out what that's all about.
-
So just to jump back to the slides,
-
so we've just been talking about Start
-
and what actually happens there, and we say that
-
it starts the GameLoop to continue.
-
So it's starting the GameLoop coroutines.
-
But we haven't talked about these yet, we're talked about functions
-
and how you can use those.
-
We need to talk about coroutines.
-
So the GameLoop
-
is going to start the round.
-
Obviously you're going to start the round, people are going
-
to start playing, driving around.
-
It's then going to wait.
-
And then they're going to be playing, so this is when
-
they're actually firing, shooting each other, running around,.
-
Then it's going to wait.
-
And then the round is going to end.
-
Ordinarily in functions, when you run a function
-
all the commands just go straight through
-
and something happens or you'll loop through
-
something but it's instantaneous.
-
But often in programming we need to
-
to kind of pause and do what we call Yield
-
and wait for a certain number of seconds or
-
wait for a certain condition to be valid.
-
And that's where coroutines come in.
-
So an ordinary function might look like
-
this - void, a non-return type and then
-
the name of the function ()
-
and then some commands within that.
-
Whereas a coroutine
-
has, first off you'll notice a different
-
return type, instead of void
-
we have this thing called IEnumerator.
-
As if the word coroutine wasn't weird enough
-
you now have something else to worry about,
-
IEnumberator, but don't be afraid of that,
-
it's just the return type.
-
And you'll also notice within this we have the
-
word yield in the middle of it.
-
So
-
what we can do is start some basic
-
commands or anything that we want to happen
-
straight away like any old function
-
but then we can stop at this point called yield.
-
At the point where you come in to contact
-
with yield what happens is
-
execution exits that function.
-
It goes away.
-
Then it waits for a certain period of time
-
or some condition to be true.
-
And then after that period of time
-
or that condition it comes back
-
in again at the same point that it yielded.
-
And then it will continue on with the coroutine.
-
So instead of just going straight through
-
and doing everything instantly
-
you put in a little pause in the middle of your
-
function, which could be very useful.
-
And it becomes really useful when you
-
start putting things like While Loops in
-
So what you can do
-
is have a while loop like this
-
with a yield instruction in the middle.
-
So if you haven't heard of while loops,
-
it's kind of like if you think of an if statement of
-
checking a condition a while loop is just there
-
to say 'whilst this thing is still true,
-
then we're going to do what's in those brackets'.
-
Okay, so in this coroutine
-
the first part of the function would start normally,
-
then it would hit the while loop,
-
so let's suppose that the condition is true.
-
It goes in to the while while loop and hits that yield.
-
Then it leaves again,
-
waits for some period of time,
-
and then comes back in to the yield
-
and continues with that while loop.
-
Whilst within a loop we've exited
-
and then come back in again after a period of time.
-
So supposing that that condition is still true,
-
we'll then go back in to the same while loop.
-
So let's say for example,
-
this might come up, we say that
-
the condition for that while loop is while there is
-
more than 1 tank left
-
keep doing this loop.
-
And then let's say that the yield was
-
come back next frame.
-
So then what we'd have is a function that wouldn't
-
finish until there was
-
only 1 tank left.
-
So you'd say while there
-
is not 1 tank left
-
do this, come back next frame.
-
Oh there's still not 1 tank left, come back next frame.
-
Oh there's still not 1 tank left, come back next frame.
-
Until there was, and then the function would finish.
-
Could be quite useful.
-
You remember the start function calls
-
StartCoroutine?
-
The coroutine that it's starting is the GameLoop.
-
So let's have a quick look at the GameLoop.
-
To see what's going on there.
-
GameLoop is saying
-
yield return StartCoroutine(RoundStarting());
-
So do you remember when it says yield it
-
waits for whatever it's got to the right
-
of it to finish before it continues on.
-
So when it's saying yield return StartCoroutine
-
it's waiting for that coroutine to finish
-
before it goes on to the next one.
-
So what's going to happen here is it's going to do
-
RoundStarting, wait for RoundStarting to finish,
-
then come back in, then it's going to do RoundPlaying,
-
wait for RoundPlaying to finish, then it's going to come back
-
etcetera for RoundEnding.
-
Finally we're going to check if there is a GameWinner
-
so if GameWinner is not equal to null.
-
So if there is a GameWinner then load
-
the current level, Application.LoadLevel
-
loadedLevel, that's just reloading this current level.
-
If there isn't a GameWinner then it's going to call StartCoroutine
-
and notice that there is no
-
yield return on that bit.
-
So it's not going to wait and then come back
-
in we're just going to call that then the
-
function will finish and there will be another GameLoop running.
-
So now we're getting to the point where we're looking
-
at how the GameManager and TankManagers interplay.
-
So this slide is going to kind of populate over time
-
but the way that we're going to do this is we're going to
-
animate in each point, we're going to talk about it and then
-
we're going to fill in that part of the script, so hopefully
-
we can kind of step you guys through it and it
-
will make sense.
-
So we've just talked about the GameLoop.
-
So the GameLoop starts off with
-
the RoundStarting.
-
So the first thing that needs to happen
-
in the RoundStarting is we need to reset
-
all the tanks, so we need to
-
set them up at their positions on the spawn points,
-
we need to enable their controls
-
and all these kind of things.
-
So the way that we do that is to parse to the
-
TankManager and 'hey, can you reset everything
-
and reposition everything?' and that's what the
-
TankManager does, so every tank remember has a
-
TankManager assigned to it and we do that.
-
We start off our GameLoop, we reset the tanks,
-
and the TankManager's in charge of resetting
-
things so De/Reactivating
-
and setting the positions back where they should be.
-
And we start off by disabling
-
all the tank controls.
-
And we have in the TankManager DisableControl()
-
so we can't move,
-
shoot and the UI is off.
-
When the game starts you'll see Round 1
-
and the tanks are there but they don't have
-
their Health UI just yet.
-
Then we do three more things
-
to start the round, we need to setup the camera position
-
and the size, so in other words
-
we take an average of the two tank's positions
-
and we setup the zoom using the size.
-
We increment the round number so it will,
-
if it's 0 we make it Round 1.
-
And then we setup the Message UI which will
-
say either Round 1 or whatever is appropriate.
-
So let's go and actually look at
-
that in the script now.
-
So currently RoundStarting just
-
has this yield return m_StartWait
-
so all it's going to do is wait for
-
3 seconds and then do nothing else.
-
So what we want to do is put in a
-
little bit of code before that.
-
First off we said we wanted to reset all of the tanks.
-
So before the yield return
-
put a couple of lines so you've got some space and
-
we've got a function called ResetAllTanks.
-
So put a call to that.
-
Next we'll put DisableTankControl
-
because we don't want people to be able to
-
control their tanks while the round is actually starting.
-
We want to wait for the RoundPlaying for them to be
-
able to control everything.
-
Then you remember we wanted to make sure that
-
the camera is set to that
-
exact size and position.
-
We don't want it to smoothly transition
-
to that, we want to set it.
-
So on the camera control,
-
so m_CameraControl.SetStartPositionAndSize()
-
So all that's doing is on the camera
-
control calling the function.
-
Next we want to increment the round number
-
because a new round has just started.
-
So that's m_RoundNumber++;
-
All that does is just add 1 to that round number.
-
And now we've got the incremented round number
-
we can set the message text
-
to be something appropriate like
-
Round and then the round number.
-
So m_MessageText.text because it's the text part of the text
-
component, well named isn't it?
-
And we're going to set that to Round + m_RoundNumber
-
Now it's very crucial here that you put
-
Round and then a space inside your string,
-
otherwise it will say, it will just say
-
Round1 like that on the screen, which you don't want.
-
So make sure you put a space in the string
-
and then we're adding on the actual number
-
on to the end of that, and it will
-
parse it straight in to the UI text.
-
Okay, so let's save the script
-
there and give that a test, see how that all works.
-
Okay, so I'm going to save my script, switch back to Unity.
-
And I'm going to press Play.
-
Okay, so this isn't working as well as I thought it might.
-
Interestingly it's just counting up the rounds.
-
Why would that be James?
-
Probably we need to actually
-
enable the control in, like,
-
the RoundPlaying bit, that'll probably do it.
-
Okay, so what we've done so far is
-
to say 'we want to start the round', and all that
-
RoundStart knows how to do is
-
to setup the UI, setup the tanks and then just be like
-
'okay, well I'm going to wait,
-
oh hey, there's a new round, I'll wait now,
-
oh hey, there's a new round'.
-
It doesn't actually let us play the game, because we didn't do that
-
bit yes, so I'm going to stop play
-
and jump back in to my code.
-
Okay so RoundPlaying, we've got this
-
yield return null there
-
so what yield return null means
-
is 'come back the next frame',
-
that's all it's doing there.
-
So just to jump back to the slide.
-
As we've said before, we've done RoundStarting
-
and those are the things that happen
-
TankManager is taking care of the
-
Resetting and DisablingControl so that's all good.
-
But RoundPlaying, so what do we do in RoundPlaying?
-
Well we need to enable the TankControl when we
-
played just now we noticed you couldn't drive or shoot.
-
So EnableControl is in TankManager.
-
It can move, it can shoot and it sets up the UI.
-
And the thing it needs to do is empty that message.
-
So as soon as we start playing we don't want to see
-
the word Round 1 on the screen, we want to scrub that out.
-
So we don't disable the UI or do anything
-
else we just empty the message string.
-
And then as James was saying earlier with the coroutine
-
we just keep waiting until there's one tank left.
-
Let's see how we write that.
-
So at the start as it said on the slide
-
we want to enable TankControl
-
so you can actually play the game.
-
So we've got a function that does that
-
and all this function does is loops
-
through all the tanks and calls their EnableControl.
-
So nothing really to it.
-
Next we want to empty that
-
message text, so m_MessageText.text
-
= string.Empty so that's just a blank string.
-
Naturally you could also just
-
do that if you wanted to,
-
but we're not doing it that way, we're going to go with
-
the string.Empty.
-
Okay, so it's looking good so far
-
but now all it's going to do
-
is it's going to enable the TankControl,
-
blank the string and then wait
-
at one frame and then
-
go on to the round ending.
-
So we don't want that, what we want is to
-
use that while loop that we saw earlier.
-
So for a while loop it's
-
while( and then the condition
-
within the ).
-
So we've got a function that's
-
OneTankLeft which returns true if the
-
there is one tank left, or less.
-
Yeah, there is a condition where it can be a draw,
-
you'll see.
-
Yeah, so this will keep
-
doing whatever is within the brackets
-
as long as there is
-
not one tank left.
-
So the thing that needs to go in there is
-
our yield return null.
-
What I should be doing with my while loop
-
is placing it around yield return null.
-
Like that.
-
Adding lots of lines for no reason.
-
There we go.
-
So until that's true it's just going to keep
-
going away one frame and waiting for it to become true.
-
Okay, we'll save that,
-
switch back to Unity,
-
and I'm going to hit Play and see what happens.
-
Okay, I've just muted my audio for the sake of everyone
-
So I knew it, I can drive around.
-
That's pretty cool,
-
I can shoot stuff.
-
Okay, so this is maybe a point to, you know,
-
hook up with a neighbour and start
-
shooting at each other, but,
-
what you'll also notice is that
-
that's great, now I can move around,
-
but I've got no idea who won that round or what happened.
-
I'll just keep infinitely adding new rounds and
-
keep playing in an endless war,
-
which I think we all know is wrong.
-
So we need to have a RoundEnding,
-
we need something to happen, we need some logic
-
to say 'what's going to happen when one tank
-
kills the other tank?'
-
So back to our slides.
-
RoundEnding is our next coroutine
-
within the overall loop.
-
So the first thing we need to do is
-
really disable the controls.
-
It's kind of great to be driving around and
-
showboating once you've won the game, but,
-
yeah, it's a little bit jazzy for my tastes.
-
So really we're going to disable the Tank Controls
-
you're going to stop dead, the camera is going to
-
focus in on where you are, it kind of puts a
-
nice pause in the game as well.
-
And then we're going to clear the existing
-
RoundWinner, so if we've already had a
-
round played we want to clear out that winner
-
and decide who's just won this round.
-
We want to check to see if any of
-
the round winner's have 5
-
rounds won and therefore are them game winner.
-
And then we want to put that in to the
-
Message UI and say 'okay, well first off
-
this person just won the round,
-
this tank has this many kills, this tank has
-
this many kills'.
-
Or we're going to say 'hey, this person
-
won the entire game'.
-
So let's have a look at how that works.
-
So back in our code the next coroutine,
-
remember IEnumerator means coroutine.
-
And we've currently got a yield that's got the EndWait.
-
So before we wait
-
we need to do a bunch of stuff.
-
Like we said, we're going to disable TankControl,
-
so we'll call that DisableTankControl function.
-
Next we said we need to clear out our RoundWinner
-
before we can check for another one.
-
So what we're going to do is
-
set m_RoundWinner to equal null.
-
So that's just saying
-
'for this round we don't know who's won yet,
-
we'll need to check'.
-
So the RoundWinners remember are
-
TankManagers, so it's referring to a
-
particular tank, same for the GameWinner.
-
It's referring to a particular tank via
-
it's TankManager.
-
So once we're not sure if there's a RoundWinner
-
we then can check, so we say that
-
m_RoundWinner = GetRoundWinner.
-
So what that's going to do is
-
it will assume that there is one
-
or fewer tanks left,
-
and then go through all of them until it finds
-
one that's active and return it.
-
Shall we have a look at that function?
-
Yeah we can have a quick look at that.
-
So if you scroll down, it's around
-
130 for me but it might be a bit different for you.
-
But what you'll see is GetRoundWinner.
-
And it's going to go through all of the tanks.
-
So it's looking for the array, looking for everyone
-
in the array, IE the length of the array.
-
And then if it finds a TankManager
-
who's instance if active
-
So it's activeSelf is true.
-
And it's going to return that tanks.
-
If it gets all the way through the array
-
and hasn't found anything,
-
then it's going to return null, so if it's not found
-
anything that's active it's going to return null,
-
that means there's going to be a draw.
-
And that happens if both tanks manage
-
to blow themselves up at the same time.
-
It is possible, I've seen it, maybe once.
-
Okay, so now after we've found our RoundWinner
-
we're going to check whether it is null or not.
-
So if that RoundWinner is not equal to null.
-
So ! for not equal to.
-
Then what we can do is add
-
to that round winner's number of wins.
-
So m_RoundWinner.m_Wins
-
or m_Wins
-
++ just to iterate it once.
-
So the RoundWinner remember links to the TankManager
-
for that tank so it says 'hey, this tank,
-
the number of wins that tank's got, lets add 1 to it'.
-
Okay next, after we've added 1
-
to somebodies number of rounds that they've won
-
they might have won the entire game.
-
So now we can check if there is a GameWinner.
-
So m_GameWinner = GetGameWinner.
-
Yeah, let's just take a look at GetGameWinner.
-
So yet again it's very similar to GetRoundWinner,
-
instead of checking if it's found something that's active
-
we're just saying 'hey, has this particular
-
tank in the list of tanks in the array,
-
is the number of wins equal to the
-
number of rounds required to win the entire game?
-
If so, return that particular tank
-
that's got the right number of wins.
-
If not return null'.
-
Once we've found a GameWinner we want
-
to get a message based on whether there is
-
no RoundWinner, no GameWinner,
-
whether there is a GameWinner, etcetera.
-
So we've got a function for doing that and
-
what we're doing is creating a string called message
-
and setting it equal to the EndMessageReturn.
-
And then once we've got that message
-
setting the MessageText's text to that message.
-
So if we wanted to do anything else with the message in-between
-
then we could do that here.
-
We're not actually going to do anything so we're just
-
setting the text straight away.
-
So we're calling this EndMessage function.
-
So EndMessage is how we actually
-
calculating what to do at this point so,
-
or what to write on the screen,
-
so I'm going to scroll down and look at this
-
very confusing looking set of strings and text.
-
So like before we have this
-
way of coloring text
-
and we had that function further up which
-
uses rich text.
-
So we've got a bunch of different things
-
that it could do, so by default what we
-
do is we say 'okay well let's just have
-
a default condition', and that default condition
-
is that there's a draw.
-
Because when there's a draw we don't really need to increment
-
anything at all so we don't worry about it at all.
-
So we're just going to put in DRAW! as our
-
default text for EndMessage.
-
Then we decide if something different that a draw has happened.
-
So we check 'hey, is RoundWinner not null?'
-
I.E. is there a RoundWinner?
-
Then we're going to send something to the message.
-
So the message this time will be
-
RoundWinner with colored text saying
-
'hey, Player 1 WINS THE ROUND!'.
-
So then we have something added on to the message
-
so remember message is our overall
-
piece of text that we're going to put in to that text field.
-
So we just keep adding stuff to it in order
-
to create a paragraph of different
-
things within the overall message.
-
The message as a string then gets added
-
all of these, so these \n
-
just means go to a new line.
-
So we're just spacing out this text, it's having
-
'this person won the round, move down a couple of lines,
-
then start writing something else'.
-
Then once we've moved down four new lines
-
we have a for loop.
-
So with that for loop we're going through
-
all of the tanks that are available
-
and within those we're adding to the
-
message some information.
-
So quite simply what we're doing here is adding on
-
that particular tank colored with PlayerText
-
then a colon
-
and a space, so it might be
-
Player 1:
-
and then the number of wins that are found for that tank.
-
So it looks at the TankManager and it says 'how many wins
-
have you got so far?' Print that as a number,
-
then put in a space and the word WINS.
-
Then put in a new line and do it again
-
for the next tank in the loop.
-
So you will have Player 1: this many wins,
-
Player 2, and so on.
-
Then what we're doing after that is saying
-
'hey, is there a GameWinner?'
-
Because if there's a GameWinner we don't want
-
it to do any of the stuff above, we don't want to actually
-
write all of this stuff in, so instead
-
of saying 'hey message +='
-
we're just clearing it out by saying =,
-
we're saying the message is going to be
-
exactly this thing and in that instance,
-
we check the GameWinner,
-
color it with the right text and we say
-
'space WINS THE GAME'
-
so it might be Player 1, it might be Player 2,
-
WINS THE GAME, that's the only message you'll
-
see on the screen, you won't see the number of
-
other wins that were won or anything like that,
-
we clear it out, put it in.
-
That's whoever won the game.
-
Then we return that message
-
so whenever we call EndMessage
-
we return that piece of text depending on
-
what's just happened in the round.
-
So when we're using EndMessage
-
back up in RoundEnding
-
that's what we're doing.
-
And finally the piece of text after it
-
gets assigned to the component's text field,
-
message, there
-
And I'm going to just jump back on to my slide
-
really quick, what we're going to do now
-
is save the script,
-
but we need you to pair up with someone
-
and fight because it's a tank game and that's how it works.
-
So remember you've got WASD,
-
for one player, and spacebar to fire.
-
Then you've got arrow keys and return for the other player.
-
So save your scene, grab a neighbour and
-
give it a test.
-
Okay, so we're going to do the same.