Shorten Raycast - Line Renderer

Hello,

I’m trying to shorten a line renderer, and I can’t for the life of me figure it out! I’m sure it’s an easy solution.

Basically, I have two line renderers, one coming from the player (which is a ball), if the player is facing another ball, the first line renderer is drawn between the player, and the other ball. A second line renderer is then draw from the other ball to wherever the balls force will go if I were to hit it… Pretty much like an aiming system you see in pool/billiard games.

All I want to do is shorten the second line renderer. Below is the code I’m using to calculate the second line direction:

var ballRadius = 0.49;
var contactPointSize = 0.1;
var rayLength : float = 30;
var direction = player.transform.TransformDirection(-Vector3.forward);

var hit : RaycastHit;
Physics.Raycast(transform.position, direction, hit, rayLength);
var newDirection = new Vector3();

newDirection = Vector3(hit.normal.x, hit.normal.y, hit.normal.z);
newDirection *= -1;
var hitPos = hit.collider.transform.position;

lineDirection.SetPosition(0, hit.collider.transform.position);

var deflection = Vector3((newDirection.x * rayLength), hitPos.y, (newDirection.z * rayLength));
var newDeflec = (hitPos + deflection) / 2;

lineDirection.SetPosition(1, deflection);

So lineDirection is the second line renderer, I want to always have its length at 5. I tried playing around with the maths, by adding hitPos and deflection, and then dividing by 2. That works fine - I get the middle of the raycast, but then when I try dividing it further, the coordinates go all messed up.

Also, I cant decrease the size the in ‘rayLength’ as then my Physics.Raycast wont shoot very far.

Any advice? Thanks.

PS, this is pretty much a repeat of a question I asked months ago, but never solved it:
http://answers.unity3d.com/questions/485577/linerenderer-setting-max-distance.html

Isn't it the example in the [docs][1]? I've only had a glance but it seems like you need to lay down multiple sections of unit length line to the size you want. [1]: http://docs.unity3d.com/Documentation/ScriptReference/LineRenderer.SetPosition.html

Unfortunately the info within the docs isnt what I am looking for, it doesn't show me how to shorten the line - my problem is more of a mathematical issue I guess, rather than the functionality behind the line renderer itself. Thanks anyway

I'm not so sure. The docs set line length by simply adding in more vertex points, I believe. Otherwise the 'sine wave curve' would be some spirograph mess :) I'm assuming the LineRenderer simply draw from a point to the next assigned vertex. If you want to make it shorter, assign a new vertex. Of course, I could be wrong... I've never used the LineRenderer, I'm just looking at the example.

Will it only have two points? If that is the case, isn´t it enough to normalize like this deflection = 5*deflection.normalized; Or did I misunderstood your question?

Yes, you are right, I missed that!

3 Answers

3

here is a thought, you can use the “slope” (well in unity it easier to use an angle) of the line between those two to get a direction. then rotate Vector3.fwd*5 by that direction. Its basically aligning a shorter vector between the ball and hit target point.

  1. Get the Y-angle between the two points.
  2. Convert it to a Quaternion(man i hate these things)
  3. Multiply the product of Quaternion and the balls fwd vector by 5.
  4. use it in the Line Direction code.

so we can use

PSEUDO code
a = Vector3.Angle(ballPos, hitpos) 
and q = Quetrion.Euler (0,a,0);
lineDirection = q*ballpos.fwd*5;

this way the line direction will always have an “length” of 5.

citation : Visualization Tools for 3D

Quaternion - Wikipedia (again, I still hate these things)


Update: I overlooked some details, ported and tested you code and got it working

var ballRadius = 0.49;
var contactPointSize = 0.1;
var rayLength : float = 30;
var direction = player.transform.TransformDirection(-Vector3.forward);
 
var hit : RaycastHit;
Physics.Raycast(transform.position, direction, hit, rayLength);
var newDirection = new Vector3();
 
newDirection = Vector3(hit.normal.x, hit.normal.y, hit.normal.z);
newDirection *= -1;
var hitPos = hit.collider.transform.position;
 
lineDirection.SetPosition(0, hit.collider.transform.position);
 
var deflection = Vector3((newDirection.x * rayLength), hitPos.y, (newDirection.z * rayLength));
deflection = (deflection -hitPos).normalized * 5 + hitPos;
var newDeflec = (hitPos + deflection) / 2;  //probably don't need this.
 
lineDirection.SetPosition(1, deflection);

I have no idea why the other versions of this code messed up for you. division screws up because it transforms based on zero as i said earlier in the comments below(when you wanted to transform it based on hitpos). when calculating the length of deflection(magnitude), remember to subtract hitpos or you will get the distance between deflection and zero instead. also on Quaternion’s still hate them.

Isnt this over complicating things? I only want to cap the max line renderer. Right now, the line goes on for a distance of 30. I want to change it to 5, but if I do that, it messes up the coordinates (unless I reduce the raycast length from 30, to 5 too - but then the raycast is restricted to 5, which I don't want)

wait what really? you could normalize it then multiply it by 5. based on that its simple vector math. http://docs.unity3d.com/Documentation/ScriptReference/Vector3-normalized.html using a piece of paper, i did this. 1. first subtract ball pos from hitpos, this will give us the direction between the two. 2. normalize that and multiply by 5, 3. add the ball vector so it return to normal. the result is your line renderer capped vector. Hint: do this after you raycast

Okay, this sounds good - let me have a play with my code, and I'll get back to you

Wait, where are you getting ball pos from? Remember, I want to cast this line from my Second ball, which is the 'hitPos', and direct it in 'newDirection * rayLength'.

The second line cast doesn't have an end position, it's just getting the first line cast, and reflecting it really.

Here we use the RayCastHit.point if applicable, or the point maxRayLength in that direction if no hit occurs.

This technique should allow you to find the right endpoint for your second LineRenderer.

	var direction: Vector3; //value should be derived from previous collision
	var lineStart: Vector3; //value should be derived from previous collision
	var maxRayLength: float =  5;
	var lineEnd: Vector3; 
	var hit:RaycastHit;
	if(Physics.Raycast(lineStart, direction, hit, maxRayLength)) {
		lineEnd = hit.point;
	}	
	else {
        //assumes direction is of length 1, change to direction.normalized if required.
		lineEnd = lineStart + (direction * maxRayLength); 
	}

    //the following for illustrative purposes only:
	var lr:LineRenderer = gameObject.GetComponent(LineRenderer);
	lr.useWorldSpace = true;
	lr.SetPosition(0,lineStart);
	lr.SetPosition(1,lineEnd);

	Debug.DrawRay(lineStart, lineEnd - lineStart, Color.red, 1.0f);		

Update:

Here is a more complete implementation.
The results illustrated can be achieved by placing this script on a GameObject, and then assigning some with LineRenderers to lr1 and lr2.

var lr2:LineRenderer;
var lr1:LineRenderer;
var direction:Vector3 = new Vector3(1,1,0);
var rayLength : float = 30;
var guideLength:float = 5;

function Update () {
	var initialPosition: Vector3 = transform.position;
	var impactPoint: Vector3;
	var guideEndPoint: Vector3;
	var hit:RaycastHit;
	
	if (!Physics.Raycast(initialPosition, direction, hit, rayLength)) {
		// handle a miss
		Debug.DrawRay(initialPosition, direction.normalized * rayLength, Color.blue, 1.0f, true);	
		lr1.enabled = false;
		lr2.enabled = false;
		return; 
	}

	impactPoint = hit.point;
	guideEndPoint = impactPoint + ( -hit.normal * guideLength );
	
	//draw rays for IDE
	Debug.DrawRay(initialPosition, impactPoint - initialPosition, Color.green, 1.0f, true);	
	Debug.DrawRay(impactPoint, guideEndPoint - impactPoint, Color.red, 1.0f, true);	
	
	//draw lineRenderers for GameScreen
	lr1.enabled = true;
	lr2.enabled = true;
	lr1.SetPosition(0, initialPosition);
	lr1.SetPosition(1, impactPoint);
	lr2.SetPosition(0, impactPoint);
	lr2.SetPosition(1, guideEndPoint);
}

alt text

This isnt capping the max length of my line renderer, only the raycast - I dont want to restrict the raycast itself, just the line renderer of a maximum of 5

lineEnd = lineStart + (direction * lineLength); will find your endpoint if: - lineStart is the starting point - direction is a unit vector defining direction - lineLength is the length you want the line to have. Once you have the start and end points just plug them into the LineRenderer.

I already have the location of my line end: 'newDirection * rayLength' - I just want to cap it at a certain length - none of these answers are working and I'm getting really confused by the whole situation here :/

Instead of rayLength use the capLength.

Where are you getting capLength from? If I change the rayLength in 'newDirection * rayLength' from something different from the raycast length (which is 30 right now), it goes all messed up, plus the line still changed length... I want it to always be a length of 5.

If you want to draw a line of fixed length from a known starting position and following some fixed direction then you can compute the two end points like this:

// Code snippet

var separation : Vector3 = transform.position - player.transform.position;

var tangent : Vector3 = Vector3.Normalize(separation) * lineLength;

var start : Vector3 = transform.position;
var end : Vector3 = start + tangent;

To give you a little more of a hint, I’ve created a Unity scene and some script code that shows just how to do this. Take a look here for details: the computation of the line you care about is all in the “TargetBall” script/