Showing posts with label XNA. Show all posts
Showing posts with label XNA. Show all posts

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 -_-  

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

Monday, June 13, 2011

XNA Game Design: Part 1 Conclusion

Alright, I'll be taking a two week break before starting part 2, so for now here are all our files and work we created in the first part of our guide.

Game1.cs

Input.cs
Cursor.cs
Audio.cs
IScene.cs
SCENE_Main.cs


Hooray! We finished part 1!

Saturday, June 11, 2011

XNA Game Design: Implementing SCENE flow

Sorry I'm not updating as frequently. It's mostly because writing these things out is long, and I'm busy myself. Still, this chapter should conclude Part 1 of the guide!

By now, you might have noticed everything is coming back to the Game1 class. Because everything must actually run from this class, our code in it will certainly get a bit bogged down, and keeping our code organized will be more difficult.

Games, no matter how simple, should not be built entirely within the "Main" class (which is essentially what our Game1 is). It's not only impractical, but it makes for code that's both hard to read and keep organized. Not to mention, it makes us loose sight of what we need to focus on.


My solution to this may not be the best, but I'd say it's pretty darn good. If you remember from the chapter "The Basics", I showed you the simple game loop. While this is the loop that all games follow, it's not the loop an XNA game will follow.


And I'm not mentioning our Input!


Our XNA loop is a bit more complicated. And as we've already done, we've thrown our call to update our inputs in the Update() method. That works just fine.


Still, if we don't do something about organizing our code, we will wind up with a huge Game1 class. While there are a number of ways of engineering around this problem, I can't guarantee the solution I'll provide is the best (truthfully, the best solution depends on the game YOU'RE designing. What I'm showing you is best for something with lots of screens or minigames, such as a RPG). Still, I think it's pretty good, as it keeps the code organized and it allows the developer to focus on the situation at hand.


The solution we will use to overcome this obstacle is to create an interface that will handle our Update and Draw methods depending on what we want our situation to focus on. As such, our game flow will look more like this;


The flow may seem slightly more complicated, but because we have static input, all we'll have to focus on from here on out is our Draw() and Update() methods.

I call this interface a SCENE. The concept is that a different object that implements the SCENE interface will take over the update and draw methods depending on the situation, and in turn that will let us code objectively to each SCENario (get it? huh? Err, let me better explain...)



When you start a game, you are often confronted with a few splash screens and maybe a intro movie before encountering a title screen. From the title screen, you can start a new game, go to the options menu, or to the load menu. From the load menu and selecting 'new game', you go to the world map. From the world map, you can go to the main menu, to many submenus, or to a battle screen. Occasionally you might have a minigame, or something else. When you're coding each of these scenarios and screens, you should only be concerned with that scene, and any transitioning to any related screen. What happened on the main menu should stay on the main menu, and so forth.


All images Courtesy of Nintendo and Gamefreak. This game serves as a good example of how the SCENE interface should work. The scope of a game such as pokemon may be daunting, but when you break it down into it's individual components it's not as complex as it seems.

Each of these would be a different SCENE object. The SCENE Interface needs to only contain the method signatures of two methods; Update and Draw .
Alright, lets do this! Create a new interface object (similar to how you would create a new class, but select 'interface' instead of class). Name is IScene.cs (good habit; prefix all interfaces you create with a capital 'I'), and add the code;
public interface IScene
{
    void Update();
    void Draw(SpriteBatch s_batch);
}


Wow. Smallest .cs file ever or what?


While this whole process is all fairly painless, but we're not finished yet. We still have to make things work in our Game1 class. By the time this is over, it should be one of the last things we will ever do in the Game1 class. In the Game1 properties, add 
public static ISCENE SCENE;

Now in our Update, add:
//Check to see if Game1.SCENE = null. 
//If so, exit. Otherwise, update.

if (SCENE == null)
    this.Exit();
else SCENE.Update();


And in our Draw method, add (between our spriteBatch.Begin() and spriteBatch.End() calls) :
SCENE.Draw(spriteBatch);

And we're done! Build, fix any errors and run. What happens?
If the screens pops up and closes itself, it's working. Why? Because of our check in update. We never set the Game1.SCENE to anything, so it's value is null. When Game1.Update() runs, it trips the if statement, and it exits with this.Exit() (alas, another non-static member hosted in Microsoft.Xna.Framework.Game). This is good for two reasons;

Firstly, if somehow our code runs in a manner to nuke the current SCENE by setting it to null, or otherwise leave us without a SCENE, it will close itself rather than throw an error (if your code is that bad though, you have more important things to worry about ). Secondly, it gives us a static method to quit the game. With the single line of code Game1.SCENE = null, we can end things on a dime.

Alright, so we at least want the game window to show up when we run. From here on out, when creating a new broad component to your game (a menu, a minigame, a title screen, etc.), make sure it will implement IScene!

    class Scene_TEMPIScene
    {

  //List your local variables here!
        public Scene_TEMP()
        {
//Use the constructor to initialize
//any local variables to your scene!

        }

        public void Update()
        {
//It's our update method!
        }

        public void Draw(SpriteBatch s_batch)
        {
  //Because the SpriteBatch passes by
//reference (it sends the pointer to 
//Game1.Spritebatch, not the contents of
//Spritebatch), we can just use s_batch
//send any calls to draw (and it's in-
//between the begin() / end() methods!
//No need to worry about that ever again!

        }
    }


This is just a basic template of a scene object we would need to create. In our Game1.Initialize() method, we simply need to specify what our initial SCENE object should be before the game officially runs. Because all input is ubiquitous and static, all components of our game are now broken down into two methods; Update and Draw. Game design could not be more straightforward.



If you can't already tell, the underlying philosophy of this guide has been "everything in one line of code". By coding objectively and creating a framework to handle more abstract tasks, we give ourselves more tools to work with. This concludes the first part of this guide. I hope you had fun, and please recommend this blog to your friends. Facebook it. Tweet it. Reddit it, so on. I'm a sucker for statistics, and the more people reading this the more inclined I am to update it.

In the next part of this guide, we will learn how to beat the dreaded Spritebatch, and display our graphics in a single line of code. Prepare for Part 2; Thinking Abstractly.

Homework: I'll soon provide the entirety of the code we should have, so the solution for this homework will be found there. In the Game1.Update() method, set the SCENE to the same SCENE object the initialize method sets whenever the F12 button is pressed (Think of this as a reset button. When F12 is pushed, the game jumps back to the title screen. The actual operations for a reset button should be more complex than this, but for now this will do).

Yesterdays Homework Solution: The code wasn't hard, just repetitive.

public static void BGM_play(string name)
{
    if(BGM != null) BGM.Stop(); 
    s_bgm = Player.Load<SoundEffect>(@"BGM\" + name);
    BGM = s_bgm.CreateInstance();
    BGM.IsLooped = true;
    BGM.Play();
}


public static void BGS_play(string name)
{
    if(BGS != null) BGS.Stop(); 
    s_bgs = Player.Load<SoundEffect>(@"BGS\" + name);
    BGS = s_bgs.CreateInstance();
    BGS.Volume = 0.8f;
    BGS.IsLooped = true;
    BGS.Play();
}


public static void SE_play(string name)
{
    if(SE != null) SE.Stop();
    s_se = Player.Load<SoundEffect>(@"SE\" + name);
    SE = s_se.CreateInstance();
    SE.Volume = 0.8f;
    SE.Play();
}


public static void ME_play(string name)
{
    if(ME != null) ME.Stop(); 
    s_me = Player.Load<SoundEffect>(@"ME\" + name);
    ME = s_me.CreateInstance();
    BGM.Pause();
    BGS.Pause();
    ME.Play();
    while(ME.state != stopped)
    {
    //this will resume the BGM and BGS
    //once the ME has completed.
    //Avoid using while statements like this though;
    //They're bad design, and they slow down the game
    //And in theory, this shouldn't even work at all.
    }
    BGM.Resume();
    BGS.Resume();
}


Wednesday, June 8, 2011

XNA Game Design: ContentManager and Audio

Unlike the SpriteBatch, the ContentManager is actually a lot easier to spawn instances of, and just requires a variable we can make a static in our game1 class. In our Game1 properties, add the following;

public static GameServiceContainer services;
and in the constructor, initialize it with

services = Services;
What is a GameServiceContainer? It seems like one of those generally random variables we seem to be needing here and there (and it is). A ContentManager needs a particular service that can import and read the files it's loading.*  GameServiceContainer objects simply contain a collection of all Game.Services so that we can pass it in lieu of picking out each service from each service provider and passing that. It's like passing a box of different cookies to children in a classroom instead of asking each child what they want, running to the factory and getting it for them. Nifty huh?


Our game will have a significant number of services, most if not all of whom are default initialized in the Microsoft.Xna.Framework.Game class. These are contained in the private Services object, which is not static and we would like public static access to. With this done, from here on out we can make ContentManagers anywhere using Game1.services .
*(Contentmanagers really only need a service for loading texture files, so this is the equivalent of giving a lumberjack an ax to trim a bonsai tree. He just needs the ax in case he's suddenly asked to chop a tree, which shouldn't happen. God these metaphors are getting weird...)


Let's create a class to play audio now! This audio playing class will contain methods designed to do the following:
  • In a single line of code, take in a String that will correspond to an asset name in our "Content/Audio" folder, and play that sound at default settings (full volume, normal pitch, no pan).
  • Allow us to change the volume, pitch or pan of the sound in a single line of code.
  • Allow us to pause the sound in a single line of code.
  • Allow us to resume the sound in a single line of code.

static class Audio
{
    public static SoundEffect s_bgm;
    public static SoundEffectInstance BGM;
    //Our Audio ContentManager will be called Player
    public static ContentManager Player = 
    new ContentManager(Game1.services, "Content/Audio");
}

We see our ContentManager is initialized, but hold on a second; there's a class... called SoundEffectInstance? What's the difference between a SoundEffect and a SoundEffectInstance? Is an instance of a SoundEffect a SoundEffectInstance? What is this I don't even-


Chillax. A SoundEffect is the direct output from the ContentManager. SoundEffect objects contain mostly metadata and no methods actually related to playing the sound. This metadata includes length, channels of audio, kbps, and other things related to sound we wouldn't want to touch with a ten foot pole. SoundEffectInstance, on the other hand, contains all our fun methods for playing, pausing, resuming, looping, paning, pitching and general tinkering with our sound. SoundEffect has a method called CreateInstance() which, as you can imagine, returns a SoundEffectInstance object. With our content manager, we can load the SoundEffect, and then use CreateInstance() to create our playable SoundEffectInstance. It's just a two step process.


So, here is our method to take in the string and play the audio:



public static void bgm_play(string name)
{
    if(BGM != null) BGM.Stop(); 
    //Without this the current sound would not stop playing
    s_bgm = Player.Load<SoundEffect>(@"BGM\" + name);
    //The @ before the string indicates that escape codes 
    //should be ignored. It should be noted that both / and \
    //would be read as a sign to look into that folder. 
    //Therefore, ("BGM/" + name) would work just as well.
    BGM = s_bgm.CreateInstance();
    BGM.IsLooped = true;
    BGM.Play();
}

That's all there is to it. From here on out, we just have to say Audio.BGM.Pause();,  Audio.BGM.Stop()Audio.BGM.Volume = x, etc. just to change things. Everything from one line of code- that's how to design a game.


Still, there is a slight problem; with this design, we only get one channel to play sound on. I named our SoundEffect s_bgm and our SoundEffectInstance BGM. BGM is designed to be background music that will loop upon completion (notice the BGM.IsLooped = true;). But because this is our only channel of audio, we would not be able to play other noises, such as spoken voices, running water, explosions off in the distance and so forth. As such, this is the homework:


Homework: In your Audio folder, create four new subfolders named BGM, BGS, SE, and ME. Initialize and create the variables for these four audio channels (BGM is already there), and create methods so that following occurs:
  • Play_BGM() is looped (Background Music)
  • Play_BGS() is looped and will play at 80% volume. (Background Sound)
  • Play_SE() is not looped, will play at 80% volume (Sound Effect)
  • Play_ME() is not looped, and when called will pause BGM and BGS. (Music Effect)
  • Each method will look for it's audio file in the respective audio subfolder