- Home /
Best way to create line of sight
Howdy, I'm making a top-down shooter and I wanted to determine if enemies can see player, so they can shoot him if there is no obstructions. After while of thinking I made script which base on two triggers.
public int lineOfSight;
public GameObject bulletPrefab;
public bool inCloseRange;
float rateOfFireTimer;
float rateOfFire;
PolygonCollider2D sightCollider;
Vector2[] trianglePoints = new Vector2[3];
void Start()
{
sightCollider = gameObject.GetComponent<PolygonCollider2D>();
LineOfSightCalculation();
}
void Update()
{
DetermineDirection();
}
void OnTriggerStay2D(Collider2D col)
{
if (col.tag == "Player")
{
RaycastHit2D inLineOfSigh = Physics2D.Raycast(gameObject.transform.position, GameObject.FindGameObjectWithTag("Player").transform.position - gameObject.transform.position);
//Debug.DrawRay (transform.position, GameObject.FindGameObjectWithTag ("Player").transform.position - transform.position, Color.green, 120);
if (inLineOfSigh.collider.gameObject.tag == "Player")
{
if (col.IsTouching(GetComponent<CircleCollider2D>()))
{
Vector3 kiepo = inLineOfSigh.point;
Vector3 rayDir = kiepo - gameObject.transform.position;
rateOfFireTimer = 0.1f;
if (rayDir.y > rayDir.x && rayDir.x > 0 && rayDir.y > 0 || rayDir.y > Math.Abs(rayDir.x) && rayDir.x < 0 && rayDir.y > 0)
{
FaceUp();
}
if (Math.Abs(rayDir.y) > rayDir.x && rayDir.x > 0 && rayDir.y < 0 || rayDir.y < rayDir.x && rayDir.x < 0 && rayDir.y < 0)
{
FaceDown();
}
if (rayDir.x > rayDir.y && rayDir.y > 0 && rayDir.x > 0 || rayDir.x > Math.Abs(rayDir.y) && rayDir.y < 0 && rayDir.x > 0)
{
FaceRight();
}
if (Math.Abs(rayDir.x) > rayDir.y && rayDir.y > 0 && rayDir.x < 0 || rayDir.x < rayDir.y && rayDir.y < 0 && rayDir.x < 0)
{
FaceLeft();
}
}
if(!col.IsTouching(GetComponent<CircleCollider2D>()))
{
rateOfFireTimer = 0.25f;
}
inCloseRange = true; // won't allow move to another point in route
rateOfFireTimer += Time.deltaTime;
if (rateOfFireTimer >= rateOfFire)
{
Instantiate(bulletPrefab, transform.position, Quaternion.identity);
rateOfFireTimer = 0;
}
gameObject.GetComponent<Rigidbody2D>().velocity = Vector2.zero;
}
}
}
void OnTriggerExit2D(Collider2D col) //quickfix for patrol route bug
{
if (col.tag == "Player" && !col.IsTouching(GetComponent<CircleCollider2D>()))
{
inCloseRange = false;
}
}
void LineOfSightCalculation()
{
Vector2 A,B;
trianglePoints[0] = Vector2.zero;
B.x = (float) -(lineOfSight * Math.Sqrt(2)) /2;
B.y = (float) -(lineOfSight * Math.Sqrt(2)) /2;
A.x = (float) (lineOfSight * Math.Sqrt(2)) /2;
A.y = (float) -(lineOfSight * Math.Sqrt(2)) /2;
trianglePoints[1] = B;
trianglePoints[2] = A;
sightCollider.SetPath(0, trianglePoints);
}
void DetermineDirection()
{
var localVel = gameObject.transform.InverseTransformDirection(gameObject.GetComponent<Rigidbody2D>().velocity);
if (localVel.x > 0 && System.Math.Abs(localVel.x) > System.Math.Abs(localVel.y))
{
FaceRight();
}
if (localVel.x < 0 && System.Math.Abs(localVel.x) > System.Math.Abs(localVel.y))
{
FaceLeft();
}
if (localVel.y > 0 && System.Math.Abs(localVel.y) > System.Math.Abs(localVel.x))
{
FaceUp();
}
if (localVel.y < 0 && System.Math.Abs(localVel.y) > System.Math.Abs(localVel.x))
{
FaceDown();
}
}
void FaceRight()
{
trianglePoints [0] = Vector2.zero;
trianglePoints [1] = new Vector2 (Math.Abs(trianglePoints[1].x), Math.Abs(trianglePoints[1].y));
trianglePoints [2] = new Vector2 (Math.Abs(trianglePoints[1].x), -Math.Abs(trianglePoints[1].y));
sightCollider.SetPath (0, trianglePoints);
//print ("going right");
GetComponent<Animator> ().Play ("enemy_right", 0);
}
void FaceLeft()
{
trianglePoints [0] = Vector2.zero;
trianglePoints [1] = new Vector2 (-Math.Abs (trianglePoints [1].x), Math.Abs (trianglePoints [1].y));
trianglePoints [2] = new Vector2 (-Math.Abs (trianglePoints [1].x), -Math.Abs (trianglePoints [1].y));
sightCollider.SetPath (0, trianglePoints);
//print ("going left");
GetComponent<Animator> ().Play ("enemy_left", 0);
}
void FaceUp()
{
trianglePoints [0] = Vector2.zero;
trianglePoints [1] = new Vector2 (-Math.Abs(trianglePoints[1].x), Math.Abs(trianglePoints[1].y));
trianglePoints [2] = new Vector2 (Math.Abs(trianglePoints[1].x), Math.Abs(trianglePoints[1].y));
sightCollider.SetPath (0, trianglePoints);
//print ("going up");
GetComponent<Animator>().Play ("enemy_up", 0);
}
void FaceDown()
{
trianglePoints [0] = Vector2.zero;
trianglePoints [1] = new Vector2 (-Math.Abs(trianglePoints [1].x), -Math.Abs(trianglePoints[1].y));
trianglePoints [2] = new Vector2 (Math.Abs(trianglePoints [1].x), -Math.Abs(trianglePoints[1].y));
sightCollider.SetPath (0, trianglePoints);
//print ("going down");
GetComponent<Animator>().Play ("enemy_down", 0);
}
So generally, when npc is going north he have triangular trigger located above of him and litte circle trigger around him. When player walks into his triangle he stops and start shoting, when player exit trigger he continue his route. And when player enter that circle npc turns around so he can shoot him, and then player must again exit triangular trigger.
But there is one problem. It's working, enemy dont shoot you through the walls, animations also are playing in right moments, but i don't like the way it is working. I just wanted to know if there is any better way to do this (and i suppose there is plenty of ways).
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
Launching a player in a certain direction - C# 1 Answer
Illuminating a 3D object's edges OnMouseOver (script in c#)? 1 Answer
Detecting collisions 3 Answers