I started working on a game project as a way of learning Unity and now I ran into the first problem where I have no idea what’s going on.
I’m trying to make my 3rd person character shoot at the point where the mouse cursor points at. I use a raycast to constantly get the world point of the mouse cursor position and then on a mouse click I do a linecast from a point at the end of the barrel of the character’s gun to the point I got with the ray cast. This line cast would then determine if the shot hits something on the way or gets all the way to the goal.
I have a Debug.DrawLine visualising the line from the gun barrel to the aimed point I got through the ray cast, this looks like everything should be good. The actual Physics.Linecast doesn’t register all the hits at what seems totally random, though.
If I keep shooting to a flat part of the ground around the character, for example, only a part of those shots register a hit with the terrain collider. Same thing with whatever other object with a collider I shoot at. Interestingly enough, if I shoot at an uphill or a downhill part of the terrain, then it seems to register all the collisions. So… huh?
Below is the relevant code, I stripped most of the extra stuff out. In addition to the temp particle on hit I’ve also had a debug.log there and a gizmo drawling to visualise the line only when a collision occurred etc… always the same thing, most of the collisions go unnoticed. Also, again, shooting at a sloped terrain will always register a hit and show the particle effect on the ground.
What am I doing wrong?
void LateUpdate () {
if (combatMode) {
RaycastHit hit, hit2;
// rotate the player's torso towards the aimed point
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit)) {
playerSpineToRotate.transform.LookAt(hit.point);
}
// debug line to see where the shot is aimed
Debug.DrawLine(gunBarrelAimPoint.transform.position, hit.point);
// if mouse button down = shooting
if (Input.GetMouseButtonDown(0)) {
// cast a line from the gun barrel to the aiming point
if(Physics.Linecast(gunBarrelAimPoint.transform.position, hit.point, out hit2)) {
// common particle for all hits for testing
Instantiate(genericHitParticle, hit2.point, Quaternion.identity);
}
}
}
}
You are raycasting from Player P, in direction D (from mouse) and getting point A, where the raycast hits.
Then you are linecasting only between P and A
In all situations, in an ideal world, the linecast will hit nothing, every time, because there can be no point B between P and A, otherwise A would have been at B in the first place.
The only reason you are sometimes getting a hit at the end of the linecast, is float imprecision.
why not just scrap the linecast, and use the info you already have in the raycast hit.
OK I’m majorly confused about where from and where to the ray cast actually goes. As it is, I can move the mouse cursor behind an object with a collider that is left between the player and the mouse cursor and still get a hit on the ground where a ray from the player surely shouldn’t hit (it should hit the said object first). So it seems to me like it casts not from the player but more like from the camera (which is hovering above and behind the player and following him)? The script with the casts is attached to the player, though. The camera has a script to follow a target (in this case the player) given to it in the inspector.
This is why I opted to do the additional line cast from the gun to the point I already got with the first ray cast. While it can actually be wanted that the player can click on the ground and/or objects that are not in their character’s field of view, they shouldn’t be able to shoot at them if there’s something in the way. So it looks to me like doing a “two pass cast” like this is the way to go to achieve that? Or is it possible to get multiple collisions along the ray cast?
I’m off to read more about the ray/line casting and experiment… I can’t quite seem to wrap my head around how it all works yet.
… edit a bit later …
Ok I managed to come up with a working solution… I still use two casts, but instead of a line cast I do another ray cast because hpjohn’s post above made me realise that the line cast was indeed in most cases falling short of the final target where as a ray cast should eventually hit the target as it is not given an end point but rather a direction…
I still do it in two casts, because I first need to get the collision point where the player is intending to shoot at, and then I do a ray cast from the gun’s barrel to the direction of the clicked collision point from the gun barrel so that the “bullet” hits whatever is in between of those two points. If there’s nothing in between it will then finally collide with the collider the player clicked on (ground most probably) in the first place.
Maybe there’s a better way to do the same thing, but this works… thanks for the reply, it got me thinking! Below’s the final code.
void LateUpdate () {
if (combatMode) {
RaycastHit hit, hit2;
// rotate the player's torso towards the aimed point
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit)) {
playerSpineToRotate.transform.LookAt(hit.point);
}
// debug line to see where the shot is aimed
Debug.DrawLine(gunBarrelAimPoint.transform.position, hit.point);
// if mouse button down = shooting
if (Input.GetMouseButtonDown(0)) {
// cast a ray from the gun barrel towards the point the player clicked on
if (Physics.Raycast(gunBarrelAimPoint.transform.position, hit.point - gunBarrelAimPoint.transform.position, out hit2)) {
// common particle for all hits for testing
Instantiate(genericHitParticle, hit2.point, Quaternion.identity);
}
}
}
}