I'm calculating a vision cone for my enemy and, if the player is in it, I try to check if there is a collider between them with Physics2D.Raycast, so as to make sure the enemy does not see the player through walls.
The walls that can't be seen through are from a mesh, and have a mesh collider attached - see this pic. They are also on a specific layer, to which I'm trying to limit my Raycast.
My code is pretty straight forward. if the enemy is facing the player, and the player is close enough, fire the raycast.
if (distanceToPlayer < viewDistance && dirToPlayer == dir) {
// player is in cone, check for collision
LayerMask lm = LayerMask.NameToLayer ("WallCollisions");
RaycastHit2D hit1 = Physics2D.Raycast(transform.position, (player.transform.position - transform.position).normalized, 1000, lm.value );
RaycastHit2D hit2 = Physics2D.Linecast (transform.position, player.transform.position);
return true;
}
When breaking on the return statement to inspect the hit, it always looks like this:
{UnityEngine.RaycastHit2D}
centroid: {(0.0, 0.0)}
collider: (null)
distance: 0
fraction: 0
normal: {(0.0, 0.0)}
point: {(0.0, 0.0)}
rigidbody: (null)
transform: (null)
Non-public members:
Which means nothing was found.
Also, note that in the pic, the guard is surrounded by the mesh, meaning that regardless of direction, there should be a hit within the distance specified..
I have seen a lot of people do byteshifting to their LayerMasks, but mimicing theirs doesn't help, and frankly I don't understand how it would.
This is now solved. To help out in the future, mesh collider is 3D, so a Physics2D raycast won't find it. Using a polygon collider solved everything.
Related
I am making a 2D game and I am using Raycast to detect if the player is on the ground. No matter what Raycast always returns false. I have used Debug.DrawRay which shows that the ray is hitting the ground. I have tried solutions from several different tutorials but nothing worked. I am pretty new to Unity so hopefully someone who is more experienced knows what's wrong.
if (Physics.Raycast(transform.position, transform.TransformDirection(-Vector3.down), DistanceDown, LayerMask)){
Debug.Log("Grounded");
return true;
}
else
{
Debug.Log("Not Grounded");
return false;
}
Revised Code:
if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.down), (float)(GetComponent<Collider2D>().bounds.extents.y + Offset)))
A Raycast has a few parameters. I will explain each as there could be a few reason as to why it is not working, so possibly breaking this up can help you realize what's wrong.
Raycast(Vector3 origin, Vector3 direction, float maxDistance = Mathf.Infinity, int layerMask = DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal);
The origin is the point in space that the ray will start from. Currently, you have it as transform.position, so it will start at the position of whatever the script is on, most likely your player.
The direction is a vector that should be normalized so there is no scale to determine where from the origin the ray should be cast. Right now you are using -Vector3.down, which would result in Vector3.Up as up and down are opposites, so negating one gives the other. For a ground check, I would be using Vector3.down as the ground is generally down or below the origin of your player.
The maxDistance is the length that the ray is cast. Right now you have it as DistanceDown which I am assuming is some float. Generally, for a ground check, I would take the collider of your character, then add some offset to determine when grounded. Something along the lines of GetComponent<Collider>().bounds.extents.y + OFFSET. The bounds.extents.y is half the size of the collider and since your origin is the center of your player, this should land at the feet or base of your collider, which is why you need the offset.
The layermask is used to ignore particular layers when casting. It is rather useful when you only want to cast and hit a single layer, where in your case could just be the ground. I do not see code for this, but I will provide an example in case this is where you are going wrong. LayerMask mask = LayerMask.GetMask("GroundLayer"); and use mask as your mask. Now the ray will only hit objects on the layer GroundLayer.
The last parameter which you are not using is called queryTriggerInteraction. Raycast by default do not hit triggers, but with this parameter, you can override it and tell it to hit triggers.
Read over each of these sections and check to make sure what you are doing matches what I am describing. If you are still unable to figure out your issues based on what I have written, I would need to know a bit more about your situation to help out.
I'm trying to make a very simple race game with spheres, however, I face many problems.
First of all, I'm trying to make a very simple AI system for opponents.
The problem I have here is that I want opponents to detect obstacles and avoid them using Raycast but only a certain type of obstacle , a simple cube is detected.
I've created a simple sphere as opponent and wrote a script so it can move and detect obstacles
Here is update function:
void FixedUpdate()
{
transform.Translate(Vector3.forward * Time.deltaTime * speed);
if (Physics.Raycast(transform.position, transform.forward, 100.0f))
print("There is something in front of the object!");
}
The message is printed only when there is a cube forward and it does not detect any other obstacles. What can be so wrong? Also, is there any idea how to move left or right when opponent raycast an obstacle?
obstacle that is detected
hierarchy
cube01 that is child of obstacle2 that is not detected
Only collider components are detected using raycast, make sure you have an appropriate collider (size of the collider does not necesarily match size of the mesh that gets rendered). Normally also layers on which objects are are important but syntax you are using is not checking for layer mask anyway
Unity Physics Best Practices (As in https://unity3d.com/pt/learn/tutorials/topics/physics/physics-best-practices) recommends that we don't use Raycasts in FixedUpdate, as it is heavy to process and may not always work.
There is also some tips about Matrix Layers that will help you improve performance and avoid such bugs.
Good luck!
You can use Debug.DrawLine() or Debug.DrawLine() to debug the line and see if it cross the obstacles or not.
Here is the documentation for them
DrawRay
DrawLine
For moving right and left I think you can add something like this
void FixedUpdate()
{
Vector3 dir = Vector3.forward * Time.deltaTime * speed;
if (Physics.Raycast(transform.position, transform.forward, 100.0f))
{
print("There is something in front of the object!");
dir += Vector3.Right * horizontalSpeed * Time.deltaTime;
}
}
You might also consider ray casting two rays to detect the direction to lean to if it will be the left or the right.
I am using Physics.Linecast in my Update() method to determine the distance to the next way point in a racing game. The code casts a line from the players transform position, and targets the nearest point on the mesh of the next way point (using ClosestPointOnBounds).
This works perfectly for the first two way points, however at some point between the 2nd and 3rd one the functionality ceases, (null reference? but....how!?).
This has got me completely stumped. The line cast correctly targets way point 3 for a while before randomly 'losing sight' of it...I have no idea why, especially as it does work for a while!
I've made my debug draw lines last for 5 seconds after they're cast. So, in the image below you'll see:
lines cast towards waypoint 2 successfully
lines beginning to be cast towards waypoint 3 successfully
suddenly lines no longer being cast towards waypoint 3
// Update is called once per frame
void Update()
{
Vector3 raycastStart = new Vector3 (transform.position.x, transform.position.y + 1, transform.position.z);
// NOTE: nextWayPoint is a gameObject, populated by referencing the relevant index position in the waypoints array
if (nextWayPoint != null)
{
// Cast a ray from the player's position towards the mesh collider of the next waypoint...
RaycastHit hit;
Physics.Linecast(raycastStart, nextWayPoint.GetComponent<MeshCollider>().ClosestPointOnBounds(transform.position) - transform.position, out hit, waypointsLayer);
// Render this line so we can see it...
Debug.DrawLine(raycastStart, hit.point, Color.red, 5f);
// Populate the distance variable with the length of the ray
disToNextWayPoint = hit.distance;
}
Any ideas, thoughts or general nudges in the right direction would certainly save me from insanity at this point!
If you want to get distance between 2 points - best way it's use Vector3.Distance (I seen your comment, know than you found this case).
As for
(null reference? but....how!?).
physics cast functions can return null hit if start point of cast was in collider - check your case for this.
Is it possible to have a script in a GameEngine script and have it check for collisions between two remote objects that are not connected to the GameEngine script, via Update() or OnTriggerStay()/OnColliderStay()?
My plan for this script is to detect situations such as putting out room that is on fire. My original plan was to have a collider around this room checking for fire particles, and if there are no more particles, the fire is out. If you have a better suggestion, please let me know.
I assume you are referring with linear motion. If so Ray Casting is the solution. Ray Casting is forming a line or vector from a specific point to another point in a 3D plane. The purpose of the ray (vector) is to determine if it intersects with any colliders or other game objects.
It can be simply used like,
void Update() {
Vector3 fwd = transform.TransformDirection(Vector3.forward);
// parameters are origin, direction and length of the ray.
if (Physics.Raycast(transform.position, fwd, 10)){
print("There is something in front of the object!");
}
}
You can find more references and tutorials on the internet. Try Unity official tutorial on Raycasting
Basically if there's collision infront of the player it shouldn't go forward anymore, but the player goes forward no matter what.
if (Input.GetAxis("Vertical") > 0 &&
!(Physics.Raycast(PlayerObject.transform.position,
Vector3.forward,
PlayerObject.collider.collider.bounds.size.z * 1.05f)))
{
PlayerObject.transform.Translate(Vector3.forward * MoveSpeed * Time.deltaTime);
}
The problem here is that you're performing a Raycast using physics, so anything not enabled for physics won't show up. You're also using Vector3.Forward instead of PlayerObject.transform.forward, so you're getting the global forward (0,0,1) instead of the player's actual vector. Furthermore, but using the player's position, it won't notice, say, a low or high-hanging wall.
Note there is also the built-in CharacterController component to handle this sort of behaviour, as well as intelligently handling Ramps and Steps.