I have been working on and off of a certain game I'm making (trying to get it finished!) and a problem that has been unsolved for a while in it is the collision detection between a ball and a square.
Basically what I want to happen eventually is depending on the angle/way the rectangle is facing, I want the ball to bounce of it accordingly (I know I could just inverse the ball direction before/as it hit the square).
At the moment though, my current problem is trying to inverse the correct X and Y components depending on the side/face that the ball collides with the square, e.g. if the ball hits the right side of the square, then I need to inverse the ball's X component.
This doesn't seem to work and I was wondering if I could somehow label each side of the rectangle, in terms of for the top of it label that 'face 1' or something, then for the right side of it 'face 2' or 'side 2', etc...
I have provided some code below (this is the code I'm using now):
//(collision with right side of square)
if (theBall.GetRectangle.Left <= thePaddle.GetRectangle.Right)
{
theBall.pVelocity.X = -theBall.pVelocity.X;
}
//(collision with bottom of square)
if (theBall.GetRectangle.Top <= thePaddle.GetRectangle.Bottom)
{
theBall.pVelocity.Y = -theBall.pVelocity.Y;
}
I have written the code for the other 2 sides of the rectangle but they are just the opposite of the two above, i.e. for collision with top of rectangle = opposite of bottom, etc.
EDIT: the object I am checking against if the ball has collided with DOES NOT move, I mean it only rotates... so I don't know if this is important (it probably is, therefore I apologise for missing this info out at the start).
EDIT # 23:36: ok, I have tried something.... and it hasn't worked... :(
public Vector2 DistBetweenBallAndBlock(Paddle thePaddle, Ball theBall)
{
Vector2 centreOfBall = new Vector2(theBall.Texture.Width / 2, theBall.Texture.Height / 2);
float distX = thePaddle.Position.X - centreOfBall.X;
float distY = thePaddle.Position.Y - centreOfBall.Y;
if (distX < 0)
{
distX = -distX;
}
if (distY < 0)
{
distY = -distY;
}
return new Vector2(distX, distY);
}
I have then tried to just print the result just to get an idea of what's going on and what sort of values are being output:
Vector2 a = ball.DistBetweenBallAndBlock(paddle1, ball);
angleOfPaddle = Math.Atan2(a.Y, a.X);
I then just print this value to screen, however I am getting the same result of 0.63...
To detect a collision between Rectangles you could use Rectangle.Intersect method, instead of checking the objects' sides.
And to detect which side of the rectangle is hit, you can compute the Vector2 between the ball center and the rectangle center. Getting its angle with Math.Atan2 you can easily know which face of the rectangle has been hit.
Looked up some Vector stuff, based on my comment.
Collision Style
The optimal way of colliding with a circular object is to collide using a vector between it and the nearest point of the object you're checking against. If the distance is less than or equal to the radius of the circle, there is a collision. The advantages of this method are that you don't have to keep track of a rectangle, you get circular collision, and you get the angle of the collision from the vector.
How You Do It
I'll assume you have some strategy to keep from considering every object every frame, and keep to the basic problem. Your paddle has 4 vertices, one for each corner. Because your ball is essentially a vertex with a picture drawn over it, you can easily check the distance between the ball and each corner of your paddle. Two will be nearest. From there, it's just a matter of finding out if that edge collides. I found a solution to that here, which includes a nice formula.
Does that help?
Related
I'm not really like to post questions about problems without doing the research, but I'm close to give up, so I thought I give it a shot and ask you about my problem.
I want to create a custom collision detection in Unity ( So please don't advice "use rigidbody and\or colliders" because I don't want to use them by purpose).
The main idea: I want to detect Basic Sphere and Basic Box collision. I already find AABB vs Sphere theme with the following solution:
bool intersect(sphere, box) {
var x = Math.max(box.minX, Math.min(sphere.x, box.maxX));
var y = Math.max(box.minY, Math.min(sphere.y, box.maxY));
var z = Math.max(box.minZ, Math.min(sphere.z, box.maxZ));
var distance = Math.sqrt((x - sphere.x) * (x - sphere.x) +
(y - sphere.y) * (y - sphere.y) +
(z - sphere.z) * (z - sphere.z));
return distance < sphere.radius;
}
And this code does the job, the box bounding and the sphere center point with radius works fine, I can detect the Sphere collision on Box.
The problem is, I want to Rotating the Cube in Runtime, so that will screw up everything, the bounding will split away and the collision will gone (or collide on random places). I've read about some comments where they said, bounding not works with rotation, but I'm not sure what else can I use to solve this problem.
Can you help me with this topic please? I'll take every advice I can get (except Colliders & Rigidbodies of course).
Thank you very much.
You might try using the separating axis theorem. Essentially, for a polyhedron, you use the normal of each face to create an axis. Project the two shapes you are comparing onto each axis and look for an intersection. If there is no intersection along any of the axes, there is no intersection of shapes. For a sphere, you will just need to project onto the polyhedron's axes. There is a great 2D intro to this from metanet.
Edit: hey, check it out-- a Unity implementation.
A good method to find if an AABB (axis aligned bounding box) and sphere are intersecting is to find the closest point on the box to the sphere's center and determine if that point is within the sphere's radius. If so, then they are intersecting, if not then not.
I believe you can do the same thing with this more complicated scenario. You can represent a rotated AABB with a geometrical shape called a parallelepiped. You would then find the closest point on the parallelepiped to the center of the sphere and again check if that point exists within the sphere's radius. If so, then they intersect. If not, then not.
The difficult part is finding the closest point on the parallelepiped. You can represent a parallelepiped in code with 4 3d vectors: center, extentRight, extentUp, and extentForward. This is similar to how you can represent an AABB with a 3d vector for center along with 3 floats: extentRight, extentUp, and extentForward. The difference is that for the parallelepiped those 3 extents are not 1 dimensional scalars, but are full vectors.
When finding the closest point on an AABB surface to a given point, you are basically taking that given point and clamping it to the AABB's volume. You would, for example, call Math.Clamp(point.x, AABB.Min.x, AABB.Max.x) and so on for Y and Z.
The resulting X,Y,Z would be the closest point on the AABB surface to the given point.
To do this for a parallelepiped you need to solve the "linear combination" (math keyword) of extentRight(ER), extentUp(EU), and extentForward(EF) to get the given point. In other words, what scalars do you have to multiply ER, EU, and EF by to get to the given point? When you find those scalars you need to clamp them between 0 and 1 and then multiply them again by ER, EU, and EF respectively to get that closest point on the surface of the parallelepiped. Be sure to offset the given point by the Parallelepiped's min position so that the whole calculation is done in its local space.
I didn't want to spend any extra time learning how to solve for a linear combination (it seems it involves things like using an "augmented matrix" and "gaussian elimination") otherwise I'd include that here too. This should get you or anyone else reading this off to the right track hopefully.
Edit:
Actually I think its a lot simpler and you don't need a parallelepiped. If you have access to the rotation (Vector3 or Quaternion) that rotated the cube you could get the inverse of that and use that inverse rotation to orbit the sphere around the cube so that the new scenario is just the normal axis aligned cube and the orbited sphere. Then you can do a normal AABB - sphere collision detection.
I want to know, how to get no collision inside an object.
I have a big circle and inside the circle is a smaller square. When the game starts, the circle is scaling down.
And what i want, is to check the collision, if circle is touching or is inside the square.
Can you please help me ? Thank you
As far as I know there is no built-in way of achieving this in Unity (There is also not a built-in way of detecting whether a collider is fully inside another collider or not).
If I understood you correctly your circle is going to shrink and when it reaches this point
you want to do something e.g. execute some code.
The way you could make it work for a circle and a square just based on maths would be this:
If the sides of your square are of length a and your circle has a decreasing radius of r, then at the moment you see in the image above the relation between the two of them is:
r = a / sqrt(2)
So you could check if (r <= a / Mathf.Sqrt(2)) in the Update function and based on that call some function. (Maybe add another boolean to ensure the function only gets called once.)
You can get your sprite widths using
width = GetComponent<SpriteRenderer>().bounds.size.x;
The radius of your circle would then obviously be half the width of the circle sprite.
You should probably also store the SpriteRenderer in a variable once instead of calling GetComponent on every frame.
For an equilateral triangle the equation would be
r = a / sqrt(3)
where a is the length of the triangle's side.
I'm currently working on a 2D Game Engine in C# using GDI+ and have gotten to the point where I want to add simple collision detection.
So far, I can check whether or not my player has intersected with another game object using the code below:
public static bool IntersectGameObject(GameObject a, GameObject b)
{
Rectangle rect1 = new Rectangle((int)a.Position.X, (int)a.Position.Y, a.Sprite.Bitmap.Width, a.Sprite.Bitmap.Height);
Rectangle rect2 = new Rectangle((int)b.Position.X, (int)b.Position.Y, b.Sprite.Bitmap.Width, b.Sprite.Bitmap.Height);
return rect1.IntersectsWith(rect2);
}
That's fantastic and I'm happy that I've gotten this far, however I wish to find out whether or not my player has intersected with the top, bottom, left or right side of a game object so that I can stop my player from moving in that direction if say... he collides with a wall. How would I go about doing this? Can some one please help me :)
By the way, game objects have Bitmaps, that are all 32 * 32 pixels, so i don't need per pixel collision
Thank you in advance :)
Computing whether two rectangles intersect is just a matter of a few comparisons (I know you're already using IntersectsWith):
if (Left > other.Right) return false;
if (Right < other.Left) return false;
if (Top > other.Bottom) return false;
if (Bottom < other.Top) return false;
return true;
To determine the direction of the collision, you'll have to take into account the possibility that:
one rectangle completely contains the other (collides in no direction)
it might create a "plus" figure (collides in all directions)
one corner of the rectangle could intersect with a corner of other corner (collides in two directions)
Anyway, to determine whether it has collided on the left side:
if (Right > other.Left && Right < other.Right && Left < other.Left)
{
// the right side of "this" rectangle is INSIDE the other
// and the left side of "this" rectangle is to the left of it
}
Last but not least, if you're computing this frame by frame, there's the possibility that an object might "jump over" the other. For more realistic physics you'd have to keep track of time and compute when it would intersect with (each edge of) the rectangle.
If you're processing frame by frame, a good choice could be storing the previous position P0x,P0y and the current position P1x,P1y.
Then, you can calculate a vector Vx,Vy and you can calculate the intersection point Ix,Iy. So you can get a more precise information about the collision.
If you need collision detection you need a rigidbody, you normally move rigidbodies with physics. As far as I saw, it's possible to live problem with above code snippet. If your moving them with transform move you should mark as is kinematic. That means it will not be moved by physics but by transform.
A kinematic rigidbody which you want to move should be done during FixedUpdate to apply collisions properly.
if you would like to know which side you hit you may raycast to the object
This code snippet gives an example that shows a raycast you could perform on collision. Ultimately, which game engine have you been using?
//this ray will generate a vector which points from the center of
//the falling object TO object hit. You subtract where you want to go from where you are
Ray MyRay = ObjectHit.position - ObjectFalling.position;
//this will declare a variable which will store information about the object hit
raycasthit MyRayHit;
//this is the actual raycast
raycast(MyRay, out MyRayHit);
//this will get the normal of the point hit, if you dont understand what a normal is
//wikipedia is your friend, its a simple idea, its a line which is tangent to a plane
vector3 MyNormal = MyRayHit.normal;
//this will convert that normal from being relative to global axis to relative to an
//objects local axis
MyNormal = MyRayHit.transform.transformdirection(MyNormal);
//this next line will compare the normal hit to the normals of each plane to find the
//side hit
if(MyNormal == MyRayHit.transform.up)
{
you hit the top plane, act accordingly
}
//important note the use of the '-' sign this inverts the direction, -up == down. Down doesn't exist as a stored direction, you invert up to get it.
if(MyNormal == -MyRayHit.transform.up)
{
you hit the bottom plane act accordingly
}
if(MyNormal == MyRayHit.transform.right)
{
hit right
}
//note the '-' sign converting right to left
if(MyNormal == -MyRayHit.transform.right)
{
hit left
}
Wouldn't it be an option to have 4 different objects each one on 1 side on your player object so depending on which side intersects with game object you could know which movement direction to prevent ?
I tried posting this in the unity forums, but it's been stuck awaiting moderation, so I'm posting it here in hopes of getting some more immediate feedback.
I have 4 objects, two planes and two arrows. One arrow points at one of the planes, and I want the second arrow to match the first arrow, but relative to the second plane. What I mean by this, is if the first arrow is moved 1 unit to the right relative to the first plane, the second arrow should move 1 unit to the right relative to the second plane. I have gotten the positions to work out correctly, but I can't figure out how to match the rotations. Some axes of rotations work correctly some of the time, but once one of the planes are rotated, the second arrow's rotation gets more out of sync with the first.
Here's the code that I'm using to test this, with the best I could figure out:
[ExecuteInEditMode]
public class RotationTest : MonoBehaviour
{
public GameObject target;
public GameObject camera;
public GameObject arrow;
void OnWillRenderObject()
{
if (!target || !camera || !arrow) return;
arrow.transform.position = target.transform.TransformPoint(transform.InverseTransformPoint(camera.transform.position));
//arrow.transform.rotation = Quaternion.Inverse(camera.transform.rotation) * transform.rotation * Quaternion.Inverse(target.transform.rotation);
//arrow.transform.eulerAngles = new Vector3(arrow.transform.eulerAngles.x, -arrow.transform.eulerAngles.y, arrow.transform.eulerAngles.z);
arrow.transform.eulerAngles = (target.transform.eulerAngles - transform.eulerAngles) + camera.transform.eulerAngles;
}
}
I put this script on the first plane, the first arrow is 'camera', the second plane is 'target', the second arrow is 'arrow'. When both planes are aligned exactly, the second arrow should be exactly the same position and rotation as the first. The commented out lines are the first thing I tried, the uncommented line is the next closest thing I got to work, but it only works correctly when the planes are rotated about the Y axis, and mess up when any other axis on the plane is rotated.
When both planes are facing the same direction, simply setting the rotation of the second arrow to the first arrow's rotation works correctly, but once the first plane is rotated this method falls apart.
I've been at this for quite a while, trying different combinations of Quaternion multiplications and differences of eulerAngles, but I can't seem to figure it out. Any help at this point is much appreciated.
Edit: Here are a few images depicting what I'd like to happen, and what is actually happening with the current script: http://imgur.com/a/MVI8x
The first three pictures show what is occurring when both planes have the same rotation (the arrow positions and rotations work correctly in this case, as I stated earlier). The 4th picture shows what happens (and should happen) when the plane is rotated about the Y axis. The blue arrow moves and rotates to look at the same point on the blue plane that the green arrow is now looking at the green plane. The last two pictures show what the blue arrow should do, but as can be seen, is rotating in odd directions. I'm testing this purely in the editor, so when I move or rotate any object, the blue arrow will respond immediately.
You can always just set the arrow's rotations equal to each other, and as long as neither is parented to anything else, they will always be exactly equal, so long as the models themselves have been authored with the same axis point and initial rotation.
public GameObject arrow1;
public GameObject arrow2;
void Update(){
arrow2.transform.rotation = arrow1.transform.rotation;
}
EDIT:
If that's not working, how are you rotating the first arrow? Is it using transform.LookAt?
When I run into rotation issues it's usually a local vs. global issue. Maybe try:
void Update(){
arrow2.transform.localRotation = arrow1.transform.localRotation;
}
Or maybe I don't understand what you're actually trying to achieve and a diagram could help clarify.
A new problem i have at the moment is sometimes when the ball is bouncing around the screen, it may all of a sudden get stuck colliding with a rectangle, so it may hit a spot between the rectangle and the side of the screen. This sometimes results in either: a) the ball looks like it's shaking or something, which i am assuming as it's still colliding with the rectangle, it's velocity (as i have defined it) is constantly inversing... but it's still stuck... (that's what i cant solve... lol :( ), or b) when the ball collides with the rectangle, it starts to move along the edge of the rectangle and goes back and forth but it continues to do this... (so it's basically stuck moving just along the edge).
I have uploaded a video to youtube showing this in action (btw... the ball gets stuck and goes up and down the right side of the screen and then starts going along the top edge of the white rectangle and then eventually bounces off into oblivion... well basically frees itself, which is kind of ok but then if you wait 5 more seconds, it repeats this process)
http://www.youtube.com/watch?v=3qfpgtoWbIU&feature=youtu.be
the code i am using for collision detection with the rectangle is:
if (theBall.GetRectangle.Bottom >= cornerSquare.GetRectangle.Top && theBall.GetRectangle.Bottom <= cornerSquare.GetRectangle.Bottom)
{
theBall.pVelocity.Y = -theBall.pVelocity.Y;
}
thats for detection if the ball hit's the top of the rectangle.
detection with the right side of the screen is:
if (pPosition.X + pTexture.Width >= screenWidth)
{
pVelocity.X = -pVelocity.X;
}
I hope someone has a simple and effective solution to this as i just want to 'be done' with all this collision detection nonsense lol... as it's taking up most of my time whilst i could be alot more productive doing other parts of the game.
Thanks for reading, taking the time to read, taking the time to help.... ETC.... :D
Try to check for collision first using a calculated test-rectangle.
For example the ball moves for Vector2(1, 1) per Update. Find out where the Rectangle for collision WOULD be at the next step BEFORE moving.
int nextPosX = currRect.X + (int)movementVector.X;
int nextPosY = currRect.Y + (int)movementVector.Y;
// find out where the rectangle would be after this update:
Rectangle nextStepsCollisionRect = new Rectangle(nextPosX, nextPosY, width, height);
// check if the test rectangle would collide:
if( collisionTest(nextStepsCollisionRect, sideWall) )
{
// do not move the intended way!
// invert X and recalc with pre-calculated rectangle again...
} else {
// no collision. now move the caculated way:
ball.CollisionRectangle = nextStepsCollisionRect;
}
This is a quite simple way. Use "ray tracing" if the ball is a very fast bullet (just to be mentioned).
If your ball is stuck along the edge you can simply bring the ball position to its previuos position before the collision (the one it had in the previous frame), in this way you change its velocity and ensure that the ball is not colliding anymore, this should do the trick.
You can achieve this by subtracting ball speed to ball position.