299 lines
12 KiB
C#
299 lines
12 KiB
C#
using UnityEngine;
|
||
using TMPro;
|
||
using RuntimeData;
|
||
using Logic;
|
||
|
||
namespace TH1Renderer
|
||
{
|
||
public class ProjectileRenderer
|
||
{
|
||
|
||
private GameObject _ROprojectile;
|
||
private ProjectileData _projectileData;
|
||
|
||
//------- Update缓存数据 -------//
|
||
private UnitData _unitData;
|
||
private GridData _gridData;
|
||
private PlayerData _playerData;
|
||
|
||
// 移动相关变量
|
||
private bool _needMove = false;
|
||
private Vector3 moveStartPos;
|
||
private Vector3 moveEndPos;
|
||
private float moveTime = 0f;
|
||
private float moveFullTime = 1f;
|
||
|
||
public ProjectileRenderer(GameObject prefab,Transform father, ProjectileData projectileData)
|
||
{
|
||
_projectileData = projectileData;
|
||
_ROprojectile = GameObject.Instantiate(prefab, projectileData.StartPos, Quaternion.identity, father);
|
||
//每个projectile从创生就要开始move
|
||
_needMove = true;
|
||
moveTime = 0f;
|
||
moveStartPos = projectileData.StartPos;
|
||
moveEndPos = projectileData.EndPos;
|
||
|
||
foreach (Transform child in _ROprojectile.transform)
|
||
child.gameObject.SetActive(false);
|
||
|
||
|
||
Sprite sp = null;
|
||
|
||
if (Table.Instance.ProjectileTypeDataAssets.GetProjectileTypeInfo(projectileData.ProjectileType,
|
||
out var info))
|
||
{
|
||
sp = info.Img;
|
||
moveFullTime = info.AnimTime * projectileData.Dis;
|
||
}
|
||
|
||
|
||
//TODO 迭代这里更新projectile icon 的方法,此外还要做projectile的管理池!
|
||
string tmpName = projectileData.ProjectileType.ToString();
|
||
var oldspr = _ROprojectile.transform.Find(tmpName);
|
||
if (oldspr != null)
|
||
oldspr.gameObject.SetActive(true);
|
||
else
|
||
{
|
||
// TODO 暂时采用兼容的方案了
|
||
var spr = _ROprojectile.transform.Find("SpAttack")?.GetComponent<SpriteRenderer>();
|
||
if (spr != null)
|
||
{
|
||
spr.sprite = sp;
|
||
spr.gameObject.SetActive(true);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
if (projectileData.ProjectileType == ProjectileType.Coin)
|
||
{
|
||
_ROprojectile.transform.Find("Coin").gameObject.SetActive(true);
|
||
moveFullTime = Table.Instance.AnimDataAssets.ProjectileCoinMoveTime * projectileData.Dis;
|
||
}
|
||
else if (projectileData.ProjectileType == ProjectileType.Faith)
|
||
{
|
||
_ROprojectile.transform.Find("Faith").gameObject.SetActive(true);
|
||
moveFullTime = Table.Instance.AnimDataAssets.ProjectileCoinMoveTime * projectileData.Dis;
|
||
}
|
||
|
||
|
||
|
||
}
|
||
|
||
|
||
public GameObject GetROProjectile() { return _ROprojectile; }
|
||
|
||
public void Die()
|
||
{
|
||
GameObject.Destroy(_ROprojectile.gameObject);
|
||
}
|
||
|
||
private static Vector3 CubicBezier(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
|
||
{
|
||
float u = 1f - t;
|
||
return u * u * u * p0
|
||
+ 3f * u * u * t * p1
|
||
+ 3f * u * t * t * p2
|
||
+ t * t * t * p3;
|
||
}
|
||
|
||
public void Update()
|
||
{
|
||
// 如果需要移动
|
||
if (_needMove)
|
||
{
|
||
float parabola_height = 0f;
|
||
float time_para = 1f;
|
||
if (_projectileData.ProjectileMoveType == ProjectileMoveType.Parabola)
|
||
parabola_height = 0.3f;
|
||
if (_projectileData.ProjectileMoveType is ProjectileMoveType.HighParabola or ProjectileMoveType.SpinHighParabola)
|
||
parabola_height = 0.5f;
|
||
if (_projectileData.ProjectileMoveType == ProjectileMoveType.CityExpHighParabola)
|
||
parabola_height = 0.4f;
|
||
if (_projectileData.ProjectileMoveType == ProjectileMoveType.CoinParabola)
|
||
{
|
||
parabola_height = -0.1f;
|
||
if (_projectileData.ProjectileType == ProjectileType.Faith)
|
||
parabola_height = 0f;
|
||
}
|
||
|
||
|
||
|
||
Vector3 linearPos;
|
||
Vector3 finalPos;
|
||
float arcHeight;
|
||
float arcOffset;
|
||
float t;
|
||
if (_projectileData.ProjectileMoveType == ProjectileMoveType.OrbBezier)
|
||
{
|
||
moveTime += Time.deltaTime / Mathf.Max(0.01f, moveFullTime);
|
||
t = Mathf.Clamp01(moveTime);
|
||
float easedT = Mathf.SmoothStep(0f, 1f, t);
|
||
|
||
Vector3 delta = moveEndPos - moveStartPos;
|
||
float distance = Mathf.Max(delta.magnitude, 0.01f);
|
||
Vector3 direction = delta / distance;
|
||
Vector3 normal = new Vector3(-direction.y, direction.x, 0f);
|
||
float side = delta.x + delta.y >= 0f ? 1f : -1f;
|
||
float sideOffset = Mathf.Clamp(distance * 0.22f, 0.35f, 1.45f);
|
||
float liftOffset = Mathf.Clamp(distance * 0.08f, 0.2f, 0.75f);
|
||
|
||
Vector3 p1 = moveStartPos + normal * (side * sideOffset) + new Vector3(0f, liftOffset, 0f);
|
||
Vector3 p2 = Vector3.Lerp(moveStartPos, moveEndPos, 0.62f)
|
||
+ normal * (side * sideOffset * 1.65f)
|
||
+ new Vector3(0f, liftOffset * 0.75f, 0f);
|
||
finalPos = CubicBezier(moveStartPos, p1, p2, moveEndPos, easedT);
|
||
|
||
_ROprojectile.transform.Rotate(0, 0, 720f * Time.deltaTime);
|
||
_ROprojectile.transform.position = finalPos;
|
||
|
||
if (moveTime >= 1.0f)
|
||
{
|
||
_needMove = false;
|
||
GameObject.Destroy(_ROprojectile);
|
||
}
|
||
|
||
return;
|
||
}
|
||
//如果是coin 或者 faith 完全单独处理
|
||
if (_projectileData.ProjectileMoveType == ProjectileMoveType.CoinParabola)
|
||
{
|
||
var startPos = moveStartPos;
|
||
startPos.y -= 5f;
|
||
if (_projectileData.ProjectileType == ProjectileType.Faith)
|
||
startPos.y += 5f;
|
||
var middlePos = startPos;
|
||
|
||
middlePos.y += 1f;
|
||
// 增加时间
|
||
moveTime += Time.deltaTime / moveFullTime;
|
||
float showTime = 0.3f;
|
||
if (_projectileData.ProjectileType == ProjectileType.Faith)
|
||
showTime = 0.5f;
|
||
//前showTime%的时间轻微移动
|
||
if (moveTime < showTime)
|
||
{
|
||
t = Mathf.Clamp01(moveTime/showTime);
|
||
|
||
// 计算水平线性插值位置
|
||
linearPos = Vector3.Lerp(startPos, middlePos, t);
|
||
|
||
// 添加抛物线效果 (在y轴方向)
|
||
// 使用sin函数创建一个弧形,在中间点达到最高
|
||
arcHeight = Vector3.Distance(startPos, middlePos) * parabola_height; // 弧高为距离的20%
|
||
arcOffset = Mathf.Sin(t * Mathf.PI) * arcHeight;
|
||
|
||
// 最终位置 = 线性插值位置 + 抛物线高度偏移
|
||
finalPos = new Vector3(
|
||
linearPos.x,
|
||
linearPos.y + arcOffset,
|
||
linearPos.z
|
||
);
|
||
|
||
// 设置投射物位置
|
||
_ROprojectile.transform.position = finalPos;
|
||
}
|
||
//后70%的时间才做抛物线
|
||
else
|
||
{
|
||
// 计算当前位置的插值因子 (0到1之间)
|
||
t = Mathf.Clamp01((moveTime - showTime) / (1f - showTime));
|
||
|
||
// 计算水平线性插值位置
|
||
linearPos = Vector3.Lerp(middlePos, moveEndPos, t);
|
||
|
||
// 添加抛物线效果 (在y轴方向)
|
||
// 使用sin函数创建一个弧形,在中间点达到最高
|
||
arcHeight = Vector3.Distance(middlePos, moveEndPos) * parabola_height; // 弧高为距离的20%
|
||
arcOffset = Mathf.Sin(t * Mathf.PI) * arcHeight;
|
||
|
||
// 最终位置 = 线性插值位置 + 抛物线高度偏移
|
||
finalPos = new Vector3(
|
||
linearPos.x,
|
||
linearPos.y + arcOffset,
|
||
linearPos.z
|
||
);
|
||
|
||
// 设置投射物位置
|
||
_ROprojectile.transform.position = finalPos;
|
||
}
|
||
|
||
|
||
// 如果移动完成
|
||
if (moveTime >= 1.0f)
|
||
{
|
||
_needMove = false;
|
||
// 延迟销毁投射物
|
||
GameObject.Destroy(_ROprojectile);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//处理其他的运动曲线
|
||
|
||
// 增加时间
|
||
moveTime += Time.deltaTime / moveFullTime;
|
||
|
||
// 计算当前位置的插值因子 (0到1之间)
|
||
t = Mathf.Clamp01(moveTime);
|
||
|
||
// 计算水平线性插值位置
|
||
linearPos = Vector3.Lerp(moveStartPos, moveEndPos, t);
|
||
|
||
// 添加抛物线效果 (在y轴方向)
|
||
// 使用sin函数创建一个弧形,在中间点达到最高
|
||
arcHeight = Vector3.Distance(moveStartPos, moveEndPos) * parabola_height; // 弧高为距离的20%
|
||
arcOffset = Mathf.Sin(t * Mathf.PI) * arcHeight;
|
||
|
||
// 最终位置 = 线性插值位置 + 抛物线高度偏移
|
||
finalPos = new Vector3(
|
||
linearPos.x,
|
||
linearPos.y + arcOffset,
|
||
linearPos.z
|
||
);
|
||
|
||
|
||
|
||
// 根据移动方向更新投射物的旋转
|
||
if (_projectileData.ProjectileMoveType != ProjectileMoveType.CityExpHighParabola && t < 0.99f && _projectileData.ProjectileMoveType != ProjectileMoveType.SpinHighParabola)
|
||
{
|
||
// 运动方向 = 当前帧目标位置 - 上一帧的实际位置
|
||
Vector3 direction = finalPos - _ROprojectile.transform.position;
|
||
|
||
if (direction.sqrMagnitude > 0.0001f) // 避免方向向量过小或为零时进行计算
|
||
{
|
||
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
|
||
// 你的箭头精灵默认朝上(Y轴正方向),因此需减去90度以对齐移动方向
|
||
_ROprojectile.transform.rotation = Quaternion.Euler(0, 0, angle - 90);
|
||
}
|
||
}
|
||
else
|
||
// 如果是SpinHighParabola
|
||
if (_projectileData.ProjectileMoveType == ProjectileMoveType.SpinHighParabola && t < 0.99f)
|
||
{
|
||
float _spinSpeed = 2000f;
|
||
//Vector3 direction = finalPos - _ROprojectile.transform.position;
|
||
//float angle = Mathf.Atan2(direction.y, direction.x) * Time.deltaTime;//Mathf.Rad2Deg;
|
||
_ROprojectile.transform.Rotate(0, 0, _spinSpeed * Time.deltaTime);
|
||
}
|
||
|
||
|
||
// 在旋转计算完毕后,最后再更新投射物的当前帧位置
|
||
// 设置投射物位置
|
||
_ROprojectile.transform.position = finalPos;
|
||
|
||
// 如果移动完成
|
||
if (moveTime >= 1.0f)
|
||
{
|
||
_needMove = false;
|
||
// 延迟销毁投射物
|
||
GameObject.Destroy(_ROprojectile);
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|