1
\$\begingroup\$

I have a small demo and I want to create a class that draws messages in screen like fps rate. I am reading a XNA book and they comment about GameComponents. I have created a class that inherits DrawableGameComponent

public class ScreenMessagesComponent : Microsoft.Xna.Framework.DrawableGameComponent

I override some methods like Initialize or LoadContent. But when I want to override draw I have a problem, I would like to pass some parameters to it.

Overrided method does not allow me to pass parameters.

        public override void Draw(GameTime gameTime)
        {
            StringBuilder buffer = new StringBuilder();

            buffer.AppendFormat("FPS: {0}\n", framesPerSecond); // Where get framesPerSecond from???

            spriteBatch.DrawString(spriteFont, buffer.ToString(), fontPos, Color.Yellow);
            base.Draw(gameTime);
        }

If I create a method with parameters, then I cannot override it and will not be automatically called:

        public void Draw(SpriteBatch spriteBatch, int framesPerSecond)
        {
            StringBuilder buffer = new StringBuilder();

            buffer.AppendFormat("FPS: {0}\n", framesPerSecond);     
            spriteBatch.DrawString(spriteFont, buffer.ToString(), fontPos, Color.Yellow);
            base.Draw(gameTime);
        }

So my questions are:

  • Is there a mechanism to pass parameter to a drawableGameComponent? What is the best practice?
  • In general is a good practice to use GameComponents?
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Why no place the logic for calculating the FPS counter in the update method of this class? \$\endgroup\$ Commented Oct 16, 2012 at 1:16
  • \$\begingroup\$ I think that fps is just an example of the data he woul like to pass to the component. \$\endgroup\$
    – Ken
    Commented Oct 16, 2012 at 6:56
  • \$\begingroup\$ Yes, fps is just an example. I plan to pass more parameters, as many as I need. \$\endgroup\$ Commented Oct 16, 2012 at 8:37

4 Answers 4

3
\$\begingroup\$

Give your DrawableGameComponent some properties e.g. framesPerSecond, which you set before calling Draw(GameTime). This might only be appropriate if the property is a 'natural' part of the Component.

The other alternative is to use the game service mechanism. Game services are a mechanism for maintaining loose coupling between objects that need to interact with each other. Add a service class to the game service container;

  //this is usually in the Game.Initialize()
  Services.AddService(typeof(ContentManager), Content);
  Services.AddService(typeof(Camera), camera);

The DrawableGameCompontent object can then access that service object through the service provider;

//DrawableGameComponent method
public void Draw(GameTime gt)
    {
        Camera camera = (Camera)Game.Services.GetService(typeof(Camera));
        foreach (ModelMesh mesh in invaderModel.Meshes)
        {
            foreach (BasicEffect effect in mesh.Effects)
            {
                //Effect Settings Goes here
                effect.LightingEnabled = true;
                effect.EnableDefaultLighting();
                effect.World = world;
                effect.Projection = camera.Proj;
                effect.View = camera.View;
            }
            mesh.Draw();
        }

    }

This question has an interesting discussion on the pros and cons of using GameComponents.

\$\endgroup\$
1
  • \$\begingroup\$ Thanks. It worked. In fact all the answer were pretty accurate but could choose only one as the accepted answer :) \$\endgroup\$ Commented Oct 21, 2012 at 10:32
1
\$\begingroup\$

There are mainly two ways:

  1. As Luis Estrada says, add logic to count Update- and Draw-Calls (this numbers might differ) to the component. If no other component needs this properties, this might be the best way.
  2. If other components also need to know about the current frame-rate, it might be a good way to implement a GameService that is responsible for this. In each DrawCall to your ScreenMessagesComponent access the Service and draw the values to the screen.

Have a look at this for calculating frame-rates: Calculating frames per second in a game

\$\endgroup\$
1
\$\begingroup\$

I have answered a very similar question to this over on Stack Overflow. To summarise:

The best way to pass an argument into a game component's method is to pass an argument in! Unfortunately this precludes the use of GameComponent - because its interface is fixed. It's a fairly weak API for this reason.

It's best to treat GameComponent as an example of how you can set up a "game component" or "actor" system in your game. Like many other parts of XNA (sprite fonts, models, the Game class itself, etc) - you're able to and encouraged to replace it if you need functionality that it does not provide.

Although it sounds like you don't really need an actor or component system here at all. I imagine each "component" you have will need different data passed in? Simply create a class with a Draw method? Take a look at this answer.


But, if you must use GameComponent, then perhaps the next-best method is to set member properties or fields on the GameComponent before their Draw and/or Update methods get called (they get called in Game.Draw and Game.Update - so where you call base.Draw and base.Update in your own game class).

(You could use another method, though. Just do the simplest thing that could possibly work.)

\$\endgroup\$
1
\$\begingroup\$

The problem is that something like GameComponent has a fixed interface; you can't pass it in as an argument. Thus, what you can do is create a new item that acts as a Service in your main game class. Then, just add it to your game's services and try to get it when you need to draw it.

So, at the start of your game (constructor or Initialize method, whatever works) you can do:

DebugInformer informer = new DebugInformer();
Services.AddService(typeof(DebugInformer), informer);

DebugInformer is a class of your own design. Give it properties that work for you like FramesPerSecond. You can add other things you want that are specific to your game, such as ItemsOnGround and whatever else suits your fancy. Later in the code, you can get the properties out and use them like so:

//In the middle of your specific Component's draw call:
DebugInformer informer = Game.Services.GetService(typeof(DebugInformer)) as DebugInformer;
if (informer != null) {
    // Informer is present, use informer.FramesPerSecond and other stuff
}

Services were designed just for problems like this, so don't feel afraid to use it.

Good luck!

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .