I am using the navmesh/agent on the player as an assistance autopathing function where the agent is disabled at all times unless the user clicks a point on the floor to walk towards. Then the agent will be disabled again once the destination is reached.
I need a way to check if the player is currently on the navmesh, within a tolerable threshold without the navmeshagent being enabled. Or, if there is a way to remove the player-binding 'effect' of the navmeshagent without disabling it, as I could use that to solve my problem too.
I guess in pseudocode, this is what i'm trying to accomplish with navmeshagent disabled:
if (!agent.isOnNavMesh){ DisableClickSelection();}
I was thinking of the possibility of comparing the Y transform of the player and the navmesh to get a height difference and using that to determine if the player is on the navmesh but i don't know how to go about getting the Y transform of the navmesh at a specific X and Z point. Maybe i can use a raycast? I'm not sure the best way. Like i said, if there is a way to remove the player-binding 'effect' of the agent on the player but keep the agent enabled I would be able to work with that too.
You should be able to do this through use of NavMesh.SamplePosition(). This method basically searches in a spherical radius around a given position for the nearest point on the navmesh. All you need to do is verify that the returned position is vertically in line with the player position, and is on/above it.
Here's an idea on how you might apply this in code:
// Don't set this too high, or NavMesh.SamplePosition() may slow down
float onMeshThreshold = 3;
public bool IsAgentOnNavMesh(GameObject agentObject)
{
Vector3 agentPosition = agentObject.transform.position;
NavMeshHit hit;
// Check for nearest point on navmesh to agent, within onMeshThreshold
if (NavMesh.SamplePosition(agentPosition, out hit, onMeshThreshold, NavMesh.AllAreas))
{
// Check if the positions are vertically aligned
if (Mathf.Approximately(agentPosition.x, hit.position.x)
&& Mathf.Approximately(agentPosition.z, hit.position.z))
{
// Lastly, check if object is below navmesh
return agentPosition.y >= hit.position.y;
}
}
return false;
}
So for using it with your example, you'd write:
if (!IsAgentOnNavMesh(agent.gameObject))
{
DisableClickSelection();
}
Hope this helps! Let me know if you have any questions.
Related
I have 3d cube which have 3d box collider.
I want to write jump system for cube with collision, but without physics (without gravity, without rotation, etc) (Like 3d platformer)
You can take advantage of the Physics.OverlapBox API to check for collisions before committing to move your object.
Your code will look something like this:
Vector3 nextPosition = transform.position;
/*
You do your normal movement code, but apply
it to nextPosition instead of transform.position
*/
Collider[] hitColliders = Physics.OverlapBox(nextPosition, transform.localScale/2);
if(hitColliders.Length == 0){
transform.position = nextPosition;
}
Note that the object itself shouldn't have a collider, otherwise it will detect itself. You also shouldn't move in big steps as this method doesn't take colliders on the path into account.
There is a great way of doing this. You will still need physics.
The Steps
Have a rigidbody on your player.
There should be a dropdown menu called spmethiing like "constraints". Find it.
Then Set all rotation constraints in this menu to true.
However, I don't understand why you wouldn't want gravity. Don't all platformers have gravity? (There must be some force to pull your player down when they jump). Anyways, if you don't want gravity, then you must uncheck Use Gravity variable.
**Notes: **
To control your player, you must use AddForce(). If you have any questions about adding force, let me know in the comments.
:) Thanks.
I don't want my player to be able to walk off ledges. I did this by shooting a single raycast downwards in front of the player, and if ground is NOT hit, then ignore input.
However this is jarring, especially if you diagonally walk along an edge you just completely stop, rather than 'slide' along it.
So I thought I could add two raycasts, one per side to detect which side the ledge is, then allow movement (or steer) the player as applicable.
The problem is I'm not sure how to proceed from here. I'm using a character controller for movement, my current code is like:
velocityXZ = velocity;
velocityXZ.y = 0; // we deal with gravity elsewhere
velocityXZ = inputDir * playerSpeed;
if (facingDropLeft || facingDropRight) {
velocityXZ.x = 0;
velocityXZ.z = 0;
}
velocity = new Vector3(velocityXZ.x, velocity.y, velocityXZ.z);
// handle gravity
charController.Move(velocity * Time.deltaTime);
Could anyone offer some insights into what direction to look into, or methods I will need?
I think that if you want to accomplish an enjoyable result you should use invisible walls. I think that probuilder can help you.
This is the approach I would have with this type of problem.
Use boxes to make wall than turn off the mesh renderers this will make invisible walls
I'm really struggling at something that I had imagined to be pretty simple: I have two meshes (fighters) instantiated from the same prefab and I want them to not overlap. The prefab is set to have a box collider. Ideally, I let the animator handles the position. I've tried several approaches:
Configure the animators to "Animate Physics". No collision is detected
Uncheck root motion and move characters using game object's transform's position. No collision is detected
Uncheck root motion, add a rigid body to each character and move character using game object's position. Collision is detected, but the reaction is governed by physics which makes it look unnatural since all I want is to just avoid the characters to pass through each other i.e. just want characters to be pushed back until they don't collide anymore
I really would like to avoid going with a manual approach to this. How would I prevent this overlapping?
Edit: isTrigger is unset on box colliders
A rigidbody is highly configurable and you want a limited set of the physics functionality it can offer. Go with the third solution you enumerated and freeze the rotation in the rigidbody. See the rigidbody page for more details.
As a side note, you might consider using a capsule collider, so that characters "slide" around each other more easily.
Although it's not necessarily preventing overlap, you could create a perimeter for characters. It would be rather simple depending on what you're doing.
If you want to set up a distance perimeter to prevent characters from getting too close to one another you could use the following equation
Vector3 focalPOne = focalPointOfCharacterOne();
Vector3 focalPTwo = focalPointOfCharacterTwo();
float dx = focalPOne.x - focalPTwo.x;
float dy = focalPOne.y - focalPTwo.y;
float dz = focalPOne.z - focalPTwo.z;
// calc distance along z plane
if (Math.Sqrt((dx * dx) + (dy * dy)) < minDistance)
{
// person is too close to the others head.
if (Math.abs(dz) < minHeightAbove)
{
// go to previous position.
}
// if there is no vertical elivation, you can go
// straight to moving them back to previous position
}
enter code here
If this wasn't what you're looking for, please let me know so I can edit or remove it.
The perimeter created would look something like this.
In order to have your characters with their respective box colliders register a collision, both need to have a RigidBody component. That's the only way to make sure the characters do not go through each other considering they share a prefab.
You don't have to handle the separation/rejection of the characters via the physics engine though. Simply make the box collider trigger, and then handle the separation via code in a C# script using onTiggerEnter.
There's a collision detection matrix that could help you, found here: https://docs.unity3d.com/Manual/CollidersOverview.html. Also this image summarizes it beautifully (in the documentation it's in two separate tables for some reason):
I'm working to let a character push objects around. The problem is that as soon as he touches an object he starts moving it, even when the touch was accidental and the character isn't facing the direction of the object.
What I want to do is get the direction the object when a collision happens, and if the camara is really facing that direction allow the player to move it.
Right now I only managed to get the direction of the object, but I don't know how to compare that with the direction of the camera.
This is what I'm trying now:
void OnCollisionEnter(Collision col) {
float maxOffset = 1f;
if (col.gameObject.name == "Sol") {
// Calculate object direction
Vector3 direction = (col.transform.position - transform.position).normalized;
// Check the offset with the camera rotation (this doesn't work)
Vector3 offset = direccion - Camera.main.transform.rotation.eulerAngles.normalized;
if(offset.x + offset.y + offset.z < maxOffset) {
// Move the object
}
}
You can try to achieve this in several different ways. And it is a bit dependent on how precise you mean facing the box.
You can get events when an object is visible within a certain camera, and when it enters or leaves using the following functions. With these commands from the moment the box gets rendered with that camera (so even if just a edge is visible) your collision will trigger.
OnWillRenderObject, Renderer.isVisible Renderer.OnBecameVisible, OnBecameInvisible
Or you could attempt to calculate whether and objects bounding box falls within the camera's view frustum, there for you could use the following geometry commands
GeometryUtility.CalculateFrustumPlanes, GeometryUtility.TestPlanesAABB
Or if you rather have a very precise facing you could also go for a Physics.Raycast So then you will only trigger the event when the ray is hitting the object.
Hope this helps ya out.
Take a compass obj and align it to ur device sorry object than when you move it you can always know where it points to.
theoretically it should work but maybe your object just moves around because of a bug in your simulator motion engine.
I'm developing a game that you drag and drop objects into boxes and I have no idea what's the best and most efficient way to detect whether my objects are in a box.
I'm well aware of colliders and I'm using BoxColliders and triggers to find out whether my object is touching a box but I want to detect the moment when my object (which we can assume to be sphere for sake of simplicity but later will be a mesh) is completely inside my box trigger/collider.
I read about "Contains" method of colliders but IIRC they just check if one single point inside that collider but I'm interested to know if the whole object is inside the collider.
Thanks in advance folks.
Short answer: If you want 100% accuracy, your algorithm will never be better than O(|V|) (worst case) where V = {all vertices in mesh}, meaning you'd run .Collides() over every vertex and break if you find one that is outside your container.
Longer answer: Several methods exist to subdivide mesh surfaces, examples include: KD-Trees, OcTrees. These are beyond the scope of an SO answer to provide a full implementation, I suggest you see the wiki pages for details.
You could use these methods to divide your mesh up in to smaller sets of vertices. To speed up your algorithm you would start at the root of your subdivision tree and test if that node is contained in your container box. Keep working through the tree until you find a node that is not contained by your box. This will allow your "contains" test to fail faster but ultimately you'll wind up testing every vertex if your box contains your mesh.
Caveat: This solution does not work if your mesh is animated. In that case your best bet is to use the bounds around things like arms, feet, legs, etc and use that information to cull your Contains() tests. Again, you'll end up having to test every vertex if the mesh is fully inside your box.
All boxes have a boxCollider. If an object touches the second box, the object must be inside the first box.
This is not a good solution but, maybe it will be usefull.
Use Renderer.bounds property to get bounding box of your object.
Depending on what object you have and how accurate you want to check it to be inside a collider you might than use one of the simple ways to determine that.
for a more refined and cleaner approach. this would be perfect for what you are doing .
you can check the distance between the object you are dragging and the box .
the box has a x,y,z value which represents its position in space.
so when you drag your gameobject it may be just 0.2 on the x,y, or z away from the centere of your box. so just use this method to calculate the distance between your dragged object and the box.
var other :Transform;
function Update()
{
var dist = Vector3.Distance(other.position, transform.position);
// while dragging
if(dist <10)// 10 being on all 3 axiz .
{
//dragged object.position = box position
}
if (dist == 0)
{
print("i am in the centre of the box");
}
}
thus your gameobject will be in the box .
The box within a box solution above is a good option, but if that won't work (due to variably sized/shaped objects) you might be able to accomplish something with Physics.Raycast or Collider.Raycast. I had a similar problem where I needed to test if arbitrary points were contained inside colliders (many of which were unusual blobby concave objects).
The basic idea is a "bed of nails" approach where I cast rays toward the point from multiple directions. If I hit the outside collider on all of the rays, then I can be pretty confident that the point is contained inside the collider (but still not completely certain). Here's a pic:
In this picture, we're trying to see if the blue point is inside the yellow collider. The GREEN arrows represent successful raycasts (the yellow collider is hit), and the PINK one is unsuccessful (the yellow collider is NOT hit).
Here is a code snippet illustrating this:
public static class CollisionUtils {
private static readonly Vector3[] raycastDirections;
// These are the directions that we shoot rays from to check the collider.
static UltrasoundCollisionUtils() {
raycastDirections = new Vector3[5];
raycastDirections[0] = new Vector3(0,1,0);
raycastDirections[1] = new Vector3(0,-1,-0);
raycastDirections[2] = new Vector3(0,0,1);
raycastDirections[3] = new Vector3(-1.41f, 0, -0.5f);
raycastDirections[4] = new Vector3(1.41f, 0, -0.5f);
}
public static bool IsContained (Vector3 targetPoint, Collider collider) {
// A quick check - if the bounds doesn't contain targetPoint, then it definitely can't be contained in the collider
if (!collider.bounds.Contains(targetPoint)) {
return false;
}
// The "100f * direction" is a magic number so that we
// start far enough from the point.
foreach (Vector3 direction in raycastDirections) {
Ray ray = new Ray(targetPoint - 100f * direction, direction);
RaycastHit dummyHit = new RaycastHit();
// dummyHit because collider.Raycast requires a RaycastHit
if (!collider.Raycast(ray, out dummyHit, 100f)) {
return false;
}
}
return true;
}
}
One way you could adapt this algorithm is rather than use Collider.Raycast, do a Physics.Raycast. If the rays hit anything other than your collider, then you know that your target object isn't entirely in the collider.
Add a BoxCollider which surrounds the whole of the object you are testing for and check its bounds min and max are contained within the BoxCollider its entering ... this may not suit for complex mesh objects but you may be able to get away with it and its cheap
Simply,
You can place a box Collider at the bottom of your container.. If your sphere or whatever object touches that then it is completely inside the container..
There is one method though....not that accurate but will work :
Make four empties with collider(Circle / Box) like four wheels of a car and make the object parent to these empty objects with collider.
Then assign every collider a bool whether it is touched or not....
Count the number of colliders touched along with the actual mesh collider...Roughly estimates the orientation of the overlap. If it is 2 that means touched or overlaped from sideways.
Ex: If two collider + the mesh collider is colliding which means it is overlapped sideways...
I solved it using bounds.contains(vector 3 point).
I placed a single point on the object to be detected, and once it was inside the checking limits, I was able to acknowledge its presence.
public class PointInsideBound : MonoBehaviour
{
public Collider BoundCollider;
private Transform pointToCheck;
private void OnTriggerEnter(Collider other)
{
if (pointToCheck == null)
{
//There is a point on top of the collided object. Getting the reference here.
pointToCheck = other.transform.GetChild(0);
}
}
private void OnTriggerStay(Collider other)
{
if (pointToCheck == null)
{
return;
}
if (BoundCollider.bounds.Contains(pointToCheck.position))
{
Debug.Log(" Point Inside the boxcollider");
BoundCollider.enabled = false;
}
}
}
You should use a collider. This is description. http://docs.unity3d.com/Documentation/ScriptReference/Collider.html