Xna 2D Camera Problem

Started by
3 comments, last by Spidi 7 years, 10 months ago

Hy everyone, so I made this Camera class, and can't find nothing wrong with it,

so please take a quick look at it:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Killer
{
    class Camera
    {       
        protected float zoom;
        protected Matrix transform;
        protected Viewport viewport;
        Vector2 center;

        public Matrix Transform
        {
            get { return transform; }
            set { transform = value; }
        }        
        
        public Camera(Viewport viewport)
        {
            zoom = 1.0f;
            this.viewport = viewport;
        }
        
        public void Update()
        {
            // Center - center of window, with player in center of window, window size: 800x600
            center = new Vector2(Player.playerPos.X + (EntityAnimation.rectangle.Width / 2) - 400,
                                 Player.playerPos.Y + (EntityAnimation.rectangle.Height / 2) - 300);
            // center.X = playerposition.X + frameWidth/2 - 400
            // center.X = 100 + 24 - 400
            // center.Y = playerposition.Y + frameHeight/2 - 300
            // center.Y = 100 + 24 - 300
            transform = Matrix.CreateScale(new Vector3(zoom, zoom, 0)) 
                        * Matrix.CreateTranslation(new Vector3(-center.X, -center.Y, 0));
        }        
    }
}

I putted some comments so you would know what values are at first when game is freshly run, now in my

Game1.cs (main XNA game file) I made a simple global variable:

Camera cam;

And in Game1.cs Initialize() method:

cam = new Camera(GraphicsDevice.Viewport);

and last but not least, here are my 2 draw methods in Game1.cs:


protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.White);
            spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, null, cam.Transform);
            // Player
            player.PlayerAnimation.PlayerDraw(spriteBatch);

            spriteBatch.End();
            DrawHud(gameTime);
            base.Draw(gameTime);
        }
        protected void DrawHud(GameTime gameTime)
        {
            spriteBatch.Begin();
            // Player Related Drawing --------------------------------------------------------------------
            player.playerStamina.DrawStatHelpers(spriteBatch, gameTime); // Stamina Help Messages, also Player.X and Player.Y
            player.playerStamina.DrawBar(spriteBatch); // Stamina Health Bar            
            //--------------------------------------------------------------------------------------------           
            spriteBatch.End();
        }

Now, when I build and run, I cant see my player, just my HUD,

BUT if I put player.PlayerAnimation.PlayerDraw(spriteBatch); in DrawHud() I can see him.

I dont know what is wrong with my Camera, please help if you can :)

Advertisement

Hi Bru Flu,

I've tried your Camera class in a vanilla XNA template project, and it worked somewhat as expected.

Your math is not perfect, but without zooming it should work (translation logic is ok).
You create a translation matrix by negating the the position to be followed by the camera + you add the half of the viewport size to your translation to center the position. This logic should work well if your goal is to follow a given position (e.g.: the player) and have it centered on the screen. The scale and translation order is not correct though, at least if your goal is to zoom in or out and keep the followed position centered.

For this you have to translate to view to the followed position, scale the view (to keep scaling centered on the followed position), and translate again with the requested offset...

A few questions, just to figure it out why the translation does not work in your project:

  • Do you call the Update method of your camera instance in the update phase of the game? I'm asking because it's not mentioned, only the initialization and draw logic, and they are correct, but the missing update call would not create the correct view matrix!
  • Are you 100% sure about the size of your screen? As I saw it in the code, you hard-coded 400*300 as the half of your view-port size, even tough you pass in the view-port of the game to the camera class which could be used instead!
  • Do you use the same playerPos X+Y and EntityAnimation.rectangle width+height variables when drawing the player sprite?

Here is a code snippet which uses correct scaling and the half of the view-port size as the offset:


public void Update()
{
	// Center - center of window, with player in center of window
	transform =
		Matrix.CreateTranslation(-Player.playerPos.X, -Player.playerPos.Y, 0) *
		Matrix.CreateScale(zoom, zoom, 0) *
		Matrix.CreateTranslation(this.viewport.Width / 2, this.viewport.Height / 2, 0);
}

Note: as you can see, I've left out the "EntityAnimation.rectangle" value from the offset logic, as it should be handled by the SpriteBatch draw logic by passing in it as the "offset" argument. If you would still like to use a workflow where it is part of your view matrix, multiply it by the zoom value for correct scaling and subtract if from the half size of the viewport in the second translation!

+ Some tips for the design of the class itself:
If you would like to reuse code in the future try to minimize hard-coding values and dependencies.
For example use the half of the view-port size as I've shown and do not hard-code the position of the player class and offsets of the EntityAnimation class. Instead use general positon and offset vectors to be followed by the camera, or use a generic entity base class to be followed by the camera (the camera could query the entity position and size for offset in its update method...).

Br.

Blog | Overburdened | KREEP | Memorynth | @blindmessiah777 Magic Item Tech+30% Enhanced GameDev

Thanks a lot for some explanations.

One weird thing yeh I forgot to put cam.Update(); in my Game1.cs Update() method hahah, but after doing that the problem was still there.

Well and I replaced my code for camera update with yours, and I made one DrawHelper() function in my camera class which draws the position X and Y of my player, and camera uses right position values, so nothing wrong about that.


 graphics.PreferredBackBufferHeight = 600;
 graphics.PreferredBackBufferWidth = 800;

This were my screen W & H, but I guess it doesn't matter now cause I use your Camera Update() now.

I will now guide you through my code creation:

1. I made an EntityAnimation class and putted all animation related stuff there - works fine.

2. I made a Player class, in which there is all logic about my player movement

3. I made Stats interface that contains Stamina class and does stamina-related stuff

and In my Game1.cs I draw stamina bar in seperate spriteBatch than the one that camera uses.(code in the post above)

So the stamina is shown, but the player is not.

4. After that Stats and Stamina I created Camera class, and well then the problem started, and well

if I put player.PlayerAnimation.PlayerDraw(spriteBatch); in DrawHud() I can see him, if it stays in spriteBatch that camera is in( in Draw() ),

then I dont.

5. Again, the player position that I am passing to cam.transform are correct, and I know this becouse I made one Draw method in Camera class that

outputs used player.position variables in a text, and they change when when I walk, and the start position is the same.

I dont know what else would be usefull information to you guys to help me. here is how my Camera class looks like right now:


class Camera
    {       
        protected float zoom;
        protected Matrix transform;
        protected Viewport viewport;        

        public Matrix Transform
        {
            get { return transform; }
            set { transform = value; }
        }        
        
        public Camera(Viewport viewport)
        {
            zoom = 1.0f;
            this.viewport = viewport;
        }
        
        public void Update()
        {
            transform = Matrix.CreateTranslation(-Player.playerPos.X, -Player.playerPos.Y, 0) *
                        Matrix.CreateScale(zoom, zoom, 0) * 
                        Matrix.CreateTranslation(this.viewport.Width / 2, this.viewport.Height / 2, 0);
        }

        public void DrawHelpers(SpriteBatch sp)
        {
            sp.DrawString(StatManager.statMessage, "Player.X:" + Player.playerPos.X + "Player.Y:" + Player.playerPos.Y, 
                          new Vector2(50, 90), Color.Black);
        }
        
    }

and again, I have camera Initialized and Updated in my Game1.cs, and well ofcourse Drawn xD but yeah I cant see nothing. :(

PS. I didnt copy those using System.blabla and Microsoft.Xna.blabla so the code would be shorter but it is there in my code ofc,

Im looking forward your help, good people ;)

IT IS FIXED. I simply had to remove this line from my Draw():


GraphicDevice.Clear(Color.White);

Does anyone know if it is a right thing to remove this line completley from my game?

Will it maybe produce some negative effects or no?

I'm glad it is working ^_^, though something feels suspicious <_< about getting it fixed by removing:

GraphicDevice.Clear(Color)

This call clears the back-buffer (or the currently set render target) to the passed in color. So sort of like sets your canvas white (or to the color you pass in) for drawing your frame in the current state of the game.
It pretty much should be called in every frame before you start drawing, otherwise you actually just keep the results of the last Draw phase in the back-buffer, and draw your current frame "over" it.
The resulting looks would resemble more like drawing with a paint program really fast :D instead of redrawing the objects/sprites on their current position.
You know, like everything has a colored contrail :lol:.
Are you certain, you had only one call to the "GraphicDevice.Clear" method in your game or that you draw your player in every frame ?
It may be, you accidentally cleared the black-buffer to white multiple times in your draw phase?!

So to summarize, GraphicDevice.Clear(Color) should be called once (and in almost every situation only once!) at the beginning of the draw phase of the game loop. Every consequent call will clear the draw results before it to the passed in color.


class MyGame : Microsoft.Xna.Framework.Game
{
	// ...
	
	protected override void Draw(GameTime gameTime)
	{
		// clear the back-buffer to the color you want at the beginning of the draw phase of the frame/game-loop.
		// this should be done here otherwise your graphics will look funky :D all objects will have a colored contrail...
		GraphicsDevice.Clear(Color.White);
		
		// start drawing your sprites here, but:
		// do not call GraphicsDevice.Clear(Color.White); from this point on,
		// or it will clear your results!
		
		base.Draw(gameTime);
	}
}


Blog | Overburdened | KREEP | Memorynth | @blindmessiah777 Magic Item Tech+30% Enhanced GameDev

This topic is closed to new replies.

Advertisement