Files
PIM_Laba5/Assets/Scripts/EnemyVisionVisualizer.cs
T
2026-05-26 01:16:57 +03:00

215 lines
6.6 KiB
C#

using UnityEngine;
using UnityEngine.Rendering;
public class EnemyVisionVisualizer : MonoBehaviour
{
public EnemyVision vision;
public EnemyStateMachine stateMachine;
public Material visionMaterialTemplate;
public int segmentCount = 28;
public float groundOffset = 0.05f;
private Transform meshRoot;
private MeshFilter meshFilter;
private MeshRenderer meshRenderer;
private Mesh visionMesh;
private Material runtimeMaterial;
private void Awake()
{
vision = vision != null ? vision : GetComponent<EnemyVision>();
stateMachine = stateMachine != null ? stateMachine : GetComponent<EnemyStateMachine>();
EnsureMeshObjects();
RebuildMesh();
UpdateVisualState();
}
private void LateUpdate()
{
EnsureMeshObjects();
UpdateVisualState();
}
private void OnValidate()
{
segmentCount = Mathf.Max(6, segmentCount);
if (!Application.isPlaying)
{
EnsureMeshObjects();
RebuildMesh();
UpdateVisualState();
}
}
private void EnsureMeshObjects()
{
int visualizationLayer = LayerMask.NameToLayer("Vision");
if (visualizationLayer < 0)
{
visualizationLayer = LayerMask.NameToLayer("Ignore Raycast");
}
if (meshRoot == null)
{
Transform existing = transform.Find("VisionCone");
if (existing != null)
{
meshRoot = existing;
}
else
{
GameObject root = new GameObject("VisionCone");
root.transform.SetParent(transform, false);
root.transform.localPosition = new Vector3(0f, groundOffset, 0f);
root.transform.localRotation = Quaternion.identity;
root.layer = visualizationLayer;
meshRoot = root.transform;
}
}
meshRoot.localPosition = new Vector3(0f, groundOffset, 0f);
meshRoot.gameObject.layer = visualizationLayer;
if (meshFilter == null)
{
meshFilter = meshRoot.GetComponent<MeshFilter>();
if (meshFilter == null)
{
meshFilter = meshRoot.gameObject.AddComponent<MeshFilter>();
}
}
if (meshRenderer == null)
{
meshRenderer = meshRoot.GetComponent<MeshRenderer>();
if (meshRenderer == null)
{
meshRenderer = meshRoot.gameObject.AddComponent<MeshRenderer>();
meshRenderer.shadowCastingMode = ShadowCastingMode.Off;
meshRenderer.receiveShadows = false;
}
}
if (visionMesh == null)
{
visionMesh = new Mesh { name = "EnemyVisionCone" };
meshFilter.sharedMesh = visionMesh;
}
if (runtimeMaterial == null)
{
runtimeMaterial = visionMaterialTemplate != null ? new Material(visionMaterialTemplate) : BuildTransparentMaterial();
runtimeMaterial.name = "EnemyVisionRuntimeMaterial";
meshRenderer.sharedMaterial = runtimeMaterial;
}
}
private void RebuildMesh()
{
if (vision == null || visionMesh == null)
{
return;
}
float radius = vision.viewRadius;
float halfAngle = vision.viewAngle * 0.5f;
Vector3[] vertices = new Vector3[segmentCount + 2];
int[] triangles = new int[segmentCount * 3];
Vector2[] uv = new Vector2[vertices.Length];
vertices[0] = Vector3.zero;
uv[0] = new Vector2(0.5f, 0f);
for (int i = 0; i <= segmentCount; i++)
{
float progress = i / (float)segmentCount;
float angle = -halfAngle + progress * vision.viewAngle;
float radians = Mathf.Deg2Rad * angle;
Vector3 direction = new Vector3(Mathf.Sin(radians), 0f, Mathf.Cos(radians));
vertices[i + 1] = direction * radius;
uv[i + 1] = new Vector2(progress, 1f);
if (i == segmentCount)
{
continue;
}
int triangleIndex = i * 3;
triangles[triangleIndex] = 0;
triangles[triangleIndex + 1] = i + 2;
triangles[triangleIndex + 2] = i + 1;
}
visionMesh.Clear();
visionMesh.vertices = vertices;
visionMesh.triangles = triangles;
visionMesh.uv = uv;
visionMesh.RecalculateNormals();
visionMesh.RecalculateBounds();
}
private void UpdateVisualState()
{
if (runtimeMaterial == null || stateMachine == null)
{
return;
}
Color stateColor = new Color(0.2f, 0.95f, 0.35f, 0.22f);
switch (stateMachine.CurrentState)
{
case EnemyStateMachine.State.Suspicion:
stateColor = new Color(1f, 0.85f, 0.2f, 0.24f);
break;
case EnemyStateMachine.State.Alert:
stateColor = new Color(1f, 0.25f, 0.2f, 0.28f);
break;
}
runtimeMaterial.color = stateColor;
if (runtimeMaterial.HasProperty("_Color"))
{
runtimeMaterial.SetColor("_Color", stateColor);
}
}
private Material BuildTransparentMaterial()
{
Shader shader = Shader.Find("Standard");
Material material = new Material(shader);
material.SetFloat("_Mode", 3f);
material.SetInt("_SrcBlend", (int)BlendMode.SrcAlpha);
material.SetInt("_DstBlend", (int)BlendMode.OneMinusSrcAlpha);
material.SetInt("_ZWrite", 0);
material.DisableKeyword("_ALPHATEST_ON");
material.EnableKeyword("_ALPHABLEND_ON");
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
material.renderQueue = (int)RenderQueue.Transparent;
material.color = new Color(0.2f, 0.95f, 0.35f, 0.22f);
return material;
}
private void OnDrawGizmosSelected()
{
if (vision == null)
{
vision = GetComponent<EnemyVision>();
}
if (vision == null)
{
return;
}
Gizmos.color = Color.cyan;
Vector3 origin = transform.position + Vector3.up * 0.1f;
Gizmos.DrawWireSphere(origin, vision.viewRadius);
Quaternion leftRotation = Quaternion.Euler(0f, -vision.viewAngle * 0.5f, 0f);
Quaternion rightRotation = Quaternion.Euler(0f, vision.viewAngle * 0.5f, 0f);
Gizmos.DrawLine(origin, origin + leftRotation * transform.forward * vision.viewRadius);
Gizmos.DrawLine(origin, origin + rightRotation * transform.forward * vision.viewRadius);
}
}