Загрузка данных


using System;
using UnityEngine;

namespace Sbrt.Av.Gameplay
{
	[DisallowMultipleComponent]
	[RequireComponent(typeof(MeshFilter))]
	public class MeshHeightStretcher : MonoBehaviour
	{
		[SerializeField] private bool recalculateNormals = true;
		[SerializeField] private bool recalculateTangents = true;
		[SerializeField] private bool updateMeshCollider = true;

		private MeshFilter _meshFilter;
		private MeshCollider _meshCollider;
		private Mesh _runtimeMesh;
		private Vector3[] _originalVertices;
		private float _originalMinY;
		private float _originalHeight;
		private bool _isInitialized;

		private void Awake() =>
			Initialize();

		public void Stretch(float heightMultiplier)
		{
			Initialize();

			if (heightMultiplier <= 0f)
				throw new ArgumentOutOfRangeException(nameof(heightMultiplier),
					"Height multiplier must be greater than zero.");

			if (_originalHeight <= Mathf.Epsilon)
				return;

			var stretchedVertices = new Vector3[_originalVertices.Length];

			for (int vertexIndex = 0; vertexIndex < _originalVertices.Length; vertexIndex++)
			{
				var originalVertex = _originalVertices[vertexIndex];
				var offsetFromBottom = originalVertex.y - _originalMinY;
				var stretchedY = _originalMinY + offsetFromBottom * heightMultiplier;

				stretchedVertices[vertexIndex] = new Vector3(
					originalVertex.x,
					stretchedY,
					originalVertex.z);
			}

			ApplyVertices(stretchedVertices);
		}

		public void ResetStretch()
		{
			Initialize();

			var restoredVertices = new Vector3[_originalVertices.Length];
			Array.Copy(_originalVertices, restoredVertices, _originalVertices.Length);

			ApplyVertices(restoredVertices);
		}

		private void Initialize()
		{
			if (_isInitialized)
				return;

			_meshFilter = GetComponent<MeshFilter>();
			_meshCollider = GetComponent<MeshCollider>();

			if (_meshFilter.sharedMesh == null)
				throw new InvalidOperationException($"Object '{name}' does not contain a mesh in MeshFilter.");

			_runtimeMesh = Instantiate(_meshFilter.sharedMesh);
			_runtimeMesh.name = $"{_meshFilter.sharedMesh.name}_RuntimeStretched";
			_meshFilter.sharedMesh = _runtimeMesh;

			_originalVertices = _runtimeMesh.vertices;

			if (_originalVertices == null || _originalVertices.Length == 0)
				throw new InvalidOperationException($"Mesh on object '{name}' does not contain vertices.");

			_originalMinY = float.MaxValue;
			var originalMaxY = float.MinValue;

			for (int vertexIndex = 0; vertexIndex < _originalVertices.Length; vertexIndex++)
			{
				var vertexY = _originalVertices[vertexIndex].y;

				if (vertexY < _originalMinY)
					_originalMinY = vertexY;

				if (vertexY > originalMaxY)
					originalMaxY = vertexY;
			}

			_originalHeight = originalMaxY - _originalMinY;
			_isInitialized = true;

			UpdateMeshCollider();
		}

		private void ApplyVertices(Vector3[] vertices)
		{
			_runtimeMesh.vertices = vertices;
			_runtimeMesh.RecalculateBounds();

			if (recalculateNormals)
				_runtimeMesh.RecalculateNormals();

			if (recalculateTangents)
				_runtimeMesh.RecalculateTangents();

			UpdateMeshCollider();
		}

		private void UpdateMeshCollider()
		{
			if (!updateMeshCollider || _meshCollider == null)
				return;

			_meshCollider.sharedMesh = null;
			_meshCollider.sharedMesh = _runtimeMesh;
		}

		private void OnDestroy()
		{
			if (_runtimeMesh == null)
				return;

			if (Application.isPlaying)
				Destroy(_runtimeMesh);
			else
				DestroyImmediate(_runtimeMesh);
		}
	}
}