Linecast losing its target randomly - c#

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.

Related

Unity Physics.Raycast does not detect an object when it should

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.

How to get collision data from Physics.OverlapBox()?

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
}
}

Mega-man 2 Camera help (Unity)

So I have tried several ways to fix a Mega-man 2 camera without actually figuring out a good way of doing it. So I would love if someone could give me some tips for I could solve this issue.
The problem: As I have written I want a Mega-man 2 camera (2D). The thing from it that I have not been able to solve is how to move it in Y like they do in Mega-man 2. What happens is that the camera does not move at all in Y, unless you go outside "camera bounds" and then it "freezes" the player and moves the camera down, see this video: https://youtu.be/NP697v8WtkU?t=5m2s (Hopefully it starts where the camera does this, otherwise go to 5:02).
But I can't get this to work in any good ways for me. What I have tried so far is, I have triggers at the top and bottom of the camera and when the player hit's one of them I start a coroutine where the camera should Lerp, however, it moves too much, so if I need to move it twice in the Y it would be all messed up. (My code is at the bottom but as I said, I need some help how I can fix it)
So please, if anyone have any idea how I can solve this, please let me know. I've been trying for several days without getting it to work perfectly.
public void ChangeY()
{
StartCoroutine(MoveCameraDown());
}
IEnumerator MoveCameraDown()
{
Vector3 CurrentCameraPos = Camera.main.transform.position;
Vector3 NewCameraPos = new Vector3(Camera.main.transform.position.x, Camera.main.transform.position.y - 5.6f, Camera.main.transform.position.z);
for (float t = 0.0f; t < 1; t += Time.deltaTime / 1.5f /* TIME IT SHOULD TAKE TO MOVE THE CAMERA DOWN */)
{
Camera.main.transform.position = Vector3.Lerp(CurrentCameraPos, NewCameraPos, t);
yield return null;
}
}
And as I have already written, I call the ChangeY() when I hit one of the triggers. This is just my testing case if you are wondering why some numbers are hardcoded and all that. But as I said, I have not been able to fix this so if anyone can help me I would really appreciate it.
It is actually easy to do. You asked for some tips and I will give you one.
Assuming you have already built your 2D world just like in the video you posted. There are many ways to solve this problem but I show you the way I consider to be the easiest. I have 5 steps for you to follow.
You need a value you can use each time to move down. Based on your code, you used -5.6f each time. The go down value should never be the same each time when the player touches the bottom trigger. It should be generated during run time.
This will be addressed in step 3.
1) If the player triggers the camera bottom trigger, first thing you should do is to stop the player, by disabling gravity then setting the rigidbody velocity and torque to zero.
For example inside your OnTriggerEnter function attched to the camera:
void OnTriggerEnter(Collider other) {
Rigidbody rgbd;
rgbd= other.gameObject.GetComponent<Rigidbody> ();
rgbd.useGravity = false;
rgbd.velocity = Vector3.zero;
rgbd.angularVelocity = Vector3.zero;
}
2) Immediately throw Ray cast downward(-y) using Physics.Raycast.
The origin position of the raycast should be the position of the player. For example,
GameObject gameOBjectHitFromRaycast;//Store Game Object detected from the raycast
RaycastHit hit;
if (Physics.Raycast(player.transform.position, Vector3.down, out hit)){
gameOBjectHitFromRaycast = hit.collider.gameObject;
}
The information of the object below the player will be stored in RaycastHit.
Use RaycastHit to get the information about the object that the player is about to land on.
3) After getting the information of that object the player is about to land on, get the y-axis position of that object then get the y-axis position of the player. Add them together then divide the result by 2. That is your new y position. The new position you need to move the camera to. For example,
Vector3 cameraNewPos = new Vector3(player.transform.position.x,(gameOBjectHitFromRaycast.transform.position.y+
camera.transform.position.y)/2,player.transform.position.z);
Basically, what we did here is to find the middle of the player's current position and the the landing position so that we can position the camera right in the middle of it. That should be do it but you can also subtract another number from the y-xis if that is not enough.
4) Use a coroutine function to move the camera smoothly to the new y axis position. Inside the corutine function, there should be public bool variable that will be used to determine if this function is done running(if camera is done moving).
Replace this function with the current camera move function you have
bool cameraMoving = false; //This variable can be used to determine when the camera is done moving
private IEnumerator MoveCameraDown (Vector3 toLocationPos, float timeToFinish)
{
float runTime = 0; //Dont change
float timer = 0; //Dont change
float tempDeltaTime;
cameraMoving = true;
//Will keep looping until camera is done moving
while (timer<timeToFinish) {
//Translate camera
Camera.main.transform.position = Vector3.Lerp (Camera.main.transform.position, toLocationPos, runTime);
tempDeltaTime = Time.deltaTime; //Get delta time once to be used multiple times to improve performance
runTime += tempDeltaTime / timeToFinish;
timer += tempDeltaTime; //Increement To exit loop
yield return null;
}
//Camera is done moving
cameraMoving = false;
}
Finally, to move the camera, you can just start the coroutine and pass in the new position calcluation in step 3 and also how much time you want it to take to do the moving. For example, we can pass in the cameraNewPos from step 3 and 2 for seconds it will take to move the camera.
StartCoroutine (MoveCameraDown (cameraNewPos , 2f));
5) When the camera is done moving, enable rigidbody gravity of the player with
Rigidbody rgbd;
rgbd= player.GetComponent<Rigidbody> ();
rgbd.useGravity = true;

Collision/Trigger detection between two remote objects

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

Unity3D: How to determine the corners of a gameobject in order to position other gameobjects according to it?

My question is about if there is a way to know the coordinates of the corners of a gameobject. What I have is three Vuforia AR targets and a gameobject, a cube in this case.
What I need to achieve, is so that when I move the targets around, the corners of the cube would follow the targets, eg. the cube would be as wide as the space between the targets.
Right now how it does it, is that it checks the distance between the targets and sets the scale of the cube according to it. This results in the cube being expanded always from its set position, which makes the positioning of the targets awkward in real life.
Here's a couple of pictures showing what it does now, taken during execution:
Here is the code attached to the cube-object:
using UnityEngine;
using System.Collections;
public class show : MonoBehaviour {
float distancex;
float distancez;
// Use this for initialization
void Start () {
renderer.enabled = false;
}
// Update is called once per frame
void Update () {
if (GameObject.Find ("target1").renderer.enabled && GameObject.Find ("target2").renderer.enabled &&
GameObject.Find ("target3").renderer.enabled && GameObject.Find ("target4").renderer.enabled)
{
renderer.enabled = true;
}
distancex = Vector3.Distance ((GameObject.Find ("target1").transform.position), (GameObject.Find ("target2").transform.position));
distancez = Vector3.Distance ((GameObject.Find ("target2").transform.position), (GameObject.Find ("target3").transform.position));
Debug.Log (distancex);
Debug.Log (distancez);
transform.localScale = new Vector3((distancex), transform.localScale.y, transform.localScale.z);
transform.localScale = new Vector3(transform.localScale.x, transform.localScale.y, (distancez));
Debug.Log (transform.localScale);
}
}
How do I make the corners of the object follow the targets? I need the width of the side to be the width of the distance between the targets, is there any way to achieve this without using the scale?
I know this is quite some time after you asked the question, but I came across this as I was looking to sort something similar out myself.
What I found I needed (and others may be helped) is to use Renderer.bounds
What it looks like in practice for me:
void Update () {
rend = currentGameObject.GetComponent<Renderer>();
Debug.Log(rend.bounds.max);
Debug.Log(rend.bounds.min);
}
My object in this case was a quad at position 0,0,200 and a scale of 200,200,1. The output of rend.bounds.max is (100.0,100.0,200.0) the min was (-100.0,-100.0,200.0). This gives you the corner position for each corner (granted my example was in 2D space, but this should work for 3d as well).
To get it a little more specific for your example if you wanted the top right corner, you could get the XY of renderer.bounds.max, for the top left you would get the renderer.bounds.max.y and the renderer.bounds.min.x. Hope that helps!
Cheers!
Create 8 empty game objects.
Make them children of the "cube" object to be tracked.
Move them in Editor to be at the 8 corners of your tracked game object. Since they are children of the tracked object, their positions will be {+/- 0.5, +/- 0.5, +/- 0.5}.
Name them memorably (i.e., "top-left-back corner").
These 8 objects will move and scale with the tracked object, staying at its corners. Any time you want to know where a corner is, simply reference one or more of the 8 game objects by name and get the Vector3 for each of their transform.position.
Note: If you're doing step 5 repeatedly, you may want to avoid potentially costly GameObject.Find() calls (or similar) each time. Do them once and save the 8 game objects in a variable.
I worked my way around the problem by determining the position between the targets and counting their mean.
This is not flawless, however, since it is difficult to determine the scale factor with which the models would accurately follow the targets' corners, when bringing external models to use with the system.

Categories

Resources