using Games.Scene; using UnityEngine; // 注:Unity不支持在运行时获得材质Property列表,因此具体每个链接特效都需要按材质属性订做 // 规格统一为,所有Mesh类型Y方向不翻转,所有Particle类型Y方向翻转90度 // 该虚构类型仅作为接口使用 public abstract class LinkFxController : MonoBehaviour { protected abstract bool isBillboard { get; } private float _baseLength; private Vector3 _baseScale; private float _currentRotation; protected LinkFxMaterialRecord[] materialRecords; protected ParticleRecord[] particleRecords; public bool revertLink; public float rotationSpeed; protected float endTime; [HideInInspector] public Vector3 sourcePoint; [HideInInspector] public Vector3 targetPoint; private void Awake() { RegisterParticleSystems(); var normalMeshRenderer = RegisterMeshMaterials(); _baseScale = transform.localScale; _baseLength = normalMeshRenderer.bounds.size.x; } private void OnDestroy() { for (var i = 0; i < materialRecords.Length; i++) materialRecords[i].DestroyMaterial(); } protected void RegisterParticleSystems() { // 不节约那几毛钱的运算量了 var particleSystems = transform.GetComponentsInChildren(false); particleRecords = new ParticleRecord[particleSystems.Length]; for (var i = 0; i < particleSystems.Length; i++) particleRecords[i] = new ParticleRecord(particleSystems[i]); } protected abstract MeshRenderer RegisterMeshMaterials(); public virtual bool TryUpdateLink() { // 源角色或者目标不存在时,隐藏锁链效果 if (sourcePoint == targetPoint) { transform.localScale = Vector3.zero; return false; } else { _currentRotation += Time.deltaTime * rotationSpeed; // 计算面板位置 var delta = sourcePoint - targetPoint; if (revertLink) delta = -delta; var valid = false; if (delta != Vector3.zero) { var scale = delta.magnitude / _baseLength; transform.position = (sourcePoint + targetPoint) * 0.5f; transform.localScale = new Vector3(_baseScale.x * scale, _baseScale.y, _baseScale.z); // 计算面板UV for (var i = 0; i < particleRecords.Length; i++) particleRecords[i].SetScale(scale); for (var i = 0; i < materialRecords.Length; i++) materialRecords[i].SetScale(scale); // 计算面板方向 - 面板修改为定时旋转 var x = delta.normalized; if (isBillboard && SceneLogic.CameraController != null && SceneLogic.CameraController.MainCamera != null) { var z = -SceneLogic.CameraController.MainCamera.transform.forward; var y = Vector3.Cross(z, x); if (y != Vector3.zero) { valid = true; z = Vector3.Cross(x, y); transform.rotation = Quaternion.LookRotation(z, y); } } else { var y = Vector3.up; var z = Vector3.Cross(x, y); if (z != Vector3.zero) { valid = true; // 将z方向按照旋转角度校正 z = Quaternion.AngleAxis(_currentRotation, x) * z; y = Vector3.Cross(z, x); //y = Quaternion.AngleAxis(currentRotation, x) * y; transform.rotation = Quaternion.LookRotation(z, y); } } } // 连线长度为0或者同摄像机视线完全平行 if (!valid) transform.localScale = Vector3.zero; return valid; } } /// /// 部分需要在结束时做出特殊表现的锁链需要结束时间 /// public void ResetEndTime(float pEndTime) { endTime = pEndTime; } protected class ParticleRecord { public readonly Vector3 boxSize; public readonly float emission; public readonly ParticleSystem particleSystem; public ParticleRecord(ParticleSystem particle) { particleSystem = particle; boxSize = particle.shape.scale; emission = particle.emission.rate.constant; } public void SetScale(float scale) { var newBoxSize = boxSize; newBoxSize.z *= scale; var newShape = particleSystem.shape; newShape.scale = newBoxSize; var emissionModule = particleSystem.emission; var rate = emissionModule.rate; rate.constant = emission * scale; emissionModule.rate = rate; } } protected class LinkFxTextureRecord { public readonly Vector2 baseTiling; public readonly string propertyName; public LinkFxTextureRecord(string propertyName, Vector2 baseTiling) { this.propertyName = propertyName; this.baseTiling = baseTiling; } } protected class LinkFxMaterialRecord { public readonly Material material; public readonly LinkFxTextureRecord[] textures; public LinkFxMaterialRecord(Material material, string[] textureNames) { this.material = material; textures = new LinkFxTextureRecord[textureNames.Length]; for (var i = 0; i < textures.Length; i++) { var tiling = material.GetTextureScale(textureNames[i]); textures[i] = new LinkFxTextureRecord(textureNames[i], tiling); } } public void SetScale(float scale) { for (var i = 0; i < textures.Length; i++) { var tiling = textures[i].baseTiling; tiling.x *= scale; material.SetTextureScale(textures[i].propertyName, tiling); } } public void DestroyMaterial() { Destroy(material); } } }