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.
Related
I'm trying to find other methods of registering collisions (other than OnCollisionEnter() and OnCollisionExit()). I'm currently using Physics.OverlapBox(), but I need more information about the collision; i.e., normal, point.
I can use Physics.BoxCast(), but the problem is that it moves a box a given distance, and using maxDistance = 0f won't work.
I need a method of checking for collisions similar to Physics.OverlapBox() except in that it would also return information about all collisions in the cast.
Any help is appreciated. Thanks.
Your concern, expressed in the comment to the first answer is valid, but the bad news is that there is no simple trick to go around it. What you should be looking for is called continuous collision detection with a simplified version described in my answer on a somewhat similar matter:
Basically, for each moving object in your scene you have to calculate
moment of next collision within the fraction of the frame 0<t0<1,
then advance positions to this moment t0 within the frame, update
velocities due to collision and proceed further to the next collision
t0<t1<1, until you reach time of tn=1 (end of frame), making sure
you don't get stuck in a the middle of the frame due to rounding of
calculation or "cornered" objects. For spherical colliders, that is
usually done by using capsule vs capsule (for pairs of objects)
intersection and capsule vs box for the boundaries.
In oppose to the simple engine from the answer I'm referring to, Unity has continuous collision detection.
So you can enable continuous collisions and continuous dynamic which computationally is very expensive.
You can also try using RigidBody.SweepTest which returns the closest collision information. Notice that even though there is also RigidBody.SweepTestAll, it doesn't help much. Not only because it returns only first 128 collisions, but also because it doesn't process them as there is no reflection. For physically correct behaviour you have to do what described above - advance time till the first collision and update velocities. Either with the physics engine or by yourself. This is very costly and not many games are doing that even cheating by using simplified objects (spheres are the cheapest ones as two swept spheres are two capsules and their intersection is a relatively cheap calculation), but amount of steps, especially in the "cornered" case when objects have nowhere to go and therefore are constantly colliding could be very large and such cases happen more than one can expect.
For complex objects you unlikely can do better than SweepTest, unless you trigger it based on the simpler primitives, such as Physics.BoxCast or Physics.SphereCast. Again, even though there are Physics.BoxCastAll and Physics.SphereCastAll they are not particularly useful as only first collision is guaranteed to occur. Those xxxCastAll are the functions you wrote you were looking for, so give it a try, they might work well enough for your use case.
You can use OverlapBox and use Collider's ClosestPoint to select a single point of overlap, and use that to make your collision calculations.
Collider[] cols = Physics.OverlapBox(...);
Vector3 myPosition = transform.position; // for example
foreach (Collider col in cols) {
Vector3 closestPoint = col.ClosestPoint(myPosition);
Vector3 positionDifference = (closestPoint-myPosition);
Vector3 overlapDirection = positionDifference.normalized;
}
This overlapDirection will point in the direction away from the the position you use in ClosestPoint to the center of each colliding collider. If you want something based on the surface of your object, what you can do is use that overlap direction to place a raycast aimed at your object, to find the normal that way:
// ...
foreach (Collider col in cols) {
Vector3 closestPoint = col.ClosestPoint(myPosition);
Vector3 positionDifference = (closestPoint-myPosition);
Vector3 overlapDirection = positionDifference.normalized;
RaycastHit hit;
int layerMask = 1; // Set to something that will only hit your object
float raycastDistance = 10.0; // something greater than your object's largest radius,
// so that the ray doesn't start inside of your object
Vector3 rayStart = myPosition + overlapDirection * raycastDistance;
Vector3 rayDirection = -overlapDirection ;
if (Physics.Raycast(rayStart, rayDirection, out hit, Mathf.Infinity, layerMask)) {
Debug.Log(hit.normal);
Debug.Log(hit.position);
} else {
// The ray missed your object, somehow.
// Most likely it started inside your object
// or there is a mistake in the layerMask
}
}
I'm recreating the movement system from the DS game Dragon Quest Heroes Rocket Slime in Unity. Currently I've got pretty desirable results but it's not 100%.
One of the things that are different is that my player bounces back from walls further on long distance slings than he does in the original game. (left is my recreation, right is original game). Obviously, I can't find out how the game was made and the exact maths/values they use for slinging but one of the things I got from the game is that:
The player moves at constant velocity while slinging in a direction (no slow down, they just stop at a certain point)
Which led me to believe the game doesn't use traditional velocity mechanics so I made it so:
While stretching, the players 'pVelocity' (potentialVelocity) goes up. When they let go a point create at curPlayerPosition + pVelocity is created. I then move the player at a constant velocity to that point. My idea to fix my issue of bouncing too far is by getting the distance from the position where I slung from to the end point and making the player bounce less the larger the distance and further the smaller the distance.
Problem is, I'm not really sure how to do that last part.
Here's my code:
Vector2 reflectedVelocity = Vector2.Reflect(pVelNormalized, foundHit.normal);
playerAnimator.SetFloat("VelocityX", reflectedVelocity.x);
playerAnimator.SetFloat("VelocityY", reflectedVelocity.y);
float DistanceFromOrigin = Vector3.Distance(slingOrigin, transform.position);
print(DistanceFromOrigin);
//ToDo: reflectedVelocity is lower the higher DistanceFromOrigin is.
The other solution I thought of is that maybe my idea of how the 'velocity' works in the original game is completely wrong and if so, please tell me how it actually is/looks to be actually done
reflectedVelocity is lower the higher DistanceFromOrigin is.
So reflectedVelocity should have an inverse relationship with DistanceFromOrigin
Vector2 reflectedVelocity = Vector2.Reflect(pVelNormalized, foundHit.normal);
float DistanceFromOrigin = Vector3.Distance(slingOrigin, transform.position);
if(DistanceFromOrigin == 0) DistanceFromOrigin = 0.00001f;//to avoid division by zero
reflectedVelocity *= (SomeConstant / DistanceFromOrigin);
playerAnimator.SetFloat("VelocityX", reflectedVelocity.x);
playerAnimator.SetFloat("VelocityY", reflectedVelocity.y);
You may use a linear relationship instead of non-linear:
reflectedVelocity *= (SomeConstant - DistanceFromOrigin);
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.
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.
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