- Home /
What's a reliable way to detect if an object is no longer being hit by a ray?
I'm aware raycasts are event driven and that colliders can't physically detect if they're being hit by a ray, and I'm aware of a way around it by sending a message to whatever a ray hits and keeping an object of its last hit and sending a message to that cached object when the ray returns not hitting anything, but it doesn't seem to be working 100% of the time.
void CreateRay(Ray ray, float length)
{
if(Physics.Raycast (ray, out hit, length))
{
if(lastHit == null)
lastHit = hit.collider.gameObject;
if(lastHit != null && lastHit != hit.collider.gameObject)
{
lastHit.SendMessage("NotHitByRay", 0, SendMessageOptions.DontRequireReceiver);
lastHit = hit.collider.gameObject;
}
lr.SetPosition (1, hit.point);
ReflectionInfo ri = new ReflectionInfo(ray.direction, hit);
if(hit.collider.gameObject.tag == "reflector")
{
hit.collider.gameObject.SendMessage("HitByRay", ri, SendMessageOptions.DontRequireReceiver);
}
}
else
{
if(lastHit != null)
lastHit.SendMessage ("NotHitByRay", 0, SendMessageOptions.DontRequireReceiver);
lastHit = null;
lr.SetPosition (1, start+(direction * 20f));
}
}
You can see I'm redundant about sending the NotHit message only because I still can't make it trigger all the time.
This is the receiver
void HitByRay(ReflectionInfo info)
{
reflectInfo = info;
if (!active) {
reflect ();
active = true;
}
}
void NotHitByRay(int num)
{
Destroy (reflection);
active = false;
}
The main issue is NotHitByRay is not beingcalled, and the laser reflections persist when the source laser stops hitting the object. IDK, is there an approach I am not seeing that could make it work?
@Ohanniganswife Please reaccept AlwaysSunny's answer. I had to unselect it to solve an error in moderation. I could re-check it myself but you'd lose out on your 5 karma.
Answer by AlwaysSunny · Jun 30, 2015 at 07:07 PM
Don't use SendMessage. Just make the method public, get the receiver component, and invoke the method on it. Vastly superior.
public void HitByRay(){}
Receiver receiver = hit.collider.GetComponent<Receiver>() as Receiver;
if (receiver) receiver.HitByRay();
"keeping an object of its last hit and sending a message to that cached object when the ray returns not hitting anything"
This is not foolproof. Not without a good bit of case-planning and forethought. Caching the object and sending a "not hitting something" event might seem like a smart optimization, but comes with complications that make it unfavorable, which you're obviously witnessing.
Just invoke the receiver method on the hit object each frame. This is not really more expensive in this particular situation, and should be 100% foolproof.
Generally this means changing your logic around a bit. You're creating the equivalent of an "OnRaycastStay" event instead of Enter/Exit events. Enter/Exit events can be done, but you have to plan for every possible contingency. In many if not most cases, the performance gain isn't worth it. Sounds like a breeding ground for bugs to me, unless you know exactly what you're doing.
Yeah I think one of the main issues is with what I'm doing which is hard to explain but it uses a static method to shoot out a loop of rays, and if a ray hits something, if that object is a reflector, the reflector creates a new prefab laser reflection, etc. Originally I just had it so the initial laser handled all the reflections, but a function I want is to make it so once an object is reflecting 1 laser, it can't reflect anymore. That's where I'm having trouble, because if the object doesn't get triggered that it's no longer reflecting, then it just has lingering reflections despite no source. I can make infinite reflections happen easily, it's just that single reflection only I can't seem to get the logic down for. I did what you suggested but it's the same result, lingering reflections when a ray stops hitting. ID$$anonymous$$
I think handling all the reflections from the initial laser will probably end up being better, and I will just have to find a different way to make it so if it's hitting an object that already has a reflection, then it should not reflect. I just already have kind of convoluted code at the laser source, so I wanted to spread that out
To satisfy the "only accept one" requirement:
$$anonymous$$anage a variable that detects whether the reflection method has been tripped this frame, and disallow additional attempts.
public void HitByRay(){
if (isHit) return; // leave here if already hit this frame
isHit=true; // set the bool to indicate got hit this frame
}
void LateUpdate() {
isHit=false; // reset the variable at the end of the frame
}
To satisfy the "need exit event" requirement:
You can have a variable recording whether you were hit last frame as well. $$anonymous$$easuring the difference between the two can serve as an Enter / Exit event.
if (isHit && !wasHit) // enter
if (!isHit && wasHit) // exit
Regarding the fact that you're still having an issue with the "exit" event, I mentioned this is an "OnRaycastStay" approach - your logic would need to change to accommodate that.
Ok thanks I'll try these out. $$anonymous$$y main disconnect in the logic was on what order things were happening in each frame because of how convoluted my current code is since a lot of it is handled outside of an Update and or it's spread out between multiple classes and I'm trying to interlink it together causing me to get lost in the spread out logic.
This might be a handy document to review:
http://docs.unity3d.com/$$anonymous$$anual/ExecutionOrder.html
Best, :)