Rotation Matrix causing sprite position to change - c#

I am not well versed in matrix math so any help would be appreciated. So I have a simple ship sprite that rotates and moves. In order to calculate the bounding rectangle I tried using a rotation matrix and the code I got off of these tutorials:
http://xbox.create.msdn.com/en-US/education/catalog/tutorial/collision_2d_perpixel
But whenever the sprite rotates it's X and Y position of the bounding rectangle change drastically, by like 100s of pixels. The method for calculating the new bounding rectangle is as follows:
public static Rectangle CalculateBoundingRectangle(Rectangle rectangle, Matrix transform)
{
// Get all four corners in local space
Vector2 leftTop = new Vector2(rectangle.Left, rectangle.Top);
Vector2 rightTop = new Vector2(rectangle.Right, rectangle.Top);
Vector2 leftBottom = new Vector2(rectangle.Left, rectangle.Bottom);
Vector2 rightBottom = new Vector2(rectangle.Right, rectangle.Bottom);
// Transform all four corners into work space
Vector2.Transform(ref leftTop, ref transform, out leftTop);
Vector2.Transform(ref rightTop, ref transform, out rightTop);
Vector2.Transform(ref leftBottom, ref transform, out leftBottom);
Vector2.Transform(ref rightBottom, ref transform, out rightBottom);
// Find the minimum and maximum extents of the rectangle in world space
Vector2 min = Vector2.Min(Vector2.Min(leftTop, rightTop),
Vector2.Min(leftBottom, rightBottom));
Vector2 max = Vector2.Max(Vector2.Max(leftTop, rightTop),
Vector2.Max(leftBottom, rightBottom));
// Return as a rectangle
return new Rectangle((int)min.X, (int)min.Y,
(int)(max.X - min.X), (int)(max.Y - min.Y));
}
I am assuming this works since it comes from the Microsoft site and all. So I think the problem is with my matrices. I thought that If I only used a rotation matrix it would only spin the thing; not change its X and Y coordinates drastically. right now this is what I have:
// calculate transformation
Matrix transformation = Matrix.CreateRotationZ((float)rotation) * Matrix.CreateRotationZ((float)rotation);;
//update bounding rectangle
rectangle = new Rectangle((int)(position.X - origin.X), (int)(position.Y - origin.Y), texture.Width, texture.Height);
rectangle = BoundingAndCollision.CalculateBoundingRectangle(rectangle, transformation);
I tried a variety of other things including without the origin matrix and different orders of them. I also tried what a tutorial said was a general one that would work for anything, but that yielded the basically the same results:
Matrix transformation = Matrix.CreateTranslation(new Vector3(origin, 0.0f)) *
Matrix.CreateRotationZ((float)rotation) *
Matrix.CreateScale(scale) *
Matrix.CreateTranslation(position.X, position.Y, 0);
If that is not clear enough I can post screenshots, just let me know.Thank you in advance for the help!

A rotation matrix rotates around 0,0 and your rectangle is already placed in the world.
To solve first subtract the rectangle's center from each vertex (translate the rectangle to be centered at 0,0) and then add it again after rotating (place again on original location). In the code im assuming Y goes from top to bottom:
public static Rectangle CalculateBoundingRectangle(Rectangle rectangle, Matrix transform)
{
Vector2 center = new Vector2(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2);
// Get all four corners in local space
Vector2 leftTop = new Vector2(rectangle.Left, rectangle.Top) - center;
Vector2 rightTop = new Vector2(rectangle.Right, rectangle.Top) - center;
Vector2 leftBottom = new Vector2(rectangle.Left, rectangle.Bottom) - center;
Vector2 rightBottom = new Vector2(rectangle.Right, rectangle.Bottom) - center;
// Transform all four corners into work space
Vector2.Transform(ref leftTop, ref transform, out leftTop);
Vector2.Transform(ref rightTop, ref transform, out rightTop);
Vector2.Transform(ref leftBottom, ref transform, out leftBottom);
Vector2.Transform(ref rightBottom, ref transform, out rightBottom);
leftTop += center;
rightTop += center;
leftBottom += center;
rightBottom += center;
// Find the minimum and maximum extents of the rectangle in world space
Vector2 min = Vector2.Min(Vector2.Min(leftTop, rightTop),
Vector2.Min(leftBottom, rightBottom));
Vector2 max = Vector2.Max(Vector2.Max(leftTop, rightTop),
Vector2.Max(leftBottom, rightBottom));
// Return as a rectangle
return new Rectangle((int)min.X, (int)min.Y,
(int)(max.X - min.X), (int)(max.Y - min.Y));
}

Related

2D XNA - Draw a Circle

Yo, Hi everybody
Is there any Way to draw a circle? I don't want to use a Texture/sprite to draw a circle Because the Player is the Circle so the Circle Should move ... and also I'm Trying to make it so the Player/Circle's Size gets bigger and bigger When he eats some food blablabla...
anyways, if anybody knows how to do it please Tell me.
OTHERWISE : IS THERE A WAY TO CHANGE A TEXTURE HEIGHT / WIDTH , THEN I WILL MAKE A SIMPLE CIRCLE TEXTURE AND CHANGE HEIGHT / WIDTH OF IT.
Thanks.
You can use 3D primitives like 'Someone' :> already posted or use the C3.XNA.Primitives2D libary where you can use a extension for SpriteBatch to draw a circle
public static void DrawCircle(this SpriteBatch spriteBatch, Vector2 center, float radius, int sides, Color color, float thickness);
If you use the same value for radius and thickness the circle appears filled.
I didn't find the offizial download link, but there are also uploads at sourceforge.
Also you can generate a circle dynamically via code like:
public static Texture2D GenerateCircleTexture(GraphicsDevice graphicsDevice, int radius, Color color, float sharpness)
{
int diameter = radius * 2;
Texture2D circleTexture = new Texture2D(graphicsDevice, diameter, diameter, false, SurfaceFormat.Color);
Color[] colorData = new Color[circleTexture.Width * circleTexture.Height];
Vector2 center = new Vector2(radius);
for (int colIndex = 0; colIndex < circleTexture.Width; colIndex++)
{
for (int rowIndex = 0; rowIndex < circleTexture.Height; rowIndex++)
{
Vector2 position = new Vector2(colIndex, rowIndex);
float distance = Vector2.Distance(center, position);
// hermite iterpolation
float x = distance / diameter;
float edge0 = (radius * sharpness) / (float)diameter;
float edge1 = radius / (float)diameter;
float temp = MathHelper.Clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
float result = temp * temp * (3.0f - 2.0f * temp);
colorData[rowIndex * circleTexture.Width + colIndex] = color * (1f - result);
}
}
circleTexture.SetData<Color>(colorData);
return circleTexture;
}
Sharpness below 1f blurs the circle.
The only way to draw primitives (e.g. circles) is in 3D:
https://msdn.microsoft.com/en-us/library/bb196414.aspx
Or you can load a texture that is 1*1, stretch it into a line and then use a bunch of those lines to make a circle.
Use:
public void Draw (
Texture2D texture,
Vector2 position,
Nullable<Rectangle> sourceRectangle,
Color color,
float rotation,
Vector2 origin,
Vector2 scale,
SpriteEffects effects,
float layerDepth
)
to stretch the texture.
Or you can just use a circle texture and stretch it.
If you are making something like agar.io then you might want to use a texture combined with the circle primitive so you can make the circle 'wobbly'.

Particle effect displaying same particles -- not randomizing, Monogame

When I create particle effects, they all have the same pattern. They are rotated, but they all have the same pattern and same colored particles. See picture:
This is how a new ParticleEffect gets created:
ParticleEffect p = new ParticleEffect(textures, Vector2.Zero, destination, speed);
Where textures is aTexture2D list, VectorZero is the starting location, and so on.
Whenever a new ParticleEffect gets created it gets added to the ParticleList list, which later loops through all the items and calls update and draw for each effect inside.
Here is where the particles are randomised:
private Particle GenerateNewParticle()
{
Random random = new Random();
Texture2D texture = textures[random.Next(textures.Count)];
Vector2 position = EmitterLocation;
Vector2 velocity = new Vector2(
1f * (float)(random.NextDouble() * 2 - 1),
1f * (float)(random.NextDouble() * 2 - 1));
float angle = 0;
float angularVelocity = 0.1f * (float)(random.NextDouble() * 2 - 1);
Color color = new Color(
(float)random.NextDouble(),
(float)random.NextDouble(),
(float)random.NextDouble());
float size = (float)random.NextDouble();
int ttl = 20 + random.Next(40);
return new Particle(texture, position, velocity, angle, angularVelocity, color, size, ttl);
}
A bunch of randoms there, but each effect still comes out the same.
Comment if you want to see more code.
Edit:
Here is how a particle gets drawn:
public void Draw(SpriteBatch spriteBatch)
{
Rectangle sourceRectangle = new Rectangle(0, 0, Texture.Width, Texture.Height);
Vector2 origin = new Vector2(Texture.Width / 2, Texture.Height / 2);
spriteBatch.Draw(Texture, Position, sourceRectangle, Color,
Angle, origin, Size, SpriteEffects.None, 0f);
}
By default, Random instances are initiated with the current time as seed, that means the same sequence of numbers will be re-generated if you create instances at the same time - do not create new instances, reuse an existing instance to get more "randomized" behavior (in your case e.g. use a static Random instance).

Rotating an inner rectangle(or square) in c#

I am trying to apply a transformation on a rectangle within another rectangle.... and having quite a bit of difficulty. Here is an example of what I am trying to achieve, the rotation will always be in increments of 90 degrees:
I have the bottom left X/Y, width, and height of both the outer and inner rectangles.... I'm trying to calculate these same values for the transformed inner rectangle.
My attempt can be found below, I tried rotating all 4 corners around the center of the large rect then put them back together as a rect. This may not work because the large rect width/height changes during the rotation. Does anyone know of a formula to accomplish this? If someone could point me to some good resource that would be fantastic.
My Code:
Vector2 center = new Vector2(largeRectWidth / 2.0f, largeRectHeight / 2.0f);
Rect innerRectRotated = RotateRectangleAroundPivot(innerRect, center, this.Rotation);
public static Rect RotateRectangleAroundPivot(Rect rect,
Vector2 pivot,
float rotation)
{
Vector2 leftTop = new Vector2(rect.x, rect.y + rect.height);
Vector2 rightTop = new Vector2(rect.x + rect.width, rect.y + rect.height);
Vector2 leftBottom = new Vector2(rect.x, rect.y);
Vector2 rightBottom = new Vector2(rect.x + rect.width, rect.y);
leftTop = RotatePointAroundPivot(leftTop, pivot, rotation);
rightTop = RotatePointAroundPivot(rightTop, pivot, rotation);
leftBottom = RotatePointAroundPivot(leftBottom, pivot, rotation);
rightBottom = RotatePointAroundPivot(rightBottom, pivot, rotation);
Vector2 min = Vector2.Min(Vector2.Min(leftTop, rightTop),
Vector2.Min(leftBottom, rightBottom));
Vector2 max = Vector2.Max(Vector2.Max(leftTop, rightTop),
Vector2.Max(leftBottom, rightBottom));
return new Rect(min.x, min.y, (max.x - min.x), (max.y - min.y));
}
public static Vector2 RotatePointAroundPivot(Vector2 point, Vector2 pivot, float angle)
{
angle = angle * Mathf.PI / 180.0f;
return new Vector2((float)(Math.Cos(angle) * (point.x - pivot.x) - Math.Sin(angle) * (point.y - pivot.y) + pivot.x), (float)(Math.Sin(angle) * (point.x - pivot.x) + Math.Cos(angle) * (point.y - pivot.y) + pivot.y));
}
well in 2D for 90deg rotations you do not need any trigonometry
use this instead:
input point (x,y) is rotated by 90deg like this:
xx=y; yy=-x;
the other direction is:
xx=-y; yy=x;
which formula is CW and CCW depends on your coordinate system
booth rotates point around point (0,0) by 90deg
so if you want to rotate by any other point just shift the points before and after ...
so do it like this:
translate point to your center of rotation
x-=x0; y-=y0;
rotate left or right
for example:
aa=x; bb=y; x=-bb; y=+aa;
translate point back
x+=x0; y+=y0;
now the x,y holds the rotated point
[notes]
if you want the center of rotation to be the middle of rectangle
then the x0,y0 is average point ...
so if rectangle is defined as x1,y1,...,x4,y4
then x0=(x1+x2+x3+x4)/4.0; and y0=(y1+y2+y3+y4)/4.0;

Matrix Transform of Rectangle after changing its bounds

I have a shape, for example a Rectangle which has the following bounds:
X = 100
Y = 100
Width = 100
Height = 100
I apply the following rotation to this rectangle using a new Matrix:
X = 100
Y = 100
Angle = 45
var transform = new Matrix();
transform.RotateAt(angle, point);
So the new Matrix has the following value:
0.7071068, 0.7071067, -0.7071067, 0.7071068, 150, -62.13202
I use this Matrix when I draw the Rectangle with Graphic:
protected override void OnPaint(PaintEventArgs e)
{
...
e.Graphic.Transform = transform;
g.DrawRectangle(Pen, bounds.X, bounds.Y, bounds.Width, bounds.Height);
}
The problem is the following: at a certain point I need to draw the same Rectangle but shifted by a certain offset, for example (50, 50). I have stored the Matrix transform and the Rectangle bounds. If I change only the bounds (adding the offset) the new Rectangle will be drawn in a wrong position, probably due to the previous rotation point.
How I have to change the Matrix in order to draw my Rectangle in the "right" position? that is, how can I retreive the right rotation point and the old rotation angle?
Try to add translation to the matrix using MatrixOrder.Prepend.
For offset (50, 50) it'll be:
transform.Translate(50, 50, MatrixOrder.Prepend);
Or create a separate matrix for this case:
var transformWithOffset = new Matrix();
transformWithOffset.Translate(50, 50);
transformWithOffset.RotateAt(angle, point);

XNA - how to draw an object farther

Here's how I draw some shape defined by vertices not shown here.
Vector3 position = (5,5,1);
Matrix world = Matrix.CreateTranslation(position);
BasicEffect basicEffect = new BasicEffect(graphicsDevice);
Matrix view = Matrix.CreateLookAt(new Vector3(0, 0, -20), new Vector3(0, 0, 100), Vector3.Up);
Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
graphics.Viewport.AspectRatio,
1.0f,
100);
// Set BasicEffect parameters.
basicEffect.World = world;
basicEffect.View = view;
basicEffect.Projection = projection;
//....draw some shape with basicEffect
I would like to paint the same shape only farther away so that its center stays in the same (x,y) pixel on screen but it is overall smaller as it's more distant.
I've tried scaling the position vector but had no success with it:
position .Z *= 2;
position .X *= 2;
position .Y *= 2;
What's the right way to do this?
Think about it geometrically: moving the object away from the camera means moving it along a line defined by two points: the camera's position and the object's position.
Now it's easy!
1) Find the vector object-to-camera, i.e.
Vector3 direction = objectPosition - cameraPosition;
2) Move the object alongside that vector by a certain amount, that is:
2.1) Normalize the direction
direction.Normalize();
2.2) Move the object by an amount x in that direction
objectPosition += direction * x;
And there you have it.

Categories

Resources