What's the trick to adding classes to a MonoGame project?

Today’s stupid question. I didn’t want to put everything in Game1.cs so I created a new file in the project called Display.cs. This is all it is:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System.IO;


namespace GSBPGEMG
{
    public class Display
    {
        public static void DisplayBanner(string Message)
        {
            SpriteFont font;
            font = Content.Load<SpriteFont>("Smythe-Regular.tff");
            spriteBatch.Begin();

            spriteBatch.DrawString(font, Message, new Vector2(100, 10), Color.Black);

            spriteBatch.End();
        }

    }
}

And it throws 48 errors all about not recognizing XNA. So, what’s the trick to add other files with classes to a MonoGame?
Thanks!

It might help to specify what some of those errors are, but I suspect they’re related to various things not being found. For example, in your code you’re referencing both Content and spriteBatch, but your new class doesn’t know what those are. You have to pass those objects along somehow… but how you achieve that is up to you.

(NOTE: In this particular case you probably don’t want to pass Content… you should get a reference to this font in your game’s LoadContent method, then just pass the font to either this static method, or to a class if you want this information to persist.)

Thanks!

Using System;

Solved the errors. Now it’s throwing this error on content (I don’t think the solution is to make Load static).

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System.IO;


namespace GSBPGEMG
{
    public class Display: Microsoft.Xna.Framework.Game
    {
        public static void DisplayBanner(SpriteBatch spriteBatch, string Message)
        {
            SpriteFont font;
            font = Content.Load<SpriteFont>("Smythe-Regular");

            spriteBatch.Begin();

            spriteBatch.DrawString(font, Message, new Vector2(100, 10), Color.Black);

            spriteBatch.End();
        }


    }
}
An object reference is required for the non-static field, method, or property 'Game.Content'	15	Active

You already have a main game class with a Content in it. So just pass it along instead just like you did with SpriteBatch. Or, as I said, just load the Font in your main game and then pass it along to DisplayBanner.

public class MainGame : Game
{
  private SpriteFont _font;
  private SpriteBatch _spriteBatch;
  // ...

  protected override void LoadContent()
  {
    _font = Content.Load<SpriteFont>("Smythe-Regular");
  }

  protected override void Draw(GameTime gameTime)
  {
    Display.DisplayBanner(_spriteBatch, _font, "Test message");
  }
}

public class Display
{
  public static void DisplayBanner(SpriteBatch spriteBatch, SpriteFont font, string Message)
  {
    // same stuff you already have except don't load font because it's a parameter now
  }
}

This will probably work, though if the font isn’t something your main game needs to keep track of then yea, you’ll want to create an object and pass it off. For example…

class MainGame : Game
{
  private SpriteBatch _spriteBatch;
  private BannerDisplay _bannerDisplay;

  protected override void LoadContent()
  {
    _bannerDisplay = new BannerDisplay(
      _spriteBatch,
      Content.Load<SpriteFont>("Smythe-Regular")
    );
  }

  protected override void Draw(GameTime gameTime)
  {
    _bannerDisplay.Draw(gameTime, "Test message");
  }
}

public class BannerDisplay
{
  private SpriteBatch _spriteBatch;
  private SpriteFont _font;

  public BannerDisplay(SpriteBatch spriteBatch, SpriteFont font)
  {
    _spriteBatch = spriteBatch ?? throw new ArgumentNullException();
    _font = font ?? throw new ArgumentNullException();
  }

  public void Draw(GameTime gameTime)
  {
    // I think you get it from here :)
  }
}
1 Like

One more thing to note! You don’t necessarily need to call Begin/End on every draw you want to do. Typically you want to batch your calls up and draw things that go together between every Begin/End call. If you always draw all your stuff the same way, you really only need one Begin/End, but if you do any shaders or specific transforms on the batch itself, you may want others.

I don’t know your project but maybe keep this in mind. If you only ever have/need a single SpriteBatch for your game, move the Begin/End call out of your banner display.

1 Like

Thanks! Just getting the hang of how MonoGame does stuff.

While I support the mantra of “How you achieve is up to you”, and truly one of the great strengths of Xna/Monogame, it can be a bit overwhelming and sometimes some guides and pointers go along way.

With that in mind it might be worth you having a look at GameComponents, a built in concept to Xna/Monogame where things can become more “self contained” which will help you scale your projects nicely.

https://docs.monogame.net/api/Microsoft.Xna.Framework.GameComponent.html

The other invaluable concept is the GameStateManagement example. I’m not sure where/if there is an official version of this but this looks decent?

Thanks for the links!
I think I’ve got a good grasp of what’s going on now. Looks like you have to pass around somethings like spriteBatch with, if possible, one drawing call. Reminiscent of how you did graphics in MFC.

Btw, this is kinda c# question, not directly MG related.

I’ve been writing in C# for years. My question really was: what ‘header’ (Using) was missing because it didn’t recognize any XNA methods. The answer was that it needed System and to pass _spriteBatch and spriteFonts.

Passing objects is however c# thing, not mg nor using related.

I’m not sure what you mean by one drawing call. If you mean the one Begin/End call then that’s not really a requirement, just an optimization. You can (and probably will) have more. Also, instead of passing things along, some folks like to make a Globals class that stores references of important things like the main SpriteBatch your game uses, your GraphicsDevice, and your Content object. I’m not a fan of this because it makes things hard to change later, especially if you want to use a different SpriteBatch, but you can do it whatever way jives with you :slight_smile:

While true, it’s also a C# question in the context of MonoGame. As a MonoGame community, I think that’s something we should help with if we can :slight_smile:

1 Like

Yeah, overuse of globals is kinda beaten out of you in school. I try to keep it to a minimum.

Then again, schools are still teaching OOP as way to go and that mutable structs are evil. I would take them with grain of salt.

I think it’s more important to understand why schools are teaching those things, and it’s usually because they want to instill good practices. Not using globals is usually one of those things that can make software tough to maintain down the road. If you want to change something out for another piece and all the information comes from globals, but you want a difference version of something the globals provide, it can start to get messy.

But, you know the scope of your project and what you intend to do with it. If maintenance isn’t something you intend to have to deal with for a meaningful period of time, maybe using globals as a quick n’ dirty solution is appropriate.

Either way, you gotta do what you gotta do :slight_smile:

Without using globals you can just pass a reference to a context object that had those objects and variables. That object can be altered much more easily, and as a bonus you can switch it out whole cloth fora fake one when testing.

Like some other environments that I’ve worked in, you have to pass around graphic device, or graphic device context, or whatever. You have to do that in MFC, too.

But, thanks, I think I’m getting the hang of it now.