188 lines
6.4 KiB
C#
188 lines
6.4 KiB
C#
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<ParticleSystem>(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;
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 部分需要在结束时做出特殊表现的锁链需要结束时间
|
||
/// </summary>
|
||
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);
|
||
}
|
||
}
|
||
} |