Reputation: 83
I'm using script from that tutorial https://www.youtube.com/watch?v=73Dc5JTCmKI that shows field of of view. But i want to inverse that so the place where you can't white mesh see will be white mesh instead of what i have now,
How to inverse the mesh so in place where is green color (nothing) would be mesh and in place where is mesh there would be no mesh.
My script:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class FieldOfView : MonoBehaviour {
public float viewRadius;
[Range(0,360)]
public float viewAngle;
public LayerMask targetMask;
public LayerMask obstacleMask;
[HideInInspector]
public List<Transform> visibleTargets = new List<Transform>();
public float meshResolution;
public int edgeResolveIterations;
public float edgeDstThreshold;
public float maskCutawayDst = .1f;
public MeshFilter viewMeshFilter;
Mesh viewMesh;
void Start() {
viewMesh = new Mesh ();
viewMesh.name = "View Mesh";
viewMeshFilter.mesh = viewMesh;
StartCoroutine ("FindTargetsWithDelay", .2f);
}
IEnumerator FindTargetsWithDelay(float delay) {
while (true) {
yield return new WaitForSeconds (delay);
FindVisibleTargets ();
}
}
void LateUpdate() {
DrawFieldOfView ();
}
void FindVisibleTargets() {
visibleTargets.Clear ();
Collider[] targetsInViewRadius = Physics.OverlapSphere (transform.position, viewRadius, targetMask);
for (int i = 0; i < targetsInViewRadius.Length; i++) {
Transform target = targetsInViewRadius [i].transform;
Vector3 dirToTarget = (target.position - transform.position).normalized;
if (Vector3.Angle (transform.forward, dirToTarget) < viewAngle / 2) {
float dstToTarget = Vector3.Distance (transform.position, target.position);
if (!Physics.Raycast (transform.position, dirToTarget, dstToTarget, obstacleMask)) {
visibleTargets.Add (target);
}
}
}
}
void DrawFieldOfView() {
int stepCount = Mathf.RoundToInt(viewAngle * meshResolution);
float stepAngleSize = viewAngle / stepCount;
List<Vector3> viewPoints = new List<Vector3> ();
ViewCastInfo oldViewCast = new ViewCastInfo ();
for (int i = 0; i <= stepCount; i++) {
float angle = transform.eulerAngles.y - viewAngle / 2 + stepAngleSize * i;
ViewCastInfo newViewCast = ViewCast (angle);
if (i > 0) {
bool edgeDstThresholdExceeded = Mathf.Abs (oldViewCast.dst - newViewCast.dst) > edgeDstThreshold;
if (oldViewCast.hit != newViewCast.hit || (oldViewCast.hit && newViewCast.hit && edgeDstThresholdExceeded)) {
EdgeInfo edge = FindEdge (oldViewCast, newViewCast);
if (edge.pointA != Vector3.zero) {
viewPoints.Add (edge.pointA);
}
if (edge.pointB != Vector3.zero) {
viewPoints.Add (edge.pointB);
}
}
}
viewPoints.Add (newViewCast.point);
oldViewCast = newViewCast;
}
int vertexCount = viewPoints.Count + 1;
Vector3[] vertices = new Vector3[vertexCount];
int[] triangles = new int[(vertexCount-2) * 3];
vertices [0] = Vector3.zero;
for (int i = 0; i < vertexCount - 1; i++) {
vertices [i + 1] = transform.InverseTransformPoint(viewPoints [i]) + Vector3.forward * maskCutawayDst;
if (i < vertexCount - 2) {
triangles [i * 3] = 0;
triangles [i * 3 + 1] = i + 1;
triangles [i * 3 + 2] = i + 2;
}
}
viewMesh.Clear ();
viewMesh.vertices = vertices;
viewMesh.triangles = triangles;
viewMesh.RecalculateNormals ();
}
EdgeInfo FindEdge(ViewCastInfo minViewCast, ViewCastInfo maxViewCast) {
float minAngle = minViewCast.angle;
float maxAngle = maxViewCast.angle;
Vector3 minPoint = Vector3.zero;
Vector3 maxPoint = Vector3.zero;
for (int i = 0; i < edgeResolveIterations; i++) {
float angle = (minAngle + maxAngle) / 2;
ViewCastInfo newViewCast = ViewCast (angle);
bool edgeDstThresholdExceeded = Mathf.Abs (minViewCast.dst - newViewCast.dst) > edgeDstThreshold;
if (newViewCast.hit == minViewCast.hit && !edgeDstThresholdExceeded) {
minAngle = angle;
minPoint = newViewCast.point;
} else {
maxAngle = angle;
maxPoint = newViewCast.point;
}
}
return new EdgeInfo (minPoint, maxPoint);
}
ViewCastInfo ViewCast(float globalAngle) {
Vector3 dir = DirFromAngle (globalAngle, true);
RaycastHit hit;
if (Physics.Raycast (transform.position, dir, out hit, viewRadius, obstacleMask)) {
return new ViewCastInfo (true, hit.point, hit.distance, globalAngle);
} else {
return new ViewCastInfo (false, transform.position + dir * viewRadius, viewRadius, globalAngle);
}
}
public Vector3 DirFromAngle(float angleInDegrees, bool angleIsGlobal) {
if (!angleIsGlobal) {
angleInDegrees += transform.eulerAngles.y;
}
return new Vector3(Mathf.Sin(angleInDegrees * Mathf.Deg2Rad),0,Mathf.Cos(angleInDegrees * Mathf.Deg2Rad));
}
public struct ViewCastInfo {
public bool hit;
public Vector3 point;
public float dst;
public float angle;
public ViewCastInfo(bool _hit, Vector3 _point, float _dst, float _angle) {
hit = _hit;
point = _point;
dst = _dst;
angle = _angle;
}
}
public struct EdgeInfo {
public Vector3 pointA;
public Vector3 pointB;
public EdgeInfo(Vector3 _pointA, Vector3 _pointB) {
pointA = _pointA;
pointB = _pointB;
}
}
}
I know that i have to change something with triangles but i don't know how to set them up.
vertices [0] = Vector3.zero;
for (int i = 0; i < vertexCount - 1; i++) {
vertices [i + 1] = transform.InverseTransformPoint(viewPoints [i]) + Vector3.forward * maskCutawayDst;
if (i < vertexCount - 2) {
triangles [i * 3] = 0;
triangles [i * 3 + 1] = i + 1;
triangles [i * 3 + 2] = i + 2;
}
}
viewMesh.Clear ();
viewMesh.vertices = vertices;
viewMesh.triangles = triangles;
viewMesh.RecalculateNormals ();
Im sorry but i don't know how to explain it more understandable.
EDIT So I still have some problems with that. Your solution doesn't work for me. First of all, there is no "circleRadius", so I use viewRadius instead of that. Secondly, it gives me argument out of range all the time. My code changed a little bit and here it's how it looks:
int vertexCount = viewPoints.Count + 1;
var vertices = new Vector3[vertexCount];
var triangles = new int[(vertexCount - 2) * 3];
vertices[0] = Vector3.zero;
for (int i = 0; i < vertexCount - 1; i++)
{
vertices[i + 1] = transform.InverseTransformPoint(viewPoints[i]);
if (i < vertexCount - 2)
{
triangles[i * 3] = 0;
triangles[i * 3 + 1] = i + 1;
triangles[i * 3 + 2] = i + 2;
}
}
So in this case i should change it to? :
int vertexCount = viewPoints.Count + 1;
Vector3[] vertices = new Vector3[(vertexCount) * 2];
int[] triangles = new int[(vertexCount) * 6];
for (int i = 0; i < (vertexCount - 1) * 2; i += 2)
{
vertices[i] = transform.InverseTransformPoint(viewPoints[i]);
vertices[i + 1] = Vector3.Normalize(viewPoints[i]) * viewRadius;
}
for (int i = 0; i < (vertexCount - 3) * 2; i += 1)
{
triangles[i * 6 + 0] = i * 4;
triangles[i * 6 + 1] = i * 4 + 1;
triangles[i * 6 + 2] = i * 4 + 2;
triangles[i * 6 + 3] = i * 4 + 1;
triangles[i * 6 + 4] = i * 4 + 2;
triangles[i * 6 + 5] = i * 4 + 3;
}
So I changed my code and it's okay, but when I test it in play mode it gives me error:
ArgumentOutOfRangeException: Argument is out of range.
Parameter name: index
FieldOfViews.DrawFieldOfView () (at Assets/_Game/FieldOfView.cs:111)
and it's on line:
vertices[i] = transform.InverseTransformPoint(viewPoints[i]) + Vector3.forward * maskCutawayDst;
When i removed this lane for testing purposes it also gives the same error on:
vertices[i + 1] = Vector3.Normalize(viewPoints[i]) * circleRadius;
I don't know if that can cause problems, but my maskCutawayDst is 0.1, and circleRadius is 15.
Upvotes: 1
Views: 544
Reputation: 664
With the code:
int vertexCount = viewPoints.Count + 1;
Vector3[] vertices = new Vector3[vertexCount];
int[] triangles = new int[(vertexCount-2) * 3];
vertices [0] = Vector3.zero;
for (int i = 0; i < vertexCount - 1; i++) {
vertices [i + 1] = transform.InverseTransformPoint(viewPoints [i]) + Vector3.forward * maskCutawayDst;
if (i < vertexCount - 2) {
triangles [i * 3] = 0;
triangles [i * 3 + 1] = i + 1;
triangles [i * 3 + 2] = i + 2;
}
}
Change to
int vertexCount = viewPoints.Count + 1;
Vector3[] vertices = new Vector3[(vertexCount) * 2];
int[] triangles = new int[(vertexCount) * 6];
for (int i = 0; i < vertexCount; i++)
{
Vector3 vertex = transform.InverseTransformPoint(viewPoints[(i == viewPoints.Count) ? 0 : i]);
vertices[i * 2] = vertex;
vertices[i * 2 + 1] = vertex.normalized * viewRadius;
}
for (int i = 0; i < (vertexCount); i++)
{
int j = (vertexCount - 1 == i) ? 0 : i;
triangles[i * 6 + 0] = j * 2 + 0;
triangles[i * 6 + 1] = j * 2 + 1;
triangles[i * 6 + 2] = j * 2 + 2;
triangles[i * 6 + 3] = j * 2 + 1;
triangles[i * 6 + 4] = j * 2 + 3;
triangles[i * 6 + 5] = j * 2 + 2;
}
Explanation:
Figure 1: A slice of what is happening
The first program created a triangle in the red area. This was between the middle and the two intersecting rays (intersect the red line). To "inverse" this, we must connect the two lines as a quad to an outside point at a fixed distance. A vector is normalized to have a unit length and then multiplied to have a fixed distance with all points. This is done with the code
for (int i = 0; i < vertexCount; i++)
{
Vector3 vertex = transform.InverseTransformPoint(viewPoints[(i == viewPoints.Count) ? 0 : i]);
vertices[i * 2] = vertex;
vertices[i * 2 + 1] = vertex.normalized * viewRadius;
}
then these vertices are turned into two triangles (to represent a quad). with the following code. Which create the green area.
for (int i = 0; i < (vertexCount); i++)
{
int j = (vertexCount - 1 == i) ? 0 : i;
triangles[i * 6 + 0] = j * 2 + 0;
triangles[i * 6 + 1] = j * 2 + 1;
triangles[i * 6 + 2] = j * 2 + 2;
triangles[i * 6 + 3] = j * 2 + 1;
triangles[i * 6 + 4] = j * 2 + 3;
triangles[i * 6 + 5] = j * 2 + 2;
}
Upvotes: 2