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(); 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); } 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; //如果是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); } } } } }