Test if one GameObject is above another with any shape colliders - c#

Essentially, I have a function designed to test if one object is positioned entirely above another while taking the collider bounds into account:
public static bool isAbove(GameObject a, GameObject b)
{
Collider2D colliderA = a.GetComponent<Collider2D>();
Collider2D colliderB = b.GetComponent<Collider2D>();
return a.transform.position.y - (colliderA.bounds.size.y * 0.5) >= b.transform.position.y + (colliderB.bounds.size.y * 0.5);
}
When I originally wrote this, I had no intention to use bounding shapes other than boxes, so the algorithm worked. Now, I am using a circle collider on one of the objects and a box collider on the other one, which makes the algorithm inconsistent, as it's still treating the circle as a box. I really want this working for any shape colliders in any combination.
I thought that I could specify a point directly below the first object and then find the max point at that x position, but I can't find this option anywhere, and I'm still not sure if it's the solution I should be looking for.

As I mentioned in my comment, Collision2D has a lot of useful information regarding the collision of two objects. Specifically for your use case, I would also look into the Vector3.Dot. To be brief, using Vector3(up, otherObject will give you a result to determine if two objects are over one another.
For a little more clarification, there are three possible cases that can be returned from Vector3.Dot and they are
Dot is 0 - The two objects are perfectly perpendicular to one another
Dot is <0 - The angle between the two objects is greater than 90º
Dot is >0 - The angle between the two objects is less than 90º
For your specific use case, I would calculate the dot product between the up vector, and the vector to the other target. If the result is greater than 0 then the other target is above your current object. You can either only check this data when the objects are actively colliding or check it every frame. It would depend on how many objects you're checking against.

This already exists anyway, you are looking for Collider2D.isTouching so you simply do
return colliderA.isTouching(colliderB);
and done ;)
However, instead of passing in GameObject and everytime use GetComponent you should rather somewhere pre-cache the Collider2D references and directly use either
if(colliderA.isTouching(colliderB))
or also
if(Physics2D.isTouching(colliderA, colliderB))
They are more or less equivalent.

Related

How do I determine if i just calculated the normal in the correct direction?

To explain the context, I procedurally generate buildings on unity in C#. I create a mesh and fill in the vertices and triangles, then I calculate the normals of the different points. I have several hundreds of buildings that are generated, but some faces are not in the right direction, the normals point inwards instead of outwards.
The normals are good
The normals are bad
To calculate the normals I create a Plane with the different vertex that make up the triangle, and then I retrieve the normal of the Plane. I also tested the cross product that gives the same result.
Plane plane = new Plane(v1, v2, v3);
normals.Add(plane.normal);
How i generate Triangle ?
Ex: I make a for loop on the points at the base of the building.
vectors.Add(v1);
vectors.Add(v2);
vectors.Add(v3); //v3 = v1+height
vectors.Add(v4); //v4 = v2+height
// index values
int idx1, idx2, idx3, idx4;
idx4 = vectors.Count - 1;
idx3 = vectors.Count - 2;
idx2 = vectors.Count - 3;
idx1 = vectors.Count - 4;
// Triangle 1
indices.Add(idx1);
indices.Add(idx3);
indices.Add(idx2);
...
mesh.triangles = indices.ToArray();
So my question is: How to determine if the normal is in the right direction (inside or outside)? If I can determine that, I can then flip the normal and normally it will work.
I do not totally understand your question, but for simple shapes a simple algorithm is ..
just take the "center" of the object (the CG is fine) and make a vector from there to your vertex or triangle. That direction is "outside". If you're pointing over 90° away from that, you're pointing in to the guts of the object
the next more complicated approach ...
move along your normal say "one meter". call that point "test point". you should now be "outside" your building, correct?
now you just need to check if testPoint is inside or outside your 3D shape.
in short, to determine if you are inside or outside a 3D object, you just cast a ray and count how many times you intersect with the walls. if odd, you're inside, if even you're outside.
You can google hundreds of discussions on this on the www, example https://stackoverflow.com/a/63572837/294884
Note that there are many, many variations on this, and many problems too. In some cases you start from "just inside" your normal (ie go backwards a little); sometimes it's better to start from the middle of the object (if such a thing is knowable); sometimes there are issues about how the ray casting system works in edge cases (like "right on" the surface, which is what your vertex is)
the first solution seems to me to be the right one
It may be. Sometimes it is useless. It depends on the nature of your shapes.
I wanted to avoid having to do raycast ..
Can assure you - you will have to constantly raycast during any construction process! If you wish to "avoid" casting, set the idea aside. You will be casting until you are sick of it :)
I wanted to avoid having to do raycast to limit the generation time
The good news is this is totally incorrect. Casting is extremely trivial and a very minor burden compared to everything going on in a 3D scene. When you play any 3D scene, casts are being made 100s and 1000s of times each frame.
but if I have two buildings that are close to each other, the ray will find the wall but from the second building so I couldn't determine the normal is "inside" or "outside", right?
TBC in the simple system I described. You would certainly ONLY cast against that same building that you are working on! So that's the way to go.
But indeed, unrelated to what you're asking about. Say you're in a "city". You can indeed cast through "all walls" and the parity (ie: odd or even) will let you know if you are indoors or out. This has problems though, relating to edges, corners etc. But in some case it is relevant to that problem.
As mentioned on this answer https://stackoverflow.com/a/63593282/294884 ultimately you can investigate convex hulls and more. It's a big topic!
Note that often when you build dynamic buildings/shapes, you havce a data structure of each "wall" and indeed it knows which side is in and which side out. When you do that first, it's then easy to draw the triangles the correct way. It's a big topic!

How to randomly place objects as player move around in an infinity map without overlap?

I trying to make a game where player only move forward in an infinity map, and the path (just thing of them like points, the path is only the visual) is procedurally generated. I want those path to have different length (something like the tree of life, but only branches of the selected path are generated).
This is how I generate branches without overlap:
List<Vector3> everyPos; //predetermined position
public void Spawn(int amount)
{
List<Vector3> possiblePos = new List<Vector3>(everyPos);
for (int i = 0; i < amount; i++)
{
int index = Random(0, possiblePos.Count); //Find a random position
SpawnObjectAt(currentPosition+possiblePos[index]));//Create a point there
possiblePos.RemoveAt(index); //Remove that position from the list
}
}
The problem is , look at this image(I can't embed image yet):
Red is where player start, green is possible spawn position in the first move.
If there are 2 point spawned at 1 and 2, player choose point1, then the possible position in the second time will be a point in the black zone, which include point2, so if I keep continue there will eventually overlap.
How can I avoid this? I'm making a mobile game so I don't want to cache every single point. Any help would be really appreciated! Thanks!
This is a small web game that have somewhat similar mechanic to what I trying to achieve: newgrounds.com/portal/view/592325/
This is an attempt here to answer, but honestly, you need to provide more information.
Depending on the language you are writing in, you can handle this differently. You may need dynamic allocation, but for now lets assume, since your idea is quite small, that you can just do one large array predefined before compile time.
I assume you know how to make an array, so create one with say, 500 length to start. If you want to 'generate' a link like they did in that game, you simply need a random function, (there is a built in library in pretty much every language I think) and you need to do a little math.
Whatever language you use will surely have a built in graphics library, or you can use a popular easy to use one. I'll just draw a picture to make this clear.
There are a number of ways you can do this mathematically as shown in the image, using angles for example, the simplest way, however, is just to follow the boxes.
If you have worked with graphics before, you know what a vector is, if not, you will need to learn. The 9 vectors presented in this image (0,1) (1,0) (1,1) etc. can be created as vector objects, or even stored as individual ints.
To make your nodes 'move' into another path, you can simply do a rand 1-9 and then correlated the result to one of 9 possible vectors, and then add them to your position vector. It is easiest to do this in array and just use the rand int as the index. In most c derived languages you do that like this:
positionVector += changeVectorArray[rand(1,9)];
You then increment your position vector by one of the 9 vectors as shown above.
The simplest way of making the 'path' is to copy the position before you add the change vector, and then store all of the changes sequentially in another 'path' array.
To show the path on screen, simply draw a line between the first and second, second and third, third and forth elements of your path array. This formula (of joining lines) is discrete mathematics if I'm not mistaken, and you can do much more complicated path shapes if you want, but you get the gist.
That should at least start you off. Without more info I can't really help you.
I could go off on a tangent describe a bunch of different ways you can make this happen differently but its probably easier if you just ask for specifics.
EDIT>>>
Continuing with this answer, yes, looking at it now, the nodes can definitely overlap. To solve this problem you could use collision detection, every time you generate a new 'position', before adding it and drawing the line you have to loop through your array like this:
boolean copy = true;
for(int i = 0; i < getLength(pathArray); i++){
if( newVector == pathArray[i]){
copy=false;
}
}
Then of course, if copy still is true, copy the new position int the pathArray. NOTE: this whole solution is sloppy as hell, and as your array gets larger, your program is going to take longer and longer to search through that loop. This may not also guarantee that the path goes in one direction, but it is likely. And note that the lines will still be able to overlap each other, even though the position vectors can't be on top of one another.
All this considered, I think it will work, the optimization is up to you. I would suggest that there is probably a much more efficient solution using a discrete formula. You can also use such a formula to make the path go in particular directions and do other more complicated things.
You could also quite easily apply constraints on your random rolls if you want to make the path go in a particular direction. But there are so many ways of doing this I can't begin to explain. You could google path-finding algorithms for that.
Good luck.

Unity 3d: rigidbody.AddForce(forceDirection.normalized * force) moving objects away from parent and joint

So, this seems to be a strange one (to me). I'm relatively new to Unity, so I'm sure this is me misunderstanding something.
I'm working on a VSEPR tutorial module in unity. VSEPR is the model by which electrons repel each other and form the shape (geometry) of atoms.
I'm simulating this by making (in this case) 4 rods (cylinder primitives) and using rigidbody.AddForce to apply an equal force against all of them. This works beautifully, as long as the force is equal. In the following image you'll see the rods are beautifully equi-distant at 109.47 degrees (actually you can "see" their attached objects, two lone pairs and two electron bonds...the rods are obscured in the atom shell.)
(BTW the atom's shell is just a sphere primitive - painted all pretty.)
HOWEVER, in the real world, the lone pairs actually exert SLIGHTLY more force...so when I add this additional force to the model...instead of just pushing the other electron rods a little farther away, it pushes the ENTIRE 4-bond structure outside the atom's shell.
THE REASON THIS SEEMS ODD IS...two things.
All the rods are children of the shell...so I thought that made them somewhat immobile compared to the shell (I.e. if they moved, the shell would move with them...which would be fine).
I have a ConfiguableJoint holding the rods to the center of the atoms shell ( 0,0,0). The x/y/z-motion is set to fixed. I thought this should keep the rods fairly immutably attached to the 0,0,0 center of the shell...but I guess not.
POINTS OF INTEREST:
The rods only push on each other, by a script attached to one rod adding force to an adjacent rod. they do not AddForce to the atom shell or anything else.
CODE:
void RepulseLike() {
if (control.disableRepulsionForce) { return; } // No force when dragging
//////////////////// DETERMINE IF FORCE APPLIED //////////////////////////////
// Scroll through each Collider that this.BondStick bumps
foreach (Collider found in Physics.OverlapSphere(transform.position, (1f))) {
// Don't repel self
if (found == this.collider) { continue; }
// Check for charged particle
if (found.gameObject.tag.IndexOf("Charge") < 0) { continue; }// No match "charge", not a charged particle
/////////////// APPLY FORCE ///////////////
// F = k(q1*q2/r^2)
// where
// k = Culombs constant which in this instance is represented by repulseChargeFactor
// r = distance
// q1 and q2 are the signed magnitudes of the charges, in this case -1 for electrons and +1 for protons
// Swap from local to global variable for other methods
other = found;
/////////////////////////////////
// Calculate pushPoints for SingleBonds
forceDirection = (other.transform.position - transform.position) * magnetism; //magnetism = 1
// F = k(q1*q2/distance^2), q1*q2 ia always one in this scenario. k is arbitrary in this scenario
force = control.repulseChargeFactor * (1 / (Mathf.Pow(distance, 2)));
found.rigidbody.AddForce(forceDirection.normalized * force);// * Time.fixedDeltaTime);
}//Foreach Collider
}//RepulseLike Method
You may want to use spheres to represent electrons, so apply forces onto them and re-orient(rotate) rods according to this sphere.
I think I found my own answer...unless someone has suggestions for a better way to handle this.
I simply reduced the mass of the electron rods, and increased the mass of the sphere. I'm not sure if this is the "best practices" solution or a kluge...so input still welcomed :-)

Removing objects from SortedSet

I've been looking at this for far too long, hopefully someone can help.
The general jist is;
I have a SortedSet of player objects (sorted by Y position), each player contains a sortedset of 'Polygon' objects which in turn contain a list of three point objects, obviously forming a triangle.
This polygon set is sorted by area of said polygon, smallest to largest.
What I want to do is iterate through the set of players and;
I. Assign the point at index[1] (the 'peak' of the triangle) from the first object in the polygon SortedSet to another variable inside the player.
II. Once the point at index[1] has been assigned to a player, I need to iterate through every player and remove any instance of a polygon that contains the current point at ElementAt(1) so no subsequent players in the 'parent' iteration can be assigned a polygon that contains that Point.
It's probably important to note that the point at polygonPoints[1] is based on the enemy position so they're consistent across all players, this is why I'm trying to use it as my 'reference' point to remove any polygon objects that contain said point.
It's a convoluted explanation but hopefully you've been able to follow.
Right now I've got the first part working, but the second part is proving to be a serious pain - I've tried remove and removewhere (using criteria that should remove everything) and the set stubbornly stays the same length no matter what I do.
For reference here's the latest iteration of code that I'm wrangling with.
List<PointF> duplicates = new List<PointF>();
foreach (Player p in playerSet)
{
//Assign lowest cost polygon to current player, then remove all polygons containing the point at p.triangleSet.ElementAt(0).polygonPoints[1] from every other player so it can't be assigned to any subsequent player.
p.currentAttackingPoint = p.triangleSet.ElementAt(0).polygonPoints[1];
//I use this method to keep track of which 'attacking points' have been assigned.
add(p.currentAttackingPoint);
//Then, in theory, I use this method to remove all polygons that contain any point in the duplicates list from every other player. Obviously this is proving to be the troublesome aspect.
remove(duplicates);
}
...
private void add(PointF i)
{
duplicates.Add(i);
}
private void remove(List<PointF> dupes)
{
foreach(PointF p in dupes)
{
foreach (Player l in playerSet)
{
//Outputs 100
textBox3.AppendText(l.triangleSet.Count.ToString() + "\r\n");
l.triangleSet.RemoveWhere(e => e.polygonPoints[1] == p);
//l.pressingTriangleSet.RemoveWhere(e => e.polygonPoints[1].X > 0); --Doesn't work either, despite it being true of every point in the set.
//Still 100
textBox3.AppendText(l.triangleSet.Count.ToString() + "\r\n");
}
}
}
Please bear in mind that the set of polygons inside each player, whilst the same length all have largely different content, they're based on the position of the player the position of the enemy and another arbitrary fact about the game state - so I can't just remove the first polygon from each player's set.
I'm thinking about converting each set to a list once they've been created because this is bizarre, it's got to be some order of operations issue that I'm overlooking.
Purely for my own sanity I knocked this out http://pastie.org/private/ikq5lhhacxxvfaoervauw and it works exactly as you'd expect it to.
EDIT - For anyone who finds this looking for a solution to a similar problem, don't waste your time using sortedsets to sort a collection of objects, you can use OrderBy to achieve the same thing with lists.
This is a longshot but sometimes comparing floats and double is a bit tricky because of precision. For example a point 1.0 might be represented as 1.0000001 because of rounding errors. It is possible that your points are not compared correctly
Try something like
e.polygonPoints[1].X - p.X < 0.00001
&& p.X - e.polygonPoints[1].X < 0.00001
&& e.polygonPoints[1].Y - p.Y < 0.00001
&& p.Y - e.polygonPoints[1].Y < 0.00001
I would have assumed that PointF equality operator would take care of that but it may not

Simple 3D AABB-Line Segment collision detection (intersection)

I just need a method to tell me whether an axis aligned bounding box in 3D intersects a line segment (not a ray) or not. I do not need the points of intersection.
The box is defined by 2 opposite corners, and the line segment by its start and end points, something like this:
Boolean intersection(Vector3 boxStart, Vector3 boxEnd, Vector3 segmentStart, Vector3 segmentEnd){...}
I've done a lot of research, and havent been able to find a code (in C# or Java hopefully) that I can understand or at least use. I need the method and not a library that will do the job...
My problem is that it needs to be 100% precise, and if the segment just touches the box (i.e. they share a single point), it has to return false. For example, if the segment is one of the edges of the box or passes though a corner, they DO NOT intersect.
Thanks
In Java, either of the intersects() methods is a candidate; but due to implementation limitations, you'd need to test it with Line2D.

Categories

Resources