Reputation: 46
My 2D engine is resolution independent and has a camera based on the article here: http://www.david-gouveia.com/portfolio/2d-camera-with-parallax-scrolling-in-xna/
I've implemented parallax scrolling into my camera class the same way the article above mentions. It works great, but it's screwed up my culling code and I'm struggling to figure out the math.
Each of my background images has a Rectangle I use to check if it's currently on screen. If it doesn't collide with my Camera rectangle, I don't draw it. The problem is that if the image is drawing as a parallax layer, the rectangle isn't being calculated where it really appears on the screen.
The transform matrix in my Camera class looks like this:
public static Matrix GetTransformMatrix(Vector2 parallax)
{
return Matrix.CreateTranslation(new Vector3(-position * parallax, 0)) * Matrix.CreateRotationZ(rotation) *
Matrix.CreateScale(new Vector3(zoom, zoom, 1)) * Matrix.CreateTranslation(new Vector3(Resolution.VirtualWidth
* 0.5f, Resolution.VirtualHeight * 0.5f, 0));
}
For each parallax layer, I call SpriteBatch.Begin() and pass it the above transform matrix with the correct parallax offset passed in depending on what layer we're drawing (foreground, background etc.)
I've successfully made a ScreenToWorld function that works for getting the position of where the mouse has been clicked. Note that I need to calculate both my resolution matrix and camera matrix for it to work.
public static Vector2 ScreenToWorld(Vector2 input, Vector2 parallax)
{
input.X -= Resolution.VirtualViewportX;
input.Y -= Resolution.VirtualViewportY;
Vector2 resPosition = Vector2.Transform(input, Matrix.Invert(Resolution.getTransformationMatrix()));
Vector2 finalPosition = Vector2.Transform(resPosition, Matrix.Invert(Camera.GetTransformMatrix(parallax)));
return finalPosition;
}
So I figured to calculate the correct Rectangle positions of my parallax layers I would need a WorldToScreen function... I tried this, but it isn't working:
public static Vector2 WorldToScreen(Vector2 input, Vector2 parallax) //I pass the same parallax value that is used in the Camera matrix function.
{
input.X -= Resolution.VirtualViewportX;
input.Y -= Resolution.VirtualViewportY;
Vector2 resPosition = Vector2.Transform(input, Resolution.getTransformationMatrix());
Vector2 finalPosition = Vector2.Transform(resPosition, Camera.GetTransformMatrix(parallax));
return finalPosition;
}
I'm guessing I'm on the right track but my math is wrong? I'm passing the above function the non-parallaxed Rectangle position with the hopes of making it update to where the parallax image is really being drawn. Thanks in advance if someone can help!
Upvotes: 0
Views: 280
Reputation: 46
Ends up my math was right but I needed to calculate my Camera's screen rect based off the Camera's matrix. If you do this you don't need to touch the background rectangles at all. Just pass in the background's parallax value into this function then check against the rectangle it returns:
/// <summary>
/// Calculates the Camera's screenRect based on the parallax value passed in.
/// </summary>
public static Rectangle VisibleArea(Vector2 parallax)
{
Matrix inverseViewMatrix = Matrix.Invert(GetTransformMatrix(parallax));
Vector2 tl = Vector2.Transform(Vector2.Zero, inverseViewMatrix);
Vector2 tr = Vector2.Transform(new Vector2(Resolution.VirtualWidth, 0), inverseViewMatrix);
Vector2 bl = Vector2.Transform(new Vector2(0, Resolution.VirtualHeight), inverseViewMatrix);
Vector2 br = Vector2.Transform(new Vector2(Resolution.VirtualWidth, Resolution.VirtualHeight), inverseViewMatrix);
Vector2 min = new Vector2(
MathHelper.Min(tl.X, MathHelper.Min(tr.X, MathHelper.Min(bl.X, br.X))),
MathHelper.Min(tl.Y, MathHelper.Min(tr.Y, MathHelper.Min(bl.Y, br.Y))));
Vector2 max = new Vector2(
MathHelper.Max(tl.X, MathHelper.Max(tr.X, MathHelper.Max(bl.X, br.X))),
MathHelper.Max(tl.Y, MathHelper.Max(tr.Y, MathHelper.Max(bl.Y, br.Y))));
return new Rectangle((int)min.X, (int)min.Y, (int)(max.X - min.X), (int)(max.Y - min.Y));
}
Upvotes: 0