Cant get my spritebatch variable from my abstract class?

Hello^^ Im currently working on sorting my code, and are at the moment trying to place all my scene variables in an abstract class for all my scenes to inherit. One problem ive come across though is that my spritebatch in draw aint being “fetched” from the abstract class? Im planning on putting all my variables in this abstract class to keep each scene as clean as possible and to avoid duplication.

// The abstract class holding variables
abstract class SceneVariables
{

    SpriteBatch spriteBatch = new SpriteBatch(GameWorld.instance.GraphicsDevice);

}

// One of my scenes
public void Draw(SpriteBatch spriteBatch)
{
GameWorld.MyGraphicsDeviceManager.GraphicsDevice.Clear(Color.Gray);

        // Am getting a nullreference in the spriteBatch below here ?
        spriteBatch.Draw(gameWorld.Content.Load<Texture2D>("DesignPlayStyle"), new Rectangle(200, 0, 400, 100), Color.White);
        spriteBatch.Draw(gameWorld.Content.Load<Texture2D>("ChooseClass"), new Rectangle(100, 200, 200, 50), Color.White);


    
        playstyleReturnWithoutSaving.Draw(spriteBatch);
        playstyleSaveAndReturn.Draw(spriteBatch);
        talents.Draw(spriteBatch);


        foreach (ASpriteSheetBtn btn in btn_list)
        {
            btn.Draw(spriteBatch);
        }
    }

Im starting to fear that ive messed up something important, now i cant add a new folder called “Spells” to my usings o.O The plot thickens!

I would say when you instianciate an object from the inherited class from scenevariable, gameworldservice might not be known, depending on the position: in the game constructor, or the load method or the initalize. We dont have these informations here.

What do you mean by gameworldservice?

I meant Gameworld. instance

Here is whats currently in my Gameworld, hope that was what you ment.

namespace EverMoreDeeper
{

public class GameWorld : Game
{
    // Makes this class a singleton class
    public static readonly GameWorld instance = new GameWorld();

    public enum ClassBtnToggled
    {
        None,
        Amazonian,
        Magus,
        ShadowSpawn,
        Blackguard,
        SenjuMonk,
        Deadeye,
        Gladiator,
        Templar,
    }
    public ClassBtnToggled classBtnToggled;

    public static GraphicsDeviceManager MyGraphicsDeviceManager;
    public static SpriteBatch spriteBatch;

    private SceneManager mySceneManager;
    //private SceneVariables mySceneVariables;

    public int screenWidth = 1360;
    public int screenHeight = 768;
    public string chosenClass;
    SpriteFont myFont;
    private const float messageDelay = 2;
    private float remainingDelay = messageDelay;
    private string designPlaystyleMessages;

    // Spell testing
    private MagusAutoAttack magustMagusAutoAttack;

    public GameWorld()
    {
        MyGraphicsDeviceManager = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";

        MyGraphicsDeviceManager.PreferredBackBufferWidth = screenWidth;
        MyGraphicsDeviceManager.PreferredBackBufferHeight = screenHeight;
        MyGraphicsDeviceManager.ApplyChanges();
        IsMouseVisible = true;

    }

    protected override void Initialize()
    {
        mySceneManager = SceneManager.instance;

        classBtnToggled = ClassBtnToggled.None;

        base.Initialize();
    }


    protected override void LoadContent()
    {
        spriteBatch = new SpriteBatch(GraphicsDevice);
        mySceneManager.LoadContent();

        //myFont = Content.Load<SpriteFont>("myFont"); /*asdadasd*/

    }


    protected override void UnloadContent()
    {
    }

    protected override void Update(GameTime gameTime)
    {
        MouseState mouse = new MouseState();
        mySceneManager.Update(gameTime, mouse);

        base.Update(gameTime);
    }

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.White);
        spriteBatch.Begin();

        mySceneManager.Draw(gameTime);

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

I didn’t closely examine this but let me just say the rules for a underlying abstract class are simple. Call base. from a derived class e.g. base.Spritebatch to access the Game version of the variable. What you need to remember is game1 is doing stuff under the hood that will not be done to your derived class necessarily be it abstract or not, thus from time to time you must update the derived abstracts references or call into base from your abstract class and manually update the variables the problem in that regard is notification and since this is not java there is no super keyword were you can call up you then need some indirection class or must force a coupling. Another thing to note is that for a non abstract this would be the case as well.

spriteBatch = new SpriteBatch(base.GraphicsDevice);

Which begs the question what is the real benefit to placing these in a abstract class.
Note that internally Game is essentially doing this already i.e. its the base class, see in your game1 template
GameWorld:Game and base.Draw, So when you wrap game you have to be careful that when unload or load executes or the device context is reset which windows itself can do or even if the state changes due to some call. Then you need to replace your abstract classes base. reference via abstract.spritebatch = game.spritebatch or in your case grab base.spritebatch ect…

This is actually a difficult way to do it all and it can get confusing and you might actually be unintentionally duplicating things in the derived classes certainly the derived references will be, I had a elaborate base wrapper i still do it is just a regular derived class from game1 that wraps it instead of abstracts it. That just does a ton of default stuff if i pick a method or enum to switch over to different setups, but in hindsite and my newer version that im slowly moving to it is far simpler.

My alternative answer here is below, yes its not elaborate its not elegant but it is simple and it works well.

public static class Eng
{
public static SpriteBatch spritebatch;
public static SetUp(SpriteBatch sb){ spritebatch = sb;}
}

public class SomeClass
{
public void SomeMethod()
{
Eng.spritebatch.Draw(… ect );
}
}

This is now cleaner from the above abstracting is now simpler but the abstraction here is fairly pointless in either case as its not really abstract if its coupled to and relys on Game.

public abstract class SomeAbstractClass
{
public virtual void AutoDraw()
{
Eng.spritebatch.Draw(… ect );
}
}

public abstract class SomeClassB:SomeAbstractClass
{
public void SomeMethod()
{
AutoDraw();
}
}

The problem your encountering now is akin to if you were to override the abstract virtual method used by someclassB above AutoDraw. You would have two separate references were the abstract in this case is akin to Game which then no longer changes SomeClassB’s derived reference.
In this case it would still reference the Eng.Spritebatch reference so that part would work yet they would still be separate method calls base or derived as far as the compiler was concerned.

How i do it is like so.
In game1 any time the application is resized and in your load method pass spritebatch to Eng.setup by that time its fully initialized, that should handle any device resets just fine. Its far simpler, i actually put a bunch of stuff in mine i put a reference to the devicemanager the device the window ect ect. what your passing is just a reference anyways not a full copy.

For default automated stuff make a default class that handles default setups. For abstracts couple them to your Engines globals Eng.somevariable Yes normally coupling is bad but in this case it is actually going to happen anyways your going to call spritebatch one way or another. So In your actual game1 load or when you your graphics device is lost which calls load anyways or when resizing the window in your on windowsclientsizedchanged method that is fired, update engine references to those values you hold.
So keep it simple and as clear as possible.

For gamewindows just poll it or use delegates its really what monogame xna is was doing anyways

But ya if you don’t wanna change it your going to eventually have to call base all over the place, the way you have it setup which sort of defeats the purpose of easily accessing these variables.

An abstract class for the scenevariables is, for me, not very usefull as you won’t have many scenes loaded at the same time, and wanting to avoid duplicating functions as and they will/should be doing one simple thing, as the name suggests, give a scene’s variables.
An abstract class would be (and why abstract it to complicate the process, an abstract class can not be instantiated) better used for items for example, as load, init, draw would be the same, only textures are changing between them (it is a simple example)
I usually make a baseclass (not abstract at all) with the lowest/minimal code to use/draw an object in my game engine, and inherit from it when i need to add more specific functionalities.
ex:
CBaseItem (with position, mesh, texture)
->CRender_Billboard (inherits from CBaseItem)
->CGame_PickableItem (inherits from CBaseItem, has some CRender_Billboard objects in it to render farthest ones)
->->CGame_Life (inherits from CGame_PickableItem)
->->CGame_Ammo (inherits from CGame_PickableItem)
->CRender_Object (with position, mesh, texture) (adds self rotation method for example)
->CRender_LightBulb (with position, mesh, texture)
->CRender_Monster (with position, mesh, texture)(adds some IA)
etc.
CBaseItem is used in the editor when an object has just been created (a cube, a spehre, etc) but we don’t know what it will be, only a sort of prototype.

I said this before: it makes a lot of sense to think of inheritance as an “is a” relation. That is, make A inherit from B if A is a B. There are exceptions, but this is not one of them, if two classes both have a reference to a spritebatch it doesn’t make sense to have a base class for them with just the spritebatch reference.

In the code in your first post, ‘spriteBatch’ refers to the parameter, not the field of the base class (the parameter hides the field), so the problem is not that spriteBatch in the base object is null, but that you pass null to Draw.