C# / XNA Line Rectangle Collision/Response - c#

To start with, I have a simple class, Line;
public class Line
{
public Vector2 P1 = Vector2.Zero;
public Vector2 P2 = Vector2.Zero;
public Line(Vector2 p1, Vector2 p2)
{
P1 = p1;
P2 = p2;
}
}
A list of all lines in the game, and my sprites bounding rectangle. I'm trying to find whether or not this rectangles bottom middle is below the point on the line it's directly above, and update it to the point on the line it's at. This picture might help you understand what I mean;
The rectangle is moving down, passes it's intersection point, then updates accordingly.
Any ideas on how you could go about this? I can easily find which line it's currently above, but I don't know how to get the point on the line it's above and keep it from falling past that point.
Code samples or references would be great.

If you know the locations of the endpoints of the line, then it's pretty trivial to get the equation of that line in the form y = mx + c. Then you need to find the midpoint of the bottom of that rectangle - seeing as the XNA rectangle gives you its height, width and centre point location, this is also trivial. From there, you take the x co-ordinate of your rectangle centre point, use the y = mx + c equation to work out the y co-ordinate of your line at that point. Then you just need to check whether the rectangle's bottom centre is below that point, and if it is, use your rectangle's height value to work out how far above that y co-ordinate you need to put the rectangle so that it appears to sit on the line. Should be simple enough.

Related

Unity 3D interaction with UI Image

I am creating a 3D game and in the UI, there is an image that I want to print different things when the user puts the cursor on different parts of the Image. In a 2D game, I would add child objects to the image and add polygon colliders to them, and use OnMouseOver() method. But as I understand, this doesn't work on UI. I also tried OnPointerEnter() method and it works but I can't split the image into different parts using this. I tried to split the image into small parts using an external tool and putting them side by side and making a whole image but Unity recognizes all images as rectangular images (I am trying to make this for Girl with a Pearl Earring painting so shapes are irregular. ). How can I do this?
While this is not the best solution, you can get the current mouse position and check first if it's "inside" the picture, and then check for different parts
(For example if the image is between [100,100] to [300,300], and one part is between [100,100] to [150,150], you can check if the mouse is between those coordinates).
So you would get the position:
Vector2 mousePosition = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
float x1 = imageBoundry, x2 = imageBoundry;
float y1 = //Same but Y
if mousePosition.X between x1 and x2 && mousePosition.Y between y1 and y2
{
Check, in which part of the image the cursor is at, and decide then
}
Or, you can raycast and check the tag, and if the tag matches to the image's one, check for the specific part
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if(Physics.Raycast(ray, hit))
{
if (hit.collider.gameObject.CompareTag(YOUR_TAG))
{
Check for inner boundaries.
}
}

Scale Sprite up and Down to give illusion of a jump

I have some code that I wrote that works, but I feel it could be better and wanted to get some feedback.
The goal I had is to have a Sprite Scale up and back down in a timely fashion when a button is pushed so that it gives the illusion of jumping in a "Top Down" view of the game. Like the character is jumping off the screen. I already know how to draw scaled images I'm more interested in the logic of the timing aspect.
This works, just not sure it's the best. Thought maybe there was some equation, a math friend told me maybe a linear equation or like a parabola or second order equation. Not being great with math.
Anyway.
Class Properties
private double _jumpingStartedAt;
private double _totalJumpTimeInSeconds = 0.7;
private double _totalJumpFrames = 14;
private double _timeSinceLastScale;
private double _jumpingHalfWayAt;
When button is pushed for the first time I start the "Jump Logic". This runs once per jump. My thought was that I'd mark the "start" time and determine the "halfway" time by the totalJumpTimeInSeconds.
_jumpingStartedAt = gameTime.TotalGameTime.TotalSeconds;
_jumpingHalfWayAt = _jumpingStartedAt + MillisecondsBetweenFrame() * (_totalJumpFrames / 2);
And then this is run on each Update() until my "jump" is complete or isJumping = false. The logic here is that I would scale up every 1 "frame" until half way point then scale back down.
_timeSinceLastScale += gameTime.ElapsedGameTime.TotalSeconds;
if (_timeSinceLastScale > MillisecondsBetweenFrame() && gameTime.TotalGameTime.TotalSeconds < _jumpingHalfWayAt)
{
Scale += 0.2f;
_timeSinceLastScale = 0;
}
else if (gameTime.TotalGameTime.TotalSeconds > _jumpingHalfWayAt)
{
Scale -= 0.2f;
if (Scale < 1.0) Scale = 1; //probably don't need this was worried if it went passed 0
if (Scale == 1.0) _isJumping = false;
}
private double SecondsBetweenFrame()
{
return _totalJumpTimeInSeconds / this._totalJumpFrames;
}
Now this works, but seems a little convoluted to me.
Stretching image when jumping - side view
Yeah, it's pretty complicated, what you created.
I assume your sprite is also moving up and down when jumping. That you have some sort of Vector2 velocity, which you change by dv = gravityAcceleration * dt in every update, and so you change Vector2 position by dp = velocity * dt. If so, I would rather use my velocity.Y value to calculate how the sprite should stretch. I think it's more natural. And your code will become much more simple.
Here's an image to describe better what I mean:
However, you can probably face the other problem here: just at the beginning of the jump your sprite will suddenly get high velocity, when still being near the ground, which can cause it to cross through the floor for a moment. To prevent that you can artificially move your sprite upwards by the smallest needed value for the time of jump. The problem is described by the image below:
As you can very well see, the first stretched ball moved upwards a little bit, but not enough. You have to calculate difference between sizes before and after stretching and then move your sprite up by that distance.
If you do it like that, your Update should shorten to just a few lines. I believe you can do simple calculations on your own.
Easier approach
...Unless you'd rather like your sprite behave like you want. Then you could modify scale according to your Y position:
if (KeyboardState.IsKeyDown(Keys.Space))
{
isJumping = true;
jumpStartPosition = Position;
}
if (!isJumping) Scale = 1f;
else
{
Scale = StretchFactor * (Position.Y - jumpStartPosition.Y);
}
where:
- isJumping is a bool,
- jumpStartPosition is a Vector2,
- Position is a Vector2 property of your sprite,
- StretchFactor is a float property of your sprite telling how much does it stretch.
And you also need to have end-of-jump condition - for example when the sprite's Position.Y becomes smaller than the jumpStartPosition.Y. But generally this solution (as well as yours) has one disadvantage - there will be problems, if you will want to start jump from one height, and end it on another:
so I would rather recommend my first solution. There you can make stop-jump condition by collision check.
Stretching image when jumping - top-down view
Bummer. Since originally it wasn't specified that it is a top-down game, like those first GTA's, I really misunderstood the question, so the answer doesn't fit much. So the answer goes now.
If you wan't it to be realistic, you should use some basic principles of perspective. As we look at the character jumping from the top, it goes closer to us, so it's image grows. Why's that? Look at the pic below.
There are two things, that are needed for perspective to work: the center of perspective and the screen. The center of perspective is the point, where all "rays" are crossing. "Ray" is a line from the any point in the world to the center of our eye. Now the screen is the plane, where image of 3d world is being created. The points of the real world are being cast into screen along their rays. Of course your game is pseudo-3d, but it shouldn't matter in that case.
When z grows, sprite comes closer to the center of perspective. If you imagine ray from the center of perspective to the edge of the sprite, the angle of ray changes, as it's distance to the center of perspective becomes lesser. And the change of angle makes the point's image on the screen moving. That's why image grows, or becomes smaller.
Now we can wonder: ok, how now put this into numbers? Look at the picture below:
I deliberately translated whole world by -C so the z coord of the center of perspective could become 0. That makes calculations simplier. What are we trying to find, is the x' - coord of the point on the screen. Let the Z* = |z - C|. If we look at this picture it becomes clear, that we can find what we need by pretty simple proportion:
Using the same method you can calculate y'. If your character is always at the center of the screen, all that you need will be x'/x = y'/y = S, i.e. your scale. That's because x in this scenario is, in fact, the half-width of the sprite, and y is the half-height. However, if your character will be able to move freely around the screen, you may want to scale & translate it, so it would be more natural:
The white square is the on-the-ground sprite, the gray square is the jumping sprite. In this case you will have to know l (left), r (right), t (top) and b (bottom) coords of the sprite's boundaries (top-bottom means Y-axis, not Z-axis). Then using the same proportion you can get l', r', t' and b' - boundaries of the sprite's image on screen. From this data you should be able to calculate both scale and translation.
Note: L is the parameter of our calculation which you have to choose yourself. Assuming, that the screen has constant width Ws and height Hs, L strictly corresponds with FOV (field of view). You can acquire it also using proportions. So L = (cos(FOV/2) * Ws)/2. I would recommend FOV = 60 deg. If you will make FOV too big, you may face the fisheye problem.

Understanding DirectX

// Draw primitives
device.VertexFormat = CustomVertex.PositionColored.Format;
device.DrawUserPrimitives(PrimitiveType.TriangleFan, 4, verts);
device.Transform.World = Matrix.RotationY(angle += 0.05f);
// Draw primitives
device.VertexFormat = CustomVertex.PositionColored.Format;
device.DrawUserPrimitives(PrimitiveType.TriangleFan, 4, verts);
device.Transform.World = Matrix.RotationZ(angle += 0.05f);
I can't understand Transform.World.
How I understand (it would be logical) You draw first Triangle and it rotates Y... Then you draw second triangle and it would rotate both triangles to Z-axis.. This code works => One triangle is rotating only Y, second only Z. Why?
The reason that the first triangle is rotated along the Z-axis, is because you set the rotation the previous function, and it is preserved until you change it. After you drew that, you changed it to be a rotation along the Y-axis, and then you draw the second triangle. Then you set the matrix to be a rotation along the Z-axis again, and the whole process starts over.
You are not adding to the rotation that was previously in device.Transform.World, you're simply resetting it. If you want to add them together, you would have to multiply them.
You should perform matrix multpilication if you want to combine multiple transformations. That is, your last command should be device.Transform.World *= Matrix.RotationZ(angle += 0.05f);. Your code simply replaces rotation along Y with rotation along Z-axis. And, btw., you should first apply the tranformation, and only then render primitives (not the other way around).
You should be able to find many detailed transformation tutorials online.

Working out point location relative to other point when rotated - C# XNA

I have a small rectangle, with a point at the center. I also have another point, just outside of the rectangle, 10 pixels to the left of the rectangle's center point when the rectangle is sitting vertically, not rotated. How would i go about keeping this outside point in the same place relative to the rectangle when the rectangle is rotated about its center?
thanks
Here is an example of how to rotate one point around another point in XNA:
public Vector2 RotatePoint(Vector2 pointToRotate, Vector2 centerOfRotation, float angleOfRotation)
{
Matrix rotationMatrix = Matrix.CreateRotationZ(angleOfRotation);
return Vector2.Transform(pointToRotate - centerOfRotation, rotationMatrix);
}
Start the "point" at the center point of your rectangle, then translate it out to where you want it, and then rotate it from there.

Check if a point is in a rotated rectangle (C#)

I have a program in C# (Windows Forms) which draws some rectangles on a picturebox. They can be drawn at an angle too (rotated).
I know each of the rectangles' start point (upper-left corner), their size(width+height) and their angle. Because of the rotation, the start point is not necessarely the upper-left corner, but that does not matter here.
Then when I click the picturebox, I need to check in which rectangle (if any) I have clicked.
So I need some way of checking if a point is in a rectangle, but I also need to take into account the rotation of each rectangle.
Does anybody know of a way to do this in C#?
Is it possible to apply the same rotation applied to the rectangle to the point in reverse?
For example, Rectangle A is rotated 45 degrees clockwise from its origin (upper left corner), you would then just rotate point B around the same origin 45 degrees COUNTER clockwise, then check to see if it falls within Rectangle A pre-rotation
You could keep a second, undisplayed image where you draw duplicates of the rectangles, each uniquely colored. When the user clicks on the picturebox, find the color of the corresponding pixel in the 2nd image, which will identify which rectangle was clicked.
Edit: After looking back, I'm using MonoGame and the OP is using Windows Forms. The following is for MonoGame.
I've been messing this for a while now and have found a couple answers, just none of them actually worked. Here is a C# function that does exactly as OP describes, if not for OP then other people Googling like I was.
It was a headache to figure this out. A lot of the typical guesswork.
bool PointIsInRotatedRectangle(Vector2 P, Rectangle rect, float rotation)
{
Matrix rotMat = Matrix.CreateRotationZ(-rotation);
Vector2 Localpoint = P - (rect.Location).ToVector2();
Localpoint = Vector2.Transform(Localpoint, rotMat);
Localpoint += (rect.Location).ToVector2();
if (rect.Contains(Localpoint)) { return true; }
return false;
}
And here it is in a single line of code. Probably faster to use.
bool PointIsInRotatedRectangle(Vector2 P, Rectangle rect, float rotation)
{
return rect.Contains(Vector2.Transform(P - (rect.Location).ToVector2(), Matrix.CreateRotationZ(-rotation)) + (rect.Location).ToVector2());
}
I know this was already answered but I had to do something similar a while ago. I created an extension method for the System.Windows.Point class that helped do exactly what Neil suggested:
public static double GetAngle(this Point pt)
{
return Math.Atan2(pt.X, -pt.Y) * 180 / Math.PI;
}
public static Point SetAngle(this Point pt, double angle)
{
var rads = angle * (Math.PI / 180);
var dist = Math.Sqrt(pt.X * pt.X + pt.Y * pt.Y);
pt.X = Math.Sin(rads) * dist;
pt.Y = -(Math.Cos(rads) * dist);
return pt;
}
This would allow me to work with the angles of points around 0, 0. So if you know the center of the rect that you are testing you would offset the point by the negative of this value (for example: pt.X -= 32; pt.Y -= 32) And then you would apply the negative rotation of the rectangle (as suggested by Neil: pt.SetAngle(-45);)...
Now if the point is within 64, 64 you know you hit the rectangle. More specifically I was checking a pixel of a rotated image to make sure I hit a pixel of a specific color.
Would the rectangles be allowed to overlap?
If so, would you want all the rectangles in a point, or just the one in the top layer?
If you know the coordinates of the corners of the rectangle, this is an fast, elegant solution that merely involves a couple of dot and scalar products: https://math.stackexchange.com/a/190373/178768
See the rectangle edges as a list of vectors linking a corner to the next, sorting corners clockwise. If the point is in the square, it must be to the right with respect to all of the edge vectors.
This can be solved by vector products, but it boils down to the following:
Iterate over rectangle corners:
the point to be checked is P=[px,py]
the current corner is C=[cx,cy] and the next corner is N=[nx,ny]
if px*ny+cx*py+nx*cy<py*nx+cy*px+ny*cx, the point is outside the square.
this would actually work for every convex polygon.

Categories

Resources