# How can I achieve a 3D-like effect with spritebatch's rotation and scale parameters

I'm working on a 2d game with a top-down perspective similar to Secret of Mana and the 2D Final Fantasy games, with one big difference being that it's an action rpg using a 3-dimensional physics engine.

I'm trying to draw an aimer graphic (basically an arrow) at my characters' feet when they're aiming a ranged weapon. At first I just converted the character's aim vector to radians and passed that into spritebatch, but there was a problem. The position of every object in my world is scaled for perspective when it's drawn to the screen. So if the physics engine coordinates are (1, 0, 1), the screen coords are actually (1, .707) -- the Y and Z axis are scaled by a perspective factor of .707 and then added together to get the screen coordinates.

This meant that the direction the aimer graphic pointed (thanks to its rotation value passed into spritebatch) didn't match up with the direction the projectile actually traveled over time. Things looked fine when the characters fired left, right, up, or down, but if you fired on a diagonal the perspective of the physics engine didn't match with the simplistic way I was converting the character's aim direction to a screen rotation.

Ok, fast forward to now: I've got the aimer's rotation matched up with the path the projectile will actually take, which I'm doing by decomposing a transform matrix which I build from two rotation matrices (one to represent the aimer's rotation, and one to represent the camera's 45 degree rotation on the x axis). My question is, is there a way to get not just rotation from a series of matrix transformations, but to also get a Vector2 scale which would give the aimer the appearance of being a 3d object, being warped by perspective? Orthographic perspective is what I'm going for, I think. So, the aimer arrow would get longer when facing sideways, and shorter when facing north and south because of the perspective. At the same time, it would get wider when facing north and south, and less wide when facing right or left.

I'd like to avoid actually drawing the aimer texture in 3d because I'm still using spritebatch's layerdepth parameter at this point in my project, and I don't want to have to figure out how to draw a 3d object within the depth sorting system I already have.

I can provide code and more details if this is too vague as a question... This is my first post on stack exchange. Thanks a lot for reading!

Note: (I think) I realize it can't be a technically correct 3D perspective, because the spritebatch's vector2 scaling argument doesn't allow for an object to be skewed the way it actually should be. What I'm really interested in is, is there a good way to fake the effect, or should I just drop it and not scale at all?

Edit to clarify without the help of a picture (apparently I can't post them yet):

I want the aimer arrow to look like it has been painted on the ground at the character's feet, so it should appear to be drawn on the ground plane (in my case the XZ plane) which should be tilted at a 45 degree angle (around the X axis) from the viewing perspective.

Alex

• You need to whip out MS paint and draw some pictures there, buddy. Too much tell, not enough show. Oct 10 '12 at 0:49
• Yes, do a drawing of what you need, it's very confusing like this. But yes it's possible for instance to use a vertex shader to rotate your sprite. Oct 10 '12 at 1:14
• Which does allow the sprite to be skewed in ways that it would not be possible with the scale parameter, such as this example. Oct 10 '12 at 1:20
• David, that would do it. I will look up vertex shaders (haven't used them before). I'm working on a visualization. The first draft... it may be the worst ms paint creation ever borne of man. Maybe I can get my girlfriend to draw something. Oct 10 '12 at 1:23
• I'll post the little code snippet that I just wrote for that screenshot then so you can try. Although I'm not sure if I'm doing something wrong, it seems to work though. Oct 10 '12 at 1:26

This can be done in two simple steps:

• Pass your custom transformation to SpriteBatch.Begin but...
• Attach a Matrix.CreateScale(1,1,0) at the end to flatten the quad and ensure it's fully visible.

Note however that the transformation matrix is relative to the world origin not the sprite's origin, so it might need some tweaking to get the result in the correct position.

Matrix matrix = Matrix.CreateRotationX(MathHelper.ToRadians(60)) *
Matrix.CreateScale(1,1,0);

spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, matrix);


I've moved the old answer here.

PS: Thanks to Andrew Russel for making me think.

• David, this is very cool. I'm not sure I'll be able to get it working Oct 10 '12 at 1:43
• EXACTLY the way I want because spritebatch won't allow me to start a new batch and still sort textures to draw by the layerdepth parameter. But I will probably be switching over to ordering draw calls myself at some point, and so I'll try this now as it looks like a really nice solution. Oct 10 '12 at 1:46
• Shouldn't you be able to do this using the built-in SpriteBatch shader with a custom transform matrix passed to Begin? Oct 10 '12 at 12:17
• @AndrewRussell You're right, it is possible with a trick. I initially tried it but it did not work because the rotated quad gets clipped by the default near-far plane combination of 0 to 1. My first solution involved a vertex shader to rotate without touching the Z. My second solution involved changing the projection matrix to have a larger depth. But I've just figured a way to make it work with the regular matrix: attach a scale matrix that flattens the Z coordinate after the rotation. Oct 10 '12 at 14:31
• Hey, sorry it took me awhile to accept! This is a great answer, and I've just been restructuring my code in order to separate out my drawing enough that I can call spriteBatch.Begin for individual objects and still have depth sorting (stupid layer depth). This process has actually led to several really great unforseen gains as well, which is always nice. Thanks for the time you spent creating and revising your answer. Nov 3 '12 at 0:42