Загрузка данных
using System;
using UnityEngine;
namespace Sbrt.Av.Gameplay
{
[DisallowMultipleComponent]
[RequireComponent(typeof(MeshFilter))]
public class MeshHeightStretcher : MonoBehaviour
{
[SerializeField] private float bottomVertexTolerance = 0.0001f;
[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 bool[] _isBottomVertex;
private float _originalMinY;
private float _originalMaxY;
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 extraHeight = _originalHeight * (heightMultiplier - 1f);
var stretchedVertices = new Vector3[_originalVertices.Length];
for (int vertexIndex = 0; vertexIndex < _originalVertices.Length; vertexIndex++)
{
var originalVertex = _originalVertices[vertexIndex];
var resultY = originalVertex.y;
// Шаг 1. Нижние вершины тянем вниз.
if (_isBottomVertex[vertexIndex])
resultY -= extraHeight;
// Шаг 2. Весь меш поднимаем вверх.
resultY += extraHeight;
stretchedVertices[vertexIndex] = new Vector3(
originalVertex.x,
resultY,
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;
_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;
_isBottomVertex = new bool[_originalVertices.Length];
for (int vertexIndex = 0; vertexIndex < _originalVertices.Length; vertexIndex++)
{
_isBottomVertex[vertexIndex] =
Mathf.Abs(_originalVertices[vertexIndex].y - _originalMinY) <= bottomVertexTolerance;
}
_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);
}
}
}