Implementing an camera class with Camera2D in the main

Hello, i’m totally new to Monogame and C# and i wrote now a code with the Monogame.Extended Package to implement an camera2D class. It all worked as long as i wrote everything in the main. Now i have trouble while writing it in an own class, so i just have _camera.Update() … etc. written in the main. May someone can help me because im already getting crazy about it. Please don’t judge me too hard, cause im a total noob. What is wrong with it? I would really appreciate an correction and explanation.

It seems like the error is that the camera has the value null, but i dont understand why. Could i do the whole thing easier or am i just too stupid?

Here are my 2 code files:

Game1.cs

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using MonoGame.Extended.ViewportAdapters;

namespace Game3
{
public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;

    private Camera _camera;
    private Texture2D _background;

   // public Vector2 _worldPosition;
    private int previousScrollValue;

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
        _camera = new Camera(Window, graphics);
        
    }

    protected override void Initialize()
    {
        // TODO: Add your initialization logic here

        base.Initialize();

        var viewportAdapter = new BoxingViewportAdapter(Window, GraphicsDevice, 800, 480);
        this.IsMouseVisible = true;
        _camera.Initialize();
    }


    protected override void LoadContent()
    {
        // Create a new SpriteBatch, which can be used to draw textures.
        spriteBatch = new SpriteBatch(GraphicsDevice);
        _background = Content.Load<Texture2D>("background");

        // TODO: use this.Content to load your game content here
    }


    protected override void UnloadContent()
    {
        // TODO: Unload any non ContentManager content here
    }


    protected override void Update(GameTime gameTime)
    {
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
            Exit();
        

        _camera.Update();
       
        // TODO: Add your update logic here

        base.Update(gameTime);
    }


    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.Black);
        var transformMatrix = _camera.GetViewMatrix(); // oder Begin(transformMatrix: _camera.GetViewMatrix)
        spriteBatch.Begin(transformMatrix: _camera.GetViewMatrix());
        spriteBatch.Draw(_background, new Vector2(0,0), Color.White);

       // TODO: Add your drawing code here
        spriteBatch.End();
        base.Draw(gameTime);
    }
}

}

Camera.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using MonoGame.Extended;
using MonoGame.Extended.ViewportAdapters;

namespace Game3
{
class Camera
{
private static int previousScrollValue;
private static Camera2D _camera;
private static int _zoom;
private BoxingViewportAdapter viewportAdapter;
private static GameWindow Window;
private static GraphicsDeviceManager graphics;
private static int size;
public Vector2 _worldPosition;

    public Camera(GameWindow Window1, GraphicsDeviceManager mgraphics)
    {
        previousScrollValue = 0;
        _zoom = 0;
        size = 1000;
        Window = Window1;
        graphics = mgraphics;
        viewportAdapter = new BoxingViewportAdapter(Window, graphics, 800, 480);
        _camera = new Camera2D(viewportAdapter); //mgraphics.GraphicsDevice?
    }

    public void Initialize()
    {
        _camera = new Camera2D(viewportAdapter);
    }

    public Matrix GetViewMatrix()
    {
        return _camera.GetViewMatrix();
    }
    public void Update()
    {
        var mouseState = Mouse.GetState();
        var keyboardState = Keyboard.GetState();
        const float movementSpeed = 1;
        const float zoomSpeed = 0.1f;
        float zoomBordersY = 0;
        float zoomBordersX = 0;

        if (_zoom == 0)
        {
            zoomBordersY = 0;
            zoomBordersX = 0;
        }
        if (_zoom == 1)
        {
            zoomBordersY = -22;
            zoomBordersX = 36;
        }
        if (_zoom == -1)
        {
            zoomBordersY = 27;
            zoomBordersX = -45;
        }

        // mouseState = Mouse.GetState();
        
        if (mouseState.X > Window.ClientBounds.Width-20 // check if cursor on side of window
            && mouseState.X <= graphics.GraphicsDevice.Viewport.Width  // check if cursor in window 
            && size + zoomBordersX > _camera.Position.X + graphics.GraphicsDevice.Viewport.Width) // check if sprite ends
            _camera.Move(new Vector2(movementSpeed, 0)); // move right

        if (mouseState.X < 20 
            && mouseState.X >= 0
            && 0 - zoomBordersX < _camera.Position.X)                              
            _camera.Move(new Vector2(-movementSpeed, 0)); // move left

        if (mouseState.Y > Window.ClientBounds.Height-20 
            && mouseState.Y <= graphics.GraphicsDevice.Viewport.Height
            && size - zoomBordersY > _camera.Position.Y + graphics.GraphicsDevice.Viewport.Height) 
            _camera.Move(new Vector2(0, movementSpeed)); // move down

        if (mouseState.Y< 20 
            && mouseState.Y> 0
            && 0 + zoomBordersY<_camera.Position.Y)                       
            _camera.Move(new Vector2(0, -movementSpeed)); // move up

        if (mouseState.ScrollWheelValue > previousScrollValue && _zoom< 1)
        {
            _camera.ZoomIn(zoomSpeed);
            _zoom++;
        }
        if (mouseState.ScrollWheelValue<previousScrollValue && _zoom> -1)
        {
            _camera.ZoomOut(zoomSpeed);
            _zoom--;
        }
        previousScrollValue = mouseState.ScrollWheelValue;
        _worldPosition = _camera.ScreenToWorld(new Vector2(mouseState.X, mouseState.Y));
    }
}

}

Thank you for your support.

Greetings
Mr.Nice Guy

I’m not familiar with MonoGame.Extended, but I can still probably help.

From what you said it sounds like a NullReferenceException is being thrown. Is that correct? If so, which line is causing the exception?

Never mind, I figured it out.

The problem is that you were constructing the Camera in the Game1 constructor, at which point, I presume, the GraphicsDevice of the Game1 class is not fully initialized, which led to an exception being thrown at this line in the Update method of the Camera class:

_worldPosition = _camera.ScreenToWorld(new Vector2(mouseState.X, mouseState.Y));

…due to the Camera2D _camera having a null GraphicsDevice.

To fix the problem, simply move the _camera initialization from the Game1 constructor to the Initialize method of Game1, before you initialize the camera:

_camera = new Camera(Window, graphics);
_camera.Initialize();

Your Game1 class should now look like this:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using MonoGame.Extended.ViewportAdapters;

namespace Game3
{
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        private Camera _camera;
        private Texture2D _background;

        // public Vector2 _worldPosition;
        private int previousScrollValue;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();

            var viewportAdapter = new BoxingViewportAdapter(Window, GraphicsDevice, 800, 480);
            this.IsMouseVisible = true;

            _camera = new Camera(Window, graphics);
            _camera.Initialize();
        }


        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            _background = Content.Load<Texture2D>("background");

            // TODO: use this.Content to load your game content here
        }


        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }


        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                Exit();


            _camera.Update();

            // TODO: Add your update logic here

            base.Update(gameTime);
        }


        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);
            var transformMatrix = _camera.GetViewMatrix(); // oder Begin(transformMatrix: _camera.GetViewMatrix)
            spriteBatch.Begin(transformMatrix: _camera.GetViewMatrix());
            spriteBatch.Draw(_background, new Vector2(0, 0), Color.White);

            // TODO: Add your drawing code here
            spriteBatch.End();
            base.Draw(gameTime);
        }
    }
}

Also, note that calling the Initialize method on the Camera class is redundant, because it’s already initialized when you call the constructor of Camera. And the fields of the Camera class:

    private static int previousScrollValue;
    private static Camera2D _camera;
    private static int _zoom;
    private BoxingViewportAdapter viewportAdapter;
    private static GameWindow window;
    private static GraphicsDeviceManager graphics;
    private static int size;

…probably shouldn’t be static. The static keyword is for fields that need to be shared by all instances of a class, which may or may not be the case here.

1 Like

Why are you creating a camera class that holds the Camera2D, which itself is a camera class? Don’t reinvent the wheel (unless the wheel is better).

Thank you very much. You really helped me a lot. You Sir, are an nice guy.

Because i need to define borders and alot of other things, and i don’t want to have anything written in the main.

You can then create a classes that separate boundaries of your game for your own needs. If you need something that doesn’t exist with the current Camera2D class I be happy to discuss on what your needs are exactly there.

@Mr_Nice_Guy No problem.

@LithiumToast I think he just wants a class to encapsulate managing and updating the Camera2D. That might be better done as a simple method, but keeping it the way it is the Camera class could probably be more clearly named as CameraManager or something like that.

I mean there is nothing wrong with declaring and using variables in the game class. Inheritance is a powerful tool but imo it shouldn’t be used unless there is a good reason too.