2
\$\begingroup\$

I am creating a 2D platformer type game in XNA.

I currently have a camera object, with a position/rotation/zoomlevel that I use to generate a transformation matrix to pass to SpriteBatch.Begin(). This allows me to draw at in game coordinates instead of screen coordinates.

The relevant bit of the Camera code:

public Matrix GetViewMatrix() {
            cameraMatrix = Matrix.CreateScale(new Vector3(1f, -1f, 1f))                   
                   * Matrix.CreateTranslation(position.X, position.Y, 0f)
                   * Matrix.CreateScale(new Vector3(zoom,zoom,1f))
                   * Matrix.CreateRotationZ(rotation)
                   * Matrix.CreateTranslation(new Vector3(screenWidth*0.5f,screenHeight*0.5f,0));
            return cameraMatrix;
}

Which is used like so:

spriteBatch.Begin(SpriteSortMode.BackToFront, null, null, null, 
                  null, null, camera.GetViewMatrix());
//Draw Stuff
spriteBatch.End();

The problem is, that in order to get anything to actually draw, I have to scale by (1,-1) when I call spriteBatch.Draw(), otherwise I believe the textures get depth culled.

spriteBatch.Draw(content.Load<Texture2D>("whiteSquare"), Vector2.Zero, null, 
                 Color.White, 0f, Vector2.Zero, 
                 new Vector2(1f, -1f), 
                 SpriteEffects.None,0f);

Notice the Vector scaling argument in the 3rd line of the last sample. My question is twofold:

  1. How do I avoid having to pass this scaling argument/calling the longest form of spriteBatch.Draw() (kind of a violation of DRY, though I could wrap it I suppose).
  2. Am I doing something wrong (not "it doesnt work wrong" but "thats the wrong way to approach that problem" wrong)? I have seen mentions of viewport.Update() functions and Matrix.CreateOrthagonal etc, but I'm not quite sure if using them is simpler/better than a simple custom camera sort of deal.

Thank you very much.


EDIT: After re-reading my post, and the first answers/comments, I think I have been thinking about this problem wrong.

I dont actually want to draw in game coordinates. What I want, is to be able to specify and work with game coordinates in such a way that positive y-axis is up, and that 1 unit of game distance equates to 1 meter equates to however many screen pixels it takes to make sure that you always view (for example) 10 meters of game distance from the left border to the right border of the viewport.

Preliminary experimenting led me to believe that passing in a world transform matrix to the spriteBatch.begin() method was the way to go about this, but now I think it may be a little more complicated. Does anyone have any advice for achieving the above goal? I have seen example code where every drawable entity implements a drawable interface that comes with a world translation matrix based on current entity position/etc, but where would I use such a matrix? Do I seperate spriteBatch.Begin()/End() calls for every drawable entity?

Sorry if this is a poorly formed question, trying to even wrap my head around the concept of the goal is proving difficult.

\$\endgroup\$
  • \$\begingroup\$ Uhh...Your matrix begins with a scale matrix which would render everything off screen. Was Matrix.CreateScale(new Vector3(1f, -1f, 1f) intentional? \$\endgroup\$ – Ben Apr 15 '14 at 16:43
  • \$\begingroup\$ Good edit. Your XY Problem catharsis is commendable. :) \$\endgroup\$ – Seth Battin Apr 19 '14 at 18:18
1
\$\begingroup\$

What you probably want to do is create wrapper around SpriteBatch or create extension methods for SpriteBatch that apply the desired per-sprite inversion to each Draw call. Then you could just do:

spriteBatch.DrawWorldSprite(texture, position, color);

(You could even create an overload with a scaling parameter, that does appropriate multiplication so you can scale things normally.)

This is by far the simplest thing to implement and use. It also doesn't violate DRY.

(It's been a while since I've done something like this. I can't remember if you need to mess with SpriteEffects.FlipY or origin changes to get the maths right.)


Note that the culling you are experiencing is not depth-culling. It is back-face culling. The default rasterizer state is RasterizerState.CullCounterClockwise. Changing it to RasterizerState.CullClockwise or RasterizerState.None will fix the culling issue. But you may find that your sprites are still upside-down.


The people suggesting Matrix.CreateOrthographic are probably suggesting you use SpriteBatch with BasicEffect. I don't think you need it, but it's handy to know about.

SpriteBatch basically provides an equivalent to a projection matrix (for "client space" or "screen space"), and using BasicEffect allows you to override that.

(Note: The matrix you pass to Begin is the equivalent of the view matrix, and the parameters to Draw are functionally equivalent to a world matrix.)


As an aside: You have the right idea with working in a sensible "world space" as much as possible, in this case with Y+ = up, and limiting the amount of code that transformation for rendering touches. Violating DRY on every draw call is nasty, but so is working in a convoluted co-ordinate system, just to suit rendering.

\$\endgroup\$
  • \$\begingroup\$ The extension method idea is a much better solution than my all-or-nothing. Even if you have to proxy everything to the most-telescoped overload of Draw, it's still simpler than trying to fix it every line or somehow work without SpriteBatch at all. \$\endgroup\$ – Seth Battin Apr 19 '14 at 18:16
0
\$\begingroup\$

To #2: yes, you are doing it wrong. You are trying to change the framework's 2d coordinate system, with its origin at top left (this follows texture coordinates convention in directX). You are allowed to make that change; the trade-off is that you have to do it all the time, with every draw. Since that's what you seem to dislike, you might consider the drawback to be too great.

Depending on how far you are into your platformer code, it may too late to stop that effort and just "go with the flow". I assume you've already built some physics and logic that needs the y-coordinate to be positive-up. But if you can change it (never too late to refactor!), then you won't have to scale-flip everything all the time.


Edit: I do see that you already made this choice consciously. You wanted to operate in world coordinates, rather that screen coordinates. Again, this is a fine choice to make, but Spritebatch operates in screen coordinates. Quandary.

You could stop using Spritebatch. You can render world-space quads fairly easily. You'll miss out on other batching features the 2d portion of XNA gives you. But it's just more tradeoff consideration.

About your DRY consideration: if you just dislike passing too many parameters, you might consider C# named parameters. It could make your method calls cleaner.

\$\endgroup\$
  • 1
    \$\begingroup\$ I think not using spritebatch is about the last thing he'd want to do. \$\endgroup\$ – Ben Apr 15 '14 at 17:11
  • \$\begingroup\$ Tough to say. Can't have your cake and spritebatch it too. \$\endgroup\$ – Seth Battin Apr 15 '14 at 18:01
  • \$\begingroup\$ @SethBattin The more I read about SpriteBatch the more I feel like dropping it. Apparently using SpriteBatch sort of locks you into using a screen centric coordinate system. What are the alternatives? Can you link me some resource that details how to render in world coordinates? Googling didnt turn up any results. \$\endgroup\$ – Sam Coulter Apr 15 '14 at 23:27
  • \$\begingroup\$ It would be really tough to offer a complete, pre-packaged replacement for spritebatch. It's probably the most featureful part of the whole framework, given all the different functions it provides, often in an almost auto-magical way. That said, drawing quads really is easy; generate triangles with position and texture coordinates, then DrawUserPrimitives(). I can't vouch for this particular example, but its description seems to fit your needs: xbox.create.msdn.com/en-US/education/catalog/sample/… I would recommend perusing the shader basics tutorials as well. Good luck. \$\endgroup\$ – Seth Battin Apr 15 '14 at 23:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.