xnaFan's Blog http://xnafan.net XNA tutorials, code and games Thu, 13 Feb 2014 15:17:45 +0000 en-US hourly 1 https://wordpress.org/?v=4.2.22 Simplified XNA drag and drop http://xnafan.net/2013/07/simplified-xna-drag-and-drop/ http://xnafan.net/2013/07/simplified-xna-drag-and-drop/#comments Mon, 29 Jul 2013 14:17:18 +0000 http://xnafan.net/?p=1073 I got a question about how to implement multiple selection with drag and drop, and could see how that might be a challenge to implement to a beginning programmer. So I thought I'd make a codesample to learn from.

image

 

When I had finished coding, it struck me that I could refactor the code so the drag and drop part was tucked away in a helperclass. Then all that was needed to use it in any solution was to have the draggable objects implement an interface to let the helperclass

  • know the boundaries of the object
  • be able to change the position of the object

I expanded the helperclass to notify the object when the mouse was hovering over it, and whether the object was selected, so all that was left for the coder using the helperclass, would be to implement different ways of drawing the object according to what the state of IsMouseOver and IsSelected is.

So without further ado - let me present the DragAndDropController Smiley

What you get

It's as easy as this to use the controllerclass in your project:

//declare the controller somewhere on your Game class
private DragAndDropController<Item> _dragDropController;

//instantiate the controller somewhere
_dragDropController =  new DragAndDropController<Item>(this, _spriteBatch);
//and add it to the Game class' Components
Components.Add(_dragDropController);

//add the items you want drag-and-drop enabled to the controller
_dragDropController.Add(item);

The items that you add to the controller must implement the IDragAndDropItem interface:

image

Or in code, if you prefer that:

/// <summary>
/// Interface describing necessary implementation
/// for working with the DragAndDropController.
/// </summary>
public interface IDragAndDropItem
{
    Vector2 Position { get; set; }
    bool IsSelected {  set; }
    bool IsMouseOver { set; }
    bool Contains(Vector2 pointToCheck);
    Rectangle Border { get; }
}

And that's it! Smiley med åben mund

What an interface does

An interface in code is a contract that allows one piece of code (the DragAndDropController in this case) to treat objects of different types in the same way. This means that no matter whether the class implementing the interface is a "PlayerCharacter", a "MoneyToken" or a "PlayingCard", as long as they implement the abovementioned methods, the controllerclass can interact with them Smiley.

Video demonstration

 

The DragAndDropController<T>

Here you can see the public properties and methods of the DragAndDropController

image

Since it is a generic class you instantiate it to handle the type of your objects, and from then on you can add or remove items of that type to and from the controller.

You can also get the item under the mouse at any time (if any).

Source code

Here is the complete source code

]]>
http://xnafan.net/2013/07/simplified-xna-drag-and-drop/feed/ 5
Simple AnimationController for animating game objects http://xnafan.net/2013/05/simple-animationcontroller-for-animating-game-objects/ http://xnafan.net/2013/05/simple-animationcontroller-for-animating-game-objects/#comments Wed, 01 May 2013 19:53:32 +0000 http://xnafan.net/?p=1029 This tutorial shows you a simple to way animate other objects, in a way that doesn't require you to add a lot of extra code to your existing classes. We will end up by creating an AnimationController class, which can be used to animate anything which has a Vector2 Position property, by moving it from point A to point B in a straight line.

Presentation of what we want to achieve

In the video below you can see why I created the AnimationController. I wanted to juice-up the "setup next level" phase of the game board in my Simple Platformer Tutorial. This game was only used to illustrate the concept, you can use the AnimationController for anything you want moved automatically Smiley

What we will cover

  • Designing intuitive APIs
  • What is the point of putting this functionality in a controllerclass?
  • Defining what something looks like (creating an interface)
  • Creating the AnimationController
  • Putting the level in place in random ways
  • Getting Funky with "Func"
  • Learning to love Lambda Expressions
  • Putting a bow on it

Designing intuitive APIs

An Application Programming Interface (API) is a set of classes which enable you perform complex actions just by calling methods, and instantiating objects from the API. APIs are also frequently referred to as "class libraries". The .NET framework is a huge API with thousands of classes. A lot of planning went into making the .NET framework accessible and intuitive. One thing the designers did was ask themselves:

What code would I like to write, if I were the user of this API?

And we will start out by asking ourselves this very same question Smiley 

My answer is:

  • I would like to write as little code as possible to be up and running
  • I would like to be able to add "anything which has a position" to the controller and ask it to move the object automatically from one point to another
  • I would like to be able to check whether all animations are done

Notice that these are the answers ... in this case.
Another time we may want to achieve different objectives Smiley, der blinker

Well - considering the requirements, we would like to be able to write something like this:

//ask the controller to animate all the items
//in the list of game objects
//in a line, ending 500 pixels to the right 
//of their starting position
Vector2 fivehundredPixelsRight = new Vector2(500,0);

foreach(var item in listOfItemsWhichHaveAPosition)
{
	animationController.MoveTo(item, item.Position + fivehundredPixelsRight);
}

and somewhere in our Game's Update() be able to ask whether all animation is done

if(animationController.Done)
{ 
  //perform normal gameloop
}

...that would be perfect for us, since we could add all the Tiles to the AnimationController, and have it move them back to their ending position from randomly selected starting positions we provide. Smiley

What is the point of putting this functionality in a controllerclass?

Q: why don't we just add all the animation functionality inside the classes we want to animate?

A: because this way we can

  • keep our other classes (the ones we want moved) much leaner which increases readability, and...
  • have the animation functionality as a separate little toolbox which can be used to animate classes which don't inherit any of our code, as long as the class has a Vector2 Position property

Defining what something looks like (creating an interface)

(If you are familiar with interfaces, you can skip to "Creating the AnimationController")

Okay - I've mentioned it a couple of times already:

"as long as the class has a Vector2 Position property"

Q: how do we go about defining that restriction in C#? 
A: By using an interface!

Here is the definition we will use:

image

Note: The prefixed "I" in IPositionable is a convention used in .NET, so interfaces are easily recognized in code.

How do we benefit from this?

With this definition, a new "Type" like string or bool has been created. It can't perform anything but we can use it to describe any class which fulfills the requirements described in the interface.

Note: it isn't enough that a class fulfills the requirements, it must declare it in its class declaration.

See the difference from here:

public class Sprite
{
    public Vector2 Position { get; set; }
    public Texture2D Texture { set; get; }
    public SpriteBatch SpriteBatch { get; set; }
}

to here?

public class Sprite : IPositionable
{
    public Vector2 Position { get; set; }
    public Texture2D Texture { set; get; }
    public SpriteBatch SpriteBatch { get; set; }
}

Both sprites have the same functionality, but only one of them explicitly tells the compiler that it can be used anywhere that an IPositionable is needed.

What this means is that we can reference very different kinds of classes through the same datatype (interface): IPositionable. Let's consider a control we've made for a game, a TextBox, which we want to move on to the screen for receiving input and then move back out of the screen:

public class TextBox : IPositionable
{
	public string Text { get; set;}
	public SpriteFont Font {get; set}
	...etc...
	public Vector2 Position {get; set;}
}

Since both the Sprite and TextBox above are IPositionables, they will both be able to be referenced through a variable of type IPositionable.

IPositionable textbox = new TextBox();
IPositionable sprite = new Sprite();

Furthermore, any method we write which accepts an IPositionable will take either the Sprite or the TextBox gladly, and work with the Position property Smiley.

Creating the AnimationController

Okay - now that you know how we can define the bare minimum for an external class to alter the position of something, let's start doing it.

What we will need

  • a way of storing all the IPositionables together with the positions where we want them to end up
  • an Update method to move all IPositionables in small steps closer and closer to their destination
  • a way of telling from the outside whether all IPositionables have reached their destination

Storing all the IPositionables together with the positions where we want them to end up

A very efficient way of storing objects as KeyValuePairs, where you can use one of them to look up the other one is the Dictionary class.

Using this, we can define the type of the item we use for lookups and the other object to store with it. And voilà we automatically have a list of "things with a position" and their destinations:

public class AnimationController
{
    private readonly Dictionary<IPositionable, Vector2> PositionablesWhichHaventCompletedMovement 
        = new Dictionary<IPositionable, Vector2>();
}

To enable people to add items, we also add a public method to store the items as pairs:

public void MoveTo(IPositionable positionableToMove, Vector2 destination)
{    
    PositionablesWhichHaventCompletedMovement.Add(positionableToMove, destination); 
}

An Update method to move all IPositionables in small steps closer and closer to their destination

This Update() method will

  • figure out the direction to move from from where the IPositionables are now
  • calculate a reasonable "step" to take in this Update()
  • move the IPositionable that step
  • remove all the IPositionable which have reached their goal from the dictionary

Note: We will be using some vector math here. If you are unfamiliar with using vectors for directions and steps in XNA, have a look at this tutorial.

We will also require the current GameTime to be sent to the Update method to smooth the animation. We will multiply the length of each step with the time passed since last Update().

Figure out the direction to move from from where the IPositionables are now

foreach (var positionableAndTarget in PositionablesWhichHaventCompletedMovement)
{
    IPositionable positionable = positionableAndTarget.Key;
    Vector2 destination = PositionablesWhichHaventCompletedMovement[positionable];
    Vector2 remainingMovement = destination - positionable.Position;
}

this way we have the entire remaining movement in which variable? ... you guessed it: remainingMovement!

The positionableAndTarget is the KeyValuePair I mentioned earlier, which has both the IPositionable (the Key) and its Vector2 destination (the Value).

Calculate a reasonable "step" to take in this Update()

Vector2 oneStep =  remainingMovement * 6f * (float)gameTime.ElapsedGameTime.TotalSeconds;

Note that we use the total seconds passed since last update in the equation. This will usually be around 1/60th of a second, which means that one step will even out at around 6/60ths or 1/10th (10%) of the remaining movement each update. This gives us a nice slowing down effect as 10% becomes fewer and fewer pixels as the remaining movement is diminished.

Move the IPositionable that step

positionable.Position += oneStep;

Remove all the IPositionable which have reached their goal from the dictionary

Now we *could* just run through the list again, and remove all the keyvaluepairs, where the position of the IPositionable and the destination are the same, but since we already iterate over the list once, let's just create another list "FinishedPositionables" and store all the finished IPositionables in that. Then we can run through that (presumably much shorter) list of finished positionables afterwards and remove those from the dictionary.

public void Update(GameTime gameTime)
{
    List<IPositionable> FinishedPositionables = new List<IPositionable>();
    foreach (var positionableAndTarget in PositionablesWhichHaventCompletedMovement)
    {
        IPositionable positionable = positionableAndTarget.Key;
        Vector2 destination = PositionablesWhichHaventCompletedMovement[positionable];
        Vector2 remainingMovement = destination - positionable.Position;
        Vector2 oneStep = remainingMovement * 6f * (float)gameTime.ElapsedGameTime.TotalSeconds;
        positionable.Position += oneStep;
        if (remainingMovement.Length() == 0)
        {
            positionable.Position = destination;
            FinishedPositionables.Add(positionable);
        }
    }
    foreach (var finishedPositionable in FinishedPositionables)
    {
        PositionablesWhichHaventCompletedMovement.Remove(finishedPositionable);
    }
}

A way of telling from the outside whether all IPositionables have reached their destination

This one is easy. If the list of IPositionables is empty, we're done. We will implement this as a read-only property:

public bool Done {
	get { return PositionablesWhichHaventCompletedMovement.Count == 0; } 
}

This property returns true when Count is zero and false in other cases (meaning that there are still items in the list).

So here is our code now:

 

public class AnimationController
{
    private readonly Dictionary<IPositionable, Vector2> PositionablesWhichHaventCompletedMovement = new Dictionary<IPositionable, Vector2>();
    private readonly List<IPositionable> FinishedPositionables = new List<IPositionable>();
    public bool Done { get { return PositionablesWhichHaventCompletedMovement.Count == 0; } }

    public void Update(GameTime gameTime)
    {

        FinishedPositionables.Clear();
        foreach (var positionableAndTarget in PositionablesWhichHaventCompletedMovement)
        {
            IPositionable positionable = positionableAndTarget.Key;
            Vector2 destination = PositionablesWhichHaventCompletedMovement[positionable];
            Vector2 remainingMovement = destination - positionable.Position;
            Vector2 oneStep = remainingMovement * 6f * (float)gameTime.ElapsedGameTime.TotalSeconds;
            positionable.Position += oneStep;
            if (remainingMovement.Length() < 1f)
            {
                positionable.Position = destination;
                FinishedPositionables.Add(positionable);
            }
        }
        RemoveAllFinishedPositionables();
    }

    private void RemoveAllFinishedPositionables()
    {
        FinishedPositionables.ForEach(inplace => PositionablesWhichHaventCompletedMovement.Remove(inplace));
    }

    public void MoveTo(IPositionable positionableToMove, Vector2 destination)
    {
        PositionablesWhichHaventCompletedMovement.Add(positionableToMove, destination);
    }
}

Adding a useful method

In our case we have a level setup, with all the tiles in their final positions, and we want to animate them from somewhere else to where they already are. This means that we will have to move the tiles away (maybe to individually random places) while storing their original position, and then pass the moved Tile along with the original position to the MoveTo() method. This complicates things unnecessarily Smiley. So why don't we just add a method which does exactly this for os.

Introducing the MoveBackFromPosition() method Smiley med åben mund. Try reading it and see whether you can figure out how this method will help us make cleaner code outside, by just sending it some IPositionable and where to move it back from.

public void MoveBackFromPosition(IPositionable positionableToMove, Vector2 startingPosition)
{
    Vector2 endPosition = positionableToMove.Position;
    positionableToMove.Position = startingPosition;
    MoveTo(positionableToMove, endPosition);
}

Basically it stores the current position (endPosition) of the IPositionable, and then moves the IPositionable out to the startingPosition, and then calls the MoveTo method to start the long move back - nifty, eh? Smiley, der blinker.

Putting the level in place in random ways

Since we now have the AnimationController class working, let's test it Smiley.

All we need is a way to insert the original tiles and some starting point.

The easiest way to perform this would be to have all the tiles start out in the top left corner (0,0) and then move to their respective places. Like this:

private void RandomizeTilePositions()
{
    foreach (var tile in Tiles)
    {
        AnimationController.MoveBackFromPosition(tile, Vector2.Zero);
    }
}

...where we use the MoveBackFromPosition method above to "teleport" all the tiles to (0,0) and then have the AnimationController move the tiles back whenever we call its Update().

Giving this result (slowed to 1/4th of ordinary movement to show it more clearly):

This way we could have all sorts of fun mathematical ways of sending Tiles to another starting position and then moving them back, spicing up the "getting ready for next level" part of a game Smiley.

"But why doesn't the Jumper fall while we're putting everything into place?" I hear you ask Smiley, der blinker.

Well, that's because in SimplePlatformerGame.Update(), I've put an IF around the update of the Jumper:

if (_board.AnimationController.Done)
{
    _jumper.Update(gameTime); 
}

..so it doesn't move before we're done setting up the board Smiley.

Getting Funky with "Func"

To spice things up even further, we want to randomize things a bit, by having different ways of putting the tiles in place. We can then "roll a die" and see which method to use for this "setting up the level", or just take them one at a time.

This could be done in the Board class like this:

Random rnd = new Random();

switch (rnd.Next(3))
{
    case 0: //move out from top left corner (0,0)
        foreach (var tile in Tiles)
        {
            AnimationController.MoveBackFromPosition(tile, Vector2.Zero);
        }
        break;
    case 1: //move from a random place
        foreach (var tile in Tiles)
        {
            AnimationController.MoveBackFromPosition(tile, new Vector2(rnd.Next(1000), rnd.Next(1000)));
        }
        break;
    case 2: //swap x and y axis to &quot;unfold&quot; level
        foreach (var tile in Tiles)
        {
            AnimationController.MoveBackFromPosition(tile, new Vector2(tile.Position.Y, tile.Position.X));
        }
        break;
                
}

...but it tends to get long-winded and has the loop-code in every case in the switch. And as soon as we start writing the same code over and over - programmers think "do this in a function!".

Here, what we want to swap out is what happens inside the foreach loop, so we would need some way of changing what code is called from there.

Func to the rescue!

In recent versions of C# we are able to reference functions using the Action (for functions which don't return a value) and Func (for functions which do). This way we can add a method to our AnimationController which accepts a Func with this signature:

Func<IPositionable, Vector2>

This means: The Func variable of this type accepts an IPositionable and returns a Vector2.

Now we can store a list of these kinds of references and just pass them to this method on AnimationController:

public void MoveBackFromPosition(IPositionable positionableToMove, Func<IPositionable, Vector2> startPositioningMethod)
{
    Vector2 endPosition = positionableToMove.Position;
    positionableToMove.Position = startPositioningMethod(positionableToMove);
    MoveTo(positionableToMove, endPosition);
}

Compare it to the MoveBackFromPosition method we already had:

public void MoveBackFromPosition(IPositionable positionableToMove, Vector2 startingPosition)
{
    Vector2 endPosition = positionableToMove.Position;
    positionableToMove.Position = startingPosition;
    MoveTo(positionableToMove, endPosition);
}

and you'll see they are awfully similar.

All we have changed is that in the new method:

  • we accept "a function which can give us a Vector2 when we feed it an IPositionable
  • we use this function to give us the new starting position and then move to the original position as usual

This would enable us to rewrite the "randomized entry" code in the Board class above like this:

Random rnd = new Random();

public Vector2 FromTopLeft(IPositionable tile) { return Vector2.Zero; }
public Vector2 FromRandomPosition(IPositionable tile) { return new Vector2(rnd.Next(1000), rnd.Next(1000)); }
public Vector2 FromSwappedXandY(IPositionable tile) { return new Vector2(tile.Position.Y, tile.Position.X); }

private void RandomizeTilePositions()
{

    Func<IPositionable, Vector2> selectedPositioningFunction = null;

    switch (rnd.Next(3))
    {
        case 0: //move out from top left corner (0,0)
            selectedPositioningFunction = FromTopLeft;
            break;
        case 1: //move from a random place
            selectedPositioningFunction = FromRandomPosition;
            break;
        case 2: //swap x and y axis to &quot;unfold&quot; level
            selectedPositioningFunction = FromSwappedXandY;
            break;
    }

    foreach (var tile in Tiles)
    {
        AnimationController.MoveBackFromPosition(tile, selectedPositioningFunction);
    }
}

At the top we declare and instantiate a Random for rolling dice, we create three methods which fit our Func signature, and in the RandomizeTilePositions, we find a reference to one of the three, and then use that in the foreach loop.

Very elegant - I like it! Smiley

Let's see it in action:

Learning to love lambda expressions

Since we're not doing a whole lot in our "IPositionable to Vector2" methods, we could sum them up using Lambda Expressions. If you don't feel ready to take this last step, just stick with the code we have created already. It's fine, and Lambda Expressions, though elegant, are not guaranteed to make you happier. Basically, they are an even more compact way of writing methods. If you're still up to a bit more learning, you can follow along and see what the benefits are Smiley.

The central part of a Lambda Expression is an arrow

=>

... which separates the parameter(s) on the left and the return value on the right.

Let's take the FromSwappedXandY() method and rewrite it using lambda:

//regular method
public Vector2 FromSwappedXandY(IPositionable tile) { return new Vector2(tile.Position.Y, tile.Position.X); }

//lambda method
tile => new Vector2(tile.Position.Y, tile.Position.X);

Notice that we don't have the return keyword either, as the Lambda knows that "I should return whatever is to the right of the arrow".

Syntactically you can't place a Lambda Expression on the class like we do with regular methods. Lambda Expressions are meant to be used right away (as a parameter to a method which accepts a Func/Action of that type) or stored in a variable for use later. Now we can rewrite the RandomizeTilePositions as follows:

Random rnd = new Random();

private void RandomizeTilePositions()
{

    Func<IPositionable, Vector2> selectedPositioningFunction = null;

    switch (rnd.Next(3))
    {
        case 0: //move out from top left corner (0,0)
            selectedPositioningFunction = tile => Vector2.Zero;
            break;
        case 1: //move from a random place
            selectedPositioningFunction = tile => new Vector2(rnd.Next(1000), rnd.Next(1000));
            break;
        case 2: //swap x and y axis to &quot;unfold&quot; level
            selectedPositioningFunction = tile => new Vector2(tile.Position.Y, tile.Position.X);
            break;
    }

    foreach (var tile in Tiles)
    {
        AnimationController.MoveBackFromPosition(tile, selectedPositioningFunction);
    }
}

When replacing regular methods (with a meaningful name) with a Lambda Expression, what you have to be careful about is losing readability. Your code will almost invariably be harder to understand the more succinct it is. You must choose how to find that balance Smiley

So why don't we go ahead and store a little toolbox of positioning methods in a class where we can get the Funcs from when needed?

Putting a bow on it

I have taken eight different ways of animating the tiles into place (see video at the top of this tutorial), and stored them in variable with meaningful names. This is a principle of Clean Code, to avoid comments and use self-commenting code.

Func<IPositionable, Vector2> fromTopLeftCorner = tile =>  Vector2.Zero; ;
Func<IPositionable, Vector2> fromTopRow = tile =>  new Vector2(tile.Position.X, 0); ;
Func<IPositionable, Vector2> fromRandomStartingPoint = tile =>  new Vector2(Rnd.Next(1000), Rnd.Next(1000));
Func<IPositionable, Vector2> fromCenter = tile =>  Vector2.One * 500; ;
Func<IPositionable, Vector2> fromLeftColumn = tile =>  new Vector2(0, tile.Position.Y);
Func<IPositionable, Vector2> fromBelow = tile => tile.Position + Vector2.UnitY * 1000; 
Func<IPositionable, Vector2> fromScaledOut = tile => tile.Position * 5 - Vector2.One * 64;
Func<IPositionable, Vector2> fromSwappedXY = tile => new Vector2(tile.Position.Y, tile.Position.X); 

You might want to remove the absolute values I've used (500, 1000, etc.) and rewrite this code for use in any screen resolution, but you get the idea from this Smiley.

These methods I place in a static class, so there is no need to "new it up", and store them in a list for easy access.

public static class PositioningFunctions
{
    public static readonly List<Func<IPositionable, Vector2>> positioningFunctions;
    private static readonly Random Rnd = new Random();

    static PositioningFunctions()
    {
        Func<IPositionable, Vector2> fromTopLeftCorner = tile =>  Vector2.Zero;
        Func<IPositionable, Vector2> fromTopRow = tile =>  new Vector2(tile.Position.X, 0);
        Func<IPositionable, Vector2> fromRandomStartingPoint = tile =>  new Vector2(Rnd.Next(1000), Rnd.Next(1000));
        Func<IPositionable, Vector2> fromCenter = tile =>  Vector2.One * 500;
        Func<IPositionable, Vector2> fromLeftColumn = tile =>  new Vector2(0, tile.Position.Y);
        Func<IPositionable, Vector2> fromBelow = tile => tile.Position + Vector2.UnitY * 1000; 
        Func<IPositionable, Vector2> fromScaledOut = tile => tile.Position * 5 - Vector2.One * 64;
        Func<IPositionable, Vector2> fromSwappedXY = tile => new Vector2(tile.Position.Y, tile.Position.X); 

        positioningFunctions = new List<Func<IPositionable, Vector2>>() 
        {    fromTopRow, fromTopLeftCorner, fromRandomStartingPoint, fromCenter,
              fromLeftColumn, fromBelow, fromScaledOut, fromSwappedXY };
    }
}

Finally I add a method to get a Func and move to the next using a private variable, so we end up with this:

public static class PositioningFunctions
{
    private static readonly List<Func<IPositionable, Vector2>> positioningFunctions;
    private static readonly Random Rnd = new Random();
    static int _currentAnim;

    static PositioningFunctions()
    {
        Func<IPositionable, Vector2> fromTopLeftCorner = tile => Vector2.Zero; ;
        Func<IPositionable, Vector2> fromTopRow = tile => new Vector2(tile.Position.X, 0); ;
        Func<IPositionable, Vector2> fromRandomStartingPoint = tile => new Vector2(Rnd.Next(1000), Rnd.Next(1000));
        Func<IPositionable, Vector2> fromCenter = tile => Vector2.One * 500; ;
        Func<IPositionable, Vector2> fromLeftColumn = tile => new Vector2(0, tile.Position.Y);
        Func<IPositionable, Vector2> fromBelow = tile => tile.Position + Vector2.UnitY * 1000;
        Func<IPositionable, Vector2> fromScaledOut = tile => tile.Position * 5 - Vector2.One * 64;
        Func<IPositionable, Vector2> fromSwappedXY = tile => new Vector2(tile.Position.Y, tile.Position.X);

        positioningFunctions = new List<Func<IPositionable, Vector2>>() 
        {    fromTopRow, fromTopLeftCorner, fromRandomStartingPoint, fromCenter,
              fromLeftColumn, fromBelow, fromScaledOut, fromSwappedXY };
    }

    public static Func<IPositionable, Vector2> NextPositioningFunction
    {
        get
        {
            _currentAnim++;
            if (_currentAnim == positioningFunctions.Count)
            {
                _currentAnim = 0;
            }
            return positioningFunctions[_currentAnim];
        }
    }
}

This is nice! We have definitions of the setup logic in variables with meaningful names. We have stored them in a list (which I have now made private to only enable access through the readonly NextPositioningFunction property), and we have an autoincrement for every get().

Now our Board class' RandomizeTilePositions looks like this:

private void RandomizeTilePositions()
{
    Func<IPositionable, Vector2> positioningFunctionToUse = PositioningFunctions.NextPositioningFunction;

    foreach (var tile in Tiles)
    {
        AnimationController.MoveBackFromPosition(tile, positioningFunctionToUse);
    }
}

Which is how I want it - clean and readable!

Source code (XNA 4.0, VS 2010)

Here is the completed solution.

I hope you learned something useful along the way. Thanks for reading! Smiley

]]>
http://xnafan.net/2013/05/simple-animationcontroller-for-animating-game-objects/feed/ 0
Simple platformer game in XNA tutorial – part six "jumping and stopping" http://xnafan.net/2013/04/simple-platformer-game-in-xna-tutorial-part-six-jumping-and-stopping/ http://xnafan.net/2013/04/simple-platformer-game-in-xna-tutorial-part-six-jumping-and-stopping/#comments Sun, 28 Apr 2013 06:15:17 +0000 http://xnafan.net/?p=1009 image

Previously on xnafan.net...

We looked at how to improve collision detection, and use recursion to have a method call itself.

In this final installment we will

  • enable Jumper to Jump
  • stop movement when blocked
  • refactor our collision code to increase readability
  • make a few minor improvements

So let's get to it Smiley

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.

public bool IsOnFirmGround()
{
        Rectangle onePixelLower = Bounds;
        onePixelLower.Offset(0, 1);
        return !Board.CurrentBoard.HasRoomForRectangle(onePixelLower);
}

Rectangle.Offset() moves a Rectangle in the direction given in the x and y parameters

Change the Up key's code

...so we only allow jumps if Jumper is on firm ground. And then boost the upward movement with a value you like for his movement.

if (keyboardState.IsKeyDown(Keys.Up) &amp;&amp; IsOnFirmGround())
{
    Movement = -Vector2.UnitY * 25;
}

Important!

Notice that I've changed the Movement -= Vector2.UnitY * 25;

to Movement = -Vector2.UnitY * 25;

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.

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.WhiteSmoke);
    _spriteBatch.Begin();
    base.Draw(gameTime);
    _board.Draw();
    WriteDebugInformation();
    _jumper.Draw();
    _spriteBatch.End();
}

private void WriteDebugInformation()
{
    string positionInText = string.Format(&quot;Position of Jumper: ({0:0.0}, {1:0.0})&quot;, _jumper.Position.X, _jumper.Position.Y);
    string movementInText = string.Format(&quot;Current movement: ({0:0.0}, {1:0.0})&quot;, _jumper.Movement.X, _jumper.Movement.Y);
    string isOnFirmGroundText = string.Format(&quot;On firm ground? : {0}&quot;, _jumper.IsOnFirmGround());

    _spriteBatch.DrawString(_debugFont, positionInText, new Vector2(10, 0), Color.White);
    _spriteBatch.DrawString(_debugFont, movementInText, new Vector2(10, 20), Color.White);
    _spriteBatch.DrawString(_debugFont, isOnFirmGroundText, new Vector2(10, 40), Color.White);
}

Stop movement when blocked

This is as simple as looking at what WhereCanIGetTo returns, and ensure that movement on either axis is set to zero if that direction was blocked.

To be able to find out whether we were able to move in this update, we need access to the previous position of Jumper, so add a private member variable to Jumper

private Vector2 oldPosition;

and a method:

private void StopMovingIfBlocked()
{
    Vector2 lastMovement = Position - oldPosition;
    if (lastMovement.X == 0) { Movement *= Vector2.UnitY; }
    if (lastMovement.Y == 0) { Movement *= Vector2.UnitX; }
}

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():

public void Update(GameTime gameTime)
{
    CheckKeyboardAndUpdateMovement();
    AffectWithGravity();
    SimulateFriction();
    MoveAsFarAsPossible(gameTime);
    StopMovingIfBlocked();
}

This is my final Jumper code

public class Jumper : Sprite
{
    public Vector2 Movement { get; set; }
    private Vector2 oldPosition;
    public Jumper(Texture2D texture, Vector2 position, SpriteBatch spritebatch)
        : base(texture, position, spritebatch)
    { }

    public void Update(GameTime gameTime)
    {
        CheckKeyboardAndUpdateMovement();
        AffectWithGravity();
        SimulateFriction();
        MoveAsFarAsPossible(gameTime);
        StopMovingIfBlocked();
    }

    private void CheckKeyboardAndUpdateMovement()
    {
        KeyboardState keyboardState = Keyboard.GetState();

        if (keyboardState.IsKeyDown(Keys.Left)) { Movement -= Vector2.UnitX; }
        if (keyboardState.IsKeyDown(Keys.Right)) { Movement += Vector2.UnitX; }
        if (keyboardState.IsKeyDown(Keys.Up) &amp;&amp; IsOnFirmGround())
        {
            Movement = -Vector2.UnitY * 20; 
        }
    }

    private void AffectWithGravity()
    {
        Movement += Vector2.UnitY * .65f;
    }

    private void SimulateFriction()
    {
        Movement -= Movement * Vector2.One * .03f;
    }

    private void MoveAsFarAsPossible(GameTime gameTime)
    {
        oldPosition = Position;
        UpdatePositionBasedOnMovement(gameTime);
        Position = Board.CurrentBoard.WhereCanIGetTo(oldPosition, Position, Bounds);
    }

    private void UpdatePositionBasedOnMovement(GameTime gameTime)
    {
        Position += Movement * (float)gameTime.ElapsedGameTime.TotalMilliseconds / 15;
    }

    public bool IsOnFirmGround()
    {
        Rectangle onePixelLower = Bounds;
        onePixelLower.Offset(0, 1);
        return !Board.CurrentBoard.HasRoomForRectangle(onePixelLower);
    }

    private void StopMovingIfBlocked()
    {
        Vector2 lastMovement = Position - oldPosition;
        if (lastMovement.X == 0) { Movement *= Vector2.UnitY; }
        if (lastMovement.Y == 0) { Movement *= Vector2.UnitX; }
    }
}

Refactor collision code to improve readability

If you go back and look at Board's WhereCanIGetTo method, you'll see that it is over thirty lines long:

public Vector2 WhereCanIGetTo(Vector2 originalPosition, Vector2 destination, Rectangle boundingRectangle)
{

    Vector2 movementToTry = destination - originalPosition;
    Vector2 furthestAvailableLocationSoFar = originalPosition;
    int numberOfStepsToBreakMovementInto = (int)(movementToTry.Length() * 2) + 1;
    Vector2 oneStep = movementToTry / numberOfStepsToBreakMovementInto;

    for (int i = 1; i &lt;= numberOfStepsToBreakMovementInto; i++)
    {
        Vector2 positionToTry = originalPosition + oneStep * i;
        Rectangle newBoundary = CreateRectangleAtPosition(positionToTry, boundingRectangle.Width, boundingRectangle.Height);
        if (HasRoomForRectangle(newBoundary)) { furthestAvailableLocationSoFar = positionToTry; }
        else
        {
            bool isDiagonalMove = movementToTry.X != 0 &amp;&amp; movementToTry.Y != 0;
            if (isDiagonalMove)
            {
                int stepsLeft = numberOfStepsToBreakMovementInto - (i - 1);

                Vector2 remainingHorizontalMovement = oneStep.X * Vector2.UnitX * stepsLeft;
                Vector2 finalPositionIfMovingHorizontally = furthestAvailableLocationSoFar + remainingHorizontalMovement;
                furthestAvailableLocationSoFar =
                    WhereCanIGetTo(furthestAvailableLocationSoFar, finalPositionIfMovingHorizontally, boundingRectangle);

                Vector2 remainingVerticalMovement = oneStep.Y * Vector2.UnitY * stepsLeft;
                Vector2 finalPositionIfMovingVertically = furthestAvailableLocationSoFar + remainingVerticalMovement;
                furthestAvailableLocationSoFar =
                    WhereCanIGetTo(furthestAvailableLocationSoFar, finalPositionIfMovingVertically, boundingRectangle);
            }
            break;
        }
    }
    return furthestAvailableLocationSoFar;
}

    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 Smiley, der blinker.

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

public struct MovementWrapper
{
    public Vector2 MovementToTry { get; private set; }
    public Vector2 FurthestAvailableLocationSoFar { get; set; }
    public int NumberOfStepsToBreakMovementInto { get; private set; }
    public bool IsDiagonalMove { get; private set; }
    public Vector2 OneStep { get; private set; }
    public Rectangle BoundingRectangle { get; set; }

    public MovementWrapper(Vector2 originalPosition, Vector2 destination, Rectangle boundingRectangle) : this()
    {
        MovementToTry = destination - originalPosition;
        FurthestAvailableLocationSoFar = originalPosition;
        NumberOfStepsToBreakMovementInto = (int)(MovementToTry.Length() * 2) + 1;
        IsDiagonalMove = MovementToTry.X != 0 &amp;&amp; MovementToTry.Y != 0;
        OneStep = MovementToTry / NumberOfStepsToBreakMovementInto;
        BoundingRectangle = boundingRectangle;
    }
}

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 Smiley, der blinker. Now see what our WhereCanIGetTo method looks like

public Vector2 WhereCanIGetTo(Vector2 originalPosition, Vector2 destination, Rectangle boundingRectangle)
{
    MovementWrapper move = new MovementWrapper(originalPosition, destination);

    for (int i = 1; i &lt;= move.NumberOfStepsToBreakMovementInto; i++)
    {
        Vector2 positionToTry = originalPosition + move.OneStep * i;
        Rectangle newBoundary = CreateRectangleAtPosition(positionToTry, boundingRectangle.Width, boundingRectangle.Height);
        if (HasRoomForRectangle(newBoundary)) {move.FurthestAvailableLocationSoFar = positionToTry; }
        else
        {
            if (move.IsDiagonalMove)
            {
                int stepsLeft = move.NumberOfStepsToBreakMovementInto - (i - 1);

                Vector2 remainingHorizontalMovement = move.OneStep.X * Vector2.UnitX * stepsLeft;
                Vector2 finalPositionIfMovingHorizontally = move.FurthestAvailableLocationSoFar + remainingHorizontalMovement;
                move.FurthestAvailableLocationSoFar =
                    WhereCanIGetTo(move.FurthestAvailableLocationSoFar, finalPositionIfMovingHorizontally, boundingRectangle);

                Vector2 remainingVerticalMovement = move.OneStep.Y * Vector2.UnitY * stepsLeft;
                Vector2 finalPositionIfMovingVertically = move.FurthestAvailableLocationSoFar + remainingVerticalMovement;
                move.FurthestAvailableLocationSoFar =
                    WhereCanIGetTo(move.FurthestAvailableLocationSoFar, finalPositionIfMovingVertically, boundingRectangle);
            }
            break;
        }
    }
    return move.FurthestAvailableLocationSoFar;
}

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.

private Vector2 CheckPossibleNonDiagonalMovement(MovementWrapper wrapper, int i)
{
    if (wrapper.IsDiagonalMove)
    {
        int stepsLeft = wrapper.NumberOfStepsToBreakMovementInto - (i - 1);

        Vector2 remainingHorizontalMovement = wrapper.OneStep.X * Vector2.UnitX * stepsLeft;
        wrapper.FurthestAvailableLocationSoFar = 
            WhereCanIGetTo(wrapper.FurthestAvailableLocationSoFar, wrapper.FurthestAvailableLocationSoFar + remainingHorizontalMovement, wrapper.BoundingRectangle);

        Vector2 remainingVerticalMovement = wrapper.OneStep.Y * Vector2.UnitY * stepsLeft;
        wrapper.FurthestAvailableLocationSoFar = 
            WhereCanIGetTo(wrapper.FurthestAvailableLocationSoFar, wrapper.FurthestAvailableLocationSoFar + remainingVerticalMovement, wrapper.BoundingRectangle);
    }

    return wrapper.FurthestAvailableLocationSoFar;
}

...then go back to WhereCanIGetTo and use our new method

public Vector2 WhereCanIGetTo(Vector2 originalPosition, Vector2 destination, Rectangle boundingRectangle)
{
    MovementWrapper move = new MovementWrapper(originalPosition, destination, boundingRectangle);

    for (int i = 1; i &lt;= move.NumberOfStepsToBreakMovementInto; i++)
    {
        Vector2 positionToTry = originalPosition + move.OneStep * i;
        Rectangle newBoundary = 
            CreateRectangleAtPosition(positionToTry, boundingRectangle.Width, boundingRectangle.Height);
        if (HasRoomForRectangle(newBoundary)) {move.FurthestAvailableLocationSoFar = positionToTry; }
        else
        {
            if (move.IsDiagonalMove)
            {
                move.FurthestAvailableLocationSoFar = CheckPossibleNonDiagonalMovement(move, i);
            }
            break;
        }
    }
    return move.FurthestAvailableLocationSoFar;
}

That's much better! Smiley med åben mund

Now I'm satisfied with our codebase - I hope you are too Smiley.

Make a few minor improvements

First of all, let's implement some code to restart with a random level whenever we want to.

So go ahead and move the initialization code of the Board constructor into a separate method like this

public Board(SpriteBatch spritebatch, Texture2D tileTexture, int columns, int rows)
{
    Columns = columns;
    Rows = rows;
    TileTexture = tileTexture;
    SpriteBatch = spritebatch;
    Tiles = new Tile[Columns, Rows];
    CreateNewBoard();
    Board.CurrentBoard = this;
}

public void CreateNewBoard()
{
    InitializeAllTilesAndBlockSomeRandomly();
    SetAllBorderTilesBlocked();
    SetTopLeftTileUnblocked();
}

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:

protected override void Update(GameTime gameTime)
{
    base.Update(gameTime);
    _jumper.Update(gameTime);
    CheckKeyboardAndReact();
}

private void CheckKeyboardAndReact()
{
    KeyboardState state = Keyboard.GetState();
    if (state.IsKeyDown(Keys.F5)) { RestartGame(); }
}

private void RestartGame()
{
    Board.CurrentBoard.CreateNewBoard();
    PutJumperInTopLeftCorner();
}

private void PutJumperInTopLeftCorner()
{
    _jumper.Position = Vector2.One * 80;
    _jumper.Movement = Vector2.Zero;
}

Add some code to let the player exit game when ESC is pressed:

private void CheckKeyboardAndReact()
{
    KeyboardState state = Keyboard.GetState();
    if (state.IsKeyDown(Keys.F5)) { RestartGame(); }
    if (state.IsKeyDown(Keys.Escape)) { Exit(); }
}

More friction on ground

If you want to you could also add extra friction when Jumper is gliding on a surface

private void SimulateFriction()
{
    if (IsOnFirmGround()) { Movement -= Movement * Vector2.One * .08f; }
    else { Movement -= Movement * Vector2.One * .02f; }
}

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 Smiley med åben mund

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 Smiley

The final code

Is here.

/Jake "xnafan"

]]>
http://xnafan.net/2013/04/simple-platformer-game-in-xna-tutorial-part-six-jumping-and-stopping/feed/ 5
Simple platformer game in XNA tutorial – part five "Improved collision detection" http://xnafan.net/2013/04/simple-platformer-game-in-xna-tutorial-part-five-improved-collision-detection/ http://xnafan.net/2013/04/simple-platformer-game-in-xna-tutorial-part-five-improved-collision-detection/#comments Sat, 27 Apr 2013 15:46:18 +0000 http://xnafan.net/?p=996 image

Previously on xnafan.net...

We looked at implementing simple collision detection, but still had a way to go before we're satisfied with the result.

Agenda

In this part of the tutorial we will

  • add gravity
  • add some debug info to the game
  • add more realistic collision and movement to the Jumper

Adding gravity

Go ahead and add a new line AffectWithGravity() to Jumper's Update():

public void Update(GameTime gameTime)
{
    CheckKeyboardAndUpdateMovement();
    AffectWithGravity();
    SimulateFriction();
    MoveIfPossible(gameTime);
}

Add the AffectWithGravity() method as well

private void AffectWithGravity()
{
    Movement += Vector2.UnitY * .5f;
}

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:

image

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:

image

and name it "DebugFont"

image

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:

protected override void Draw(GameTime gameTime)
{
    string positionInText =
        string.Format("Position of Jumper: ({0:0.0}, {1:0.0})", _jumper.Position.X, _jumper.Position.Y);
    string movementInText =
        string.Format("Current movement: ({0:0.0}, {1:0.0})", _jumper.Movement.X, _jumper.Movement.Y);

    GraphicsDevice.Clear(Color.WhiteSmoke);
    _spriteBatch.Begin();
    base.Draw(gameTime);
    _board.Draw();
    _spriteBatch.DrawString(_debugFont, positionInText, new Vector2(10, 0), Color.White);
    _spriteBatch.DrawString(_debugFont, movementInText, new Vector2(10, 20), Color.White);
    _jumper.Draw();
    _spriteBatch.End();
}

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.

image

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.

image

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
  • returns where it was possible to move the Rectangle to

Creating the WhereCanIGetTo() method

Go ahead and add a method WhereCanIGetTo() to the Board class:

public Vector2 WhereCanIGetTo(Vector2 originalPosition, Vector2 destination, Rectangle boundingRectangle)
{
}

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

int numberOfStepsToBreakMovementInto = (int)(movementToTry.Length() * 2) + 1;

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.

private Rectangle CreateRectangleAtPosition(Vector2 positionToTry, int width, int height)
{
    return new Rectangle((int)positionToTry.X, (int)positionToTry.Y, width, height);
}

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 Smiley.

public Vector2 WhereCanIGetTo(Vector2 originalPosition, Vector2 destination, Rectangle boundingRectangle)
{
    Vector2 movementToTry = destination - originalPosition;
    Vector2 furthestAvailableLocationSoFar = originalPosition;
    int numberOfStepsToBreakMovementInto = (int)(movementToTry.Length() * 2) + 1;
    Vector2 oneStep = movementToTry / numberOfStepsToBreakMovementInto;

    for (int i = 1; i <= numberOfStepsToBreakMovementInto; i++)
    {
        Vector2 positionToTry = originalPosition + oneStep * i;
        Rectangle newBoundary = 
            CreateRectangleAtPosition(positionToTry, boundingRectangle.Width, boundingRectangle.Height);
        if (HasRoomForRectangle(newBoundary)) { furthestAvailableLocationSoFar = positionToTry; }
        else { break; }
    }
    return furthestAvailableLocationSoFar;
}

Q: Why do you iterate from "1 to i <= numberOfSteps..." instead of "0 to i < numberOfSteps..." ?

A: Because it wouldn't make much sense to take a "zero" step which was right on top of the original position.

If we did, positionToTry would be originalPosition + (oneStep * 0) on the first step, and then we would end up one step from the finishing point instead of on it.

Figure it this way: We want to move ten meters, do we do this in ten steps: 0 meters, then 1 meters ... up to 9 meters, or would it make more sense to move 1 meter, then 2 meters ... up to 10 meters?

Finally, change the MoveIfPossible() method on Jumper. Delete the last line where we let any movement depend on whether the final destination was unblocked:

if (!Board.CurrentBoard.HasRoomForRectangle(Bounds)) { Position = oldPosition; }

and change it to:

Position = Board.CurrentBoard.WhereCanIGetTo(oldPosition, Position, Bounds);

...so we move as far as possible along the wanted route instead Smiley

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:

private void MoveAsFarAsPossible(GameTime gameTime)
{
    Vector2 oldPosition = Position;
    UpdatePositionBasedOnMovement(gameTime);
    Position = Board.CurrentBoard.WhereCanIGetTo(oldPosition, Position, Bounds);
}

Press F5 to run the program and try moving around. It is better, but not perfect. Especially because we can "stick to walls" :).

image

Here's a little status quo explanation:

If you start out stuck...

Sometimes your little Jumper will start out stuck in a Tile like this:

image

To make sure that he is born a free little Sprite, you can set that tile's IsBlocked property to false when you construct a board object.

Create a well named method like this:

private void SetTopLeftTileUnblocked()
{
    Tiles[1, 1].IsBlocked = false;
}

And then call SetTopLeftTileUnblocked() in Board's constructor:

public Board(SpriteBatch spritebatch, Texture2D tileTexture, int columns, int rows)
{
    Columns = columns;
    Rows = rows;
    TileTexture = tileTexture;
    SpriteBatch = spritebatch;
    Tiles = new Tile[Columns, Rows];
    InitializeAllTilesAndBlockSomeRandomly();
    SetAllBorderTilesBlocked();
    SetTopLeftTileUnblocked();
    Board.CurrentBoard = 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.

image

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 by Vector2.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 (HasRoomForRectangle(newBoundary)) { furthestAvailableLocationSoFar = positionToTry; }
    else { 
    //here! :)
    break; 
    }
    

    "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! :)).

    Add an if where we can add code to handle remaining diagonal movement

    bool isDiagonalMove = movementToTry.X != 0 && movementToTry.Y != 0;
    if (isDiagonalMove)
    {
        
    }
    

    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) {... }

    Vector2 remainingHorizontalMovement = oneStep.X * Vector2.UnitX * stepsLeft;
    Vector2 finalPositionIfMovingHorizontally = furthestAvailableLocationSoFar + remainingHorizontalMovement;
    furthestAvailableLocationSoFar =
        WhereCanIGetTo(furthestAvailableLocationSoFar, finalPositionIfMovingHorizontally, boundingRectangle);
    
    Vector2 remainingVerticalMovement = oneStep.Y * Vector2.UnitY * stepsLeft;
    Vector2 finalPositionIfMovingVertically = furthestAvailableLocationSoFar + remainingVerticalMovement;
    furthestAvailableLocationSoFar =
        WhereCanIGetTo(furthestAvailableLocationSoFar, finalPositionIfMovingVertically, boundingRectangle);
    

    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 :)

    public Vector2 WhereCanIGetTo(Vector2 originalPosition, Vector2 destination, Rectangle boundingRectangle)
    {
    
        Vector2 movementToTry = destination - originalPosition;
        Vector2 furthestAvailableLocationSoFar = originalPosition;
        int numberOfStepsToBreakMovementInto = (int)(movementToTry.Length() * 2) + 1;
        Vector2 oneStep = movementToTry / numberOfStepsToBreakMovementInto;
    
        for (int i = 1; i <= numberOfStepsToBreakMovementInto; i++)
        {
            Vector2 positionToTry = originalPosition + oneStep * i;
            Rectangle newBoundary = CreateRectangleAtPosition(positionToTry, boundingRectangle.Width, boundingRectangle.Height);
            if (HasRoomForRectangle(newBoundary)) { furthestAvailableLocationSoFar = positionToTry; }
            else
            {
                bool isDiagonalMove = movementToTry.X != 0 && movementToTry.Y != 0;
                if (isDiagonalMove)
                {
                    int stepsLeft = numberOfStepsToBreakMovementInto - (i - 1);
    
                    Vector2 remainingHorizontalMovement = oneStep.X * Vector2.UnitX * stepsLeft;
                    Vector2 finalPositionIfMovingHorizontally = furthestAvailableLocationSoFar + remainingHorizontalMovement;
                    furthestAvailableLocationSoFar =
                        WhereCanIGetTo(furthestAvailableLocationSoFar, finalPositionIfMovingHorizontally, boundingRectangle);
    
                    Vector2 remainingVerticalMovement = oneStep.Y * Vector2.UnitY * stepsLeft;
                    Vector2 finalPositionIfMovingVertically = furthestAvailableLocationSoFar + remainingVerticalMovement;
                    furthestAvailableLocationSoFar =
                        WhereCanIGetTo(furthestAvailableLocationSoFar, finalPositionIfMovingVertically, boundingRectangle);
                }
                break;
            }
        }
        return furthestAvailableLocationSoFar;
    }
    

    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

    The code so far

    Is here Smiley

    Next up...

    In the final part of this tutorial we will have a look at how to make Jumper jump, and how to stop his Movement when he hits something.

    ]]>
    http://xnafan.net/2013/04/simple-platformer-game-in-xna-tutorial-part-five-improved-collision-detection/feed/ 0
    Simple platformer game in XNA tutorial – part four “Simple collision detection” http://xnafan.net/2013/04/simple-platformer-game-in-xna-tutorial-part-four-simple-collision-detection/ http://xnafan.net/2013/04/simple-platformer-game-in-xna-tutorial-part-four-simple-collision-detection/#comments Sat, 27 Apr 2013 11:47:42 +0000 http://xnafan.net/?p=977 image

    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 Smiley, der blinker...

    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.

    Example

    We create three rectangles using the following code:

    Rectangle _rectangle1 = new Rectangle(5, 5, 25, 20);
    Rectangle _rectangle2 = new Rectangle(25, 10, 15, 10);
    Rectangle _rectangle3 = new Rectangle(35, 25, 10, 5);
    
    

    The parameters to the constructor are:

    X and Y of upper left corner

    width and height

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

    image

    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! 😀

    Add the following code to the Sprite class:

    public Rectangle Bounds
    {
        get { return new Rectangle((int)Position.X, (int)Position.Y, 
                        Texture.Width, Texture.Height); }
    }
    

    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:

    public bool HasRoomForRectangle(Rectangle rectangleToCheck)
    {
        foreach (var tile in Tiles)
        {
            if (tile.IsBlocked && tile.Bounds.Intersects(rectangleToCheck))
            {
                return false;
            }
        }
        return true;
    }
    

    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.

    Now your Board class should look like this:

    public class Board
    {
        public Tile[,] Tiles { get; set; }
        public int Columns { get; set; }
        public int Rows { get; set; }
        public Texture2D TileTexture { get; set; }
        private SpriteBatch SpriteBatch { get; set; }
        private Random _rnd = new Random();
        public static Board CurrentBoard { get; private set; }
    
        public Board(SpriteBatch spritebatch, Texture2D tileTexture, int columns, int rows)
        {
            Columns = columns;
            Rows = rows;
            TileTexture = tileTexture;
            SpriteBatch = spritebatch;
            Tiles = new Tile[Columns, Rows];
            InitializeAllTilesAndBlockSomeRandomly();
            SetAllBorderTilesBlocked();
            Board.CurrentBoard = this;
        }
    
        private void InitializeAllTilesAndBlockSomeRandomly()
        {
            for (int x = 0; x < Columns; x++)
            {
                for (int y = 0; y < Rows; y++)
                {
                    Vector2 tilePosition = 
                        new Vector2(x * TileTexture.Width, y * TileTexture.Height);
                    Tiles[x, y] = 
                        new Tile(TileTexture, tilePosition, SpriteBatch, _rnd.Next(5) == 0);
                }
            }
        }
    
        private void SetAllBorderTilesBlocked()
        {
            for (int x = 0; x < Columns; x++)
            {
                for (int y = 0; y < Rows; y++)
                {
                    if (x == 0 || x == Columns - 1 || y == 0 || y == Rows - 1)
                    { Tiles[x, y].IsBlocked = true; }
                }
            }
        }
    
        public void Draw()
        {
            foreach (var tile in Tiles) { tile.Draw(); }
        }
    }
    

    Updating Jumper's Update()

    Now we will add code to Jumper's Update() method to

    • store where we were before we move
    • move (already implemented)
    • check whether we've collided
    • if we collided, move back to where we came from

    Our Update looks like this now:

    public void Update(GameTime gameTime)
    {
        CheckKeyboardAndUpdateMovement();
        SimulateFriction();
        UpdatePositionBasedOnMovement(gameTime);
    }
    

    but we no longer want to unconditionally UpdatePositionBasedOnMovement(). We want to Move If Possible :).

    So cut the the last line of Update() into the clipboard, and instead write:

    MoveIfPossible(gameTime);
    

    Position your cursor inside the MoveIfPossible methodname and press ALT + SHIFT + F10, and you'll see this.

    image

    Click the "Generate method stub..." menuitem and you'll get a method like this:

     

    private void MoveIfPossible(GameTime gameTime)
    {
    }
    

    ...where you can paste your UpdatePositionBasedOnMovement(gameTime); methodcall.

    This is my preferred method of coding: Write it like you would like to read it, and then have Visual Studio implement methods which support exactly that.

    Now go ahead and

    • store the position of Jumper before updating the Position
    • check the board to see whether Jumper's new position is blocked
    • restore Position to the previous position if Jumper can't go there

    When you're done, check to see whether you have something along these lines:

    private void MoveIfPossible(GameTime gameTime)
    {
        Vector2 oldPosition = Position;
        UpdatePositionBasedOnMovement(gameTime);
        if (!Board.CurrentBoard.HasRoomForRectangle(Bounds)) { Position = oldPosition; }
    }
    

    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:

    image

    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().

    So we need to find out how far we can get in the direction we want. And that's what we'll do in part five of this tutorial

    The updated class diagram

    Here you can see that the Bounds property has been added to the Sprite class, and the HasRoomForRectangle method to the Board class:

    image

    What we've covered

    You should take the following away from this chapter of the tutorial:

    • You can store a reference to an object on the class using the static keyword. This enables code from anywhere to gain access to that object.
    • You should think about which class you choose to add functionality to. Which class should have the responsibility. Think of the task of finding out whether there is room for a rectangle on the Board.
    • Simple collision detection can be implemented using Rectangles.Intersects()
    • Simple collision detection isn't perfect 😉

    The code for the project so far

    ...is here :).

    Next up...

    In the next part of this tutorial, we will improve the collision detection to let us know exactly how far we can get before we must stop completely.

    ]]>
    http://xnafan.net/2013/04/simple-platformer-game-in-xna-tutorial-part-four-simple-collision-detection/feed/ 1
    Simple platformer game in XNA tutorial – part three “Movement” http://xnafan.net/2013/04/simple-platformer-game-in-xna-tutorial-part-three/ http://xnafan.net/2013/04/simple-platformer-game-in-xna-tutorial-part-three/#comments Tue, 23 Apr 2013 16:33:47 +0000 http://xnafan.net/?p=946 image

    Previously …on xnafan.net Smiley

    In the previous parts of this tutorial we looked at

    • how to create a basic Sprite class (part one)
    • 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

    jumper

    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 Smiley

    public class Jumper : Sprite
    {
        public Vector2 Movement { get; set; }
    
        public Jumper(Texture2D texture, Vector2 position, SpriteBatch spritebatch)
            : base(texture, position, spritebatch)
        {
        }
    
        public void Update(GameTime gameTime)
        {
        }
    }
    

    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 Trist smiley.

    image

    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 Smiley - 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 Smiley).

    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!)

    if (keyboardState.IsKeyDown(Keys.Left)) { Movement =  new Vector2(-1,0); }
    if (keyboardState.IsKeyDown(Keys.Right)) { Movement = new Vector2(1, 0); }
    if (keyboardState.IsKeyDown(Keys.Up)) { Movement = new Vector2(0, -1); }
    

    Q: Can you figure out what is wrong with the approach above?

    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:

    if (keyboardState.IsKeyDown(Keys.Left)) { Movement +=  new Vector2(-1,0); }
    if (keyboardState.IsKeyDown(Keys.Right)) { Movement += new Vector2(1, 0); }
    if (keyboardState.IsKeyDown(Keys.Up)) { Movement += new Vector2(0, -1); }
    

    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 Smiley.

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

    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 Smiley.

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

    Vector2 aPosition = new Vector2(6,12);  //X is set to 6 and Y to 12
    aPosition /= 2; //results in X=3 (6 divided by 2) and Y=6 (12 divided by 2)
    aPosition += new Vector2(4, 5.3f); //results in X=7 (3 plus 4) and Y=11.3 (6 plus 5.3)
    

    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:

    _jumper = new Jumper(_jumperTexture, new Vector2(50, 50), _spriteBatch);
    

    and call _jumper.Update from SimplePlatformerGame.Update

    protected override void Update(GameTime gameTime)
    {
        base.Update(gameTime);
        _jumper.Update(gameTime);
    }
    

    Now run the program and try moving Jumper around Smiley

    Not what you expected? Smiley, der blinker

    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 Smiley, der blinker.

    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 Smiley.

    Your Update should look like this now:

    public void Update(GameTime gameTime)
    {
        KeyboardState keyboardState = Keyboard.GetState();
        if (keyboardState.IsKeyDown(Keys.Left)) { Movement += new Vector2(-1, 0); }
        if (keyboardState.IsKeyDown(Keys.Right)) { Movement += new Vector2(1, 0); }
        if (keyboardState.IsKeyDown(Keys.Up)) { Movement += new Vector2(0, -1); }
    
        Movement *= Movement * new Vector2(.1f, .1f);
        Position += Movement;
    }
    

    Hit F5 and try it again Smiley  … 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! Grædende ansigt Smiley, der blinker

    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! Smiley med åben mund

    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? Smiley, der blinker).

    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:

    Position += Movement * (float)gameTime.ElapsedGameTime.TotalMilliseconds;
    

    the (float) is necessary because TotalMilliseconds is a double, which we can’t multiply with a Vector2. So we cast it (change it) to a float before doing the math.

    Try it again – and you’ll see that movement gets waaayy to fast (for me at least Smiley, der blinker). So divide those TotalMilliseconds with something until you’re happy. I ended up with this which suited me fine:

    Position += Movement *(float)gameTime.ElapsedGameTime.TotalMilliseconds/15;
    

    Cleaning your code

    Our Update is already six lines of code and growing, let’s prune it back by using some Clean Code.

    If you were a regular Joe coding along, you would add comments to your Update method and it would end up like this:

    public void Update(GameTime gameTime)
    {
        //get the keyboard state
        KeyboardState keyboardState = Keyboard.GetState();
                
        //change movement to reflect the keypresses
        if (keyboardState.IsKeyDown(Keys.Left)) { Movement += new Vector2(-1, 0); }
        if (keyboardState.IsKeyDown(Keys.Right)) { Movement += new Vector2(1, 0); }
        if (keyboardState.IsKeyDown(Keys.Up)) { Movement += new Vector2(0, -1); }
                
        //simulate friction
        Movement -= Movement * new Vector2(.1f, .1f);
    
        //update position based on movement
        Position += Movement * (float)gameTime.ElapsedGameTime.TotalMilliseconds / 15;
    }
    

    BUT … since you’re NOT a regular Joe, you have realized through years and years of practical experience that:

    • Comments lie!  Not always, but they get out of sync with the code as we update it (which we invariably will) and forget to modify the comments to reflect the changes
    • When we return to our own code (or read other people’s code) we don’t want to sift through lines and lines to find what we need, to be able to correct an error or modify functionality

    So you quickly move the code from Update() into three methods

    public void Update(GameTime gameTime)
    {
        CheckKeyboardAndUpdateMovement();
        SimulateFriction();
        UpdatePositionBasedOnMovement(gameTime);
    }
    
    private void CheckKeyboardAndUpdateMovement()
    {
        KeyboardState keyboardState = Keyboard.GetState();
    
        if (keyboardState.IsKeyDown(Keys.Left)) { Movement += new Vector2(-1, 0); }
        if (keyboardState.IsKeyDown(Keys.Right)) { Movement += new Vector2(1, 0); }
        if (keyboardState.IsKeyDown(Keys.Up)) { Movement += new Vector2(0, -1); }
    }
    
    private void SimulateFriction()
    {
        Movement -= Movement * new Vector2(.1f, .1f);
    }
    
    private void UpdatePositionBasedOnMovement(GameTime gameTime)
    {
        Position += Movement * (float)gameTime.ElapsedGameTime.TotalMilliseconds / 15;
    }
    

    The benefits of this approach are

    • You don’t need to keep comments in sync, and code never lies – it does what it says Smiley
    • 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 Smiley, der blinker)
    • 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! Smiley med åben mund

    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.

    The code for the project so far

    Is available here.

    Next up: Collision detection

    In the fourth part, we will look at how to stop our Jumper when he collides with something.

    ]]>
    http://xnafan.net/2013/04/simple-platformer-game-in-xna-tutorial-part-three/feed/ 2
    Simple platformer game in XNA tutorial – part two “Tile and Board” http://xnafan.net/2013/04/simple-platformer-game-in-xna-tutorial-part-two/ http://xnafan.net/2013/04/simple-platformer-game-in-xna-tutorial-part-two/#comments Sun, 21 Apr 2013 20:18:05 +0000 http://xnafan.net/?p=922 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

    Demo of platformer

    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:

    Take the Sprite class, add an IsBlocked property and call it a Tile Smiley.

    Go ahead, right click the SimplePlatformer project, and choose “Add > Class…”

    image

    Call it “Tile”, and you will get something like this:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace SimplePlatformer
    {
        class Tile
        {
        }
    }
    

    Inheriting Sprite

    To make Tile inherit Sprite we change

    class Tile { }
    

    into

    class Tile : Sprite { }
    

    like this:

    image

    And see that Visual Studio rewards our work with a red squiggle under the “Tile” declaration.

    Hover your mouse over it, and you will get an explanation:

    “Base class ‘SimplePlatformer.Sprite’ doesn’t contain parameterless constructor”:

    image

    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 parent class).

    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!!” Smiley

    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:

    class Tile : Sprite
    {
        public bool IsBlocked { get; set; }
    
        public Tile(Texture2D texture, Vector2 position, SpriteBatch batch, bool isBlocked)
            : base(texture, position, batch)
        {
            IsBlocked = isBlocked;
        }
    }
    

    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 Smiley, der blinker. 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:

    public void Draw()
    {
        SpriteBatch.Draw(Texture, Position, Color.White);
    }
    

    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 Smiley

    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

    So go ahead make the Draw() method in the Sprite class virtual

    public virtual void Draw()
    {
        SpriteBatch.Draw(Texture, Position, Color.White);
    }
    

    and code the Draw() method in Tile class with the override keyword

    public override void Draw()
    {
        if (IsBlocked) { base.Draw(); }           
    }
    

    ..so it only draws the Tile graphics if it is blocked.

    Try it out

    Go ahead, try to figure out how to add two Tile objects to your game class, and make one of them blocked (visible) and the other passable (invisible) and run your Game to see only one.

    image

    If you get stuck – get the sample code here:

    public class SimplePlatformerGame : Game
    {
        private GraphicsDeviceManager _graphics;
        private SpriteBatch _spriteBatch;
        private Texture2D _tileTexture, _jumperTexture;
        private Sprite _jumper;
    
        private Tile _blockedTile, _nonBlockedTile;
    
        public SimplePlatformerGame()
        {
            _graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }
    
        protected override void LoadContent()
        {
            _spriteBatch = new SpriteBatch(GraphicsDevice);
            _tileTexture = Content.Load<Texture2D>("tile");
            _jumperTexture = Content.Load<Texture2D>("jumper");
            _jumper = new Sprite(_jumperTexture, new Vector2(50, 50), _spriteBatch);
            _blockedTile = new Tile(_tileTexture, Vector2.One * 100, _spriteBatch, true);
            _nonBlockedTile = new Tile(_tileTexture, Vector2.One * 170, _spriteBatch, false);
        }
    
        protected override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
        }
    
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.WhiteSmoke);
            _spriteBatch.Begin();
            base.Draw(gameTime);
            _jumper.Draw();
            _nonBlockedTile.Draw();
            _blockedTile.Draw();
            _spriteBatch.End();
        }
    }
    

    Finally, clean up by removing the _blockedTile and _nonBlockedTile variables.

    Now while that is fine, we don’t want to have to keep track of hundreds of tiles in our SimplePlatformerGame class, so let’s encapsulate them in:

    The Board class

    The goal here is again: to encapsulate as much functionality as possible, so it is fast and simple to instantiate a Board full of tiles, and have it do its own drawing.

    Before looking at the code beneath, try to implement this:

    Add a new class Board to the project

    Add a property Tiles of type Tile[,] (a double-array of tiles) to the Board class

    Add two int properties Columns and Rows to the Board class

    Add a Texture2D TileTexture property

    Add a SpriteBatch property

    Add a constructor which takes a SpriteBatch, a Texture2D and the two ints columns and rows

    …go ahead – you’ll learn more if you try it out yourself first – believe me! Smiley, der blinker

    When you’re done, you should have something like this:

    public class Board
    {
        public Tile[,] Tiles { get; set; }
        public int Columns { get; set; }
        public int Rows { get; set; }
        public Texture2D TileTexture { get; set; }
        public SpriteBatch SpriteBatch {get; set; }
    
        public Board(SpriteBatch spritebatch, Texture2D tileTexture, int columns, int rows)
        {
        }
    }
    

    We need the SpriteBatch and the Texture2D for instantiating all the tiles we will have in the Board, and the colums and rows for the size of the Board.

    Important when using Arrays as properties!

    Whenever you have a property of type array, or multidimensional array like our Tiles [,] array, you have to remember to instantiate it before using it.

    This often means that you have to perform instantiation in the constructor. In the Board class we also want to instantiate all the Tile objects and add them to the doublearray. So let’s do that Smiley:

    Instantiating the Board

    We will perform three things in the constructor:

    • Save the parameters for future use
    • Instantiate the double array to a number of columns and rows as specified by the parameters to the constructor
    • Instantiate a Tile object for each arrayindex in the Tiles[,] doublearray, which is at the correct position
      public Board(SpriteBatch spritebatch, Texture2D tileTexture, int columns, int rows)
      {
          Columns = columns;
          Rows = rows;
          TileTexture = tileTexture;
          SpriteBatch = spritebatch;
          Tiles = new Tile[Columns, Rows];
          for (int x = 0; x < Columns; x++)
          {
              for (int y = 0; y < Rows; y++)
              {
                  Vector2 tilePosition = 
                      new Vector2(x * tileTexture.Width, y * tileTexture.Height);
                  Tiles[x, y] = 
                      new Tile(tileTexture, tilePosition, spritebatch, true);
              }
          }
      }
      

    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.

    Here is a sample of properties which don’t store negative values:

    private int _columns, _rows;
    public int Columns 
    {
        set { if (value >= 0) {_columns = value;}}
        get {return _columns;}
    }
    public int Rows 
    {
        set { if (value >= 0) {_rows = value;}}
        get {return _rows;}
    }
    
    

    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 Smiley

    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.

    public void Draw()
    {
        for (int x = 0; x < Columns; x++)
        {
            for (int y = 0; y < Rows; y++)
            {
                Tiles[x, y].Draw();
            }
        }
    }
    

    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 Smiley.

    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.

    When you are done, your code should look something like this:

    public class SimplePlatformerGame : Game
    {
        private GraphicsDeviceManager _graphics;
        private SpriteBatch _spriteBatch;
        private Texture2D _tileTexture, _jumperTexture;
        private Sprite _jumper;
        private Board _board;
    
        public SimplePlatformerGame()
        {
            _graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }
    
        protected override void LoadContent()
        {
            _spriteBatch = new SpriteBatch(GraphicsDevice);
            _tileTexture = Content.Load<Texture2D>("tile");
            _jumperTexture = Content.Load<Texture2D>("jumper");
            _jumper = new Sprite(_jumperTexture, new Vector2(50, 50), _spriteBatch);
            _board = new Board(_spriteBatch, _tileTexture, 15, 10);
        }
    
        protected override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
        }
    
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.WhiteSmoke);
            _spriteBatch.Begin();
            base.Draw(gameTime);
            _board.Draw();
            _jumper.Draw();
            _spriteBatch.End();
        }
    }
    

    If you got it right, you will get something like this:

    image

    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 Smiley

    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.

    _graphics.PreferredBackBufferWidth = 960;
    _graphics.PreferredBackBufferHeight = 640;
    

    That’s better:

    image

    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:

    Tiles[x, y] = new Tile(tileTexture, tilePosition, spritebatch, _rnd.Next(5) == 0);
    

    You should get something like this

    image

    Adding border tiles to the Board

    For the final step in this part of the tutorial, we want to add borders all around, by setting all the outer tiles to IsBlocked = true.

    So in the constructor of the Board class add a line right after the double loop which instantiates the Tiles.

    SetAllBorderTilesBlocked();
    

    Put your cursor inside the method name and press ALT+SHIFT+F10 and select “Generate method stub…”

    image

    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.

    Is this the way to do it?

    Q: can we improve on this code?

    private void SetAllBorderTilesBlocked()
    {
        for (int x = 0; x < 15; x++)
        {
            for (int y = 0; y < 10; y++)
            {
                if (x == 0 || x == 14 || y == 0 || y == 9)
                {
                    Tiles[x, y].IsBlocked = true;    
                }
                        
            }
        }
    }
    

    A: Yes - the control statement of the two for loops should 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.

    So try and improve the code by using variables instead Smiley

    Solution here below:

    private void SetAllBorderTilesBlocked()
    {
        for (int x = 0; x < Columns; x++)
        {
            for (int y = 0; y < Rows; y++)
            {
                if (x == 0 || x == Columns - 1 || y == 0 || y == Rows - 1)
                { Tiles[x, y].IsBlocked = true; }
            }
        }
    }
    

    Our game now

    image

    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 Smiley.

    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

    • four lines for storing columns, rows, tileTexture and spritebatch
    • a method call for instantiating the Tiles[,] and tiles within it
    • the call to SetAllBorderTilesBlocked()

    My suggestion for the final code of the Board class:

    public class Board
    {
        public Tile[,] Tiles { get; set; }
        public int Columns { get; set; }
        public int Rows { get; set; }
        public Texture2D TileTexture { get; set; }
        private SpriteBatch SpriteBatch { get; set; }
        private Random _rnd = new Random();
    
        public Board(SpriteBatch spritebatch, Texture2D tileTexture, int columns, int rows)
        {
            Columns = columns;
            Rows = rows;
            TileTexture = tileTexture;
            SpriteBatch = spritebatch;
            InitializeAllTilesAndBlockSomeRandomly();
            SetAllBorderTilesBlocked();
        }
    
        private void InitializeAllTilesAndBlockSomeRandomly()
        {
            Tiles = new Tile[Columns, Rows];
            for (int x = 0; x < Columns; x++)
            {
                for (int y = 0; y < Rows; y++)
                {
                    Vector2 tilePosition =
                        new Vector2(x * TileTexture.Width, y * TileTexture.Height);
                    Tiles[x, y] = 
                        new Tile(TileTexture, tilePosition, SpriteBatch, _rnd.Next(5) == 0);
                }
            }
        }
    
        private void SetAllBorderTilesBlocked()
        {
            for (int x = 0; x < Columns; x++)
            {
                for (int y = 0; y < Rows; y++)
                {
                    if (x == 0 || x == Columns - 1 || y == 0 || y == Rows - 1)
                    { Tiles[x, y].IsBlocked = true; }
                }
            }
        }
    
        public void Draw()
        {
            foreach (var tile in Tiles)
            {
                tile.Draw();
            }
        }
    }
    

    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.


    image

     

    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

    The code

    And here is the code so far – have fun! Smiley

    In the third part of this tutorial series, we will look at moving the Jumper.

    ]]>
    http://xnafan.net/2013/04/simple-platformer-game-in-xna-tutorial-part-two/feed/ 15
    Simple platformer game in XNA tutorial – part one “The basic Sprite” http://xnafan.net/2013/04/simple-platformer-game-in-xna-tutorial-part-one/ http://xnafan.net/2013/04/simple-platformer-game-in-xna-tutorial-part-one/#comments Wed, 17 Apr 2013 21:35:22 +0000 http://xnafan.net/?p=887 This tutorial covers all the steps in coding your own platformer for use in your games. We will cover a bit of the principles in Object Oriented Design and Clean Code along the way Smiley.

    What we want to end up making is something like this:

    If you just want to try it out and play around with it, you can download the complete source code here.

    The agenda

    Here’s the agenda.

    • Setting up
    • The Game class
    • The Sprite class
    • The Tile and Board class
    • The Jumper class
    • Introducing simple collision detection
    • Improving our collision detection
    • Adding gravity and jump ability
    • Refactoring code for readability

    In this first part of the tutorial I will cover the first three bulletpoints Smiley

    Qualifications needed to understand this tutorial

    I expect you to know C#, some Object Oriented programming principles (like how to create a class, and add properties to it), and have a basic understanding of how XNA works (LoadContent/Update/Draw).

    If you aren’t at this level – then you’re welcome to try and keep up anyway, and ask in the comments if there’s something you want to understand and aren’t able to figure out by searching the web Smiley.

    Setting up

    Create a new solution with a XNA game project and a Content project. I have named my project “SimplePlatformer”. You may want to follow my naming to stay in sync Smiley.

    If you want to, you can create your own graphics, or you can download the tile.png (64x64 pixels) and jumper.png (40 x 40 pixels) textures here:

    jumper  tile

    right click –> save as… and save them somewhere where you can find them again. Then add them to the content project by right clicking the project - like this:

    image

    So your content project looks like this:

    image

    The Game class

    Next we’ll clean up the Game1.cs file. Rename it to SimplePlatformerGame.cs, and you will be asked

    image

    say yes, so the class name is also updated everywhere in the solution.

    Now clean away all unnecessary code in the SimplePlatformerGame class so you only have the following methods left:

    The constructor “SimplePlatFormerGame()”
    LoadContent()
    Update()
    Draw()

    Compile to check that everything is working.

    Your SimplePlatformerGame class should look like this
    (“usings” and namespace declaration omitted for brevity in most samples in this tutorial)

    public class SimplePlatformerGame : Game
    {
        private GraphicsDeviceManager _graphics;
        private SpriteBatch _spriteBatch;
    
        public SimplePlatformerGame()
        {
            _graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }
    
        protected override void LoadContent()
        {
            _spriteBatch = new SpriteBatch(GraphicsDevice);
        }
    
        protected override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
        }
    
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            base.Draw(gameTime);
        }
    }
    

    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:

    public class Sprite
    {
        public Vector2 Position { get; set; }
        public Texture2D Texture { set; get; }
    }
    

    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:

    Sprite mySprite =
     new Sprite {Texture = Content.Load("tile"), Position = new Vector2(100,50)};
    

    and draw it like this in the SimplePlatformerGame class’ Draw() method:

    SpriteBatch.Draw(mySprite.Texture, mySprite.Position, Color.White);
    

    We can do better…

    And we will, because we want each tile to be able to draw itself and work as a self-contained unit with data and functionality in one neat package. So we will also add

    • a SpriteBatch variable which the Sprite will use to draw itself.
    • a constructor to ensure that a Sprite object can not be created from the Sprite class without the necessary data for the Sprite to function (which is a main task for a constructor)
    • a Draw method which uses the provided SpriteBatch to draw to the graphics memory (see code sample below)
    public class Sprite
    {
        public Vector2 Position { get; set; }
        public Texture2D Texture { set; get; }
        public SpriteBatch SpriteBatch { get; set; }
    
        public Sprite(Texture2D texture, Vector2 position, SpriteBatch batch)
        {
            Texture = texture;
            Position = position;
            SpriteBatch = batch;
        }
    
        public void Draw()
        {
            SpriteBatch.Draw(Texture, Position, Color.White);
        }
    }
    

    Now it’s easy peasy

    …to initialize, and use the Sprite class from our SimplePlatformerGame:

    We can create a new Sprite object like this:

    Sprite mySprite = new Sprite(_tileTexture, new Vector2(40,40), _spriteBatch);
    

    (provided we have a texture stored in the variable _tileTexture, and a reference to the SimplePlatformerGame object’s SpriteBatch in the variable _spriteBatch)

    In case you didn’t catch the difference between the creation of the Sprites in the two sections above: In the first case I used an object initializer, where

    Sprite mySprite = new Sprite {Texture = _aTexture, Position = _aPosition};
    

    is just shorthand for

    Sprite mySprite = new Sprite(); 
    mySprite.Texture = _aTexture; 
    mySprite.Position = _aPosition;
    

    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 Smiley).

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

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.WhiteSmoke);
        _spriteBatch.Begin();
        base.Draw(gameTime);
        mySprite.Draw();
        _spriteBatch.End();
    }
    

    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 Smiley, der blinker).

    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):

    private GraphicsDeviceManager _graphics;
    private SpriteBatch _spriteBatch;
    private Texture2D _tileTexture, _jumperTexture;
    private Sprite _aSpriteForTesting
    

    Add code to LoadContent() to load and store the two textures:

    protected override void LoadContent()
    {
        _spriteBatch = new SpriteBatch(GraphicsDevice);
        _tileTexture = Content.Load("tile");
        _jumperTexture = Content.Load("jumper");
        _aSpriteForTesting = new Sprite(_jumperTexture, new Vector2(50,50), _spriteBatch);
    }
    

    Finally call the Sprite’s Draw() from the SimplePlatformerGame’s Draw():

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.WhiteSmoke);
        _spriteBatch.Begin();
        base.Draw(gameTime);
        _aSpriteForTesting.Draw();
        _spriteBatch.End();
    }
    

    Run the game (F5) and you should see something like this:

    image

    Yay! We’ve got something on the screen Smiley

    Assignment for the reader

    To ensure you’ve understood what’s going on – try adding another Sprite variable and load a Sprite with the Tile texture underneath and to the right.

    image

    If you get stuck – the solution code is here:

    public class SimplePlatformerGame : Game
    {
        private GraphicsDeviceManager _graphics;
        private SpriteBatch _spriteBatch;
        private Texture2D _tileTexture, _jumperTexture;
        private Sprite _aSpriteForTesting, _anotherSpriteForTesting;
    
        public SimplePlatformerGame()
        {
            _graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }
    
        protected override void LoadContent()
        {
            _spriteBatch = new SpriteBatch(GraphicsDevice);
            _tileTexture = Content.Load("tile");
            _jumperTexture = Content.Load("jumper");
    
            _aSpriteForTesting = 
                new Sprite(_jumperTexture, new Vector2(50, 50), _spriteBatch);
            _anotherSpriteForTesting = 
                new Sprite(_tileTexture, new Vector2(90, 120), _spriteBatch);
        }
    
        protected override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
        }
    
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.WhiteSmoke);
            _spriteBatch.Begin();
            base.Draw(gameTime);
            _aSpriteForTesting.Draw();
            _anotherSpriteForTesting.Draw();
            _spriteBatch.End();
        }
    }
    

    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)

    Solution so far

    Here’s the code at the stage we’re at now, with the extra Sprite variable removed and the remaining sprite variable renamed _jumper

    Class diagram

    image

    Here we see that a Sprite has three properties: Position, SpriteBatch and Texture, and implements a Draw method and a Sprite constructor.

    It doesn’t show that Sprite implicitly inherits Object, but if a class doesn’t explicitly inherit another class, it is a subclass of Object.

    Next up: the Tile and Board

    In the next part we will look at the Tile and Board class…

    ]]>
    http://xnafan.net/2013/04/simple-platformer-game-in-xna-tutorial-part-one/feed/ 4
    Creating a visual selection control from scratch http://xnafan.net/2013/03/creating-a-visual-selection-control-from-scratch/ http://xnafan.net/2013/03/creating-a-visual-selection-control-from-scratch/#comments Fri, 29 Mar 2013 20:10:02 +0000 http://xnafan.net/?p=830 In this post we will extend the DrawableGameComponent to create a control which will let the user select one of a number of possible options.

    Here you can see what we want to achieve:

    The different options will be implemented as one big picture, where only the  currently selected option is displayed in the control.

    visiblepart

    How we will build the control

    1. Subclass DrawableGameComponent
    2. Add basic properties for
      1. positioning the control (Vector2)
      2. the menutexture (Texture2D)
      3. the number of possible selections (int)
      4. the currently selected index (int)
      5. the SpriteBatch to draw to
    3. Add functions for manipulating the selected index
      1. MoveToNext()
      2. MoveToPrevious()
    4. Override base Update() and Draw() to react to keyboard input and change selection

    After building this control in a basic version, which "blinks" from selection to selection,  we will improve it, so the selection slides from one selected item to the next.

    Subclassing DrawableGameComponent

    The benefits of using DrawableGameComponent for basic game mechanics like this one is that once the gameobject is created and added to the Game object's Components collection, our component will automatically have Update() called (if our object's Enabled property is true) and Draw() called (if our object's Visible property is true). This way we can leave a lot of the "housekeeping" to the XNA engine.

    So go ahead, whip up a new XNA solution in your favorite editor and add a new class "VisualSelectionControl", which inherits DrawableGameComponent:

    public class VisualSelectionControl : DrawableGameComponent
    {
    }
    

    When subclassing another class which doesn't have a default constructor (a "default constructor" is also known as  an "empty constructor" or a "parameterless constructor"), we often choose to pass the parameters for the superclass' constructor to the subclass' constructor, so the subclass 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 VisualSelectionControl without creating a DrawableGameComponent. And the DrawableGameComponent wants a Game object in its constructor - or it just won't play :).

    So we add a constructor to our VisualSelectionControl which takes a Game object, which we pass on to DrawableGameComponent's constructor:

    public class VisualSelectionControl : DrawableGameComponent
    {
        //creates a VisualSelectionControl by first creating a DrawableGameComponent
        //The constructor takes a Game object, and passes it on to the superclass (the "base" class)
        public VisualSelectionControl (Game game) : base(game)
    	{
    	}
    }
    

    Add basic properties

    Since we already know what properties we need for our control to work (se item 2 in "How we will build our control"), let's add them to the class, and as parameters to the constructor, so we can't instantiate a VisualSelectionControl object without these variables:

    public class VisualSelectionControl : DrawableGameComponent
    {
        //the position (top-left corner) of the menu
        public Vector2 Position { get; set; }
        //the spritebatch to draw to
        public SpriteBatch SpriteBatch { get; set; }
        //stores the entire menu in one big picture
        public Texture2D MenuTexture { get; private set; }
        //how many selectable items the texture should be split into
        public int NumberOfPossibleSelections { get; private set; }
        //Currently selected index 
        public int CurrentIndex { get; set; }
    
        //creates a VisualSelectionControl by first creating a DrawableGameComponent
        //takes a Game object, and passes it on to the superclass (DrawableGameComponent)
        public VisualSelectionControl(Game game, Vector2 position, 
                                        SpriteBatch spriteBatch, Texture2D menuTexture,
                                        int numberOfPossibleSelections) : base(game)
        {
            //store parameters
            Position = position;
            SpriteBatch = spriteBatch;
            MenuTexture = menuTexture;
            NumberOfPossibleSelections = numberOfPossibleSelections;
        }
    }
    

    As you can see we've omitted the CurrentIndex property from the parameters to the constructor, since we're okay with that defaulting to zero when the control is created.

    All we need to do now for our control to work is implement the Draw method on the control, so we can see what is selected.

    Since the Draw method already exists on the DrawableGameComponent, we override it:

    public override void Draw(GameTime gameTime)
    {
        //call the superclass' implementation of the method we're overriding
        base.Draw(gameTime);
        //calculate the height of one menuitem
        int heightOfOneMenuItem = MenuTexture.Height / NumberOfPossibleSelections;
        //get the source rectangle we want, based on current index
        Rectangle sourceRect = new Rectangle(0, heightOfOneMenuItem* CurrentIndex, 
            (int)MenuTexture.Width, heightOfOneMenuItem);
        //draw to spritebatch, at control's position, from the sourcerectangle
        SpriteBatch.Draw(MenuTexture, Position, sourceRect, Color.White);
    }
    

    Calling overridden method from overriding method

    As you can see, we call the base class' Draw() method from our new Draw() method. This is important to do whenever you override a method from a class where you're not entirely sure what is going on inside the overriden method. This is to ensure that the functionality in the parent object remains intact.

    Then we add our own code to calculate how tall one menuitem is based on the complete menutexture and the number of items in the menu. Finally we create a source Rectangle, which we use to render only that part of our texture to the screen.

    Test it in the Game class

    Go ahead and download the Doom difficulty menu image (right click and save)

    doom_difficulty

    and add it to your content project.

    Then add code to the LoadContent() method of the Game class, to load the texture and instantiate a VisualSelectionControl, which we add to the Game object's Components collection

    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        VisualSelectionControl _selectionControl;
    
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }
    
    protected override void LoadContent()
    {
        spriteBatch = new SpriteBatch(GraphicsDevice);
        Texture2D _levelselectionTexture = Content.Load<Texture2D>("doom_difficulty");
        _selectionControl = new VisualSelectionControl(this, Vector2.One * 100, spriteBatch, _levelselectionTexture, 5);
        Components.Add(_selectionControl);
    }
    

    Adding the VisualSelectionControl to the Game.Components collection ensures (as we've been over) that our component will automatically:

    1. have Update() called if our object's Enabled property is true (Enabled is inherited from GameComponent)
    2. have Draw() called if our object's Visible property is true (Visible is inherited from DrawableGameComponent)

    BUT - if we run the game now, we will get an exception, as our control tries to draw to the SpriteBatch, and we haven't called SpriteBatch.Begin. So let's change the Draw() method in the Game class, to ensure that we call SpriteBatch.Begin() before calling the Draw() of all GameComponents, and SpriteBatch.End() afterwards:

    protected override void Draw(GameTime gameTime)
    {
        //clear the screen using any color you like (I like blood red)
        GraphicsDevice.Clear(Color.DarkRed);
        //begin drawing
        spriteBatch.Begin();
        //call the Game object's Draw() method, 
        //which draws all visible DrawableGameComponents in the Components collection
        base.Draw(gameTime);
        //end drawing
        spriteBatch.End();
    }
    

    Run the program - and voilà:

    image

     

    To test the menu control, you can try setting the CurrentIndex to 2 in the LoadContent method

    _selectionControl.CurrentIndex = 2;
    

    That should change the selected index, which in turn will make the calculations in Draw() create a different source Rectangle, rendering a different part of the Texture2D:

    image

    Add functions for manipulating the selected index

    Now the good thing about having a property

    //Currently selected index 
    public int CurrentIndex { get; set; }
    

    instead of a public variable

    //Currently selected index 
    public int CurrentIndex;
    

    is that we've encapsulated the data inside with accessors, so we have full control over reading and writing to and from the variable. We will use that now, to ensure we only store valid values.

    First we change the automatic property to a property with a backing variable:

    //Currently selected index 
    private int _currentIndex;
    
    public int CurrentIndex
    {
        get { return _currentIndex; }
        set { _currentIndex = value; }
    }
    

    and then we only set the _currentIndex variable, if the value we get is valid

    set {
        //ensure only valid values are set
        if (value >= 0 &amp;amp;&amp;amp; value < NumberOfPossibleSelections) 
        {
            _currentIndex = value;
        }
    

    This way we can easily add two helpermethods for moving step by step:

    public void MoveToPrevious() { CurrentIndex--; }
    public void MoveToNext() { CurrentIndex++; } 
    

    The code in the CurrentIndex property's Set will ensure we don't go beyond the boundaries of the menu.

    Add code to handle Keys.Up and Keys.Down presses

    The choice of where to implement this functionality is up to you. It can go in your Game class, or in the control we're making. The problem with creating it in the control, is that it will listen to the KeyboardState all the time, and many different controls in the same game could start reacting to the same keypresses. But you can easily set the VisibleSelectionControl's Enabled property to false and by that make sure the Update() method of the control is not called. So we're going to go ahead and implement keyboard handling in the control for now. First we will implement simple keyboard handling with a flaw. Then we will look at why it doesn't work, and improve it by adding a little more code.

    Naïve implementation of keyboard handling

    We add a variable to the control for storing the keyboard's state:

    //stores the state of the keyboard
    private KeyboardState _currentKeyboardState;
    

    Then we make a naïve implementation of reacting to the keyboard input in the Update() method:

    public override void Update(GameTime gameTime)
    {
        //get the keyboard's state
        _currentKeyboardState = Keyboard.GetState();
        //call the superclass' implementation of the method we're overriding
        base.Update(gameTime);
        //use input to change state
        if (_currentKeyboardState.IsKeyDown(Keys.Up)) { MoveToPrevious(); }
        if (_currentKeyboardState.IsKeyDown(Keys.Down)) { MoveToNext(); }
    }
    

    Go ahead and try it out. You will only see the first and last options in the menu.

    "Why?" you ask?

    Because the Update() method is called arond sixty times per second, and you probably won't release the Up or Down key fast enough to only trigger MoveToPrevious()/MoveToNext() once or twice, but five or ten times. So we have to only move one step, right when the key is pressed.

    We do this by storing the keyboard's state from the previous Update() and then only acting on a new press, i.e. when the key's last state was UP and it is now DOWN.

    Improved keyboard handling

    So go ahead and update your code:

    //stores the state of the keyboard
    private KeyboardState _currentKeyboardState, _previousKeyboardState;
    
    public override void Update(GameTime gameTime)
    {
        //get the keyboard's state
        _currentKeyboardState = Keyboard.GetState();
        //call the superclass' implementation of the method we're overriding
        base.Update(gameTime);
        //use input to change state
        if (_currentKeyboardState.IsKeyDown(Keys.Up) &amp;amp;&amp;amp; _previousKeyboardState.IsKeyUp(Keys.Up)) { MoveToPrevious(); }
        if (_currentKeyboardState.IsKeyDown(Keys.Down) &amp;amp;&amp;amp; _previousKeyboardState.IsKeyUp(Keys.Down)) { MoveToNext(); }
        //store this Update()'s keyboard state for use in next Update
        _previousKeyboardState = _currentKeyboardState;
    }
    

    and try it out again. Now you should only trigger movement when you first press the key, and have to release it again to make another move - COOL huh? Smiley, der blinker

    Not good enough you say?

    So you think it would be nicer to have a smooth, scrolling motion - huh? Well okay - for a final encore, let's implement scrolling and call it a day Smiley med åben mund

    Whenever you want movement in a game (even in a menu) you can make a variable to store the amount you want to change an object's position. Then  for every update you can add that amount to the position of whatever you want moved. In our case, we want the source rectangle (the place we grab the visible part of the menu from) to move gradually from where it was, to a position based on the new CurrentIndex.

    To do that, all we need is to store how far we are from where we want to be, and then gradually diminish that distance every Update.

    This technique can be used for a LOT of other animations as well in a game, just so you know Smiley, der blinker

    Okay - so let's implement that...

    Store where we are currently at, so we can calculate how to get to where we want to be

    Since we want the source rectangle to move gradually now, we want to store its current position in a variable, so we can manipulate it in every Update().

    So go ahead and move the source rectangle into its own member variable on the control. The position of the source rectangle will change every update and move towards the currently selected index.

    //store the source rectangle
    private Rectangle _sourceRectangle;
    

    And since we will be needing the height of one menu item more places than one now, move it into its own member variable as well

    //stores the height of one menu item
    private float _heightOfOneMenuItem;
    

    and initialize both of those variables in the constructor:

    //calculate the height of one menuitem
    _heightOfOneMenuItem = MenuTexture.Height / NumberOfPossibleSelections;
    //create the source rectangle
    _sourceRectangle = new Rectangle(0, 0, MenuTexture.Width, (int)_heightOfOneMenuItem);
    

    All we need to do now is move the source rectangle towards its destination in the Update() method:

    //calculate where the sourcerectangle is supposed to be on the Y axis of the texture
    float sourceRectangleYDestination = _heightOfOneMenuItem * CurrentIndex;
    //get the difference between where it is now and where it is supposed to be
    float differenceOnYAxis = sourceRectangleYDestination - _sourceRectangle.Top;
    //calculate a thrirty percent movement
    float thirtyPercentMovement = differenceOnYAxis * .3f;
    //move the source rectangle
    _sourceRectangle.Offset(0, (int)thirtyPercentMovement);
    
    

    Here we calculate what the difference is between where the source rectangle is now and where it should be. Then we move the source rectangle thirty percent of the distance every Update(). That percentage will result in a slowing down as the absolute distance in pixels gets smaller (30 % of 200 pixels is 60, but 30 % of 40 pixels is only 12 Smiley)

    This section of code is a functional unit, which performs one operation, so it is a perfect candidate for moving  into its own function, so we can call it from the Update() method:

    MoveSourceRectangleTowardsDestination();
    

    This method of cutting parts of your code out and making them into their own well-named methods is a good idea. It keeps your code readable for both yourself and others as it gets more complex.

    That's it!

    We're done - we've got a control which works, and we can use it with very few lines of code in any game.

    Your next assigment is to create a control which let's the user slide selections sideways Smiley med åben mund Have fun!

    Here's the final code:

    public class VisualSelectionControl : DrawableGameComponent
    {
    
        #region Variables and properties
        //the position (top-left corner) of the menu
        public Vector2 Position { get; set; }
        //the spritebatch to draw to
        public SpriteBatch SpriteBatch { get; set; }
        //stores the entire menu in one big picture
        public Texture2D MenuTexture { get; private set; }
        //how many selectable items the texture should be split into
        public int NumberOfPossibleSelections { get; private set; }
        //Currently selected index 
        private int _currentIndex;
        //stores the states of the keyboard
        private KeyboardState _currentKeyboardState, _previousKeyboardState;
        //store the source rectangle
        private Rectangle _sourceRectangle;
        //stores the height of one menu item
        private float _heightOfOneMenuItem;
    
        public int CurrentIndex
        {
            get { return _currentIndex; }
            //ensures only valid values are set
            set
            {
                if (value >= 0 &amp;amp;&amp;amp; value < NumberOfPossibleSelections)
                {
    
                    //calculate the height of one menuitem
                    int heightOfOneMenuItem = MenuTexture.Height / NumberOfPossibleSelections;
    
                    _currentIndex = value;
    
                }
            }
        }
        #endregion
    
        #region Constructor
        //creates a VisualSelectionControl by first creating a DrawableGameComponent
        //takes a Game object, and passes it on to the superclass (DrawableGameComponent)
        public VisualSelectionControl(Game game, Vector2 position,
                                        SpriteBatch spriteBatch, Texture2D menuTexture,
                                        int numberOfPossibleSelections)
            : base(game)
        {
            //store parameters
            Position = position;
            SpriteBatch = spriteBatch;
            MenuTexture = menuTexture;
            NumberOfPossibleSelections = numberOfPossibleSelections;
            //calculate the height of one menuitem
            _heightOfOneMenuItem = MenuTexture.Height / NumberOfPossibleSelections;
            //create the source rectangle
            _sourceRectangle = new Rectangle(0, 0, MenuTexture.Width, (int)_heightOfOneMenuItem);
        } 
        #endregion
    
        #region Draw and Update
        public override void Draw(GameTime gameTime)
        {
            //call the superclass' implementation of the method we're overriding
            base.Draw(gameTime);
            //draw to spritebatch, at control's position, from the sourcerectangle
            SpriteBatch.Draw(MenuTexture, Position, _sourceRectangle, Color.White);
        }
    
        public override void Update(GameTime gameTime)
        {
            //get the keyboard's state
            _currentKeyboardState = Keyboard.GetState();
            //call the superclass' implementation of the method we're overriding
            base.Update(gameTime);
            //use input to change state
            if (_currentKeyboardState.IsKeyDown(Keys.Up) &amp;amp;&amp;amp; _previousKeyboardState.IsKeyUp(Keys.Up)) { MoveToPrevious(); }
            if (_currentKeyboardState.IsKeyDown(Keys.Down) &amp;amp;&amp;amp; _previousKeyboardState.IsKeyUp(Keys.Down)) { MoveToNext(); }
    
            MoveSourceRectangleTowardsDestination();
    
            //store this Update()'s keyboard state for use in next Update
            _previousKeyboardState = _currentKeyboardState;
        } 
        #endregion
    
        #region Helpermethods
        private void MoveSourceRectangleTowardsDestination()
        {
            //calculate where the sourcerectangle is supposed to be on the Y axis of the texture
            float sourceRectangleYDestination = _heightOfOneMenuItem * CurrentIndex;
            //get the difference between where it is now and where it is supposed to be
            float differenceOnYAxis = sourceRectangleYDestination - _sourceRectangle.Top;
            //calculate a thrirty percent movement
            float thirtyPercentMovement = differenceOnYAxis * .3f;
            //move the source rectangle
            _sourceRectangle.Offset(0, (int)thirtyPercentMovement);
        }
    
        public void MoveToPrevious() { CurrentIndex--; }
        public void MoveToNext() { CurrentIndex++; } 
        #endregion
    
    }
    

    And for the Game class:

    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        VisualSelectionControl _selectionControl;
    
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }
    
        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            Texture2D _levelselectionTexture = Content.Load<Texture2D>("doom_difficulty");
            _selectionControl = new VisualSelectionControl(this, Vector2.One * 100, spriteBatch, _levelselectionTexture, 5);
            Components.Add(_selectionControl);
        }
    
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.DarkRed);
            spriteBatch.Begin();
            base.Draw(gameTime);
            spriteBatch.End();
        }
    }
    

    Class diagram

    Here you can see how our VisualSelectionControl inherits the DrawableGameComponent, which in turn inherits the GameComponent

    image

     

    Source code for download

    Here's the code Smiley

    ]]>
    http://xnafan.net/2013/03/creating-a-visual-selection-control-from-scratch/feed/ 0
    Simple rendering optimization for 2D, non-tilebased maps http://xnafan.net/2013/03/simple-rendering-optimization-for-2d-non-tilebased-maps/ http://xnafan.net/2013/03/simple-rendering-optimization-for-2d-non-tilebased-maps/#comments Wed, 27 Mar 2013 21:20:54 +0000 http://xnafan.net/?p=818 imageWhew... that title is a tongue-twister Smiley, but it is nonetheless as precise as I can describe this way of keeping track of what to render.

    If your game-world is tilebased, you can pretty much rely on finding out what tiles the corners of your screen is in, and then render all the tiles in the columns and rows between and including those four tiles.

    BUT ... if your objects have random positions and sizes, it may be hard to figure out what to draw (because it is within your screen's borders) and what not to draw (because it is in a part of the world that is currently not visible on the player's screen).

    This tutorial will give you an understanding of how you can optimize what your graphics adapter spends its time rendering, but only for your stationary objects. The reason this algorithm can not be used for moving objects is that the calculations for the optimization are performed at startup, and then that data is used every time you redraw the screen.

    If you want to optimize rendering of moving objects, then you can look into the quadtree algorithm (javascript visualization). Here's a C# implementation.

    The algorithm

    1. Split your entire world into a number of rectangles (a number of rows and columns)
    2. For each rectangle in your world
      1. create a list to hold the objects which are even partly visible in that rectangle
    3. For each stationary items in your world
      1. find all the rectangles that the item is even partly in
      2. add the item to those rectangles' lists

    Algorithm visualization

    Here you can see the result of

    1. splitting the world into 2 rows and 2 columns
    2. adding all the items to lists for each rectangle resulting in four lists with 4, 5, 6 and 4 items in (of which 3 items are in more than one list)

    contents-of-lists

    This means that if our screen is entirely in the bottom right quarter of our world, we only have to render 4 items out of 15 - giving us a 73% decrease in items rendered! Smiley

    Let's see this in action


    The code

    Here is the code which initializes a Dictionary which enables me to find a List<Sprite> quickly by looking it up in the Dictionary with a given Rectangle,

    //the dictionary which holds the list of sprites that overlap a given rectangle
    Dictionary<Rectangle, List<Sprite>> _rectangleListDictionary 
        = new Dictionary<Rectangle, List<Sprite>>();
    

    Here's the code which generates the rectangles

    void SplitMapIntoSegments(Vector2 worldSize, int columns, int rows)
    {
        Rectangle[,] rectangles = new Rectangle[columns, rows];
        int screenSegmentWidth = (int)(worldSize.X / columns);
        int screenSegmentHeight = (int)(worldSize.Y / rows);
    
        for (int x = 0; x < columns; x++)
        {
            for (int y = 0; y < rows; y++)
            {
                rectangles[x, y] = new Rectangle(x * screenSegmentWidth, y * screenSegmentHeight, screenSegmentWidth, screenSegmentHeight);
            }
        }
        _rectangles = rectangles;
    }
    

    And the code for adding all items in the gameworld to the rectangles they are partly in

    //loops through all rectangles that the map is currently split into
    //finds all sprites that overlap a given rectangle and adds them to a list
    //adds the list to the RectangleDictionary with the rectangle as key, for fast lookups later
    void AddSpritesToListsInRectangleDictionary()
    {
        _rectangleListDictionary.Clear();
    
        for (int x = 0; x <= _rectangles.GetUpperBound(0); x++)
        {
            for (int y = 0; y <= _rectangles.GetUpperBound(1); y++)
            {
                List<Sprite> spriteListForThisRectangle = new List<Sprite>();
                Rectangle currentRectToCheckSpritesFor = _rectangles[x, y];
    
                foreach (var sprite in _sprites)
                {
                    if (currentRectToCheckSpritesFor.Intersects(sprite.Bounds))
                    {
                        spriteListForThisRectangle.Add(sprite);
                    }
                }
    
                _rectangleListDictionary.Add(currentRectToCheckSpritesFor, spriteListForThisRectangle);
            }
        }
    }
    

    Source code

    Here is the sample project (vs2010 and XNA 4.0).

    ]]>
    http://xnafan.net/2013/03/simple-rendering-optimization-for-2d-non-tilebased-maps/feed/ 0