We have:
Vector3 A - lower near left point
Vector3 B - highest far right point
We need to check if an arbitrary Vector3 C is in this rectangle
Unity has a class for that
And a method
Bounds b = new Bounds();
b.SetMinMax(A,B);
b.Contains(C)
Hello!
I used this:
// Vector3 Min = ...;
// Vector3 Max = ...;
Bounds bounds = new Bounds();
bounds.SetMinMax(Min, Max);
But for some reason it doesn’t always work
Weird, it should.
Please post the case with specific values when it is not working and we can take a look
I’m writing a plugin for a game where the map rotates all the time and rooms change places. I’m working with local coordinates relative to the room and turning them into global coordinates. Could this be the cause of the problem?
Room room = Exiled.API.Features.Room.Get(Room);
Bounds bounds = new Bounds();
bounds.SetMinMax(API.GetRelativePosition(Min, room), API.GetRelativePosition(Max, room));
Room room = Exiled.API.Features.Room.Get(Room);
Log.Info($"Room: {room.Type}");
Log.Info($"Min local coordinates: {Min}");
Log.Info($"Max local coordinates: {Max}");
Bounds bounds = new Bounds();
Vector3 min = API.GetRelativePosition(Min, room);
Vector3 max = API.GetRelativePosition(Max, room);
Log.Info($"Min global coordinates: {min}");
Log.Info($"Max global coordinates: {max}");
bounds.SetMinMax(min, max);
Room room = Exiled.API.Features.Room.Get(Room);
Log.Info($"Room: {room.Type}");
Log.Info($"Min local coordinates: {Min}");
Log.Info($"Max local coordinates: {Max}");
Bounds bounds = new Bounds();
Vector3 min = API.GetRelativePosition(Min, room);
Vector3 max = API.GetRelativePosition(Max, room);
Primitive primitive = Primitive.Create(min);
primitive.Color = Color.yellow; // Create cube with yellow color
Primitive.Create(max); // Create cube with white color
Log.Info($"Min global coordinates: {min}");
Log.Info($"Max global coordinates: {max}");
bounds.SetMinMax(min, max);
Log.Info(bounds.SqrDistance(player.Position));
When it doesn’t work it’s logs when staying at this rectangle: ~3.260395 and other (not 0)
When it works: 0
Bounds doesn’t have rotation, so it’s better that A,B, and C are in local coordinates
Bounds bounds = new Bounds();
bounds.SetMinMax(Min, Max);
...
Log.Info(bounds.Contains(room.transform.InverseTransformPoint(player.Position));
Doesn’t works
Bounds: Center: (-1.77, 2.31, -0.04), Extents: (1.69, 1.81, -1.68)
Player position at local room: (-2.47, 0.96, -0.01)
I don’t know what exactly your problem is.
You can play around with this toy example to see how bounds and contains work
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BoundsTest : MonoBehaviour
{
public Transform a;
public Transform b;
public Transform c;
public Color inside = Color.green;
public Color outside = Color.red;
public bool wired = true;
public Bounds bounds;
public bool isInside;
private void Update()
{
Vector3 localMin = transform.InverseTransformPoint(a.position);
Vector3 localMax = transform.InverseTransformPoint(b.position);
bounds.SetMinMax(localMin, localMax);
Vector3 localPosition = transform.InverseTransformPoint(c.position);
isInside = bounds.Contains(localPosition);
}
private void OnDrawGizmos()
{
Matrix4x4 cacheMatrix = Gizmos.matrix;
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.color = isInside ? inside : outside;
if (wired)
{
Gizmos.DrawWireCube(bounds.center, bounds.size);
}
else
{
Gizmos.DrawCube(bounds.center, bounds.size);
}
Gizmos.matrix = cacheMatrix;
}
}
I’ve drawn the bounds now. Very strange, but they look the same in all cases (right and wrong)
Well, I do The extent of the bounds must NOT be negative. The result would be a negative volume. The bounds is centered around the center point and the extents defines half the size of the volume.
As we can see in the min / max coordinates, the max coordinate has a smaller z position than the min coordinate. Not sure where you got your two points from, but if you’re not sure which parts are “min” and which are “max”, you could do either
Vector3 min = Vector3.Min(A, B);
Vector3 max =Vector3.Max(A,B);
which ensures that your min vector is actually the minimum in all 3 axis and max is actually max. Or you could do something like this:
Bounds bounds = new Bounds(A, Vector3.zero);
bounds.Encapsulate(B);
This does essentially the same as the first solution. At least internally.
Why not just use trigger colliders?
There are some situations where polling a condition is more convenient or less fiddly than setting up an event-oriented mechanism to track the condition. Bounds.Contains is polling, OnTriggerEnter is an event. OnTriggerEnter has a lot of cases where it’s not reliable, since it is dealing with state in both Unity-world and PhysX-world. For example, enter a trigger collider, change parents [stand on moving platform, etc.], and never get the OnTriggerExit.
Oops! I could have sworn you could poll rectangle colliders, but no. I think what I was actually thinking of is OverlapBox.
https://docs.unity3d.com/ScriptReference/Physics.OverlapBox.html
The advantage here is that you could supply the orientation and Unity will do all of the fiddly rotatey stuff.
You can do (myCollider.ClosestPoint(v3) == v3) and hope that matrix roundoff error is within the sloppy == tolerance. Sometimes I dream about being given a month on the Unity team just to add a lot more orthogonal API consistency to the physics classes, and a Collider.ContainsPoint() would be high on the list.
One cute way to do this for an arbitrary rectangular prism is to check the point against all 6 of your rectangular prism’s planes with Unity - Scripting API: Plane.GetSide .
If the point is on the positive side (or negative side, depending on how you construct the planes in your code) of all 6 planes, it is inside the prism.