RecentComments

Comment RSS

XNA Game Development (Scenes and Game Services)

by Ioannis 15. January 2010 07:30

In the previous post, we have talked about decentralizing our XNA application by taking advantage of the GameComponent and DrawableGameComponent classes. In this post, we will explore two things:

 

  • The way to decouple the GameComponents' interfaces from the main game logic by using "Game Services".
  • The way to create "Scenes". Scenes in games are those displays that occur before the game starts with th game menu, the help screen, etc etc.

 

Let's start with Game Services. In the project of the previous post (also available here) in the CrazyIvanSprite class in the constructor we have a call to the game.Services.GetService method as follows:

public CrazyIvanSprite(Game game)
    : base(game)
{
    _ivanPosition = new Vector2(400, 300);
    _game = game;
    _ivan = _game.Content.Load<Texture2D>("Ivan");
    _spriteBatch = (SpriteBatch)game.Services.GetService(typeof(SpriteBatch));
}

Note that this call retrieves the SpriteBatch object in order to use it later when rendering the sprite. Where does it find it? Well in the main game loop (GameMainLoop class), in the LoadContent method, where all the initializations take place, we find a call to Services.AddService method

protected override void LoadContent(){
    spriteBatch = new SpriteBatch(GraphicsDevice);
    Services.AddService(typeof(SpriteBatch), spriteBatch);
    ...
}

In other words, the main game loop has placed an object in the game's Services and this object was retrieved by its type by the sprite that needed it. You may be wondering at this point why is this different from passing the desired object as a method parameter or via a public accessor property to the sprite. Well, technically there is no difference. 

The thing is that by using game services the main game loop does not have to know the interfacing details of each sprite. On the contrary it places the objects that may be needed to the services pool and announces that they are there and can be retrieved by anybody that wants to use them. Moreover,any sprite that wants to make something available to anybody can add an object to the services pool.

Now you may be wondering why this functionality holds the name of "Services" when it seems that it is merely a way to make objects available to game components. This is because the explanation so far has used GameServices as a mean to pass objects between sprites. The ideal design and implementation of a game specifies that the Game Services mechanism should be used as follows:

 

  • Design your game and decide which functionality will be needed to be provided by the main game loop to your sprites.
  • Write an Interface defining the methods for this functionality (for example IGraphicsServices).
  • Write a class implementing this interface (for example myGraphicsService).

 

Having those at some point during initialization of the game instanstiate the class and provide it as a service:

protected override void LoadContent(){
    myGraphicsService GService = new myGraphicsService ();
    Services.AddService(typeof(IGraphicsServices), GService );
    ...
}

Now the term "Services" makes more sense since the game announces that there is a service in the game that provides the methods of the IGraphicsService interface. The consumer sprites of the service can now retrieve it as follows:

public ASprite(Game game)
    : base(game)
{
    ...
    IGraphicsService GService = (IGraphicsService )game.Services.GetService(typeof(IGraphicsService ));
}

Lets move now to the way we implement "Scenes" in our game. As previously mentioned, scenes are the game screens that appear during th lifecycle of the game (eg the main menu scene, the main game scene, the game over scene, the help scene etc). Also note that in XNA we do not have the GUI goodies that we are used to in WinForms or WPF meaning that we have to implement everything from scratch.

As a reminder, from the previous post we have seen that everything in the main game scene is a DrawableGameComponent. In fact, the same holds for every other scene in the game. For example a flashing text string in a typical game menu saying "Press any key to begin" is a DrawbleGameComponent having a timer that in specific intervals changes the color of the text. Also, clicking on a menu selection is not caught by any mouse handler but on the contrary the mouse position is polled during the Update phase of the main game loop and the programmer has to check to see whether the mouse pointer falls into the area where the specific menu item is.

So other scenes just like the main game scene consist also of a set of sprites that interact to each other and to the user's input. Since we can control when a DrawableComponent will be shown on screen by its Visible property, we can make the DrawableComponents that compose the game scene all visible and updatable when we are at the game scene and make all other compoents not visible and vice versa when we are at another scene.

This is a solution but there is a better one. We will follow the Main game loop's architecture by creating a class Scene as follows:

public class Scene:DrawableGameComponent
{
    public List<GameComponent> SceneComponents { get; set; }
    public Scene(Game game) : base(game){
        SceneComponents = new List<GameComponent>();
        Visible = false;
        Enabled = false;
    }
    public void Show(){
        Enabled = true;
        Visible = true;
    }
    public void Hide(){
        Enabled = false;
        Visible = false;
    }
    public void Pause(){
        Enabled = false;
        Visible = true;
    }
    public override void Update(GameTime gameTime){
        foreach (GameComponent Component in SceneComponents)
        {
            if (Component.Enabled) Component.Update(gameTime);
        }
        base.Update(gameTime);
    }
    public override void Draw(GameTime gameTime){
        foreach (GameComponent Component in SceneComponents)
        {
            if (Component is DrawableGameComponent)
            {
                if ((Component as DrawableGameComponent).Visible)
                (Component as DrawableGameComponent).Draw(gameTime);
            }
        }
        base.Draw(gameTime);
    }
}

As you can see your scene has a list of DrawableGameComponent objects and is a DrawableGameComponent itself. In its Update method the scene calls the Update methods of all the DrawableGameComponent objects and the same for its Draw method. This means that now to create a scene we just create a class inheriting from the Scene class and insert its sprites and its logic in this class. In the previous post's example everything that was included in the main game loop, can be transferred to a scene named GameScene that inherits from Scene.

In the main game loop now we do not manage the sprites but we manage which scene will be enabled and visible.

In other words Game Services along with Scenes are two additional mechanisms that allow us to further harness the complexity of a game application.

The full application of the previous post with basic scene management can be downloaded here.

Shout it

kick it on DotNetKicks.com

Tags:

XNA

XNA Game Development (Decentralize)

by Ioannis 23. December 2009 07:31

In the previous post, we have talked about drawing and moving 2D textures on the XNA window. In this post, we will explore the basic architecture that XNA uses in order to separate the game logic from the sprite logic. So prior to reading this post, it is suggested to go through the previous one.

The main code smell there, was the fact that we had to put all the game logic for moving the Ivan sprite to the Update and Draw methods of the game loop. Imagine how messed up those too methods would look if we placed more sprites on the game scene. Therefore our first tendency is to take all this logic that moves and draws the Ivan sprite and draws the background and place it in on two separate classes.

Here is the code for the CrazyIvanSprite.cs file that has the CrazyIvanSprite class.

public class CrazyIvanSprite
{
    private Game _game;
    private Texture2D _ivan;
    private Vector2 _ivanPosition;

    public CrazyIvanSprite(Game game){
        _ivanPosition = new Vector2(400, 300);
        _game = game;
    }
    public void LoadContent(){
        _ivan = _game.Content.Load<Texture2D>("Ivan");
    }
    public void UnloadContent(){
        _ivan.Dispose();
    }
    public void Update(Vector2 MouseVector){
        Vector2 IvanToMouseVector = -(_ivanPosition - MouseVector);
        IvanToMouseVector.Normalize();
        _ivanPosition += IvanToMouseVector;
    }
    public void Draw(SpriteBatch spriteBatch){
        spriteBatch.Draw(_ivan, _ivanPosition, Color.White);
    }
}

 

In other words we have recreated in the CrazyIvanSprite class the same methods that the Game loop had and placed in there the logic that moves Ivan. The same is done for the background image. The main game loop in the main class is transformed as follows:

private CrazyIvanSprite _ivan;
private BackgroundSprite _background;

protected override void LoadContent(){
    spriteBatch = new SpriteBatch(GraphicsDevice);
    _ivan = new CrazyIvanSprite(this);
    _background = new BackgroundSprite(this);
    _ivan.LoadContent();
    _background.LoadContent();
}
protected override void UnloadContent(){
    _ivan.UnloadContent();
    _background.UnloadContent();
}
protected override void Update(GameTime gameTime){
    _ivan.Update();
    base.Update(gameTime);
}
protected override void Draw(GameTime gameTime){
    GraphicsDevice.Clear(Color.CornflowerBlue);
    spriteBatch.Begin();
    _background.Draw(spriteBatch);
    _ivan.Draw(spriteBatch);
    spriteBatch.End();
    base.Draw(gameTime);
}
 

Note that the game's methods just call methods with the same same in the two classes we have just created (the project that follows this approach can be downloaded here). Now the beautiful thing is the fact that XNA has thought of this approach before we did and actually has taken it one step furher. The approach is as follows:

"If we create our sprite classes to inherit from DrawableGameComponent or GameComponent and add them in the main game loop to the Components collection of the Game class, then XNA will execute automatically our sprite's Update and Draw methods within its own Update and Draw methods"

Let's see how this works for the CrazyIvan sprite. The new sprite's class is as follows:

public class CrazyIvanSprite:DrawableGameComponent
{
    private Game _game;
    private Texture2D _ivan;
    private Vector2 _ivanPosition;
    private SpriteBatch _spriteBatch;

    public CrazyIvanSprite(Game game):base(game){
        _ivanPosition = new Vector2(400, 300);
        _game = game;
        _ivan = _game.Content.Load<Texture2D>("Ivan");
        _spriteBatch = (SpriteBatch)game.Services.GetService(typeof(SpriteBatch));
    }
    protected override void Dispose(bool disposing){
        _ivan.Dispose();
        base.Dispose(disposing);
    }
    public override void Update(GameTime gameTime){
        MouseState MState=Mouse.GetState();
        Vector2 MouseVector = new Vector2(MState.X, MState.Y);
        Vector2 IvanToMouseVector = -(_ivanPosition - MouseVector);
        IvanToMouseVector.Normalize();
        _ivanPosition += IvanToMouseVector;
        base.Update(gameTime);
    }
    public override void Draw(GameTime gameTime){
        _spriteBatch.Draw(_ivan, _ivanPosition, Color.White);
        base.Draw(gameTime);
    }
}
 

As you can see, the updated CrazyIvanSprite class now overrides the typical Update , Dispose and Draw methods of the DrawableGameComponent class. The real magic happens in the main game loop's methods:

private BackGround _back;
private CrazyIvanSprite _ivan;

protected override void LoadContent(){
    spriteBatch = new SpriteBatch(GraphicsDevice);
    Services.AddService(typeof(SpriteBatch), spriteBatch);
    _back = new BackGround(this);
    _ivan = new CrazyIvanSprite(this);
    this.Components.Add(_back);
    this.Components.Add(_ivan);
}
protected override void UnloadContent()
{}
protected override void Update(GameTime gameTime){
    base.Update(gameTime);
}
protected override void Draw(GameTime gameTime){
    GraphicsDevice.Clear(Color.CornflowerBlue);

    spriteBatch.Begin();
    base.Draw(gameTime);
    spriteBatch.End();
}

Now that the sprites inherit from DrawableGameComponent, all we have to do is just create them and add them in the Components collection of the Game class. The rest is taken care by XNA and that is why Update and Draw methods of the sprite are not explicitely called in the corresponding Game loop's methods. Those will be executed when base.Update() and base.Draw() will be called.

Therefore by inheriting from DrawableGameComponent and GameComponent classes we manage to separate the sprites' logic from the game logic allowing sprite reuse between game and the distribution of the implementation of the sprites among many developers in a team.

Two things to note here:

  • The main difference between DrawableGameComponent and GameComponent is that the first implements a Draw() and Update() method while the second only implements the Update() method for logic that does not need drawing to the window.
  • If in a specific point we want to tell our game not to call the Update() method of a sprite we can set its Enabled property to false.
  • If in a specific point we want to tell our game not to call the Draw() methods of a sprite we can set its Visible property to true

This is it for now. In the next post we will talk about Game Services and Scene management. You can download the project of the final code here. Happy coding.

 

Shout it

kick it on DotNetKicks.com

Tags:

XNA

XNA Game Development (First Steps)

by Ioannis 19. December 2009 13:32

Welcome to a short introduction in game Development using the XNA Framework!

Let's start with the basics by examining how different is Windows programming to XNA Game programming. As we know, windows programming is "event driven". This means that our logic resides in event handlers that get called when a resource or a Control needs to tell us that something happened. So our program is nothing more than a loop that awaits for events to happen and a bunch of event handlers that respond to those events.

Game programming is different. Game programming is real-time and thus cannot afford to be lazy by waiting for some event to happen to actually do something useful. Therefore, game software cannot be event driven but rather operate on a polling basis. This means that our game will again be executing a loop (the typical game loop as it is called) and whenever the programmer likes, he/she will be asking resources about their status and whether something happened that needs attention. So the logic in games does not reside in event handlers but happens within the loop and knowing what happens in resources is in our hands to ask and discover.

A typical game loop consists of the steps shown in the following image:

 

 

The magic happens in the light-blue shaded area which is the loop that gets the user's input (e.g. polls the keyboard for a pressed key), calculates the positions of all the game actors on the screen, tests whether certain gameplay related criteria have been met (e.g. a collision occurred and the gamer should loose a life) and provides feedback to the gamer in the form of sound, control vibration or of course graphics.

The typical game loop in XNA is realized by the following methods where the "Get User Input", "Calculate" and "Test Criteria" parts happen within the Update() methods and the visual feedback happens in the Draw() method.

 

 

So lets implement our first program in XNA. First you need to download and install XNA Game Studio. Then start a new Visual Studio project and select to create a Windows Game. Now you will notice that the template has created several files in the project with most important the Game1.cs file that contains the game loop and the Content folder that will contain all the resources. We right-click the Content folder, click on "Add/Existing Item..." and locate the two image files we need (Ivan.png and SeaBottom.png). The two images are added to the project under the Contents folder and if you see their properties you will notice that they have been given an "Asset Name" , "Ivan" and "SeaBottom" respectively. Those are the names that will be used for referencing those resources within the game.

So what do we want to do? We want to load our resources and then draw them on screen. Therefore we need to put some code in the LoadContent() method (to load them), code in the UnloadContent() method (to free the resources when no longer needed), We do not need to alter the Update() method since we just want to draw the two images, so we will just add some drawing code to Draw(). So here is goes:

private Texture2D _ivan;
private Texture2D _backImage;
private Vector2 _ivanPosition;

public Game1()
{
    _graphics = new GraphicsDeviceManager(this);   
    _graphics.PreferredBackBufferWidth = 800;
    _graphics.PreferredBackBufferHeight = 600;
    Window.Title = "CrazyIvan Ver. 1.0 - DevDays 2009,Athens - Giannis Panagopoulos";
    _ivanPosition = new Vector2(400, 300);
    Content.RootDirectory = "Content";
}

 

protected override void LoadContent()
{
    _ivan = Content.Load<Texture2D>("Ivan");
    _backImage = Content.Load<Texture2D>("SeaBottom");
    spriteBatch = new SpriteBatch(GraphicsDevice);
}

 

protected override void UnloadContent()
{
    _ivan.Dispose();
    _backImage.Dispose();
}

 

 

In the beginning we set the size of the window, set the title and initialize the position of the Ivan (that is the position that image Ivan.png will be drawn).

Then we define two Texture2D objects that will hold the background image (SeaBottom) and the Ivan image (Ivan) respectively. In LoadContent() we load the images and initialize the textures and in the UnloadContent() we Dispose the textures. Pretty easy right?

Now in the Draw() method we draw the textures. Note that we have available a spriteBatch object created for us automatically by the framework which we will use to draw whatever we like. All drawing should be happening within spriteBatch.Begin() and spriteBatch.End() method:

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);
    spriteBatch.Begin();
    spriteBatch.Draw(_backImage, new Vector2(0f, 0f), Color.White);
    spriteBatch.Draw(_ivan, _ivanPosition, Color.White);
    spriteBatch.End();
    base.Draw(gameTime);
}

 We draw the background image at postion 0,0 and Ivan at the position defined by the Vector2D _ivanPosition object.

So now how can we make Ivan to move based on the arrow key pressed? This should happen in update. So we need to check whether a key was pressed and if so, we need to alter Ivan's position accordingly. This can be realized as follows:

protected override void Update(GameTime gameTime)
{
    KeyboardState KeyState = Keyboard.GetState();
    if (KeyState.IsKeyDown(Keys.Up))
        _ivanPosition = new Vector2(_ivanPosition.X, _ivanPosition.Y - 1);
    if (KeyState.IsKeyDown(Keys.Down))
        _ivanPosition = new Vector2(_ivanPosition.X, _ivanPosition.Y + 1);
    if (KeyState.IsKeyDown(Keys.Left))
        _ivanPosition = new Vector2(_ivanPosition.X-1, _ivanPosition.Y);
    if (KeyState.IsKeyDown(Keys.Right))
        _ivanPosition = new Vector2(_ivanPosition.X+1, _ivanPosition.Y);
              
    base.Update(gameTime);
}

 On the other hand if we want Ivan to follow the mouse we need to implement our Update() method as follows:

protected override void Update(GameTime gameTime)
{
   
    MouseState MState = Mouse.GetState();
    Vector2 MouseVector = new Vector2(MState.X, MState.Y);
    Vector2 IvanToMouseVector = -(_ivanPosition - MouseVector);
    IvanToMouseVector.Normalize();

 

    _ivanPosition += 4*IvanToMouseVector;
   
    base.Update(gameTime);
}

 

Compile and run the project with the two alternatives to check them out. Remember that XNA repeatedly calls your two methods (Update and Draw) and it never waits for any event.

You can download the whole project here.

In the next post we will discuss about the typical architecture of an XNA game that enables us to harness the complexity involved in having to deal with a lot of sprites in the game scene and also may help a team of developers partition the work that needs to be done.

  

kick it on DotNetKicks.com

Shout it

Tags:

XNA

Powered by BlogEngine.NET 1.5.0.7

Programming Blogs - BlogCatalog Blog Directory Add to Technorati Favorites

MVP Award

Ioannis Panagopoulos





This blog is using BlogEngine.Net and is hosted in the hoster below. I have not experienced any problems installing BlogEngine.Net in the host and I am satisfied with the host's response times. Therefore I recommend it.


DiscountASP Add