Monday, July 4, 2011

XNA Game Design: Spritesheets


At this point, 2D game development should feel like an arts and crafts project (and if it doesn't, it's because you have no imagination and should probably pursue a career as an accountant you sad, boring person). You have a 2D canvas with cartesian coordinates to draw images across. You just have to lay out the rules of how they should be rendered.


By making a spritesheet class, we are specifying an image that contains multiple graphics, but should be cut up and only have one specific "tile" of the spritesheet drawn at a time. Videogame spritesheets are good for holding a single image with multiple animations in one specific file. It's more economical to hold more images in one solid file rather than to have multiple files each holding a single image, especially if the images in the file are all related (like corresponding frames to a character animation).

It should be noted not all videogame sprites operate the same way. Sometimes the media is limited on internal data storage, and it becomes more efficient to just specify the exact values of where to "cut" out each sprite in the code. The method I am suggested makes for very neat code, but would not pass for quality development on older forms of data storage. Thankfully, we're developing a game for windows in the year 2011, so we've already thrown most professional programming habits out of the moving vehicle. In it's stead, we will be left with very clean looking code.



Consider the following; the walking animation of Mega Man is composed of ten frames. Each frame is (overall) a different total width and height. By specifying a maximum size, we may ensure that whatever other sized animation frames fit into the source rectangle, but all that surrounding area is moot data. Truthfully, it's not a bad tradeoff. But if we were concerned with data storage, just specifying the coordinates of each next frame wouldn't hurt (it would just suck and would require tables of rectangle data. Not fun).

The idea behind our animated spritesheet is as follows:

  • The sprite will take in two additional parameters; an int describing the number of columns, and an int describing the number of rows.
  • The sprite will have six additional properties; two private ints describing the maximum number of columns and rows, two private ints describing the source rectangle width and height, a public int describing the current x index, and a public int describing the current y index.
  • Before drawing, it will perform a check to determine whether the x and y indices are within acceptable bounds.
So first off, just what do we mean by an x and y index?

These refer to the x and y coordinates of our sprite that gets cut out. Think of it like a two-dimensional array; the x index refers to the horizontal column that is cut out, and the y index refers to the vertical row that is extracted.

This shows an example set of x and y indices that represent the cutouts of each image.

Alright, so now we just need our code.


class Sprite_Set : ASprite
{
    private int Frame_width, Frame_height;
    private int X_Frame_Max, Y_Frame_Max;
    public int X_index = 0; 
    public int Y_index = 0;

    //All optional arguments must be listed last.
    public Sprite_Set(string path, int columns, int rows,
    Vector2? v = null) : base(path, v)
    {
        //Just trying to avoid a DivideByZeroException
        if (columns < 1 || rows < 1)
        {
            throw new Exception("Sprite_Set " + this + 
            " has too few rows or columns");
        }
        X_Frame_Max = columns;
        Y_Frame_Max = rows;
        Frame_width = image.Width / columns;
        Frame_height = image.Height / rows;
        }

        //This overwrites the source rectangle, which
        //we use to designate the coordinates and size of 
        //the image we want to cut out.
        public new Rectangle src_rect
        {
            get
            {
            return new Rectangle(X_index * Frame_width,
            Y_index * Frame_height, Frame_width, Frame_height);
            }
        }

        //This Update method ensures we are not asking for
        //indices that don't exist.
        public void Update()
        {
            if ((X_index >= X_Frame_Max) || 
            (Y_index >= Y_Frame_Max))
            {
                throw new Exception("Index out of bounds: 
                X (" + X_index + " / " + X_Frame_Max + "), 
                Y (" + Y_index + " / " + Y_Frame_Max + ")");
            }
        }

        //Our good old fashioned Draw Method
        public new void Draw(SpriteBatch s_Batch)
        {

            Update();
            base.Draw(s_Batch);
        }
}

It's a bit more code than we're used to, but it works rather well. With this, we can specify spritesheets of equidistant rectangular tiles, and draw only one tile at a time. While this makes it  perfect for things such as sprite animations, it's not the best for drawing entire maps from select tiles (as each drawn tile has a reference to the entire Texture2D spritesheet).

The more game related things we begin to focus on, the more broad these topics tend to become, and the more things will begin to depend on how you want you're game designed. I'm sure some reader can think of a dozen improvements to my sprite_set class. But in terms of a general skeleton, I like what this provides.


Homework: create two methods that increment the x and y indices respectfully. If the indices reach their maximum, it sets the respective index value to 0. These will be useful for creating simple animation update calls (Sprite_Set.Increment_Y();)


Yesterdays Homework Solution: You'll need a private float value and public integer value with get and set accesors to add opacity to your sprites. In the class variables, add:
private float _Opacity = 1.0f; //full opacity
and our public integer:
public int Opacity

{
    get
    {
        return (int)(_Opacity * 255);
    }
    set
    {
        if (!(value >= 0) || (value > 255)) return;
        else _Opacity = (float) (value/255);
    }
}

Finally, the Draw method just needs to be changed to premultiply the alpha values.
s_Batch.Draw(image, pos, src_rect, blend_color * _Opacity, angle, origin, scale, effect, z);

Tuesday, June 28, 2011

XNA Game Design: Abstract Sprite (part 2)

In the last part I said our constructor will look a little bit funky. Here it is:


public ASprite(string path, Vector2? loc = null)
{
    pos = loc ?? Vector2.Zero;
    image = content.Load<Texture2D>("Graphics/" + path);
}

If you're baffled by what is going on, I don't blame you. It certainly looks quite funky, so let me explain what we're trying to do.

When the user wants to create a new sprite, they must provide the path name to the Texture2D resource they want to use, and they may also provide the Vector2 position of their sprite. C# takes in optional arguments by specifying their default values in the parameters (for example, method(int x, int y = 5) requires an int for x, and an optional value for y that defaults to 5 when unspecified).

The question mark after the Vector2 specifies the value we're taking in is nullable. A nullable type is like it's original type, but that it's range of values can also acceptably include null. In our case, we're saying that if the developer does not specify the optional value for our vector2, it will default to null.

Inside the constructor, the first line uses a special operator (??) that sets pos equal to loc if it has a value (is not null), or otherwise sets it to the value of Vector2.Zero (0.0, 0.0). This ensures that if the developer specified a starting Vector2, it's position is set. Otherwise, if it's not set, or set to null, it defaults to the top-left corner (0.0, 0.0).

If you're paying attention, you might be thinking, "Why don't we just say (... Vector2 loc = Vector2.Zero) and avoid the nullable thing all together?"

The problem here is rather asinine; to specify optional parameters you need compile-time constants (nothing that requires extra computation to determine). This means you can't specify it as (... Vector2 loc = new Vector2(0.0, 0.0) ) either.


Wow. That's a lot of explanation for a simple constructor. Lets finish this bad boy up. All we need now is just a draw method. As it should, it will take our spriteBatch and use that to draw our sprites.


public void Draw(SpriteBatch sb)
{
sb.Draw(image, pos, src_rect, blend_color, angle, origin, scale, effect, z);
}

That's a lot of parameter's, innit?
Now, we just need to make an actual Sprite Class that we can make instances of. In a separate class, add the following code:
class Sprite : ASprite
{
    public Sprite(string path, Vector2? v = null) : base(path, v)
    {
    }       
    public new void Draw(SpriteBatch s_Batch)
    {
        base.Draw(s_Batch);
    }
}


The Sprite class now extends ASprite, and holds all it's properties and methods. These members can be overwritten with the new keyword (as we did with the Draw method) and the inherited members can be called with the base keyword.


To draw a new image, in the SCENE.Update(), just create a new instance of Sprite. initiate and tweak your Sprite instances as you see fit (Sprite.Rotation = 45). They can be easily rotated, flipped, and moved. There just simply needs to be a call somewhere in the SCENE.Draw() to actually draw the sprite.


Not shabby at all?


So why did we want to make an abstract class in the first place? After all, having only one class to shape after an abstract class is kinda pointless. Unless of course, we want to create more classes that would extend our abstract class. Such as a class to cut the image into pieces and draw only one piece at a time. Like, say for example, in a sprite sheet...


Slots animation by Mia. Unsure of how to link to her work, but this sprite sheet serves a good example of what we will be tackling.


Homework: Opacity in XNA images is calculated strangely. You would think it would be derived by the Alpha value used in the blending color, but it is in fact much different. Alpha values are imparted onto drawn graphics by multiplying a float value to the blending color, with 0.0f being full transparency, and 1.0f being fully opaque. So to draw an image at 50% opacity, you would blend with blend_color * 0.5f .


Create one extra public integer value for Sprite called Opacity. When the developer changes the value of opacity (from 0-255), change the draw method so the image blends with the set opacity.

Monday, June 27, 2011

Supreme Court Rules on Brown V EMA

With 7-2 for the games industry. Thomas and Breyer dissented. Scalia wrote the opinion.
Details here, here and here. Opinion Here.


Right now, an ex-lawyer in miami is crying.

What irked me about the regulation was not it's blatant violation of the first amendment (although that did sting), but that the law was so vague. That's the only way Senator Lee was able to get it passed after all. Once you start getting into the nitty-gritty of legislation, it gets harder to pass. When politicians approve vague legislation that has to get straightened out by the court, it should be expected that it gets thrown out and trashed in the first place. Senator Lee brought this on himself.




I think this is what irked Scalia as well.

Saturday, June 25, 2011

XNA Game Design: Abstract Sprite (part 1)


The problem with displaying a texture2D image is that there are a number of variables associated with how it should be displayed (Vector2 position, Vector2 scale, Color blending color, float rotation... you get the idea). In many cases we wouldn't want to deal with most of variables, but so many are tied together, it actually makes sense to create a object that holds all this information for us. That is why we will begin creating an abstract ASprite class.

Alongside that, it will also allow us to create methods that can be called from SCENE.Update() that would allow us to do more advanced things with our images, such as make them flash a certain color with a certain intensity for X frames (a bonus project I'll put up later. Too tough for a homework assignment -_-). Being able to call Player.Sprite.Flash(...) is very gratifying, and easy on later development.

Our abstract Sprite class will hold nine variables. These are;
  • the Texture2D image
  • a Rectangle source rectangle (for if we only want to display part of an image)
  • a Vector2 position
  • a Vector2 scale
  • a Vector2 origin
  • a SpriteEffects effect (for indicating whether to flip horizontally or vertically)
  • a private Float angle with a public Int Rotation (you'll see)
  • a Float z scale (for determining the draw order of images and how they "stack")
  • a Color blend color

These are a lot of variables that do a bunch of different things, and if you have any questions about them I'll answer them in the comments. We will also need a static ContentManager to load our texture2D, a simple constructor, and a few extra methods. The only necessary value we will require is our Texture2D image, and we retrieve this by asking for a string.

(Note: depending on your game, you might not want to be holding all of these variables. A NES clone would not use a blending color or a rotation, and if you're crafty you might not even want a source rectangle or scale value. Again, all of this depends on the game YOU are making.)

As established, we will begin by making an abstract class to hold all of these variables.

abstract class ASprite
{
    public Texture2D image;
    public Rectangle src_rect;
    public Vector2 pos = Vector2.Zero;
    public Vector2 origin = Vector2.Zero;
    public Vector2 scale = Vector2.One;
    public float z = 0.5f; //default z value
    private float angle = 0.0f; //rotation in radians
    public Color blend_color = Color.White;
    public SpriteEffects effect = SpriteEffects.None;
    private static ContentManager content = 
    new ContentManager(Game1.services, "Content/Graphics");

Now there's one more variable we need: the int rotation. The reason we need another value to represent our angle is because the draw want's the angle of rotation as a float value in radians. If you're so comfortable with radians that you feel you can specify your rotations with exact values, I commend you. But for us humans, we will instead use rotation to specify our angle of rotation in degrees, and use a set accesor to change the angle value into radians. 

The get and set accesors allow the developer to specify extra operations whenever properties are changed. They are very useful for situations like these.

public int Rotation
{
    set
    {
        angle = (MathHelper.Pi * value / 180)
        % (MathHelper.TwoPi);
    }
    get
    {
        return (int)(angle * (180 / mathhelper.Pi);
    }
}

Comic by Bill Amend. It's not that radians are difficult to think in terms of. It's that it's easier to say rotation = 45 as opposed to angle = 0.785398 (pi / 4 as a float).


We will want the constructor to look a little funky, which is why this is being split into two parts. We're almost finished though! No homework because I couldn't think of any just yet -_-  

So didya hear the news?

New York decided to join the 21st century. Glad to know some republican senators took this as an issue of personal freedom rather than pandering to their party.


I felt the the blog top deserved a little makeover. Just for a bit, at least. ;)


Ah Colorado. You're so damn close... Get with the program.

Thursday, June 23, 2011

XNA Game Design: Thinking Abstractly

The second part of this guide will focus on making two abstract classes to better handle certain aspects of our game. The first will involve creating a class that will better handle image drawing and holding related bitmap data (such as position, rotation, reflection, etc.). The second will use our improved sprites to create a standardized game window that will contain elements such as text, sprites, and selectable choices.

The reason this section is called thinking abstractly is because it will focus intently on elements of polymorphism and inheritance through the use of abstract classes.

To refresh your memory, an abstract class uses the abstract keyword and cannot have instances made of it. Simply put, it acts as a parent class perfect to hold the messier, often untouched variables used by it's child classes.


The Grandpa isn't dead, he's just abstract.
When thinking back to our biggest unsolved problem, we still have the spriteBatch lingering around our SCENE.Draw() method. At the moment, all image drawing must be called from the spritebatch.Draw() method. This makes for horrible looking code because all the methods are directed from the spriteBatch.

  • spriteBatch.Draw(Player.Texture2D, Player.Vector2, Player.Color...)
  • spriteBatch.Draw(Enemy.Texture2D, Enemy.Vector2, Enemy.Color...)
  • spriteBatch.Draw(PowerUp.Texture2D, PowerUp.Vector2, PowerUp.Color...)
  • //This repetitive code is boring!

Every line would be that repetitive. The less code we have to write, the better. So the first part of Abstraction will focus on creating a set of classes to let us instead say;
  • Player.Sprite.Draw(spriteBatch);
  • Enemy.Sprite.Draw(spriteBatch);
  • PowerUp.Sprite.Draw(spriteBatch);
  • //Easy like strawberry daiquiris in Tahiti!

The second part will focus on making a standardized game window, which is just a background with borders and organized elements. While every game does this visual aspect differently, It's inclusion is videogames is so ubiquitous it honestly deserves it's own focus. If you don't believe me, just see for your self. Menus, text boxes and selectable choices are so common in video games, and they're all composed of related objects. As such, it makes perfect sense to tackle them early on. By the end of this chapter, you should have the perfect framework to make and menu system or options screen your game would need (boring, yeah, but it'll make those parts of the game seem like a piece of cake).

Wednesday, June 22, 2011

American Bob


Occasionally there's the plug for the political stuff, and now that I'm back from my brief vacation I'll be continuing the XNA guide again. Until I have a new post ready, check this out; MovieBob, better known for his work on The Escapist, now is trying a political show. Considering his political stripes match my own, his show mostly takes on the air of "angry moderate who can't stand the extreme right but still enjoyed Atlas Shrugged". It's fantastic for anybody who has to stick with left-wing politics because a certain grand old party forgot whom their constituency is.



Check it out. And unless you can't tell by the thumbnail, it's NSFW.