Tip:
Highlight text to annotate it
X
Hello and welcome to the second tutorial in this series about making html5 games with
WADE. We're going to continue from where we left off with part 1.
We have this very simple game, where you score one point if you hit this mole that keeps
coming out of its hole, and you lose a point if you miss it. Now we're going to make it
a bit more interesting. We want to have lots of these moles on the
screen at the same time. To do this most people who are familiar with object-oriented programming
would immediately think to use a class, to store all the properties and behaviour of
the mole. Then you'd create more of these instances, and you're done.
Javascript, however, is not necessarily an object-oriented language, and you may have
better ways of doing the same thing. We have a simple system in WADE of doing something
that is similar to a class, but a bit less object-oriented: it's called a Behavior.
A scene object in WADE can be assigned one or more Behaviors. Behaviors determine how
an object "behaves": how it responds, and so on. The fact that we can assign one or
more behaviors to an object, means that we can do some really interesting things:
To give you a simple example, let's say that you have a spaceship that moves around and
shoots. You could assign it a "shooting" behavior and a "moving" behavior. Then when you want
to create another spaceship that moves around without shooting, you can reuse your "moving"
behavior for it, without using the "shooting" behavior. But for this mole game, we're just
going use one behavior per object, to keep it simple.
I am going to create a different file here (right-click awesome game, new, javascript
file) and I call it mole.js. Now in this blank file I define a Mole function,
like this: Mole = function()
And in this function I'm going to define the behavior of the moles. So let's go to our
main app script, awesome.js. First of all, we want to load this mole.js file in the loading
stage of our game, so in the load function. wade.loadScript('mole.js')
And now here, where we create the scene object for the mole, we tell it to use the Mole behavior,
like this.
So what happens now, is that whenever an event is triggered for our mole scene object...
say for example onMoveComplete. This is triggered for the object, but also for all of its behaviours.
This means that we can move this onMoveComplete function in our behavior file. So let's do
it (cut and paste) Of course now we want onMoveComplete to be
a member of the Mole function, so we have to change 'mole' to 'this'.
But now, inside the function, we're still referencing a variable called 'mole' (with
a lowercase m). This variable doesn't exist here, and in fact if you're using WebStorm
it's going to be underlined... and if you put your mouse over it, it's going to tell
you: "Unresolved variable". In fact, this mole variable really represents
the sceneObject that is connected to this Mole behaviour. We call it the "owner" of
the behaviour. So we can solve the problem here by replacing all occurrences of "mole"
with "this.owner" Let's quickly go to Chrome to make sure that
it's still working.... And yes, it is. Good. So, this code still isn't very tidy. For example,
we are defining the state of the mole in awesome.js, then using it in mole.js. That is because
we're saying that the "state" of the mole is a property of the mole scene object, but
in reality it would make more sense to say that it's a property of the Mole behavior.
So let's define it in the other file instead (cut and paste again).
By defining it as "this.state" in our behavior function, we are saying that now "state" is
a property of our behavior function. And so we need to reference it as "this.state" down
here too. The goal here is to move everything that deals
with our mole object, from the main App file into the behavior file. We do this to encapsulate
the code for the object, so the main App file become much cleaner, and it's easier to create
several instances of our object. And what about the onMouseDown function? Let's
do the same with the onMouseDown function.... Cut and paste...
And now you see that we have a bit of a problem here: we're using these "score" and "scoreText"
variables that were declared as local variables in our main App function. So how can we access
them from here? The first step is to make those variables
members of the App function (rather than local variables). Back to awesome.js, we replace
"score" with "this.score And we replace "scoreText" with "this.scoreText"
So, in mole.js, can we just use "this.score" and "this.scoreText"? No, of course we can't
because of the meaning of the keyword "this" in javascript. "This" indicates the current
execution context -- or to put it more simply, with WADE, it indicates the "parent" of the
function that's being executed . So if score and scoreText were members of
the "Mole" function, we could use "this.score" and "this.scoreText" here. But they aren't,
they're members of the main App function. So how do we access them from here?
In WADE there's a simple way of doing this, which is through "wade.app". wade.app indicates
the main app function of WADE. So there we go....
But wait a minute, if we go back to awesome.js, we are going to have the same problem here:
in the hole's onMouseDown function, we cannot use "this.score"! Because when this function
is executed, "this" is the hole object. So again, we replace "this" with "wade.app",
and problem solved.... Now there's one more thing that I don't like:
the fact that we're having to listen for the 'onMouseDown' event of the mole object here
in the main app file. Again, this really should be part of our Mole behaviour. So let's cut
this from here.... And where do we paste it? Well, the most appropriate
place would be in a function called onAddToScene. This function is called automatically by WADE
whenever the scene object is added to the scene.
And again, it isn't "mole", but "this.owner". And you know what, even this line here that
gets it moving at the beginning, should probably be in the onAddToScene function of our behavior...
As you can see, here we've moved almost all of the code that deals with our mole object
into its behavior in a separate file. But not quite... there are still a couple of things
missing, because it takes 4 lines of code to create a mole. First of all, let me tell
you about this trick you can do, to set the initial position of an object: rather than
calling setPosition, you can pass the coordinates directly into the constructor of the sceneObject,
like this.... And now this line that deals with the creation
of the sprite, that is going to be used by our mole object. As you can see we're passing
the sprite as a parameter when we create the sceneObject. But we don't have to do it necessarily
this way. We could also pass no sprites here, by passing 0 for example, and then doing:
mole.addSprite(sprite). What's the difference? Well, if we do it like
this, we can then move this sprite stuff into our behavior function.
In the onAddToScene function to be precise. this.owner.addSprite()
Now we're left with 2 lines of cold... cold? Now we're left with 2 lines of code to deal
with the creation of this mole. In reality, we don't need this local variable "mole",
as it isn't used anywhere else. So we can just do this....
And there we go, we can create a mole with a single line of code from our app file. All
the logic that deals with what the mole does and what it looks like is in its behaviour
file. Let's quickly refresh Chrome to see if we've
broken anything... ... no, it's all working. Good.
Now we should do exactly the same thing with our hole object.
Right-click here on awesome game, New.... Javascript file.... And we call it hole.js
this time. We create a function called Hole, capital
H.... Then an onAddToScene function....
In the load function of awesome.js we load the new script:
wade. loadScript('hole.js') Then we cut and paste these 2 lines that deal
with the sprite creation into the new file, in the onAddToSceneFunction we just created.
And here we do "this.owner.addSprite" for both these sprites....
Then you see here in awesome.js where we're setting the offsets of the sprites? Well,
we can pass the offsets directly into the addSprite function, to save some unnecessary
typing. And we get rid of those 2 lines.
The onMouseDown function goes straight into the other file, except that now it's this.onMouseDown.
And the line that we use to listen for the onMouseDown event goes into the onAddToScene
function. Again we have to replace "hole" with "this.owner".
Of course in the main app file we have to say that we want to use this Hole behavior,
like this: we remove the references to the sprites here, as the sprites creation is handled
by the behavior, and pass the behavior into the constructor of the SceneObject.
Finally, we combine these 2 lines of code, and we're done....
Let's check with Chrome once more.... All good.
We haven't added any new functionality, or created any new code as yet. We've just moved
some code around. But nowwe can create as many moles and as many holes as we like. But
there is one more problem that we need to address:
Let's see what happens if we change the coordinates of our objects. Say that we want to shift
everything by 200 units along the X axis: the mole goes to (200, 50), and the hole,
which was at (0, 0) since we didn't specify any coordinates, now goes to (200, 0). However
this won't work... let's see what happens in Chrome.
It's a bit funny. The hole is where it's supposed to be, but the mole goes back to its original
position in the middle of the screen, and then keeps going up and down.
This is because in mole.js we are using some fixed coordinates as the arguments of our
moveTo functions here... here... and here. Instead, we really would like that these coordinates
be relative to the owner's position. And it's quite simple to do that:
this.originalPosition = this.owne. getPosition() Now this.originalPosition is the initial position
of our owner. And we move to this.originalPosition.x and this.originalPosition.y ... and... here...
And down here it's going to be plus 50 on the Y axis.
And we also remove this offset of 50 units here when we create the object, because we
don't need it anymore if we're doing everything relative to the initial position of the mole
object. Let's see it it works now... yes, it does!
And now comes the fun part, because we are going to create 4 of these things.
Let's imagine that we want to lay them out onto a grid, so to speak, of size 2x2. So:
var gridSize = 2 Then we do two for loops to iterate over all
the cells of the grid. And inside these for loops, we create a hole
and a mole for each cell. But where do we create it? So let's say that a cell is 300
wide and tall. var cellSize = 300
So if we want the grid to be centered at (0, 0), the coordinates that we want to use are:
var x = (i - gridSize / 2 + 0.5) * cellSize; var y = (j - gridSize / 2 + 0.5) * cellSize;
I'm not going to go through the maths here, but it's pretty simple anyway.
Now let's try it in Chrome... We might have to move our score object up
a bit, say at minus 350 for now, then we'll think of how to calculate a better position
for it later on Ok, that's cool, isn't it.
So our game has become a bit more interesting, and a little bit more challenging. Now that
we've got some solid foundations, we're going to make it much more enjoyable to play in
our next video tutorial. So once again, thank you for watching, and
see you soon.