- Home /
How to check if raycast is hitting object's child's collision box?
I'm firing a raycast to check if my enemies are hitting anything in the level other than the player, and to not do anything unless they're hitting the player specifically:
private void CheckCanHitPlayer()
{
// Gets the angle between the enemy and player
targetDirection = player.transform.position - transform.position;
RaycastHit hit;
if (Physics.Raycast(transform.position, targetDirection, out hit, 1000))
{
GameObject theObject = hit.collider.gameObject;
if (hit.collider.gameObject.CompareTag("Player"))
{
canHitPlayer = true;
}
else
{
canHitPlayer = false;
}
}
}
When I use the code above it's working for everything in the level except the doors, as in it comes back canHitPlayer = false
on everything other than the doors, so the enemies will shoot at the player even if a door is in between the player and themselves.
Now, the doors don't have collision boxes on the parent object but they do on the children (in fact, they actually have both a normal collision box and a trigger collision box across the various children), so why is the raycast check and canHitPlayer
not coming back as false when hitting the doors here?
Any ideas?
How can I double check if the raycast has hit the children of the door to make sure canHitPlayer
returns false when it should in this case?
Note: The doors aren't set to any special layer or anything, and I'm not using a layer mask, so I just don't get why canHitPlayer
isn't also coming back false whenever the raycast hits a door.
Answer by SuperScience · Oct 22, 2018 at 08:08 PM
Hello Impurekind,
First.. are you sure that you are doing the correct operation? Like: what specifically is the goal, and is Raycast the correct probe for the job? Check the physics class definition and you will see a large number of possible probes. Raycast is only one type of probe. ( https://docs.unity3d.com/ScriptReference/Physics.html )
From your code, I think you want to use Physics.Linecast. That is the ideal way to detect "can I see something from here". One caveat is that you might hit yourself, depending on the size and shape of your collider. I do not think that backfaces on collision meshes register as collisions, however.
If you want to say "If I shoot this gun, will it hit an enemy", you want to use Physics.Raycast(gun.transform.position, gun.transform.forward) // with lots of other options. That will send your probe in the -z direction local to your gun.
Also... weird question... you say you're trying to hit a child collider? Does the parent and the child both have a rigidbody? Does the parent collider overlap the child? There are a few things that could go wrong here. Physics can be a bit nuanced.
From the wording it sounds like a line-of-sight check. And from the Linecast documentation you linked, I bet that would both fix it and be computationally less expensive. Regarding colliding with the object and source could you not just turn off the colliders of those objects before the linecast and turn them back on after?
Well I don't really know if I'm doing the correct thing. $$anonymous$$y goal is simply to check if there's anything in between the enemy and the player, and if there is then I want to stop the enemy firing at the player, basically, so the enemy only fires at the player when it has a clear line of sight. So right now I just fire the raycast from the enemy in the direction of the player, and if it hits something before it reaches the player then it comes back as canHitPlayer = false
and the enemy won't fire at the player. Currently this works on everything in the level except the doors.
I also just tried the Linecast and it works exactly the same. Although I don't know how to enter that last couple of parameters in this function properly, so I'm only using the first two, position and target, but I think I might like to tell it to do the querytriggerinteraction bit. How do I fill it in properly to do this, with caring about the layer mask part of the function?
The parent object of the door--and, to be clear, the door is the only object the raycast/linecast check seems to be ignoring--is just a completely empty object that I solely use for storing all the other parts of the door object neatly inside. Some of the door's child objects have colliders though (one has a normal collider and the other a trigger collider, and at least one is big enough to cover the full size of the door).
Hi impurekind,
If you want to check if there is line-of-sight between the player and the enemy, you should use LineCast, which is a raycast between two specific points in space.
It sounds like using the base API (position, position) is fine.
I don't think that the parenting is an issue. In my own projects, I use empty GameObjects extensively to keep the project tidy.
Does the door, or its subcomponents have rigidbodies that are kinematic?
$$anonymous$$aybe just for a sanity check, try adding a collider to the door and see if that works?
Yeah, I switched over to a Linecast to keep things simple and clean, and I also added a collider to the door parent too, which made made the Linecast stuff work properly but required me setting up some old layer stuff I used previously to avoid other stuff causing issues. It seems to have done the trick. :)
Note: Neither the parent nor child objects have rigidbodies. It's just the colliders, which were only on the children before but there's also one on the parent now too.
Answer by GamitusLabs · Oct 22, 2018 at 07:05 PM
I'm not super familiar with raycast but I can see it being one of two things:
The door isn't in the same layer as described by 'bit mask' here: https://docs.unity3d.com/ScriptReference/Physics.Raycast.html
Or, it could be the way raycast parses through collidable objects. Raycast returns the first object collision, correct? Therefore, the player may be higher on the parse priority or child objects may be parsed after parent objects.
Just out of curiosity, have you tried putting a collider on the doors parent object?
Well the door is just in the default layer, as are any of the other objects in the world. And, I'm not using the layer mask part of the function so I presume that means it won't deliberately be trying to ignore any particular layers.
I'm not sure about the parse layer stuff, but the player is literally behind the door object, and the enemy is on the other side--the raycast is being fired from the enemy towards the player--so I don't get why the raycast would choose the player over the door first.
I tried putting a collider on the door's parent object, and it does work when I do this, but it breaks a whole bunch of other code I'm using that specifically wants to avoid a raycast hitting the main door collision box or various reasons.
So I know it is something to do with the collision box, but like I said, there's collision boxes on the children, so even if it doesn't hit one on the parent, I'm not sure why it's ignoring the ones on the children. As you can see in the code provided, I'm not telling it to ignore any layers or whatever, so I'd assume it would simply hit the first collidable thing it reaches on its way to the player, which is the door.
Answer by thieum · Mar 07, 2021 at 11:57 AM
Bumping into this issue with compound / nested colliders with rigidbodies. As reported, the raycast fails to detect children colliders and return the parent, in best case. I just found that the QueryTrigger parameter solves it all, as if children colliders were treated as "triggers"! So, try this method instead:
if (Physics.Raycast(ray, out hit, 20.0f, mask, QueryTriggerInteraction.Collide)){}
A layermask is now required (use -1 value to include all layers by default), because QueryTrigger is the 5th argument.