Check if object is within FOV (field of view) in Unity 3d - c#

I am trying to create a game in Unity 3d where NPC's move and look around and check which objects are inside their view angle. To calculate what objects are inside the field of view angle i use unity's build in function Vector3.Angle, see:
// Get current position of the Robot.
var robotObjectPosition = currentTransform.parent.position;
// Get the direction to the object
var directionToObject = (objectToFind.transform.position - robotObjectPosition) / 2;
// Calculate the angle to the object and check if it's inside our viewAngle.
var isInsideAngle = Vector3.Angle(currentTransform.forward, directionToObject) < viewAngle / 2;
Now i have a weird problem where if i move to close to an object (a plane in the shape of a goal line in this case) the above check returns false, even though the object is still in front of it and the goal line is still clearly inside of his vision. I made a screenshot to visualize my problem.
In the first image the goal line is inside of his vision, but in the second ( i moved him a small step forward) the above function returns false, even though i can clearly see he is still in front of the object. I am geussing it has something to do with the width of the other object, (because i divide the viewangle by 2) but i havent been able to find a fix for it.
Mainly i would like to understand what is happening here.

I tried your code myself and got to the same result. The problem is you need to remember that the angle you are calculating has two important characteristics:
1. it checks for "objectToFind"'s center/pivot point. once the center is not inside the angle you will get false.
2. it is in 3D and not 2D so if you get closer the angle between the robot and the object (on the Y axis) does not include the center of the objectToFind.
As a solution I would cast a ray instead of checking if it is inside the angle range.
Hope that helps

Related

Test if one GameObject is above another with any shape colliders

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.

How to move an object back based on its rotation and bounds in Unity?

Alright, so I have an object and it also has a bounds. I also have an anchorPoint, if you will, which is where the object is positioned and I have a boundary Vector3 position that I don't want the object to pass.
EDIT:
I will clarify my example in the following scenario:
Alright, lets take a rectangular prism since it is a perfect example. Obviously, when you are looking at it long ways it won't be as close to you if you rotate it 90 degrees on the y to where you are looking at the end of the object. Even though the object didn't change positions and is essentially in the same position, just rotated. I want to check if during rotation did the object pass a certain point called boundary. If it is past the point then I want to move the object forward in its z-direction so that the end of the object intersects the boundary position.
Assume that the object's initial position stays the same in the image below.
END EDIT
The problem comes into play when I want to rotate the object. How do I detect if part of the object has moved in front of this boundary position; that is not to say that the center of the object has passed this position but that part of the object has. I figured the best way to do this would be using bounds but, I'm not quite sure how to incorporate the rotation in with the bounds.
I haven't really had any luck with anything I have written so far. Any help on the matter would be greatly appreciated.
Assuming the simplest case, where you have a rectangular, axis aligned bounding box, then you have 8 points that make up your bounding box (the 8 corners).
The first check you want to make is to see if your anchor point is on the "wrong" side of your boundary plane. This is a simple vector dot product check.
Vector3 anchorDir = anchorPoint - boundaryPoint;
bool wrongSide = Vector3.Dot(anchorDir, boundaryDir) <= 0;
As long as your anchor point is on the right side of the boundary, you can move on to check each of the 8 bounding box points in the exact same way. However, you will need to make sure that your bounding points and your boundaryPoint are in the same coordinate system (world vs local) (this actually applies to your anchorPoint too). You could translate all the points to World space using Transform.TransformPoint (https://docs.unity3d.com/ScriptReference/Transform.TransformPoint.html) but it would be more efficient to translate the boundaryPoint to your bounding box's local coordinate system using InverseTransformPoint (https://docs.unity3d.com/ScriptReference/Transform.InverseTransformPoint.html).
Transform boundingBox = ...
Vector3 localBoundaryPoint = boundingBox.InverseTransformPoint(worldBoundaryPoint);
Vector3 localBoundaryDir = boundingBox.InverseTransformDirection(boundaryDirection);
foreach(Vector3 pt in boundingBoxPoints)
{
Vector3 pointDir = pt- localBoundaryPoint;
bool ptWrongSide = Vector3.Dot(pointDir , localBoundaryDir ) <= 0;
//How you handle that is up to you (return false, set a local flag and break, whatever)
}
EDIT:
If you want to know how far you are past the boundary so that you can shift your box back, you will need to handle that a little differently. You will have to check all the points and not just return when you find the first violator because you can't be sure which one is furthest.
float maxDistance = 0;
foreach(Vector3 pt in boundingBoxPoints)
{
Vector3 pointDir = pt- localBoundaryPoint;
//note the negative sign
float distance = -Vector3.Dot(pointDir, localBoundaryDir );
if (distance > maxDistance) maxDistance = distance;
}
if (maxDistance> 0)
{
//Shift your object by maxDistance amount along the localBoundaryDir;
}
Note that for your Dot Product to return the correct distance, localBoundaryDir needs to be a unit vector.
EDIT 2
Easier to describe the Boundary Point issue here than in the comments.
Your question doesn't explain how you define a Boundary Point, but let's assume that it's just a GameObject. You can implement a script on it but technically you don't even need that.
GameObject boundaryObject = ...
Vector3 boundaryPoint = boundaryObject.transform.position;
Vector3 boundaryDir = boundaryObject.transform.forward;
transform.forward always points along the z-axis, the blue arrow. So if, in the Unity editor you are constructing your scene, the blue arrow points along your plane's normal. Your "boundary line" extends to the right and left (x axis, red arrow) and up and down (y axis, green arrow).

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 :-)

Rotating a polygon is not working in C#

I'm trying to rotate a polygon in windows forms using C# and below is code written .
Please tell me what wrong in the code, there is no output on windows form.
Before and after rotation polygons not visible.
public void RotatePolygon(PaintEventArgs e)
{
pp[0] = new Point(624, 414);
pp[1] = new Point(614, 416);
pp[2] = new Point(626, 404);
e.Graphics.DrawPolygon(myPen2, pp);
System.Drawing.Drawing2D.Matrix myMatrix1 = new System.Drawing.Drawing2D.Matrix();
myMatrix.Rotate(45, MatrixOrder.Append);
myMatrix.TransformPoints(pp);
e.Graphics.Transform = myMatrix1;
e.Graphics.DrawPolygon(myPen, pp);
}
Thanks
You code does not compile if left unmodified. There are two matrices used - one declared in your method (myMatrix1) attached to the graphics object and one declared outside your method (myMatrix without the 1) used to explicitly transform the point array.
I tried the code with the required changes and it works flawless - I used myMatrix1 for both transformations and the effective rotation angle was, as expected, twice the one specified. So I guess you are using two transformation that cancel if the transformed points end where they began.
There could be this problems:
[1] your pens have no color/thickness (where do you define them?)
[2] your polygone is to big, so you only see the inside of it but not the border. --> Test Graphics.FillPolygon-Methode so you will see if [2] is right
You're both transforming the points and changing the transform matrix for the Graphics object - you need to do one or the other.
You also need to think about the fact that the rotate is going to be happing about (0,0) rather than about some part of the object - you may well need a translate in there too.
Bear in mind that TransformPoints just manipulates some numbers in an array - which you can easily inspect with the debugger - this will be a more effective technique than displaying an invisible object and wondering where it went.
Starting with a much smaller rotation angle (10 deg, perhaps?) may also help with the problem of losing the object - it will be easier to work out what's happening if you haven't moved so far.

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