- Home /
How do I make the crosshair go closer/get bigger if an item/wall is close to you?
I'm doing the crosshair by getting a 3D model (that is just a texture on a face) and placing it in front of you, the problem with this is that when I get close to a wall, the crosshair dissapears behind it, as I would've expected, I wanted the crosshair to get bigger/closer if an object goes in front of it. Is there anyway I can do that?
Also, is there anyway I can make the crosshair lock onto an opponent if it spots one, similar to Goldeneye or the Timesplitters games?
Answer by TonyLi · Jul 28, 2013 at 07:03 PM
(When you have two questions, please post them separately next time.)
For your first question (crosshair bigger), I recommend displaying the crosshair as an overlaid GUI texture, not a 3D model. You can scale the texture based on the distance. Or, if you stick with the 3D model, you can just move it closer, but what happens when your player is right up against the wall? In the example below, I'll go with your 3D model approach, and assume that the model, named "crosshair," is a child of the camera object.
To figure out how far the wall is, use Physics.Raycast. You'll raycast from the center of the screen. If it hits anything, record the distance. I added two public properties: layerMask, which you set to the layers that you want to detect collisions against, and maxDistance, which is the "default" distance of the 3D model if it hasn't hit anything.
public LayerMask layerMask;
public float maxDistance = 20f;
void Update() {
float distance = maxDistance;
Ray ray = Camera.main.ScreenPointToRay(new Vector3(Screen.width / 2, Screen.height / 2));
RaycastHit hit;
if (Physics.Raycast(ray, out hit, maxDistance, layerMask)) {
distance = hit.distance;
}
crosshair.localPosition = new Vector3(crosshair.localPosition.x, crosshair.localPosition.y, distance);
}
To make the crosshair lock onto an opponent, you'll want to broaden the scope of your collision detection. You could cast out several rays, or use a SphereCast or a box collider on your player. Let's say you go with the box collider, which is rotated 45 degrees with one of the corners positioned on your player and the rest of the box in front of the player. This forms a 90-degree sweep in front of the player.
As other colliders (enemies) enter the player's box collider, they will trigger OnTriggerEnter or OnColliderEnter messages, depending on whether you set up the box collider as a trigger or not. Record them as they enter or exit. Then decide on which one to target. Finally, run a raycast to the target. If it hits, move the 3D model to the hit point. Otherwise move it to maxDistance (the default distance) in the direction of the ray.
I think the Camera Frustum working more perfect in this case than sphere or box collider, can see in this link: http://answers.unity3d.com/questions/142676/camera-fustrum-calculation.html Besides, transform.LookAt is better and easy to understand.
I tried out your code, and it's not working, the crosshair still goes through objects if I get close to one, unless I'm right at it, then it zooms right up to you!
I've discovered that in the scene panel, it does zoom in, but it doesn't zoom in enough for you to see it, unless you're close enough, this is very strange...
...and yes, I've set the layer$$anonymous$$ask to my player's. The player collider layer is the same as all the enemies, this is to make sure the enemy AI sees me with their ray/linecasts. So by using layer$$anonymous$$ask, the crosshair ray will ignore the enemies as well and go behind them, which is no good.
Just in case you ask for my code, it's completely identical to the answer's.
Agreed; sounds like a layer$$anonymous$$ask issue. Your raycast is probably hitting the player collider. A Debug.Log() line would confirm this:
if (Physics.Raycast(ray, out hit, maxDistance, layer$$anonymous$$ask)) {
Debug.Log("Hit: " + hit.collider.name);
distance = hit.distance;
}
I don't see any reason why you can't have the player and enemies on separate layers to resolve this issue.
(The suggestion by @nixcs2512 will work, too. It just requires a little more code to calculate frustum planes and then test them, rather than letting Physics handle it and just send OnTriggerEnter messages, which I think is easier.)
I've tried it, and it doesn't register the player collider, it doesn't actually log anything unless an object is actually in front of it, so it's not a problem with the player collider.
You may need to set the crosshair model's distance to something just a little closer than the raycast hit distance.
Or, if you draw a texture in OnGUI() ins$$anonymous$$d of using a 3D model, you can bypass the whole issue. Just replace the whole 3D model thing with something like:
void OnGUI() {
float scale = <compute some scaling factor based on raycast distance>
float size = textureSize * scale;
float halfSize = size / 2;
float centerX = Screen.width / 2;
float centerY = screen.height / 2;
Rect rect = new Rect(centerX - halfSize, centerY - halfSize, size, size);
GUI.DrawTexture(rect, myCrosshairTexture);
}
Answer by rowdyp · Jul 31, 2013 at 11:28 PM
putting the cross hair as a GUI will still add all the functionality desired, but keep it in front of the environment.
Well, I actually have a reason for making it the way i am, I want to make it so that when I turn, the crosshair moves slightly in the direction you're turning (and I already a script to do that), and the guns have been programmed to constantly looking at it, so as the crosshair moves, the guns also turn, giving a sort of illusion of the player turning, similar to how it was in FPS games such as Goldeneye and the Timesplitters Series. It also guarentees that the gun will shoot where the crosshair is without fail.
Is it possible to do the same thing using a GUI crosshair?
well you can apply an offset of the texture with the movement of the character or weapon.
You can still use the same raycast code from my first post, where I actually recommended using a GUI texture because as @rowdyp said it's easier to guarantee that it will always be in front. You can scale the texture by distance, and offset it by rotational movement.