Crosshair aim problem

Hi all, hopefully a simple one, but I have been scratching around for a solution for ages.

Got the following script attached to MainCamera on the vanilla, standard Unity first person controller.

Game involves running around selecting things (via mouse click) at close range, most of which are cubes with edge about 0.5m, so not precision stuff.

My script shows the crosshair fine and the movement is just great using the standard mouselook script.

You can select things when you “hit”, but often the aim is off centre by quite a way. Have tried adding offsets and other things to get it to work, but nothing seems to be reliable. The (commented out) mouse-click version of the code below works fine, just not the crosshair methods.

// attached to MainCamera to provide point-and-click interaction

var v_crosshair : Texture2D; // 86x86 pixel PNG crosshair image with transparency, crosshair centred in image centre
var v_selected : Transform; // object that is currently selected by the player

var v_x_offset : int; // can adjust this in inspector 
var v_y_offset : int;

function OnGUI () {
	//crosshair
	GUI.Label (Rect (v_x_offset + Screen.width / 2, v_y_offset + Screen.height / 2, 86, 86), v_crosshair);

	var e : Event = Event.current;
	if (e.button == 0  e.isMouse  v_selected == null) {
		// left-click, attempt to find actionable item within 20 metres

		var v_ray : Ray = camera.ScreenPointToRay(Vector3(Screen.width / 2, Screen.height / 2,0)); // use crosshair to find object
		
		// here are some other ways I have tried, only the mouse position alternative works
		//var v_ray : Ray = camera.ViewportPointToRay(Vector3(0.5,0.5,0)); // use crosshair to find object
		//var v_ray : Ray = camera.ScreenPointToRay (Input.mousePosition); // use mouse pointer to find object

		// Do a raycast
		var v_obj : RaycastHit; // object that is forward from the fpc
		var v_h : Transform; // transform of object that was hit
		var v_tag : String; // tag of hit objected
		if (Physics.Raycast (v_ray, v_obj, 20)) {
			v_h = v_obj.transform;
			v_tag = v_h.tag;
			// check if it is a selectable item - don't do a layermask so non-selectable objects do get hit and prevent see-through-walls type disasters
			if (v_tag == "server") { // check if the item is selectable by a player
				print("do stuff here");
			}
		}
	}
}

Any advice gratefully received.

What are your v_offset values set to?

Your label code with no offset values will draw the image off center, as you are drawing the lower left corner of the crosshair at the center of the screen - not the center of the crosshair at the center of the screen.

var imageRect : Rect = Rect((Screen.width - v_crosshair.width) / 2, (Screen.height - v_crosshair.height), v_crosshair.width, v_crosshair.height); //rect for an image centered on the screen

Regards,

Thanks for the code, it’s a neater way of centering the image than my use of offset variables. But my variables are both set to -43 for a 86x86px image, so it amounts to the same thing.

Is my ScreenPointToRay line correct for finding objects at the centre of the crosshair?

var v_ray : Ray = camera.ScreenPointToRay(Vector3(Screen.width / 2, Screen.height / 2,0)); // use crosshair to find object

The code itself looks ok. I’d put it in Update personally, but I doubt that’s your root problem.

Try to Debug.DrawRay() the Ray you’re casting and Log the Object that was hit by the Raycast.
Doesn’t look like a Code-Problem to me, are you missing Colliders?
Is the Object you’re trying to hit to far away? You’re limiting the length of the Raycast to 20 meters, which isn’t that far.

But you really should move it to the Update function, OnGUI can be called several times per frame.

I tend to do it backwards, to get perfect accuracy to the crosshair. I shoot the ray from the camera’s transform.forward (which will always shoot forward from the center of the viewport), and the use WorldToScreenPoint() to position the crosshair.

-Don’t forget to subtract half of the width/height of the crosshair texture from the x/y positions of your GUI rect, otherwise it won’t be centered.

Anyway, here’s how i’d do it:

var rayDistance : float = 10.0;
var crosshair : Texture2D;

private var cam : Camera;
private var camTrans : Transform;
private var crosshairPos : Vector3;

function Start(){
	cam = Camera.main;
	camTrans = cam.transform;
}

function Update(){
	var hit : RaycastHit;
	if(Physics.Raycast(camTrans.position, camTrans.forward, hit, rayDistance)){
		crosshairPos = hit.point;
	}
	else{
		crosshairPos = camTrans.position + camTrans.forward*rayDistance;
	}
	
	crossHairPos = cam.WorldToScreenPoint(crosshairPos);

}

function OnGUI(){
	GUI.DrawTexture(Rect((crossHairPos.x-43), (crossHairPos.y-43), 86, 86), crosshair);
}

Thanks for the continued feedback. Very interesting results.

legend411, using your GUI.DrawTexture method to position the crosshair places it on the screen in a different place (can see this because am using both methods simultaneously).

And the two crosshairs are of different sizes! (yours is bigger by about 20% or so compared to my GUI.Label method). What could account for that?

Thanks both for the tip to place this stuff in Update(). Am I right that I have to keep the var e : Event = Event.current; line inside the OnGUI() function? I was getting an error when I moved this into the Update() function.

Yeah… events are a GUI thing do stuff with OnGUI(), but you don’t want to be doing the raycast stuff in there. I would probably just use Input.GetKey(KeyCode.Mouse0)){ } in Update() instead.

I don’t use DrawTexture too often and wrote that off the cuff, not sure why you’d see those results though. In my code you can replace “DrawTexture” with “Label” and see what happens.

Thanks. Replacing DrawTexture with Label in your code places the crosshair in the same location (well actually 1px difference on both width height) and at the same size.

So Label is resizing the texture. Interesting. Reported in forums here as being to do with Labels being affected by margins, etc, in the GUI Style.

Crosshair is now working perfectly with spot on accuracy. Thanks a million.