TH1/Unity/Assets/Scripts/TH1_Renderer/ProjectileRenderer.cs

299 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
}
}
}
}
}