- Home /
Vertical auto-aim with raycasts
So I'm working on a top-down shooter that only allows for x and z control, but need a way to shoot ground enemies as well. This code (much of which I "borrowed" from another question here), is designed to find the closest enemy directly in front of the player and adjust the gun's pitch accordingly. If no enemy is directly ahead, it's just supposed to fire forward (that part works). The problems are that it only detects a hit from very close, no matter what I set the ray distance to, and the end points of the rays stay fixed at their initial z position.
Also, any suggestions to clean up my ugly tag check in the linecast would be cool...
I'm sure this is amateur-hour stuff, but any help is appreciated.
#pragma strict
public var maxAngle : float = 180;
public var increments : int = 15;
public var rayDistance : float = 150; //Set to 0 for infinity.
public var sphereCastRadius : float = 2;
public var targetTag01 : String = "Enemy";
public var targetTag02 : String;
public var targetTag03 : String;
function Start ()
{
if (rayDistance == 0)
{
rayDistance = Mathf.Infinity;
}
}
function Update ()
{
}
function LateUpdate ()
{
}
function FixedUpdate()
{
transform.rotation = Quaternion.Euler(0, 0, 0);
RaycastSweep();
}
function RaycastSweep()
{
var startPos : Vector3 = transform.position; // umm, start position !
var targetPos : Vector3 = Vector3.zero; // variable for calculated end position
var startAngle : float = ( -maxAngle * 0.5 ); // half the angle to the Left of the forward
var finishAngle : float = ( maxAngle * 0.5 ); // half the angle to the Right of the forward
// the gap between each ray (increment)
var inc : float = ( maxAngle / increments );
// step through and find each target point
var hit : RaycastHit;
var targetDistance:float = 215;
for ( var i : float = startAngle; i < finishAngle; i += inc ) // Angle from forward
{
targetPos = (Quaternion.Euler( i, 0, 0 ) * transform.forward ).normalized * rayDistance;
// linecast between points
if ( Physics.Linecast(startPos, targetPos, hit) && ((hit.collider.gameObject.tag == targetTag01) || (hit.collider.gameObject.tag == targetTag02) || (hit.collider.gameObject.tag == targetTag03)))
{
var diff = (transform.position - hit.point);
var curDistance = diff.magnitude;
if (curDistance < targetDistance)
{
transform.Rotate(i, 0, 0);
targetDistance = curDistance;
Debug.Log( "Hit " + hit.collider.gameObject.name );
}
// to show ray just for testing
Debug.DrawLine( startPos, targetPos, Color.green );
}
// to show ray just for testing
Debug.DrawLine( startPos, targetPos, Color.red );
}
}
Here is the working code. Thanks again to both of you for the help.
#pragma strict
public var maxAngle : float = 180;
public var increments : int = 60;
public var rayDistance : float = 150; //Set to 0 for infinity.
public var layer$$anonymous$$ask : int = 12;
function Start ()
{
layer$$anonymous$$ask = 1 << layer$$anonymous$$ask;
if (rayDistance == 0)
{
rayDistance = $$anonymous$$athf.Infinity;
}
}
function Update ()
{
}
function LateUpdate ()
{
}
function FixedUpdate()
{
RaycastSweep();
}
function RaycastSweep()
{
transform.rotation = Quaternion.Euler(0, 0, 0); // reset object to face forward
var startPos : Vector3 = transform.position; // linecast source
var targetPos : Vector3 = Vector3.zero; // linecast end
var startAngle : float = ( -maxAngle * 0.5 ); // half the angle to the Left of the forward
var finishAngle : float = ( maxAngle * 0.5 ); // half the angle to the Right of the forward
var inc : float = ( maxAngle / increments ); // the gap between each ray (increment)
var hit : RaycastHit;
var targetDistance:float = 1500; // change target if closer than this
var targetRotation:Quaternion; // rotation that object will be set to
// step through and find each target point
for (var i : float = startAngle; i < finishAngle; i += inc ) // Angle from forward
{
targetPos = transform.position + (Quaternion.Euler(i, 0, 0 ) * transform.forward).normalized * rayDistance;
// linecast between points
if (Physics.Linecast(startPos, targetPos, hit, layer$$anonymous$$ask))
{
// find closest hit
var diff = (transform.position - hit.point);
var curDistance = diff.magnitude;
if (curDistance < targetDistance)
{
targetRotation = Quaternion.Euler(i, 0, 0);
targetDistance = curDistance;
Debug.Log( "Hit " + hit.collider.gameObject.name );
}
// draw test line
Debug.DrawLine( startPos, targetPos, Color.green );
}
// draw test line
Debug.DrawLine( startPos, targetPos, Color.red );
}
transform.rotation = targetRotation;
}
Answer by Fattie · Feb 11, 2013 at 09:52 AM
regarding the tag check it's extremely likely you need to learn about PHYSICS LAYERS.
http://docs.unity3d.com/Documentation/Components/LayerBasedCollision.html
it is absolutely central to game programming in any game engine, hope it helps
Thanks for the tip - I removed the tag-check and used layers ins$$anonymous$$d. That should simplify a few things some.
However, my two main problems persist, unfortunately.
I changed things a bit; now the hit detection seems to work better at further distances, but the endpoints of the rays still don't seem to be updating accordingly when I change my Z position. I can still basically move forward past the end of the raycast sweep.
Updated code:
#pragma strict
public var maxAngle : float = 180;
public var increments : int = 15;
public var rayDistance : float = 150; //Set to 0 for infinity.
public var layer$$anonymous$$ask : int = 12;
function Start ()
{
layer$$anonymous$$ask = 1 << layer$$anonymous$$ask;
if (rayDistance == 0)
{
rayDistance = $$anonymous$$athf.Infinity;
}
}
function Update ()
{
}
function LateUpdate ()
{
}
function FixedUpdate()
{
RaycastSweep();
}
function RaycastSweep()
{
transform.rotation = Quaternion.Euler(0, 0, 0);
var startPos : Vector3 = transform.position; // umm, start position !
var targetPos : Vector3 = Vector3.zero; // variable for calculated end position
var startAngle : float = ( -maxAngle * 0.5 ); // half the angle to the Left of the forward
var finishAngle : float = ( maxAngle * 0.5 ); // half the angle to the Right of the forward
// the gap between each ray (increment)
var inc : float = ( maxAngle / increments );
// step through and find each target point
var hit : RaycastHit;
var targetDistance:float = 215;
var targetRotation:Quaternion;
for (var i : float = startAngle; i < finishAngle; i += inc ) // Angle from forward
{
targetPos = (Quaternion.Euler(i, 0, 0 ) * transform.forward).normalized * rayDistance;
// linecast between points
if ( Physics.Linecast(startPos, targetPos, hit, layer$$anonymous$$ask))
{
var diff = (transform.position - hit.point);
var curDistance = diff.magnitude;
if (curDistance < targetDistance)
{
targetRotation = Quaternion.Euler(i, 0, 0);
targetDistance = curDistance;
Debug.Log( "Hit " + hit.collider.gameObject.name );
}
// to show ray just for testing
Debug.DrawLine( startPos, targetPos, Color.green );
}
// to show ray just for testing
Debug.DrawLine( startPos, targetPos, Color.red );
}
transform.rotation = targetRotation;
}
To further clarify: The "fan" of raycasts, end points and all, should be moving forward with the ship, but the end points are not.
I suspect my problem is in line 51, possibly giving me a world coordinate ins$$anonymous$$d of a relative coordinate... Anyone?
If I understand correctly what you want to do, then your error is in line 36. You are calculating an absolute position for the targetPos. You want tartegPos to follow the ship, so you want something like:
targetPos = transform.position + (Quaternion.Euler(i, 0, 0 ) * transform.forward).normalized * rayDistance;
Your answer
Follow this Question
Related Questions
rotation problems when snapping to terrain 1 Answer
Camera collision detection. 0 Answers
default cylinder to indicate hit.normal 1 Answer
raycast to determine pivot 1 Answer
use the raycast with the rotation 0 Answers