Enabling Jumper to jump

First of all, we only want Jumper to be able to jump when he has a solid surface under his feet. We can test this by checking if a Rectangle one pixel lower than Jumper's bounding rectangle would be blocked. If that is the case, we let him jump.

So go ahead and introduce this method in the Jumper class. Notice the "!" in front of the last line. That means not. Which means that if HasRoomForRectangle is true (Jumper is floating) then we return NOT true (we're not on firm ground) and vice versa.

because no matter whether we were falling fast when we hit the floor, we don't want our upward momentum to have any memory of our downward speed (which would be the result of using the -= operator), so we just set it to a firm negative 25 upward.

Go ahead and fiddle around with the values in AffectWithGravity and SimulateFriction until you are satisfied with how Jumper moves.

Updating debug information

Go ahead and add some additional debug information to the game, so you can see whether Jumper is on firm ground.

And since our Draw method is getting pretty long, refactor the it, so you extract the writing of debug information in a separate method.

What happens in StopMovingIfBlocked is that we get the last movement by subtracting where Jumper was before he moved from where he ended up. If there was no movement on an axis, we set that part of the current movement to zero.

We will then call StopMovingIfBlocked as the last line of Jumper's Update():

If we could get that down to about ten lines we might increase readability. So see what I do here, and if you like it, copy it. If you don't - then don't .

Wrapping multiple variables and related functionality into a wrapper

First of all we have four lines which are just initialization. We could wrap all those variables and the math up into a structure, and initialize it with the same data that WhereCanIGetTo receives. Add a new class to your SimplePlatformerGame project, name the file MovementWrapper.cs and move all this code

We use a struct instead of a class, since objects (instantiated from classes) are only cleaned out (garbage collected) at irregular intervals, which might make our game stutter. Structs (like int, float, Vector2, etc.) are garbage collected the moment you leave the "{...}" in which they were created.

The most important gotcha to remember when working with structs is that you always get a copy of it when you pass it to a method, so you can't just pass it to a method, change some values on the struct in the method, and then expect to see the changes reflected in the reference to the struct that you had outside the method (see example 2 for more info on this).

That not only made four lines into one, it also collected all the information about the move we're about to perform into one neat little package, which makes it easier to refactor further, and extract methods from whatever else we're performing, just wait and see . Now see what our WhereCanIGetTo method looks like

Basically, we just updated the references to the four variables to use the variables contained in MovementWrapper instead.

Refactor nondiagonal movement out into a separate method

Since we have all the variables for moving in the MovementWrapper struct, we can easily refactor the nondiagonal part of the movement out in a separate method. First create the method CheckPossibleNonDiagonalMovement, and move the code there.

And add a new method call CheckKeyboardAndReact to listen for keyboard input in SimplePlatformerGame.Update. In This method will react to someone pressing F5, and call a new method RestartGame() which creates a new Board and puts Jumper back in the top left corner:

There are many small changes you can make. I added some functionality which adds a shadow under our debug text, and changed the jump key to SPACE instead of UP. You go ahead and do whatever you wish - it's your code now

Here's the final result

Play around with it, expand it and have fun!

If you need help with anything in particular, or you want to give feedback on improving my tutorial feel free to leave a comment

This line adds half a pixel of vertical movement (downward) using the constant UnitY

(which is (0,1) you may recall :)). We may have to adjust this later, but for now we have something to work with ;).

I'm stuck!!

Now you can see that Jumper will fall whenever he is unsupported, but we also get some unwanted sideeffects, the most annoying being - we can't move once we've hit the floor :(, and sometimes we don't even hit the floor:

In situations like this one, it is a good idea to add some debug info to your game so you can see what is going on.

Adding debug info to the game

To write something onscreen you will need a SpriteFont in your content project. So go ahead and add a SpriteFont to your contentproject:

and name it "DebugFont"

Add a SpriteFont membervariable to the SimplePlatformerGame class

private SpriteFont _debugFont;

and load the font in the LoadContent method of SimplePlatformerGame

_debugFont = Content.Load<SpriteFont>("DebugFont");

Update Draw to show debug info

Then add some lines to the SimplePlatformerGame's Draw method to show where Jumper is, and where he is headed:

If you are unfamiliar with String.Format(), have a look here. Basically you pass it a string with placeholders named "{0}", "{1}", etc. for all the variables you want inserted into it. This makes it easier to read the format of the string, since formatting and data are kept separate.

You can add conversion information inside the brackets, using a separating colon.

E.g. {0:c} for currency or {0:0.0} for one decimal.

When .NET renders the result on screen you will see that the decimal separator on my screendumps is a comma, not a period, as my PC uses european (da-DK) culture :).

Now we can see what is going on

Jumper still has a desired vertical downward movement of 4.5, which makes him want to bury himself.

Q: Why is Jumper sometimes floating?

A: Same reason why we couldn't get close to the wall when moving sideways until we let go of the left/right key: the movement we're attempting would make Jumper end up inside a blocked Tile, and the Board object's HasRoomForRectangle() won't allow that move.

Q: Why can't we move sideways?

A: We haven't stopped Jumper's downward movement when he landed on the ground, which means that even if we try to move sideways, Jumper would still try to move downward and sideways.

Let's fix the floating first by improving how we handle leftover movement for Jumper.

Improving Jumper's movement

Here you can see what we want to accomplish.

In the illustration above, Jumper is moving diagonally down and left. The current Movement for Jumper wants to move him to the end of the diagonal red arrow in this Update().

We want that movement to be stopped right when he hits the horizontal row of blocks, and have the leftover motion carry over into horizontal movement, which only terminates when he hits the vertical wall of blocks a bit later.

Algorithm

What we're going to implement is a function WhereCanIGetTo on the Board class which

gets the origin and destination of a Rectangle

breaks down that movement into a number of half pixel steps

checks for every step whether it is blocked

if it is blocked, tries to carry over any diagonal movement into vertical or horizontal movement

Creating the WhereCanIGetTo() method

The reason we don't send the bounding rectangle of Jumper to the method, is that Rectangle uses ints for positioning, and we need more finegrained movement here.

We're going to do some vector math here, so if you're not used to that, have a look at this :).

Before we begin stepping along the path from origin to destination, we need some variables. These variables will be used to store

the complete movement we want to try (the distance from origin to destination)

the furthest available location we've found so far

the direction only (without distance)

the direction and length of one step

the number of steps we want to break movement into

Add some code to calculate these values. First calculate the movement from origin to destination:

Vector2 movementToTry = destination - originalPosition;

This means that if Jumper was at wants to go to (80, 120) and starts out at (100,100), the movement he wants to carry out is (80 - 100, 120 - 100) = (-20, 20), or in other words, -20 on the x-axis (meaning left) and 20 on the y-axis meaning down.

we assume that the originalPosition is in a nonblocked area, so we use the original position as the furthest possible location we have found along the path we want to travel

Vector2 furthestAvailableLocationSoFar = originalPosition;

to figure out how many steps we want to break the movement into, we multiply the length of the movement by 2 (so we approximately try once per half pixel), and add one, to make sure we at least try one step for very small movements

In the example mentioned above ((-20,20).Length() being approximately 28.3) this would work out at

(28.3 * 2) ≈ 57 + 1 = 58 small steps

And finally figure out how far one step is by dividing the entire move by the number of steps

Vector2 oneStep = movementToTry / numberOfStepsToBreakMovementInto;

Each step would be (-20,20) / 58 ≈ (-0.34, 0.34)

One small step at a time

Now that we have these values we can make a loop where we:

keep trying the next step along the movementToTry and see if we can go there. We do this by creating a Rectangle at that position and asking HasRoomForRectangle whether it is blocked

if that move was unblocked, we store that position in furthestAvailableLocationSoFar and continue

If that place is blocked, we exit the loop and return furthestAvailableLocation

Before we begin coding that, we need functionality to create a new Rectangle at a given position, so we have something to test each step with. So add a new function to the Board class which receives a Vector2 and a width and height, and creates a Rectangle at that position.

We will lose some precision in converting the floats from positionToTry to ints, but we will finetune that later.

Now we have the basic skeleton of our improved collision detection up and running. So update the WhereCanIGetTo method. I suggest you don't copy and paste the code, but code it while making sure you understand every step of it, but suit yourself .

Also update the MoveIfPossible name to MoveAsFarAsPossible, to better reflect that it is no longer a do/don't move decision, but a movement within the confines of the possible. Now your MoveIfPossible method should look like this:

Recycle leftover diagonal movement as horizontal or vertical movement

Right now our movement ends as soon as we take a step which ends in a blocked position.

What we want to do is:

If we get blocked before finishing a move:

find out if we we're moving diagonally when we got blocked, and if we were:

try to move as far horizontally and/or vertically as possible and return the farthest possible location

We can illustrate it like this: Here we hit a blocked Tile about halfway into the move the Jumper is trying to make. When that happens we check whether it is a diagonal move (neither of the X and Y part of the movement vector is zero), and then we test movement using the remaining movement along the X and Y axis.

Remember you can turn a movement vector into horizontal movement by multiplying it by Vector2.UnitX (thereby setting its movement on the y-axis to zero) and into vertical movement by multiplying it byVector2.UnitY (setting its x-axis movement to zero).

"If we get blocked before finishing a move"

The place to add code for this case is inside the else part of the loop in the WhereCanIGetTo() method

"If it is a diagonal move"

Create a boolean variable to store whether it is a diagonal move, and add it to the beginning of the else in WhereCanIGetTo(). As you can see, we store a true if neither the x- nor the y-movement is zero (think about it! :)).

To calculate the steps left, we have to subtract the step we just tried, as that moved us into a blocked area (if HasRoomForRectangle() returned true we wouldn't be down here handling all the messy details :)), and subtract the result from however many steps we were supposed to take on the entire path.

int stepsLeft = numberOfStepsToBreakMovementInto - (i - 1);

Example: We want to move 10 steps in this complete Update(). When testing step 7, we find it to be blocked, so we subtract 1 from 7 to find the last valid position (7-1 = 6), and then subtract that step from the entire trip (10 - 6 = 4) to find out how many steps we still need to try.

"try to move as far horizontally and/or vertically as possible"

We're almost there now - we can see the finishing line ... so let's perform the final sprint!

As mentioned earlier, to get only the horizontal/vertical movement part of a vector, we multiply by Vector2.UnitX or Vector2.UnitY respectively. So for each type of movement, we calculate the remaining movement in that direction, find out where we want to end up of we completed that movement by adding the remaining movement to furthestAvailableLocationSoFar.

Now we have the position to start from, and where we want to end up ... if only there were some way of finding out how far little Jumper could get to along that path...? 😉

"But there IS!" (I hear you cry!)

"Just feed those two positions right back into WhereCanIGetTo(), and it'll tell you!".

Right you are - so here is the final part of our if (isDiagonalMove) {... }

The calling of a function from itself is called recursion. In a lot of cases the calling can be nested many times deep, but we only ever call two layers deep. When calling functions recursively it is very important to have a criteria for when to stop, otherwise the program enters an infinite loop. The reason our calling stops is that the recursion is only performed when movement is diagonal, and the parameters to the second call to WhereCanIGetTo is never diagonal.

Go ahead and try it out, you will see that little Jumper no longer sticks to walls or floors, and slides right along - YAY! Go celebrate with a cup of coffee/cola/juice/water... 😀

The final version of WhereCanIGetTo

Read it through and see if there is something you still don't understand. If there is, now is the time to scroll back up and read the explanation again

This method is too long for my tastes. But simplifying it would mean having to put some of the code into other methods and passing along a lot of parameters (which is also not optimal for readability), or encapsulating the functionality in a small class. At the end of the next and final part of this tutorial, I will show you how that can be achieved.

What we've covered

After this part of the tutorial you should have learnt that

Gravity can be implemented simply by adding a downward momentum in every Update

You can benefit from adding debug information in your games to let you know why something is happening

You can reuse a method inside itself using recursion, and you should always ensure you have a stopping condition, so the program doesn't enter an infinite loop

Previously on xnafan.net

In the last part we looked at movement, and how to respond to keyboard input.

The current codebase lets the Jumper move all over the screen without any constraints at all - not exactly what we had in mind. This must be stopped! And it will ...

The agenda

First we will take a look at a very simple implementation of how to detect collisions in XNA.

Then we will add a boundingrectangle to our Sprite class, so everything we draw has knowledge of its outer bounds

We will add code to test for collisions and stop the Jumper's movement if it hits something

How simple collision detection works

XNA offers us a very simple way of testing whether two items overlap. We define a Rectangle for each of two items, where the rectangles define the outer boundary of the items. Then we send one of the rectangles to the Intersects() method on the other, and it returns a bool telling us whether they intersect.

Here I've illustrated where these three rectangles appear, and whether Intersects() returns true or false.

Adding a bounding rectangle to our Sprite class

Since a Rectangle can be constructed using the coordinates of the upper lefthand corner, plus width and height, it's easy to to create a bounding rectangle, because that's the information we've got in the Position property on the Sprite and the Width and Height of the Texture :).

The easy way of exposing a Rectangle property to the world is to implement a read-only property (a property with no set part) which, when accessed from outside, creates a new Rectangle based on the Position and Texture of the Sprite. Since we want to keep things simple in this tutorial, that's what we'll do! 😀

We need to cast the Position.X and Position.Y to int, since they are of type float, and will lose precision (the decimal part) when stored in an int. A lot of these operations which can introduce subtle, implicit errors are automatically checked by the compiler for us. By writing (int) in front of the value we want to convert, we can silence those warnings.

It's like saying to the compiler: "It's alright, I know what's going on and I explicitly allow it" 😉

Now whenever anybody wants to get the outer boundaries of our Sprite, they can retrieve the current position and size of our Sprite as a Rectangle through the Bounds property.

And remember that since Jumper and Tile inherit Sprite, they now also have a Bounds property.

Simple collision detection

Now, whenever we want to move something in our game, we have to test that object's bounding rectangle against the bounding rectangle of everything else, to see whether we hit something. In a large level we would have to cut the level up into smaller quadrants and store the objects in those quadrants somewhere for fast lookups. If we didn't, our game might slow down due to the large amounts of collision checks performed each Update(). Our gameboard is 15 * 10 tiles, and about one quarter of those will be blocked. This will give us about 15 * 10 /4 = 37.5 collisiondetections per movement we want to perform. That's not even close to being a problem :). When we refine our collision detection, we will get quite a few more checks per Update(), but still nothing to worry about.

Letting the Board tell whether is has room for something

Now we have to figure out where to put the code for detecting collisions.

We could put it in Jumper, but this would mean that we could only reuse that code in classes which inherit Jumper. We could also put it in Sprite, so all other subclasses of Sprite had access to it, but some of those classes might not need it. And the functionality is very closely related to the Board. So we'll add a method to Board to check for a given Rectangle whether there is room for it:

Q: Why do I first check for IsBlocked, and then check Intersects afterwards, and not the other way around?

A: Because the first check takes less computations (a simple true/false lookup) than the other (mathematical comparisons with position, width, height). This way we don't calculate if there is no need

Letting Jumper access the Board object through a static property

To give Jumper access to the Board object and all its tiles, we could just add a Board property to Jumper (or Sprite) and store a reference to the Board object here. This would enable Jumper to detect collisions. But I am going to show you a technique that requires less coding and enables any code in our project to access the Board.

In case you're still a bit wobbly on the concepts of class and object, think of the Class as the definition (the mold) for objects. There is only one class, but from that class you can instantiate ("new up") many objects, each with their own data.

Even though there may be many objects of any given class, there is only one class of that type. So we will create a property on the Board class instead of on the objects which we've done so far. This property will store a reference to the current Board object.

Since we can write "Board" anywhere, and gain access to the class, we will automatically have access to any public properties on the class.

Adding a static property to a class

To tell the compiler that we want a property (or method) stored on the class instead of on the objects created from the class, we add the keyword "static" to the property.

Go ahead and add a static Board property "CurrentBoard" to the Board class:

public static Board CurrentBoard { get; private set; }

We mark the property's setter private, so nobody can accidentally set a new Board outside the Board class. Right now we only want one board instance (object).

In the constructor of the Board class we add a final line to store the newly created Board object in the CurrentBoard property.

Board.CurrentBoard = this;

What's "this"?

"this" is a special word meaning "the object I am currently in. So what we're doing here is getting a reference to the Board object we're constructing through this and storing it on the class. Now we can access the Board object from Jumper :).

IMPORTANT! With the approach we've chosen here, the latest created Board object will always overwrite the previous (if any), so we should only construct the board once.

Try it out - and don't be too sad that it isn't perfect ..yet!

Okay - now go ahead and change the LoadContent() method of SimplePlatformerGame to move the starting point for _jumper to 80 * 80, so he doesn't start out inside the border.

Here I use the shorthand notation for new Vector2(80,80) by multiplying a vector of (1,1) by 80:

_jumper = new Jumper(_jumperTexture, Vector2.One * 80, _spriteBatch);

Okay - now run the game, and check that the code works, but probably not exactly what you had hoped for.

Here's an explanation of what's happening.

The short of it is that because we're still "teleporting" from one position to the next, we are stopped too far out. Look at this illustration:

Jumper wants to update his position from an unblocked tile to a blocked tile. Since that isn't possible, Jumper doesn't move any closer to the wall. Jumper doesn't move till we release the arrow key, and speed decreases to the point where possible movement occurs in Update().

Previously …on xnafan.net

how to specialize that class into a Tile class and create a Board of Tiles (part two)

In this part of the tutorial we will create a specialization of the Sprite class for the little blue guy

and enable him to be moved around. We will do this by storing all the necessary functionality in the Jumper class, including the math for movement, and the Keyboard input.

We will do this by

extending the Sprite class

add an Update() method to Jumper, so we can get keyboard input and update Movement

add a Vector2 Movement variable to update the Position with in every Update()

The Jumper class

First I want you to go ahead and create a new class Jumper which inherits Sprite, like we did with Tile in part two of the tutorial.

Try performing the following steps without peeking at part two, so you practice recalling how to create subclasses, sending parameters to the superclass’ constructor, etc.

Add an Update method with the same signature (name, return type and parameterlist) as the Update() in the Game class. Later I will tell you why we need the GameTime parameter in Jumper, and how it helps us create smooth movement.

Add an automatic property Vector2 Movement to the class.

Add a constructor which accepts all the parameters that Sprite needs, and passes them on to the base class (Sprite).

When you’re done, take a look at the code below and make sure you have something similar

Movement in computergames

It is beneficial to think of movement in computergames as an illusion created by teleporting between locations. We don’t actually move anything. Instead we show an image in a new position 30-60 times per second and our brain just fills in the blanks to maintain the illusion of smooth movement. In fact our Sprite hasn’t been to more than a couple of the positions inbetween the origin and endpoint.

This is important to remember because we may be “moving” so fast that in an Update() we may teleport our object from one side of an obstacle to another and miss the collision. Here that situation is illustrated by an acceleration while falling, where the first call to Jumper.Update() moves him 50 pixels down from top to middle position and second call is moving even faster moving him 70 pixels down and across the 64 pixel tall Tile. We never detect the collision .

Therefore we either:

have to make sure we move slowly enough that each Update() only tries to teleport us a distance shorter than the smallest obstacle on the Board

or

we have to split every Update’s “teleport” into smaller parts and test for obstacles at every little step

We are going to go with the second approach - but more about collisions in next part of this tutorial. Let’s just get something moving already!

Keyboard input

In XNA, keyboard input is obtained through the Keyboard.GetState() method. The GetState() method returns a KeyboardState which is a snapshot of the keyboard which allows you to test which keys were pressed and which weren’t at the time of the snapshot.

Every Update() we will:

get the snapshot

inspect the Left, Right, Up, Down keys

change the amount to move (Movement)

change the Position by the amount in Movement

In case you need to understand a bit more about using Vector2 for movement have a look at this tutorial (you can stop at “How to point a Texture2D in the correct direction”, since our Jumper won’t rotate ).

Updating the Update() method

First – we will get the snapshot of the keyboard state.

KeyboardState keyboardState = Keyboard.GetState();

Converting input to movement – a naïve approach

In a naïve approach we could then change the Movement property like this (NOT the way to to it!)

A: Since all the if checks are performed in order, in case you had Right and Up pressed at the same time, Movement would first be set to +1 on the X-axis and 0 on the Y axis, and then the next if would set Movement to 0 on the x-axis and –1 on the Y axis (up). The player would not get any reaction from his right key.

Converting input to movement – a better approach

What we want to do instead of replacing the Movement is adjust it like this:

The += operator adds the value to the to the right of the operator to the variable on the left. This operator also exists in a lot of other variants, to subtract, multiply and divide (–=, *=, /=) …and more .

BTW: I haven’t added Keys.Down, since gravity will be performing that movement for us later .

This will give us the benefit of gradual acceleration/deceleration, since a fast movement left, say 5 pixels per update, wouldn’t be replaced by a sudden reversal to the right when the right key is pressed. Instead Jumper decelerates to 4 pixels/update, 3 pixels/update, etc. until he stands still and then accelerates slowly right. Right now we don’t know if a whole pixel is too much or not enough, but we can fiddle around with it later till it seems right .

In case you haven’t tried Vector math before, basically all operations are performed on both the X and the Y.

The f after the 5.3 in the third line above is necessary because decimal numbers in C# are doubles by default (larger variables than floats which Vector2 use) so we explicitly declare the 5.3 a float with the “f” suffix.

Updating Position based on Movement

To update Jumper’s Position based on the Movement variable we just add Movement to Position.

call Jumper’s Update() in every call

Changing the _jumper Sprite to a Jumper

On the SimplePlatformerGame class we had a membervariable _jumper of type Sprite:

private Sprite _jumper;

go ahead and change its type to Jumper

private Jumper _jumper;

…change the instantiation in LoadContent(), so we instantiate a Jumper instead of a Sprite:

Your Jumper will fly off screen pretty fast, because we haven’t got any collision detection in place, nor any automatic slowing down. Collision detection is next tutorial, so let’s make sure we slow Jumper down at the end of every Update().

Constraining speed of movement

If we coded a real physics engine we would have an amount of drag (wind-resistance, ground-friction, etc.) which would increase with our speed and at some point we would automatically reach a maximum speed. That would require us to introduce more math though, and the result might not be discernible to the user anyway.

And the basic rule of creating games states that:

“Don’t make it if you can fake it!”

…which we will .

So go ahead and add code to Jumper’s Update to decrease the speed by a tenth every update (right before you add Movement to Position):

Movement -= Movement * new Vector2(.1f, .1f);

Read this as “Multiply both the X and Y values of Movement by .1, subtract the result of that from Movement and store it in Movement again”.

This could also have been written as

Movement *= new Vector2(.9f, .9f);

…but I like to be able to read what I use to change something with (a tenth of the speed), as opposed to what remains (nine tenths of the motion). Suit yourself, just make sure you understand what you’re doing .

Hit F5 and try it again … muuuuch better! (I hope? …otherwise go over your code again. If something is not working, set a breakpoint and look at the math for each Update() to see what is happening).

Smoothing movement

Though you may not notice it right now your Update is called 60 times a second. In a perfect world this would be every 1/60th of a second. But (and I hate to break it to you) it’s not a perfect world!

We may have 3/60ths of a second between two Updates and then two Updates right after each other with close to a millisecond interval to catch up. So what do we do? We compensate!

We just add the elapsed time into our math, so an Update with 1/60th uses 1/60th of the movement for a complete second, and an Update which takes three times that amount of time also performs a three times longer move (it’s a “teleport” – remember? ).

We can get the elapsed time since last Update() in the gameTime parameter in the Update method. So let’s add that to our equation. Change the last line of Update() to:

You don’t need to keep comments in sync, and code never lies – it does what it says

You already have a very readable description of what Update() does inside Update(), just by reading the code.

You have a structure in Update() which makes it easy to build upon it when things get more complicated (and they will )

You don’t need to read every line to understand what it does, if you need to change something, go to that method

You don’t need to spend time writing comments – just stay productive and CODE!

A few pointers on Vector2

There are a few constants on the Vector2 struct which are nice to know

Vector2.One is equivalent to new Vector2(1,1)

Vector2.Zero is equivalent to new Vector2(0,0)

Vector2.UnitX is equivalent to new Vector2(1,0)

Vector2.UnitY is equivalent to new Vector2(0,1)

These are nice to know because they increase readability (once you know what they are), and they are easy to modify movement with.

Stop movement:

Movement = Vector2.Zero;

Constrain to horizontal movement by multiplying X with one and Y with zero:

Movement *= Vector2.UnitX;

Constrain to vertical movement by multiplying X with zero and Y with one:

Movement *= Vector2.UnitX;

What we’ve covered

Now hopefully you’ve store inside that pretty little noggin’ of yours, that

Movement in computergames is “teleportation”, not real movement

To best let the user change movement, we get the state of the controller (Keyboard in this case), we modify the movement we want to perform (accelerate, brake, block, slow, etc.) and then we update the position based on the movement

We use the elapsed time in our movement math, to compensate for computers doing other things while we’re playing our game

We can make our code self commenting by using the Clean Code principles.

In part one of this tutorial we covered how to get something drawn onto the screen.

In this tutorial we will cover how to

create a Tile class which is a Sprite with an IsBlocked property

create a Board class which can create, store and draw a number of Tiles in rows and columns

The Tile class

A tile is basically just an image at a specified position which can be blocked or unblocked. You may have already recognized that this description is an awful lot like the description of our Sprite class: “basically just a help for combining a texture and a position”.

In object-oriented programming we can take the properties and functionality of a class and add to it by inheriting one class in another. So that’s the plan:

Explanation for missing parameterless constructor

When subclassing another class which doesn't have a default constructor (also known as an "empty constructor" or a "parameterless constructor"), we *have* to explicitly pass constructor parameters from the subclass (inheriting class) to the superclass (base class or parentclass).

This is the equivalent of the compiler sitting in a corner, sulking and muttering:

“oh – the programmer wants to control object-creation, I better mind my own business then!!”

In this situation we often choose to pass the parameters for the superclass' constructor to the subclass' constructor, so the subclass’ constructor can pass the parameters on to the superclass. This means that our class will not compile as it is right now, because we can't create a Tile without creating a Sprite. And the Sprite wants a Texture2D, a Vector2 and a SpriteBatch in its constructor.

Making the Tile constructor work

We add a constructor to our Tile class which takes the necessary three parameter we mentioned above. Those we pass on to Sprite’s constructor. We will also add a boolean parameter isBlocked to the constructor’s parameters, and store it in the Tile object:

As you can see above, the constructor on Tile gets four parameters, and calls the base class (Sprite) with three of them, while storing the last one in a public property IsBlocked.

You can’t see a Draw() method nor a Position or Texture property on the Tile class

… but they are there . This is the beauty of object oriented programming - that we get what we need through inheritance.

Q: How do object-oriented programmers get rich?

A: They inherit!(insert canned laughter)

Making nonblocked tiles invisible

The final part we want to modify on the Tile class, before making a whole bunch of’em into a Board, is to have non-blocked tiles not show up. This is easily done, because right now both a Sprite and a Tile draw their Texture at their Position through the Draw() method in Sprite:

So basically we just need to change the drawing of a Tile to *not* use this Draw functionality if the IsBlocked property is false. Piece of cake – let’s get to it

OOP INFO

In Object Oriented Programming, we always build on top of classes that other developers have developed. At the very top of the hierarchy is System.Object, which we subclass if we don’t specifically choose otherwise. Because we always inherit existing code, a very helpful principle has been implemented, to make sure we don’t change functionality by accident in a subclass, by implementing a new method with the same name as one in a superclass. This is two-step security which demands thata superclass must explicitly declare that a given method can be overridden in a subclass, by adding the virtual keyword to the method declaration

a subclass must explicitly declare that a given method is overriding a method from a superclass by adding the override keyword to the method declaration

Read through the code, and make sure you understand what is going on. If you don’t, set a breakpoint in the loops (F9) and run the program to inspect the calculated values. What we’re doing is multiplying the column and rows with the width and height of the texture we’re using, to position each Tile correctly.

Notice that we store the variables columns and rows in the properties first and then use the properties from then on, not the variables. The point being – if we want to ensure that column/row could never be a negative number or exceeed the window size, etc. we could change the automatic properties to properties with a backing variable and only store values we approve of.

By storing the parameters sent to the constructor in properties and from then on always referring to the properties and not the parameters, we retain this possibility of using the properties as a “filter”. To keep our code simple in this tutorial, I will not implement value validation, but feel free to add it if you want

Drawing the Board

Now we’ve got all the data we need, to start drawing the Board. So add a Draw method, where you iterate over the rows and columns and call the Draw() method of each tile.

Simplifying the Draw method

Since we don’t care in which order the tiles are drawn, since none of them overlap each other, we can just use a foreach loop to iterate over the Tiles.

public void Draw()
{
foreach (var tile in Tiles)
{
tile.Draw();
}
}

If you prefer the doble for-loop stick with it, but since I am using the principles of Clean Code, I try to keep my code as succinct as possible. The advantage of the second implementation is that it is very fast to read what is going on: “we’re drawing all tiles”. In the first implementation it is necessary to both look at the values of the two variables we’re using to iterate with and how we’re indexing into the array, to figure out exactly what is going on. It is a very small distinction, but I hope you get the idea .

Q: “Why didn’t we do the same in the double for-loop where we instantiate the tiles?”

A: Because we needed the x and y counter variables to calculate the Position property of the Tile objects.

Using the Board in the game

Finally – let’s create a Board in the SimplePlatformerGame class. Try to figure out what steps you have to perform in order to

instantiate a Board (15x10 tiles)

store it in a member variable so all methods in the SimplePlatformerGame can access it

draw the board

Try implementing it first, and use the following guide and code if you get stuck.

Go ahead and create a Board _board member variable on the SimplePlatformerGame class, and instantiate it in the LoadContent() method.

In the Draw() method of SimplePlatformerGame add a _board.Draw() call.

If you can’t see the _jumper, then you might have drawn it first and the board on top. In that case, switch the draw order

Enlarging the Window

We can’t see the entire board, because the default size of our Game window isn’t big enough. So calculate the window size needed for 15 columns and 10 rows each 64x64 pixels, and set the GraphicsDeviceManager’s PreferredBackBufferWidth and –Height in your constructor.

Creating randomly blocked Tiles and a border

Finally we will set randomly blocked tiles on the Board and then make sure that all bordertiles are blocked.

Randomly blocked Tiles

We instantiate our Tile objects in the Board constructor in this line

Tiles[x, y] = new Tile(TileTexture, tilePosition, spritebatch, true);

The true (fourth) parameter to the Tile constructor makes all Tiles blocked, so we need this to be randomly true or false.

Add a Random member variable to your Board class and instantiate it in the declaring line.

private Random _rnd = new Random();

Now, whenever we create a Tile we can “roll the dice” and set the tile blocked if it is zero. This way we can define what percentage of the tiles should be blocked by rolling a smaller or larger random value. If we roll _rnd.Next(2), then half the time we will get a zero and half the time a one. If we want only 10 % blocked tiles, we can roll _rnd.Next(10) and set the Tile blocked if it is zero, which will only be a tenth of the time.

Go ahead and change the true parameter of the tile instantiation in the constructor of the Board class, so 20 % of the tiles are blocked:

This is the Clean Code way of coding. Express what your code performs via methodnames and keep methods short. A definite advantage of this is that it is easy to see what is going on in a method if it has few lines and most of the code is calls to methods whose names sum up what they perform.

In the SetAllBorderTilesBlocked, create a double loop, and if the x counter is a border column (0 or 14) or the y counter is a border row (0 or 9) then set the tile blocked.

A: Yes - the control statement of the two for loopsshould not have hardcoded values, in case we want to change the size of the Board from the SimplePlatformerGame class. Same thing with the hardcoded values 14 and 9 for border tiles.

Our game now

As you can see we get a random board with borders all around. Go ahead and fix the position of the Jumper if you want to so he isn’t embedded in the border. We will add code to move him in the next part of the tutorial anyway .

Clean Code mini-assignment

Now you’ve seen how to move code into a method which summarizes what it performs in a meaningful name. Try to move the Tiles[,] initialization and the first two loops of the Board initialization into a separate method as well. This should leave our constructor with only

The Class diagram

Here you can see how our Tile class inherits the Sprite class. It also shows that Tile adds an IsBlocked property, a new Draw method and a Tile constructor to the definition of Sprite. The Board has a collection (arrows with double heads) of Tiles.

What we’ve covered

In this tutorial you’ve seen how to

subclass another class (Tile from Sprite)

pass parameters from a subclass’ constructor to the superclass’ constructor

use variables instead of constants in your code (the for loops)

simplify code according to Clean Code principles of using meaningful names

You can see that I’ve renamed the graphics to _graphics and spriteBatch to _spriteBatch. I do this so that I will always know when using a variable somewhere in my code whether this is a local variable or a member variable, which a lot of other methods are using.

The Sprite class

The Sprite class is basically just a help for combining a texture and a position. It will make it easier for us to place all the elements which we will need to create a complete game.

Create a new classfile Sprite.cs in your project class and the variables to store a Texture2D with a Vector2 for position:

Are we satisfied?

Now while this would be enough if we implemented the code for setting Texture and Position outside the Sprite, and had the drawing code outside (e.g. in the SimplePlatformerGame class) we would have a satisfactory class.

We could initialize it like this in the SimplePlatformerGame class’ LoadContent() method:

in the other sample I used a constructor with parameters which ensures that it is no longer possible to instantiate the object like this:

Sprite mySprite = new Sprite();

because the parameterless constructor (or “default constructor”) is removed when you explicitly create one yourself. The logic of the compiler being “oh – the programmer wants to control object-creation, I better mind my own business then ).

Now, whenever we need the Sprite drawn, we just call the Sprite’s Draw() method from the SimplePlatformerGame class’ Draw method:

The call to base.Draw(gameTime) is a best practice when we are overriding a method (notice the override void Draw(…)). Since our SimplePlatformerGame class inherits Microsofts Game class, we actually don’t know what is going on inside the SimplePlatformerGame.Draw method. To ensure we don’t break any internal mechanics, we always call the overriden method’s base implementation (unless we really know what we’re doing ).

Let’s test it

Add two Texture2D member variables to the top of the SimplePlatformerGame class, where the _graphics and _spriteBatch are declared, and a Sprite (just for testing, that variable will be deleted shortly):

What we’ve covered

After reading this part one of the tutorial, you should have learnt that

a class is a nifty way to combine data which should be logically paired, such as the Texture and Position in the Sprite class

a class can be used to encapsulate both data (image, position, spritebatch) and functionality (draw, constructor) in a little package which can then be interacted with using very little code (the Sprite.Draw() method)

you can use a constructor to ensure that an object can only be constructed from a class if all the parameters are passed along (though null values can be substituted)