Collision checking on slopes - c#

I'm working on a new game, and am trying to detect whether or not the player (on a slope) is colliding with a given mesh based off of their coordinates relative to the coordinates of the slope. I'm using this function, which doesn't seem to be working (the slope seems too small or something)
//Slopes
float slopeY = max.Y-min.Y;
float slopeZ = max.Z-min.Z;
float slopeX = max.X-min.X;
float angle = (float)Math.Atan(slopeZ/slopeY);
//Console.WriteLine(OpenTK.Math.Functions.RadiansToDegrees((float)Math.Atan(slopeZ/slopeY)).ToString()+" degrees incline");
slopeY = slopeY/slopeZ;
float slopeZX = slopeY/slopeX;
//End slopes
float surfaceposX = max.X-coord.X;
float surfaceposY = max.Y-coord.Y;
float surfaceposZ = min.Z-coord.Z;
min-=sval;
max+=sval;
//Surface coords
//End surface coords
//Y SHOULD = mx+b, where M = slope and X = surfacepos, and B = surfaceposZ
if(coord.X<max.X& coord.X>min.X&coord.Y>min.Y&coord.Y<max.Y&coord.Z>min.Z&coord.Z<max.Z) {
if(slopeY !=0) {
Console.WriteLine("Slope = "+slopeY.ToString()+"SlopeZX="+slopeZX.ToString()+" surfaceposZ="+surfaceposZ.ToString());
Console.WriteLine(surfaceposY-(surfaceposY*slopeY));
//System.Threading.Thread.Sleep(40000);
if(surfaceposY-(surfaceposZ*slopeY)<3 || surfaceposY-(surfaceposX*slopeZX)<3) {
return true;
} else {
return false;
}
} else {
return true;
}
} else {
return false;
}
Any suggestions?
Sample output:
59.86697
6.225558 2761.331
68.3019 degrees incline
59.86698,46.12445
59.86698
6.225558 2761.332
0 degrees incline
EDIT: Partially fixed the problem. Slope detection works, but now I can walk through walls???
//Slopes
float slopeY = max.Y-min.Y;
float slopeZ = max.Z-min.Z;
float slopeX = max.X-min.X;
float angle = (float)Math.Atan(slopeZ/slopeY);
//Console.WriteLine(OpenTK.Math.Functions.RadiansToDegrees((float)Math.Atan(slopeZ/slopeY)).ToString()+" degrees incline");
slopeY = slopeY/slopeZ;
float slopey = slopeY+1/slopeZ;
float slopeZX = slopeY/slopeX;
//End slopes
float surfaceposX = min.X-coord.X;
float surfaceposY = max.Y-coord.Y;
float surfaceposZ = min.Z-coord.Z;
min-=sval;
max+=sval;
//Surface coords
//End surface coords
//Y SHOULD = mx+b, where M = slope and X = surfacepos, and B = surfaceposZ
if(coord.X<max.X& coord.X>min.X&coord.Y>min.Y&coord.Y<max.Y&coord.Z>min.Z&coord.Z<max.Z) {
if(slopeY !=0) {
Console.WriteLine("Slope = "+slopeY.ToString()+"SlopeZX="+slopeZX.ToString()+" surfaceposZ="+surfaceposZ.ToString());
Console.WriteLine(surfaceposY-(surfaceposY*slopeY));
//System.Threading.Thread.Sleep(40000);
surfaceposZ = Math.Abs(surfaceposZ);
if(surfaceposY>(surfaceposZ*slopeY) & surfaceposY-2<(surfaceposZ*slopeY) || surfaceposY>(surfaceposX*slopeZX) & surfaceposY-2<(surfaceposX*slopeZX)) {
return true;
} else {
return false;
}
} else {
return true;
}
} else {
return false;
}

Have you considered implementing a BSP tree? Even if you work out the bugs with the code you're using now, it'll be dog slow with a mesh of any decent size/complexity. A BSP or quadtree will go a long way towards simplifying your code and improving performance, and they're very easy to implement.
Edit
Here's a link to a nice BSP tutorial and overview.
If you're only concerned with terrain (no vertical walls, doors, etc), a quadtree might be more appropriate:
Here's a nice quadtree tutorial at gamedev.net.
Both of these algorithms are intended to divide your geometry into a tree to make searching easier. In your case, you're searching for polygons for collision purposes. To build a BSP tree (very briefly):
Define a structure for the nodes in the tree:
public class BspNode
{
public List<Vector3> Vertices { get; set; }
// plane equation coefficients
float A, B, C, D;
BspNode front;
BspNode back;
public BspNode(Vector3 v1, Vector3 v2, Vector3 v3)
{
Vertices = new List<Vector3>();
Vertices.AddRange(new[] { v1, v2, v3 });
GeneratePlaneEquationCoefficients();
}
void GeneratePlaneEquationCoefficients()
{
// derive the plane equation coefficients A,B,C,D from the input vertex list.
}
bool IsInFront(Vector3 point)
{
bool pointIsInFront=true;
// substitute point.x/y/z into the plane equation and compare the result to D
// to determine if the point is in front of or behind the partition plane.
if (pointIsInFront && front!=null)
{
// POINT is in front of this node's plane, so check it against the front list.
pointIsInFront = front.IsInFront(point);
}
else if (!pointIsInFront && back != null)
{
// POINT is behind this plane, so check it against the back list.
pointIsInFront = back.IsInFront(point);
}
/// either POINT is in front and there are no front children,
/// or POINT is in back and there are no back children.
/// Either way, recursion terminates here.
return pointIsInFront;
}
/// <summary>
/// determines if the line segment defined by v1 and v2 intersects any geometry in the tree.
/// </summary>
/// <param name="v1">vertex that defines the start of the ray</param>
/// <param name="v2">vertex that defines the end of the ray</param>
/// <returns>true if the ray collides with the mesh</returns>
bool SplitsRay(Vector3 v1, Vector3 v2)
{
var v1IsInFront = IsInFront(v1);
var v2IsInFront = IsInFront(v2);
var result = v1IsInFront!=v2IsInFront;
if (!result)
{
/// both vertices are on the same side of the plane,
/// so this node doesn't split anything. Check it's children.
if (v1IsInFront && front != null)
result = front.SplitsRay(v1, v2);
else if (!v1IsInFront && back != null)
result = back.SplitsRay(v1, v2);
}
else
{
/// this plane splits the ray, but the intersection point may not be within the face boundaries.
/// 1. calculate the intersection of the plane and the ray : intersection
/// 2. create two new line segments: v1->intersection and intersection->v2
/// 3. Recursively check those two segments against the rest of the tree.
var intersection = new Vector3();
/// insert code to magically calculate the intersection here.
var frontSegmentSplits = false;
var backSegmentSplits = false;
if (front!=null)
{
if (v1IsInFront) frontSegmentSplits=front.SplitsRay(v1,intersection);
else if (v2IsInFront) frontSegmentSplits=front.SplitsRay(v2,intersection);
}
if (back!=null)
{
if (!v1IsInFront) backSegmentSplits=back.SplitsRay(v1,intersection);
else if (!v2IsInFront) backSegmentSplits=back.SplitsRay(v2,intersection);
}
result = frontSegmentSplits || backSegmentSplits;
}
return result;
}
}
Pick a "partition" plane (face) from your mesh that roughly divides the rest of the mesh in two. This is a lot easier to do with complex geometry as fully convex items (spheres and the like) tend to wind up looking like lists instead of trees.
Create a new BspNode instance from the vertices that define the partition plane.
Sort the remaining faces into two lists - one that is in front of the partition plane, and one containing those faces that are behind.
Recurse to step 2 until no more nodes are in the list.
When checking for collisions, you have two options.
Single-point: check the coordinates that the character or object is moving to against the tree by calling your root node's .IsInFront(moveDestination) If the method returns false, the target point is "inside" the mesh, and you've collided. If the method returns true, the target point is "outside" the mesh, and no collision has occurred.
Ray intersection. This one gets a little tricky. Call the root node's .SplitsRay() method with the object's current position and it's target position. If the methods returns true, moving between the two positions will transition through the mesh. This is a superior (though more complex) check because it will catch edge cases, such as when the desired movement would take an object completely through an object in one step.
I just threw that sample code together quickly; it's incomplete and probably won't even compile, but it should get you going in the right direction.
Another nice thing about the BSP: Using the .SplitsRay() method, you can determine if one point on a map is visible from another point. Some games use this to determine if NPCs/AIs can see each other or real players. You could use a slight modification of that to determine if they can hear each other walking, etc.
This may seem a lot more complicated than your original approach, but it's ultimately far more powerful and flexible. It's worth your time to investigate.

Related

How to compare between two lines?

I have a code that allows me to draw lines and limit the number of lines that can be drawn.
My problem is that I want to create a line (with for example line renderer)
and then allow the user to try drawing a similar (not necessarily exactly the same) line and the code needs to know according to the setting if the line is similar enough or not, but I can't figure it.
I would appreciate any tips.
public class DrawLine : MonoBehaviour
{
public GameObject linePrefab;
public GameObject currentLine;
public LineRenderer lineRenderer;
public EdgeCollider2D edgeCollider;
public List<Vector2> fingerPositions;
public Button[] answers;
public bool isCurrButtonActive;
int mouseButtonState = 0;
void Update()
{
Debug.Log(rfgrhe);
if (isCurrButtonActive)
{
if (Input.GetMouseButtonDown(0))
{
if (mouseButtonState == 0)
{
CreateLine();
}
}
if (Input.GetMouseButtonUp(0))
{
mouseButtonState++;
}
if (Input.GetMouseButton(0))
{
if (mouseButtonState == 1)
{
Debug.Log(Input.mousePosition.ToString());
if (Input.mousePosition.x < 100 || Input.mousePosition.y > 420 || Input.mousePosition.x > 660 || Input.mousePosition.y < 7)
{
return;
}
Vector2 tempFingerPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
if (Vector2.Distance(tempFingerPos, fingerPositions[fingerPositions.Count - 1]) > .1f)
{
UpdateLine(tempFingerPos);
}
}
}
}
}
void CreateLine()
{
mouseButtonState++;
currentLine = Instantiate(linePrefab, Vector3.zero, Quaternion.identity);
lineRenderer = currentLine.GetComponent<LineRenderer>();
edgeCollider = currentLine.GetComponent<EdgeCollider2D>();
fingerPositions.Clear();
fingerPositions.Add(Camera.main.ScreenToWorldPoint(Input.mousePosition));
fingerPositions.Add(Camera.main.ScreenToWorldPoint(Input.mousePosition));
lineRenderer.SetPosition(0, fingerPositions[0]);
lineRenderer.SetPosition(1, fingerPositions[1]);
edgeCollider.points = fingerPositions.ToArray();
}
void UpdateLine(Vector2 newFingerPos)
{
fingerPositions.Add(newFingerPos);
lineRenderer.positionCount++;
lineRenderer.SetPosition(lineRenderer.positionCount - 1, newFingerPos);
edgeCollider.points = fingerPositions.ToArray();
}
public void ActivateCurrentButton()
{
// Debug.Log(isCurrButtonActive);
isCurrButtonActive = true;
for (int i = 0; i < answers.Length; i++)
{
if (answers[i].CompareTag("onePoint"))
{
answers[i].GetComponent<MapLvl>().isCurrButtonActive = false;
}
else if (answers[i].CompareTag("TwoPoints"))
{
answers[i].GetComponent<DrawLine>().isCurrButtonActive = false;
}
}
}
}
For example in that case, the blue line is the correct one, the green and the red ones are two options of an answer from the user.
What I want is that the program will acknolage only the green line as a correct answer.
EDIT: Since it's clearer now what we want, here's a way to achieve it:
The function float DifferenceBetweenLines(Vector3[], Vector3[]) below gives you a measure of the "distance between the two lines".
It walks along the line to match with a maximum step length, and for each point, computes the distance from the closest point on the draw line.
It sums the squares of those distances and divide them by the length of the line to match (don't ask me to explain this with mathematical rigor).
The smaller the return value, the closer the first line matches the second -- the threshold is yours to decide.
float DifferenceBetweenLines(Vector3[] drawn, Vector3[] toMatch) {
float sqrDistAcc = 0f;
float length = 0f;
Vector3 prevPoint = toMatch[0];
foreach (var toMatchPoint in WalkAlongLine(toMatch)) {
sqrDistAcc += SqrDistanceToLine(drawn, toMatchPoint);
length += Vector3.Distance(toMatchPoint, prevPoint);
prevPoint = toMatchPoint;
}
return sqrDistAcc / length;
}
/// <summary>
/// Move a point from the beginning of the line to its end using a maximum step, yielding the point at each step.
/// </summary>
IEnumerable<Vector3> WalkAlongLine(IEnumerable<Vector3> line, float maxStep = .1f) {
using (var lineEnum = line.GetEnumerator()) {
if (!lineEnum.MoveNext())
yield break;
var pos = lineEnum.Current;
while (lineEnum.MoveNext()) {
Debug.Log(lineEnum.Current);
var target = lineEnum.Current;
while (pos != target) {
yield return pos = Vector3.MoveTowards(pos, target, maxStep);
}
}
}
}
static float SqrDistanceToLine(Vector3[] line, Vector3 point) {
return ListSegments(line)
.Select(seg => SqrDistanceToSegment(seg.a, seg.b, point))
.Min();
}
static float SqrDistanceToSegment(Vector3 linePoint1, Vector3 linePoint2, Vector3 point) {
var projected = ProjectPointOnLineSegment(linePoint1, linePoint1, point);
return (projected - point).sqrMagnitude;
}
/// <summary>
/// Outputs each position of the line (but the last) and the consecutive one wrapped in a Segment.
/// Example: a, b, c, d --> (a, b), (b, c), (c, d)
/// </summary>
static IEnumerable<Segment> ListSegments(IEnumerable<Vector3> line) {
using (var pt1 = line.GetEnumerator())
using (var pt2 = line.GetEnumerator()) {
pt2.MoveNext();
while (pt2.MoveNext()) {
pt1.MoveNext();
yield return new Segment { a = pt1.Current, b = pt2.Current };
}
}
}
struct Segment {
public Vector3 a;
public Vector3 b;
}
//This function finds out on which side of a line segment the point is located.
//The point is assumed to be on a line created by linePoint1 and linePoint2. If the point is not on
//the line segment, project it on the line using ProjectPointOnLine() first.
//Returns 0 if point is on the line segment.
//Returns 1 if point is outside of the line segment and located on the side of linePoint1.
//Returns 2 if point is outside of the line segment and located on the side of linePoint2.
static int PointOnWhichSideOfLineSegment(Vector3 linePoint1, Vector3 linePoint2, Vector3 point){
Vector3 lineVec = linePoint2 - linePoint1;
Vector3 pointVec = point - linePoint1;
if (Vector3.Dot(pointVec, lineVec) > 0) {
return pointVec.magnitude <= lineVec.magnitude ? 0 : 2;
} else {
return 1;
}
}
//This function returns a point which is a projection from a point to a line.
//The line is regarded infinite. If the line is finite, use ProjectPointOnLineSegment() instead.
static Vector3 ProjectPointOnLine(Vector3 linePoint, Vector3 lineVec, Vector3 point){
//get vector from point on line to point in space
Vector3 linePointToPoint = point - linePoint;
float t = Vector3.Dot(linePointToPoint, lineVec);
return linePoint + lineVec * t;
}
//This function returns a point which is a projection from a point to a line segment.
//If the projected point lies outside of the line segment, the projected point will
//be clamped to the appropriate line edge.
//If the line is infinite instead of a segment, use ProjectPointOnLine() instead.
static Vector3 ProjectPointOnLineSegment(Vector3 linePoint1, Vector3 linePoint2, Vector3 point){
Vector3 vector = linePoint2 - linePoint1;
Vector3 projectedPoint = ProjectPointOnLine(linePoint1, vector.normalized, point);
switch (PointOnWhichSideOfLineSegment(linePoint1, linePoint2, projectedPoint)) {
case 0:
return projectedPoint;
case 1:
return linePoint1;
case 2:
return linePoint2;
default:
//output is invalid
return Vector3.zero;
}
}
The math functions at the end are from 3d Math Functions - Unify Community Wiki
Here is how it can be used to compare a LineRenderer against another:
Array.Resize(ref lineBuffer1, lineRenderer1.positionCount);
Array.Resize(ref lineBuffer2, lineRenderer2.positionCount);
lineRenderer1.GetPositions(lineBuffer1);
lineRenderer2.GetPositions(lineBuffer2);
float diff = DifferenceBetweenLines(lineBuffer1, lineBuffer2);
const float threshold = 5f;
Debug.Log(diff < threshold ? "Pretty close!" : "Not that close...");
A few things to consider:
The performance of SqrDistanceToLine could definitely be improved on
You get a measure of how close the first line matches the second, not the other way around -- that is, the first line can be longer or go for a walk mid-way as long as it comes back on track and "covers" the other line closely enough. You can solve this by calling DifferenceBetweenLines a second time, swapping the arguments, and taking the biggest result of them two.
We could work with Vector2 instead of Vector3
Original answer:
Similar?
As #Jonathan pointed out, you need to be more precise about "similar enough":
does similarity in size matter ?
does orientation matter ?
do similarity in proportions matter (or only the "changes of direction" of the line) ?
...
As you might guess, the fewer of those criteria matter, the harder it will be; because
your concept of similarity will become more and more abstract from the raw positions
you've got in the first place.
For example, if the user needs to draw a cross, with exactly two strokes,
that cover more or less a defined area, the task is as easy as it gets:
You can measure the distance between the area's corners and each stroke's first
and last points, and check that the lines are kind of straight.
If you want to check if the user drew a perfect heart-shape, in any orientation,
it's noticeably trickier...
You might have to resort to a specialized library for that.
Another thing to consider is, does the user really need to make a line similar to another one,
or should it only be close enough that it can be differentiated from other possible lines?
Consider this example:
The user needs to draw either a cross (X) or a circle (O):
If there is only one stroke that comes back close to the starting point, assume a circle.
If there is strokes whose general directions are orthogonal, assume a cross.
In this case, a more involved system would probably be overkill.
A few "raw pointers"
Assuming simple requirements (because assuming the opposite, I wouldn't be able to help much),
here are a few elements:
Exact match
The user has to draw on top of a visible line: this is the easiest scenario.
For each point of his line, find out the distance from the closest point on the reference line.
Sum the square of those distances -- for some reason it works better than summing the distances
themselves, and it's also cheaper to compute the square distance directly.
LineRenderer.Simplify
Very specific to your use-case, since you're using Unity's LineRenderer,
it's worth knowing that it packs a Simplify(float) method, that decreases the
resolution of your curve, making it easier to process, and particularly effective
if the line to match is made of somewhat straight segments (not complex curves).
Use the angles
Sometimes you'll want to check the angles between the different sub-segments of your line,
instead of their (relative) lengths. It will measure changes in direction regardless
of the proportions, which can be more intuitive.
Example
A simple example that detects equilateral triangles:
LineRenderer.Simplify
close the shape if the ends are close enough
check the angles are ~60deg each:
For arbitrary lines, you could run the line to match through the same "filter" as the lines the user draws, and compare the values. It will be yours to decide what properties matter most (angles/distances/proportions...), and what's the threshold.
Personally I would take points along the users line and then figure out the angles on the lines and if the average angle is within a specific range then it is acceptable. If the points you draw angles from are close enough together then you should have a pretty accurate idea whether the user is close to the same line.
Also, if the line needs to be in a particular area then you can just check and make sure the line is within a specified distance of the "control" line. The math for these should be pretty simple once you have the points. I am sure there are many other ways to implement this, but I personally would do this. Hope this helps!

AABB vs Circle collision in custom physics engine

I have followed this tutorial: https://gamedevelopment.tutsplus.com/tutorials/how-to-create-a-custom-2d-physics-engine-the-basics-and-impulse-resolution--gamedev-6331 to create a 2d physics engine in c# (he works in almost all the time wrong and inconsistent pseudo c++) I've got Circle vs Circle collision and AABB vs AABB collision working fine. But when trying the AABB vs Circle collison (below) the two rigidbodies just stick together and slowly move glitchy in one direction.
I would be super thankful if someone could help me with this as I have spent days and still don't know what's causing the error.
If someone needs more information from my code, I'd be happy to provide it.
public static bool AABBvsCircle(ref Collision result) {
RigidBody AABB = result.a.Shape is AABB ? result.a : result.b;
RigidBody CIRCLE = result.b.Shape is Circle ? result.b : result.a;
Vector2 n = CIRCLE.Position - AABB.Position;
Vector2 closest = n;
float x_extent = ((AABB)AABB.Shape).HalfWidth;
float y_extent = ((AABB)AABB.Shape).HalfHeight;
closest.X = Clamp(-x_extent, x_extent, closest.X);
closest.Y = Clamp(-y_extent, y_extent, closest.Y);
bool inside = false;
if (n == closest) {
inside = true;
if (Abs(n.X) > Abs(n.Y)) {
// Clamp to closest extent
if (closest.X > 0)
closest.X = x_extent;
else
closest.X = -x_extent;
}
// y axis is shorter
else {
// Clamp to closest extent
if (closest.Y > 0)
closest.Y = y_extent;
else
closest.Y = -y_extent;
}
}
Vector2 normal = n - closest;
float d = normal.LengthSquared();
float r = ((Circle)CIRCLE.Shape).Radius;
// Early out of the radius is shorter than distance to closest point and
// Circle not inside the AABB
if (d > (r * r) && !inside)
return false;
// Avoided sqrt until we needed
d = (float)Sqrt(d);
if (inside) {
result.normal = -normal / d;
result.penetration = r - d;
}
else {
result.normal = normal / d;
result.penetration = r - d;
}
return true;
}
edit 1 collison resolution method in "Collision" struct
public void Resolve() {
Vector2 rv = b.Velocity - a.Velocity;
float velAlongNormal = Vector2.Dot(rv, normal);
if (velAlongNormal > 0)
return;
float e = Min(a.Restitution, b.Restitution);
float j = -(1 + e) * velAlongNormal;
j /= a.InvertedMass + b.InvertedMass;
Vector2 impulse = j * normal;
a.Velocity -= a.InvertedMass * impulse;
b.Velocity += b.InvertedMass * impulse;
const float percent = 0.2f; // usually 20% to 80%
const float slop = 0.01f; // usually 0.01 to 0.1
Vector2 correction = Max(penetration - slop, 0.0f) / (a.InvertedMass + b.InvertedMass) * percent * normal;
if (float.IsNaN(correction.X) || float.IsNaN(correction.Y))
correction = Vector2.Zero;
a.Position -= a.InvertedMass * correction;
b.Position += b.InvertedMass * correction;
}
Before doing any detailed examining of the code logic, I spotted this potential mistake:
result.normal = -normal / d;
Since d was set to normal.LengthSquared and not normal.Length as it should be, the applied position correction could either be (much) smaller or (much) bigger than intended. Given that your objects are "sticking together", it is likely to be the former, i.e. d > 1.
(The fix is of course simply result.normal = -normal / Math.Sqrt(d);)
Note that the above may not be the only source of error; let me know if there is still undesirable behavior.
Although your tag specifies C#; here are basic AABB to AABB & AABB to Circle collisions that are done in C++ as these are take from: LernOpenGL:InPractice:2DGame : Collision Detection
AABB - AABB Collsion
// AABB to AABB Collision
GLboolean CheckCollision(GameObject &one, GameObject &two) {
// Collision x-axis?
bool collisionX = one.Position.x + one.Size.x >= two.Position.x &&
two.Position.x + two.Size.x >= one.Position.x;
// Collision y-axis?
bool collisionY = one.Position.y + one.Size.y >= two.Position.y &&
two.Position.y + two.Size.y >= one.Position.y;
// Collision only if on both axes
return collisionX && collisionY;
}
AABB To Circle Collision Without Resolution
// AABB to Circle Collision without Resolution
GLboolean CheckCollision(BallObject &one, GameObject &two) {
// Get center point circle first
glm::vec2 center(one.Position + one.Radius);
// Calculate AABB info (center, half-extents)
glm::vec2 aabb_half_extents(two.Size.x / 2, two.Size.y / 2);
glm::vec2 aabb_center(
two.Position.x + aabb_half_extents.x,
two.Position.y + aabb_half_extents.y
);
// Get difference vector between both centers
glm::vec2 difference = center - aabb_center;
glm::vec2 clamped = glm::clamp(difference, -aabb_half_extents, aabb_half_extents);
// Add clamped value to AABB_center and we get the value of box closest to circle
glm::vec2 closest = aabb_center + clamped;
// Retrieve vector between center circle and closest point AABB and check if length <= radius
difference = closest - center;
return glm::length(difference) < one.Radius;
}
Then in the next section of his online tutorial he shows how to do Collision Resolution using the above method found here: LearnOpenGL : Collision Resolution
In this section he adds an enumeration, another function and an std::tuple<> to refine the above detection system while trying to keep the code easier & cleaner to manage and read.
enum Direction {
UP,
RIGHT,
DOWN,
LEFT
};
Direction VectorDirection(glm::vec2 target)
{
glm::vec2 compass[] = {
glm::vec2(0.0f, 1.0f), // up
glm::vec2(1.0f, 0.0f), // right
glm::vec2(0.0f, -1.0f), // down
glm::vec2(-1.0f, 0.0f) // left
};
GLfloat max = 0.0f;
GLuint best_match = -1;
for (GLuint i = 0; i < 4; i++)
{
GLfloat dot_product = glm::dot(glm::normalize(target), compass[i]);
if (dot_product > max)
{
max = dot_product;
best_match = i;
}
}
return (Direction)best_match;
}
typedef std::tuple<GLboolean, Direction, glm::vec2> Collision;
However there is a slight change to the original CheckCollsion() function for AABB to Circle by changing its declaration/definition to return a Collision instead of a GLboolean.
AABB - Circle Collision With Collision Resolution
// AABB - Circle Collision with Collision Resolution
Collision CheckCollision(BallObject &one, GameObject &two) {
// Get center point circle first
glm::vec2 center(one.Position + one.Radius);
// Calculate AABB info (center, half-extents)
glm::vec2 aabb_half_extents(two.Size.x / 2, two.Size.y / 2);
glm::vec2 aabb_center(two.Position.x + aabb_half_extents.x, two.Position.y + aabb_half_extents.y);
// Get difference vector between both centers
glm::vec2 difference = center - aabb_center;
glm::vec2 clamped = glm::clamp(difference, -aabb_half_extents, aabb_half_extents);
// Now that we know the the clamped values, add this to AABB_center and we get the value of box closest to circle
glm::vec2 closest = aabb_center + clamped;
// Now retrieve vector between center circle and closest point AABB and check if length < radius
difference = closest - center;
if (glm::length(difference) < one.Radius) // not <= since in that case a collision also occurs when object one exactly touches object two, which they are at the end of each collision resolution stage.
return std::make_tuple(GL_TRUE, VectorDirection(difference), difference);
else
return std::make_tuple(GL_FALSE, UP, glm::vec2(0, 0));
}
Where the above functions or methods are called within this function that does the actually logic if a collision is detected.
void Game::DoCollisions()
{
for (GameObject &box : this->Levels[this->Level].Bricks)
{
if (!box.Destroyed)
{
Collision collision = CheckCollision(*Ball, box);
if (std::get<0>(collision)) // If collision is true
{
// Destroy block if not solid
if (!box.IsSolid)
box.Destroyed = GL_TRUE;
// Collision resolution
Direction dir = std::get<1>(collision);
glm::vec2 diff_vector = std::get<2>(collision);
if (dir == LEFT || dir == RIGHT) // Horizontal collision
{
Ball->Velocity.x = -Ball->Velocity.x; // Reverse horizontal velocity
// Relocate
GLfloat penetration = Ball->Radius - std::abs(diff_vector.x);
if (dir == LEFT)
Ball->Position.x += penetration; // Move ball to right
else
Ball->Position.x -= penetration; // Move ball to left;
}
else // Vertical collision
{
Ball->Velocity.y = -Ball->Velocity.y; // Reverse vertical velocity
// Relocate
GLfloat penetration = Ball->Radius - std::abs(diff_vector.y);
if (dir == UP)
Ball->Position.y -= penetration; // Move ball bback up
else
Ball->Position.y += penetration; // Move ball back down
}
}
}
}
// Also check collisions for player pad (unless stuck)
Collision result = CheckCollision(*Ball, *Player);
if (!Ball->Stuck && std::get<0>(result))
{
// Check where it hit the board, and change velocity based on where it hit the board
GLfloat centerBoard = Player->Position.x + Player->Size.x / 2;
GLfloat distance = (Ball->Position.x + Ball->Radius) - centerBoard;
GLfloat percentage = distance / (Player->Size.x / 2);
// Then move accordingly
GLfloat strength = 2.0f;
glm::vec2 oldVelocity = Ball->Velocity;
Ball->Velocity.x = INITIAL_BALL_VELOCITY.x * percentage * strength;
//Ball->Velocity.y = -Ball->Velocity.y;
Ball->Velocity = glm::normalize(Ball->Velocity) * glm::length(oldVelocity); // Keep speed consistent over both axes (multiply by length of old velocity, so total strength is not changed)
// Fix sticky paddle
Ball->Velocity.y = -1 * abs(Ball->Velocity.y);
}
}
Now some of the code above is GameSpecific as in the Game class, Ball class, Player etc. where these are considered and inherited from a GameObject, but the algorithm itself should provide useful as this is exactly what you are looking for but from a different language. Now as to your actually problem it appears you are using more than basic motion as it appears you are using some form of kinetics that can be seen from your Resolve() method.
The overall Pseudo Algorithm for doing AABB to Circle Collision with Resolution would be as follows:
Do Collisions:
Check For Collision: Ball With Box
Get Center Point Of Circle First
Calculate AABB Info (Center & Half-Extents)
Get Difference Vector Between Both Centers
Clamp That Difference Between The [-Half-Extents, Half-Extents]
Add The Clamped Value To The AABB-Center To Give The Point Of Box Closest To The Circle
Retrieve & Return The Vector Between Center Circle & Closest Point AABB & Check If Length Is < Radius (In this case a Collision).
If True Return tuple(GL_TRUE, VectorDirection(difference), difference))
See Function Above For VectorDirection Implementation.
Else Return tuple(GL_FALSE, UP, glm::vec2(0,0))
Perform Collision Resolution (Test If Collision Is True)
Extract Direction & Difference Vector
Test Direction For Horizontal Collision
If True Reverse Horizontal Velocity
Get Penetration Amount (Ball Radius - abs(diff_vector.x))
Test If Direction Is Left Or Right (W,E)
If Left - Move Ball To Right (ball.position.x += penetration)
Else Right - Move Ball To Left (ball.position.x -= penetration)
Else Test Direction For Vertical Collision
If True Reverse Vertical Velocity
Get Penetration Amount (Ball Radius - abs(diff_vector.y))
Test If Direction Is Up Or Down (N,S)
If Up - Move Ball Up (ball.position.y -= penetration)
Else Down - Move Ball Down (ball.position.y += penetration)
Now the above algorithm shown assumes that the boxes are not rotated and that their top & bottom edges are parallel with the horizontal and that their sides are parallel with the left and right edges of the window-screen coordinates. Also in the bottom section with the vertical displacement this also assumes that the top left corner of the screen - the first pixel is (0,0), thus the opposite operation for vertical displacement. This also assumes 2D collisions and not 3D Ridged or Ragdoll type collisions. You can use this to compare against your own source - implementation, but as far as just looking at your code without running it through a debugger it is extremely hard for me to see or find out what is actually causing your bug. I hope this provides you with the help that you need.
The above code from the mentioned OpenGL tutorial website does work as I have tested it myself. This algorithm is of the simplest of collision detections and is by far no means a comprehensive system and it still has caveats or pitfalls not mentioned here, but does suffice for the application it was used in. If you need more information about Collision Detections there is a few chapters that can be read in Ian Millington's book Game Physics Engine Development Although his book is based on a generalized 3D Physics Engine and only briefly discuses Collision Detection as their are full Books dedicated to the growing popularity of such complex beasts.

Xna Isometric point to click movement

I'm working on an isometric game (diamond grid) and I've stumbled across a minor problem regarding a character movement.
I'm using A* to find a path between 2 points and then I want to move my character from point A to point B going through all the tiles which form the path but I can't find a way to do this , I mean a simpler and accurate method.
So far I've scrapped this piece of code but it's kinda "rusty"
public void Destination(tile destination)
{
for (int i = 0; i < 8; i++)
{
if (AdjacentTile[i] == destination)
{
characterDirection = i;
}
}
animation.changeSpriteDirection(characterDirection); //After I found which adjacent tile is the next destination I change the character direction based on it's position (1 = North , 2 = Nort Est etc) .. so the Y of the Animation_sourceRectangle it's changed//
Vector2 Position;
Position.X = current_characterTile.X - destination.X;
Position.Y = current_characterTile.Y - destination.Y;
rotation = (float)Math.Atan2(-Position.X, Position.Y);
moveVector = (Vector2.Transform(new Vector2(0, -1), Matrix.CreateRotationZ(rotation))) * characterSpeed;
movingCommand = 1; // the character is supposed to be moving..
Move(); //this function moves the sprite until the *tile.i and tile.j* of the character is the same as tile.j and tile.i of the destination
//something like this
if ( characterTile.i == destination.i && characterTile.j == destination.j)
movingCommand = 0 //stop
else
character_Position += moveVector;
}
If anyone could give me a hint on what to do or help me I'll be very grateful.
Thank You.
Possibilities:
At each tile, determine the character's speed vector and also determine how much time it will take for the character to move to next tile. When that time elapses, immediately begin moving to the next tile. (This is what I implemented below.)
At each tile, determine the character's speed vector. Then, when the character is sufficiently close to the next tile (say, the difference between their X and Y coordinates is less than 2 pixels?), snap it to the tile and begin moving to the next tile. This will causes artifacts and be in general less precise.
A solution:
Let's assume you already ran your pathfinding algorithm and found a linked list of a tiles that you must go through to arrive at target. Let's also assume those tiles cannot become blocked partway through the movement (it is simple to modify the algorithm if they can, though).
I usually do something like this to handle this problem:
Run the pathfinding algorithm, which returns a List, if a path
exists.
character.Path = theListThatAStarReturned;
character.beginMovingToTarget(character.Path[0]);
character.Path.RemoveAt(0);
The beginMovingToTarget() method will determine the velocity vector and also determine the the time needed to arrive at the tile. When the time is reached, we immediately go to the next tile, until the Path is empty. Let's call this time variable character.timeToArrival.
Update():
if (!character.Moving) return; // Or just don't execute the rest of this code.
character.position += character.speed * elapsedSeconds;
character.timeToArrival -= elapsedSeconds;
// Did the character arrive in a tile?
if (character.timeToArrival <= 0)
{
// This will ensure the character is precisely in the tile, not a few pixels veered off.
character.position = character.movingToTile.position;
if (character.Path.Count == 0)
{
character.Moving = false;
// We are at final destination.
}
else
{
character.beginMovingToTarget(character.Path[0]);
character.Path.RemoveAt(0);
}
}
And the beginMovingToTarget(targetTile) function:
this.movingToTile = targetTile;
Vector2 direction;
direction = targetTile.position - this.position;
this.timeToArrival = direction.Length() / this.speedPerSeconds;
direction.Normalize();
direction *= this.speedPerSeconds;
this.speed = direction;
// Here, you may also want to change the character's animation, if you want to, or you may do that directly in the Draw() method based on its speed vector.
Make sure the division is in floats, not integers.

Collision detection in XNA 3.1

I'm currently making a 3D car game using XNA 3.1. It is a taxi game. So my main vehicle encounters traffic vehicles during the game. I'm having problems with coding the collision detection among traffic vehicles and the main vehicle. I used the bounding box method instead of bounding sphere method because bounding spheres don't cover the vehicles properly.
Below is the code i used to achieve collision. Problem with it is when the vehicle turns left or right bounding box doesn't change according to that.
I wrote this code in the update method.
carWorld = Matrix.CreateScale(1f) * Matrix.CreateTranslation(vehicalClassObs[0].Position);
trafficWorld = Matrix.CreateScale(1f) * Matrix.CreateTranslation(carObject.Position);
BoundingBox b=CalculateBoundingBox(carO);
BoundingBox c=CalculateBoundingBox(car);
Vector3[] obb = new Vector3[8];
b.GetCorners(obb);
Vector3.Transform(obb, ref carWorld, obb);
BoundingBox worldAABB = BoundingBox.CreateFromPoints(obb);
Vector3[] occ=new Vector3[8];
c.GetCorners(occ);
Vector3.Transform(occ, ref trafficWorld, occ);
BoundingBox worldAACC = BoundingBox.CreateFromPoints(occ);
if (worldAABB.Intersects(worldAACC))
col = true;
else col = false;
Below is the CalculateBoundingBox method
public BoundingBox CalculateBoundingBox(Model m_model)
{
// Create variables to hold min and max xyz values for the model. Initialise them to extremes
Vector3 modelMax = new Vector3(float.MinValue, float.MinValue, float.MinValue);
Vector3 modelMin = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
foreach (ModelMesh mesh in m_model.Meshes)
{
Matrix[] m_transforms = new Matrix[m_model.Bones.Count];
m_model.CopyAbsoluteBoneTransformsTo(m_transforms);
//Create variables to hold min and max xyz values for the mesh. Initialise them to extremes
Vector3 meshMax = new Vector3(float.MinValue, float.MinValue, float.MinValue);
Vector3 meshMin = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
// There may be multiple parts in a mesh (different materials etc.) so loop through each
foreach (ModelMeshPart part in mesh.MeshParts)
{
// The stride is how big, in bytes, one vertex is in the vertex buffer
// We have to use this as we do not know the make up of the vertex
int stride = part.VertexDeclaration.GetVertexStrideSize(0);
byte[] vertexData = new byte[stride * part.NumVertices];
mesh.VertexBuffer.GetData(part.BaseVertex * stride, vertexData, 0, part.NumVertices, 1); // fixed 13/4/11
// Find minimum and maximum xyz values for this mesh part
// We know the position will always be the first 3 float values of the vertex data
Vector3 vertPosition=new Vector3();
for (int ndx = 0; ndx < vertexData.Length; ndx += stride)
{
vertPosition.X= BitConverter.ToSingle(vertexData, ndx);
vertPosition.Y = BitConverter.ToSingle(vertexData, ndx + sizeof(float));
vertPosition.Z= BitConverter.ToSingle(vertexData, ndx + sizeof(float)*2);
// update our running values from this vertex
meshMin = Vector3.Min(meshMin, vertPosition);
meshMax = Vector3.Max(meshMax, vertPosition);
}
}
// transform by mesh bone transforms
meshMin = Vector3.Transform(meshMin, m_transforms[mesh.ParentBone.Index]);
meshMax = Vector3.Transform(meshMax, m_transforms[mesh.ParentBone.Index]);
// Expand model extents by the ones from this mesh
modelMin = Vector3.Min(modelMin, meshMin);
modelMax = Vector3.Max(modelMax, meshMax);
}
// Create and return the model bounding box
return new BoundingBox(modelMin, modelMax);
}
If someone can help me to solve this problem it wil be very helpful. If there is a better way to achieve collision other than the way i used please let me know about that method.
You have a couple of options here. The easiest is to transform the vehicle's bounding box according to the vehicle's world transforms (no projection or view required here since you're not concerned about camera position when checking for collisions.)
Assuming you already have the vehicle's original bounding box,
/// <summary>
/// Transforms a bounding box for collision detection
/// </summary>
/// <param name="vehicleBounds">Original, object-centered bounding box that contains a car model</param>
/// <param name="vehicleWorldMatrix">Vehicle's world transformation matrix (does not include projection or view)</param>
/// <returns>An axis-aligned bounding box (AABB) that will com </returns>
protected BoundingBox TransformBoundingBox(BoundingBox vehicleBounds, Matrix vehicleWorldMatrix)
{
var vertices = vehicleBounds.GetCorners();
/// get a couple of vertices to hold the outer bounds of the transformed bounding box.
var minVertex = new Vector3(float.MaxValue);
var maxVertex = new Vector3(float.MinValue);
for(int i=0;i<vertices.Length;i++)
{
var transformedVertex = Vector3.Transform(vertices[i],vehicleWorldMatrix);
/// update min and max with the component-wise minimum of each transformed vertex
/// to find the outer limits fo teh transformed bounding box
minVertex = Vector3.Min(minVertex, transformedVertex);
maxVertex = Vector3.Max(maxVertex, transformedVertex);
}
var result = new BoundingBox(minVertex, maxVertex);
return result;
}
For each vehicle, use that method to create a temporary bounding box to use for collisions. Only test transformed bounding boxes against each other, and do not overwrite you're original bounding box as you'll need to recalculate this box from your source any time the vehicle moves.
If you're using a multi-mesh model, use BoundingBox.CreateMerged() to combine them to get a box that contains the entire model, or perform your collisions for each sub-mesh bounding box (though this can get expensive without using some sort of acceleration structure).
What I have been using is a very simple method which can fit almost any situation. Here it is:
//Create one of the matricies
//Vector3 loc = new Vector3(0, 0, 0); //Wherever the model is.
//Matrix world1 = Matrix.CreateTransform(loc);
private bool IsCollision(Model model1, Matrix world1, Model model2, Matrix world2)
{
for (int meshIndex1 = 0; meshIndex1 < model1.Meshes.Count; meshIndex1++)
{
BoundingSphere sphere1 = model1.Meshes[meshIndex1].BoundingSphere;
sphere1 = sphere1.Transform(world1);
for (int meshIndex2 = 0; meshIndex2 < model2.Meshes.Count; meshIndex2++)
{
BoundingSphere sphere2 = model2.Meshes[meshIndex2].BoundingSphere;
sphere2 = sphere2.Transform(world2);
if (sphere1.Intersects(sphere2))
return true;
}
}
return false;
}
You can change all the spheres to boxes, but this might work. Additionally, what I do is move the location one axis at a time (X axis then Y axis then Z axis). This creates smoother collision.

Distance between Matrix Objects, relative to parents orientation, using XNA?

I would like to understand how to measure the distance between two 3D objects, let's call them a parent object and a child object. Think of the parent as the body of a car and the child being a wheel of the car.
I understand how to get the difference based on the objects position in world space but I would like to get the difference as a measurement based on the parents relative object space. 
E.g if the parent is facing East and the child is 2X, 3Y from the parent, measured in a relative sense. Such that if the parent rotated 60 degrees, the relative location of the child remains at a distance of 2x, 3y in the object space. Where as in a world space sense the child objects measurement as a Vector3 would be quite different. 
Basically I just want a predictable way to get the difference so that a child object which is on the right of the patent can always stay right of the parent object. 
This is the parent component, this update is run every frame:
[Serializable]
public class Component_Parent : BaseComponentAutoSerialization<ISceneEntity>
{
public override void OnUpdate(GameTime gameTime)
{
PassThrough.ParentMatrix = ParentObject.World;
PassThrough.ParentTranslation = ParentObject.World.Translation;
}
}
This next part is the child component:
[Serializable]
public class Component_Child : BaseComponentAutoSerialization<ISceneEntity>
{
Vector3 _parentOffset;
Quaternion _parentQuaternionOffset;
public override void OnUpdate(GameTime gameTime)
{
// Get a sceneobject from the ParentObject
SceneObject sceneobject = (SceneObject)ParentObject;
// This relies on the position never being at 0,0,0 for setup, so please don't do that
// or change it with more look ups so that you don't need to rely on a Zero Vector3 :-)
if (PassThrough.GroupSetupMode || _parentOffset == Vector3.Zero)
{
if (PassThrough.ParentTranslation != Vector3.Zero)
{
_parentOffset = sceneobject.World.Translation - PassThrough.ParentTranslation;
// Decompose World Matrix (Parent)
Quaternion parentQ = new Quaternion();
Vector3 parentSpot = new Vector3();
Vector3 parentScale = new Vector3();
PassThrough.ParentMatrix.Decompose(out parentScale, out parentQ, out parentSpot);
Matrix identity = Matrix.Identity;
// Decompose Identity Matrix (Parent)
Quaternion identityQ = new Quaternion();
Vector3 identitySpot = new Vector3();
Vector3 identityScale = new Vector3();
identity.Decompose(out identityScale, out identityQ, out identitySpot);
_parentQuaternionOffset = identityQ - parentQ;
}
}
else
{
if (_parentOffset != Vector3.Zero)
{
// Decompose World Matrix (Child)
Quaternion rotationQ = new Quaternion();
Vector3 spot = new Vector3();
Vector3 scale = new Vector3();
sceneobject.World.Decompose(out scale, out rotationQ, out spot);
// Decompose World Matrix (Parent)
Quaternion parentQ = new Quaternion();
Vector3 parentSpot = new Vector3();
Vector3 parentScale = new Vector3();
PassThrough.ParentMatrix.Decompose(out parentScale, out parentQ, out parentSpot);
Matrix location = Matrix.CreateTranslation(PassThrough.ParentTranslation);
Matrix rotation = Matrix.CreateFromQuaternion(parentQ);
Matrix rotation2 = Matrix.CreateFromQuaternion(_parentQuaternionOffset);
Matrix newWorld = rotation * location;
Vector3 testTranslation = newWorld.Translation + ((newWorld.Left * _parentOffset.X) + (newWorld.Up * _parentOffset.Y) + (newWorld.Forward * _parentOffset.Z));
Matrix scaleM = Matrix.CreateScale(scale);
//sceneobject.World = scaleM * (rotation * (Matrix.CreateTranslation(testTranslation)));
sceneobject.World = (Matrix.CreateTranslation(testTranslation));
}
}
}
}
I think it has something to do with keeping track of an offset rotation, from the identity matrix and I have started trying to add some code to that effect but really unsure of what next now.
Additional:
If I have the parent object facing the direction of the world space it all works, if it's facing a different direction then it's an issue and the child seems to rotate by the same amount when they are grouped together.
I've uploaded a demo video to try and explain:
http://www.youtube.com/watch?v=BzAKW4WBWYs
I've also pasted up the complete code for the components, the static pass through and the scene entity.
http://pastebin.com/5hEmiVx9
Thanks
Think wheels on a car. I want the right wheel to always be in the same
spot relative to the body of the car.
It sounds like you want to be able to locate the position of the wheel for any given orientation or position of the car. One built in method that XNA has to help here is Model.CopyAbsoluteBoneTransformsTo(Matrix[]); However, your code looks like you want to handle parent child relationship manually. So here is a way to do it without using the built in method. It assumes you do have offset information at load time:
Before the game loops starts (say, in the LoadContent method), after loading the car & wheel and assuming they load into the proper positions, you can then create your offset vector ( _parentOffset )
Vector3 _parentOffset = wheel.meshes[?].ParentBone.Transform.Translation - car.meshes[?].ParentBone.Transform.Translation;//where ? is the mesh index of the mesh you are setting up.
Save that vector and don't modify it.
Later, after the car's matrix has been rotationally and or positionally displaced, set the wheel's matrix like this:
Matrix wheelMatrix = carMatrix;
wheelMatrix.Translation += (wheelMatrix.Right * _parentOffset.X) +
(wheelMatrix.Up * _parentOffset.Y) +
(wheelMatrix.Backward * _parentOffset.Z);
This allows that the wheel matrix will inherit any rotational and translational information from the car but will displace the wheel's position appropriately regardless of car's orientation/position.
The distance between two objects is NOT a function of either orientations.
What you basically want is the distance of the child object to the orientation line of the parent object. Assuming you have a global cartesian coordinate system this can be simply calculated as h=sqrt(x^2+y^2)*sin(Theta), x and y being the relative coordinates of the child with respect to the parent and Theta the orientation of the parent measured from x axis.
But still the question is a little bit confusing to me. If you only want to make sure that the child is on the right side of the parent why don't you simply check the relative x? If it's positive it's on the right and if it's negative it's on the left?
The issue was the way I was trying to use the offset of the world space.
Thanks to flashed from #XNA on EFnet, this code works perfectly:
[Serializable]
public class Component_Child_fromxna : BaseComponentAutoSerialization<ISceneEntity>
{
Vector3 _parentOffset;
Matrix _ParentMatrixOffset;
public override void OnUpdate(GameTime gameTime)
{
// Get a sceneobject from the ParentObject
SceneObject sceneObject = (SceneObject)ParentObject;
// This relies on the position never being at 0,0,0 for setup, so please don't do that
// or change it with more look ups so that you don't need to rely on a Zero Vector3 :-)
if (PassThrough.GroupSetupMode || _parentOffset == Vector3.Zero)
{
if (PassThrough.ParentTranslation != Vector3.Zero)
{
// The old offset - This is just in world space though...
_parentOffset = sceneObject.World.Translation - PassThrough.ParentTranslation;
// Get the distance between the child and the parent which we keep as the offset
// Inversing the ParentMatrix and multiplying it by the childs matrix gives an offset
// The offset is stored as a relative xyz, based on the parents object space
_ParentMatrixOffset = sceneObject.World * Matrix.Invert(PassThrough.ParentMatrix);
}
}
else
{
if (_parentOffset != Vector3.Zero)
{
//Matrix pLocation = Matrix.CreateTranslation(_parentOffset);
//sceneObject.World = Matrix.Multiply(pLocation, PassThrough.ParentMatrix);
sceneObject.World = Matrix.Multiply(_ParentMatrixOffset, PassThrough.ParentMatrix);
}
}
}
}

Categories

Resources