0
\$\begingroup\$

I am making a 2D sidescroller and I would like to draw my sprite to world space instead of client space so I do not have to lock it to the center of the screen and when the camera stops the sprite will walk off screen instead of being stuck at the center. In order to do this I wanted to make a transformation matrix that goes in my draw call.

I have seen something like this: https://stackoverflow.com/questions/3570192/xna-viewport-projection-and-spritebatch

I have seen Matrix.CreateOrthographic() used to go from Worldspace to client space but, how would I go about using it to go from clientspace to worldspace?

I was going to try putting my returns from the viewport.unproject method I have into a scale matrix such as:

blah = Matrix.CreateScale(unproject.X,unproject.Y,0);

however, that doesn't seem to work correctly.

Here is what I'm calling in my draw method(where X is the coordinate my camera should follow):

Vector3 test = screentoworld(X, graphics);
var clienttoworld = Matrix.CreateScale(test.X,test.Y, 0);
animationPlayer.Draw(theSpriteBatch, new Vector2(X.X,X.Y),false,false,0,Color.White,new Vector2(1,1),clienttoworld);

Here is my code in my unproject method:

Vector3 screentoworld(Vector2 some, GraphicsDevice graphics):

Vector2 Position =(some.X,some.Y);
var project = Matrix.CreateOrthographic(5*graphicsdevice.Viewport.Width, graphicsdevice.Viewport.Height, 0, 1);
var viewMatrix = Matrix.CreateLookAt(
  new Vector3(0, 0, -4.3f), new Vector3(X.X,X.Y,0), Vector3.Up);

//I have also tried substituting (cam.Position.X,cam.Position.Y,0) in for the (0,0,-4.3f)

 Vector3 nearSource = new Vector3(Position, 0f);
 Vector3 nearPoint = graphicsdevice.Viewport.Unproject(nearSource,
        project, viewMatrix, Matrix.Identity);

return nearPoint;

I would also like to be able to move the camera around without moving my sprite. Before I was doing something like:

if(directionofsprite == positive && sprite position is not less than 200)
 {
   sprite-=5;
   map-=5;
 }

This would make it appear as though the camera is moving to the right. I just want to simplify all of this by making my sprites draw to worldspace.

\$\endgroup\$
3
  • \$\begingroup\$ Sorry but I still couldn't figure out what you're trying to do. When I draw my scenes with SpriteBatch, I'm usually drawing in world space already. I pass a view matrix to the SpriteBatch which takes care of moving them into view space, and the built in orthographic projection matrix takes care of the rest. If I want to add an offset so that the world's origin starts at the center of the screen, I add that offset to the view matrix. I've never had the need to mess with the projection matrix on the SpriteBatch. What problem are you trying to solve exactly? \$\endgroup\$ Commented Apr 3, 2012 at 17:08
  • \$\begingroup\$ I'm using Demina animation Player to draw my sprite so it's drawing to the viewport(1280x720) instead of the 6400x720 space I have. Basically when my camera moves all of the sprites I have drawn move. I don't want them to move because of my camera moving. If I have enemies on the screen they have to be hard coded to move in the viewport rather than just moving around in my 6400x720 space. \$\endgroup\$ Commented Apr 3, 2012 at 17:15
  • \$\begingroup\$ Okay now I understood your problem, and the solution is simple. Give me 5-10 minutes and I'll post it. \$\endgroup\$ Commented Apr 3, 2012 at 17:25

3 Answers 3

2
\$\begingroup\$

You wrote in a comment that because you're using Demina, your sprites are forcibly being drawn to client space instead of world space. But although I've never used Demina before, I've just checked its source code and that doesn't seem to be the case.

Instead Demina takes a camera transformation matrix which is used to convert the sprites from world space to view space, but you're passing it the wrong matrix. Here's what I recommend you to do. First add a simple camera class like the following to your project:

public class Camera
{
    public Camera(Viewport viewport)
    {
        Origin = new Vector2(viewport.Width / 2.0f, viewport.Height / 2.0f);
        Zoom = 1.0f;
    }

    public Vector2 Position { get; set; }
    public Vector2 Origin { get; set; }
    public float Zoom { get; set; }

    public Matrix ViewMatrix
    {
        get 
        {
            return Matrix.CreateTranslation(new Vector3(-Position, 0.0f)) *
                   Matrix.CreateScale(Zoom, Zoom, 1) *
                   Matrix.CreateTranslation(new Vector3(Origin, 0.0f));
        }
    }
}

This class automatically sets the origin to the center of your viewport which seemed to be what you wanted. So create an instance of that class at the top of your game, and use the Position and Zoom properties to scroll or zoom your camera around. If you'd like to lock the camera onto your character just do camera.Position=character.Position on the update loop. For more complex operations read this article.

As for rendering you can use the same overload you're already using but pass it a position that is defined in world space, i.e. if the character is at 5000x400 pass it that value. As for the cameraTransform parameter pass it the value of camera.ViewMatrix which will take care of the world to view space conversion. There's no need to mess with any projection matrices because the internal orthographic projection matrix created by SpriteBatch already takes care of mapping view space to client space. In other words:

animationPlayer.Draw(spriteBatch, sprite.Position, false, false,
                     0, Color.White, Vector2.One, camera.ViewMatrix);

All the unproject and matrix code you showed on your question is not needed. And now your sprites and camera are completely independent. If you want to move a sprite, just change the sprite's position. If you want to move the camera just change the camera's position.

\$\endgroup\$
8
  • \$\begingroup\$ I'm still getting the same problem. I set the sprites position to 1000 and it still moves. \$\endgroup\$ Commented Apr 3, 2012 at 18:55
  • \$\begingroup\$ @NickCrowther I don't get it. If you set the sprite to position 1000 and then scroll the camera, of course the position of the sprite on screen will change. If you want the sprite to have a fixed position in relation to the camera, lock the camera to the sprite, and move the sprite instead. You could also do it the other way around and set the sprite's position to be the camera position, but that would be counter intuitive. \$\endgroup\$ Commented Apr 3, 2012 at 19:17
  • \$\begingroup\$ by it's moving I meant it's fixed on the screen moving with the camera sorry if I worded it wierd. Another way to put it is it's still moving with the camera instead of being fixed in world space like the background art is. \$\endgroup\$ Commented Apr 3, 2012 at 19:24
  • \$\begingroup\$ @NickCrowther Paste what you currently have on Pastie so I can take a look. \$\endgroup\$ Commented Apr 3, 2012 at 19:27
  • \$\begingroup\$ pastie.org/3722771 It's a lot of code. \$\endgroup\$ Commented Apr 3, 2012 at 19:32
0
\$\begingroup\$

I think you're over thinking this. You should separate your logic and draw code so that you have world space and client space.

As a camera class you can just use a float called leftOffset. Then use this leftOffset to determine the screen position by drawing at position + leftOffset.

Unless you're also going to do zooming in and out this is all you need :).

\$\endgroup\$
1
  • \$\begingroup\$ I'm not sure what you mean by this. For my draw code it doesn't matter what coordinates I put in, it will draw it according to the viewport. My enemy sprites are also drawn according to the viewport so it's hard to try and keep track of both of those. I've been trying to just hardcode numbers and it creates a big mess. \$\endgroup\$ Commented Apr 3, 2012 at 16:50
0
\$\begingroup\$

I was trying your method after seen that there was an unproject command. But it turns out to be a 3d command.

I then tried this code and works perfectly for me.

var dest = ConvertUnits.ToSimUnits((new Vector2(ms.X, ms.Y) - Util.screenCenter())/Util.camera.Zoom) + ship._compound.Position;

Basically, you need mouse position relative to screen center offset. This value should divide by camera.zoom. Camer.zoom = 1 when no zoom is done, =0.4 when zoomed out by a bit and =4 when zoomed in 4x.

You can then convert this value to sim units and plus the known sim world center position of camera.

This code might not work for your original request, but the idea is to unzoom and you can already get the 2d position in sim world.

\$\endgroup\$

You must log in to answer this question.

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