836 lines
28 KiB
C#
836 lines
28 KiB
C#
using Assets.Scripts;
|
||
using DG.Tweening;
|
||
using DG.Tweening.Core;
|
||
using DG.Tweening.Plugins.Options;
|
||
using System.Collections;
|
||
using System.Collections.Concurrent;
|
||
using System.Collections.Generic;
|
||
using System.Data;
|
||
using System.Linq;
|
||
using Unity.VisualScripting;
|
||
using UnityEngine;
|
||
using UnityEngine.InputSystem;
|
||
using UnityEngine.InputSystem.EnhancedTouch;
|
||
using UnityEngine.UIElements;
|
||
using static UnityEngine.GraphicsBuffer;
|
||
|
||
public class Shot : MonoBehaviour
|
||
{
|
||
public RingManager ringManager;
|
||
public Transform impact;
|
||
public Transform muzzleFlare;
|
||
public Transform bullet;
|
||
|
||
[Tooltip("炮弹连续穿透并自动射击附近的敌人的次数")]
|
||
public int continueShotCount = 3;
|
||
|
||
public Transform turret;
|
||
public Transform gun;
|
||
[Tooltip("发射音效")]
|
||
public AudioClip shotAudioClip;
|
||
[Tooltip("击中音效")]
|
||
public AudioClip hitAudioClip;
|
||
[Tooltip("发射时间间隔")]
|
||
public float shotDuration = 0.2f;
|
||
[Tooltip("连发发射时间间隔")]
|
||
public float shotNextDuration = 0.1f;
|
||
[Tooltip("发射距离")]
|
||
public float shotDistance = 50f;
|
||
[Tooltip("炮弹速度")]
|
||
public float shotSpeed = 1;
|
||
[Tooltip("初始化炮弹数量")]
|
||
public int initBulletCount = 300; // 修改为int类型
|
||
[Tooltip("发射Z坐标偏移前移动倍数,相当于乘以gun.forward")]
|
||
public float shotOffsetZMul = 0;
|
||
|
||
[Tooltip("发射X坐标角度修正")]
|
||
public float shotFixAngleX = 0;
|
||
|
||
[Tooltip("炮台转向,炮管锁定速度")]
|
||
public float lerpSpeed = 5f;
|
||
[Tooltip("是否自动跟踪锁定最近的敌人")]
|
||
public bool isAutoLock = false;
|
||
//[Tooltip("子弹最多多少秒后自动销毁")]
|
||
//public float bulletMaxDuration = 3;
|
||
|
||
[Header("输入绑定")]
|
||
[SerializeField] private InputActionReference pressAction;
|
||
|
||
private bool isShoting = false;
|
||
//private Coroutine shotCoroutine = null;
|
||
//private bool isInitLookedEnemy = false;
|
||
|
||
//// 跟踪正在使用的对象,防止重复分配
|
||
//private HashSet<Transform> activeBullets = new HashSet<Transform>();
|
||
//private HashSet<Transform> activeMuzzles = new HashSet<Transform>();
|
||
private Vector3 turretLerpEuler = Vector3.zero;
|
||
private float smoothXAngle = 0f;
|
||
private float targetXAngle = 0f;
|
||
private float hitDistance = 0.2f; // 命中检测距离阈值
|
||
private bool isCanShot = false;
|
||
private bool isStartAttack = false;
|
||
private ConcurrentQueue<NextShotInfo> nextShotInfos = new();
|
||
private QiuObject[] _qiuObjects = new QiuObject[] { };
|
||
|
||
|
||
|
||
// 使用Queue代替List,避免重复分配同一对象
|
||
public List<Transform> bulletPool = new();
|
||
public List<Transform> muzzlePool = new();
|
||
public List<Transform> impactPool = new();
|
||
public List<Transform> activeBulletPool = new();
|
||
public List<Transform> activeImpactPool = new();
|
||
|
||
|
||
private Camera _cam = null;
|
||
void Awake()
|
||
{
|
||
if (turret == null)
|
||
{
|
||
turret = new GameObject("Turret").transform;
|
||
turret.SetParent(transform);
|
||
turret.localPosition = Vector3.zero;
|
||
turret.localEulerAngles = Vector3.zero;
|
||
turret.localScale = Vector3.one;
|
||
}
|
||
|
||
if (gun == null)
|
||
{
|
||
gun = new GameObject("Gun").transform;
|
||
gun.SetParent(turret);
|
||
gun.localPosition = Vector3.zero;
|
||
gun.localScale = Vector3.one;
|
||
gun.forward = turret.forward;
|
||
}
|
||
|
||
turretLerpEuler = turret.eulerAngles;
|
||
smoothXAngle = gun.localEulerAngles.x;
|
||
isCanShot = true;// !isAutoLock;//如果不是自动锁定,开火按钮设成True
|
||
_cam = Camera.main;
|
||
|
||
if (transform.parent != null)
|
||
{
|
||
_qiuObjects = transform.parent.GetComponentsInChildren<QiuObject>();
|
||
}
|
||
// 初始化对象池(使用Queue而非全局List)
|
||
InitializePool();
|
||
|
||
}
|
||
|
||
void Start()
|
||
{
|
||
StartCoroutine(ContinueShot());
|
||
StartCoroutine(QueueNextShot());
|
||
}
|
||
|
||
private void ClearCachePool()
|
||
{
|
||
foreach (var bullet in this.activeBulletPool)
|
||
{
|
||
var bulletComp = bullet.GetComponent<Bullet>();
|
||
bulletComp.IsNeedDestroyInRecycle = true;
|
||
}
|
||
// 清理所有对象
|
||
foreach (var bullet in this.bulletPool)
|
||
{
|
||
if (bullet != null)
|
||
{
|
||
bullet.DOKill(true);
|
||
Destroy(bullet.gameObject);
|
||
}
|
||
}
|
||
|
||
foreach (var muzzle in this.muzzlePool)
|
||
{
|
||
if (muzzle != null)
|
||
{
|
||
muzzle.DOKill();
|
||
Destroy(muzzle.gameObject);
|
||
}
|
||
}
|
||
|
||
foreach (var impact in this.activeImpactPool)
|
||
{
|
||
if (impact != null)
|
||
{
|
||
impact.DOKill();
|
||
var recycle = impact.GetComponent<RecycleImpact>();
|
||
recycle.IsNeedDestroyInRecycle = true;
|
||
}
|
||
}
|
||
foreach (var impact in this.impactPool)
|
||
{
|
||
if (impact != null)
|
||
{
|
||
impact.DOKill(true);
|
||
Destroy(impact.gameObject);
|
||
}
|
||
}
|
||
|
||
|
||
//foreach (var bullet in activeBullets)
|
||
//{
|
||
// if (bullet != null)
|
||
// {
|
||
// bullet.DOKill();
|
||
// Destroy(bullet.gameObject);
|
||
// }
|
||
//}
|
||
|
||
//foreach (var muzzle in activeMuzzles)
|
||
//{
|
||
// if (muzzle != null)
|
||
// {
|
||
// muzzle.DOKill();
|
||
// Destroy(muzzle.gameObject);
|
||
// }
|
||
//}
|
||
// 清空旧数据
|
||
this.activeImpactPool.Clear();
|
||
this.activeBulletPool.Clear();
|
||
this.bulletPool.Clear();
|
||
this.muzzlePool.Clear();
|
||
this.impactPool.Clear();
|
||
}
|
||
|
||
private void RecycleImpactToPool(Transform impactTransform)
|
||
{
|
||
PoolTool.RecycleImpactToPool(impactTransform, impactPool);
|
||
}
|
||
|
||
public void InitializePool()
|
||
{
|
||
//activeBullets.Clear();
|
||
//activeMuzzles.Clear();
|
||
|
||
ClearCachePool();
|
||
// 创建子弹池
|
||
for (int i = 0; i < initBulletCount; i++)
|
||
{
|
||
// 子弹对象
|
||
var bulletObj = Instantiate(bullet);
|
||
bulletObj.gameObject.SetActive(false);
|
||
bulletObj.SetParent(null); // 强制设为世界对象
|
||
bulletObj.name = $"Bullet_{i}";
|
||
this.bulletPool.Add(bulletObj);
|
||
|
||
// 枪口火焰对象
|
||
var muzzleObj = Instantiate(muzzleFlare);
|
||
muzzleObj.gameObject.SetActive(false);
|
||
muzzleObj.SetParent(null);
|
||
muzzleObj.name = $"Muzzle_{i}";
|
||
this.muzzlePool.Add(muzzleObj);
|
||
|
||
for (int j = 0; j < Mathf.Max(1, continueShotCount / 3); j++)
|
||
{
|
||
// 爆炸火焰对象
|
||
var impactObj = Instantiate(impact);
|
||
impactObj.gameObject.SetActive(false);
|
||
impactObj.SetParent(null);
|
||
impactObj.name = $"Impact_{i}";
|
||
impactObj.GetComponent<RecycleImpact>().RecycleAction = this.RecycleImpactToPool;
|
||
this.impactPool.Add(impactObj);
|
||
}
|
||
}
|
||
//for (int i = 0; i < _qiuObjects.Length; i++)
|
||
//{
|
||
// _qiuObjects[i].impactPrefab = impact;
|
||
//}
|
||
}
|
||
|
||
private void OnDestroy()
|
||
{
|
||
// 停止协程
|
||
//if (shotCoroutine != null)
|
||
//{
|
||
// StopCoroutine(shotCoroutine);
|
||
//}
|
||
StopAllCoroutines();
|
||
}
|
||
|
||
private void OnEnable()
|
||
{
|
||
pressAction.action.performed += PressAction_performed;
|
||
pressAction.action.canceled += PressAction_canceled;
|
||
pressAction.action.Enable();
|
||
}
|
||
|
||
private void PressAction_performed(InputAction.CallbackContext ctx)
|
||
{
|
||
isShoting = true;
|
||
}
|
||
|
||
private void PressAction_canceled(InputAction.CallbackContext ctx)
|
||
{
|
||
isShoting = false;
|
||
}
|
||
|
||
private IEnumerator ContinueShot()
|
||
{
|
||
//float duration = shotDistance / shotSpeed; // 正确的飞行时间计算:距离/速度
|
||
|
||
while (true)
|
||
{
|
||
if (isCanShot)
|
||
{
|
||
SingleShot(lockEnemy, false);
|
||
//isCanShot = !isAutoLock;
|
||
}
|
||
|
||
yield return new WaitForSeconds(shotDuration);
|
||
}
|
||
}
|
||
|
||
private IEnumerator QueueNextShot()
|
||
{
|
||
//float duration = shotDistance / shotSpeed; // 正确的飞行时间计算:距离/速度
|
||
|
||
while (true)
|
||
{
|
||
if (nextShotInfos.TryDequeue(out NextShotInfo info))
|
||
{
|
||
SingleShot(info.Enemy, true, info.CurrentShotCount, info.InitBulletPos, info.BulletObj);
|
||
}
|
||
if (shotNextDuration == 0)
|
||
{
|
||
yield return null;
|
||
}
|
||
else
|
||
{
|
||
yield return new WaitForSeconds(shotNextDuration);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 单次发射方法
|
||
/// </summary>
|
||
/// <param name="lockEnemy"></param>
|
||
/// <param name="isNextLineShot">是否弹射</param>
|
||
/// <param name="initBulletPos"></param>
|
||
/// <param name="bulletObj"></param>
|
||
private void SingleShot(Enemy lockEnemy, bool isNextLineShot, int currentShotCount = 0, Vector3? initBulletPos = null, Transform bulletObj = null)
|
||
{
|
||
Transform muzzleObj = null;
|
||
if (bulletObj == null || bulletObj.IsDestroyed())
|
||
{
|
||
// 从池中获取对象(确保线程安全)
|
||
bulletObj = PoolTool.GetFromPool(this.bulletPool, this.activeBulletPool, this.activeImpactPool);
|
||
if (!isNextLineShot)
|
||
{
|
||
muzzleObj = PoolTool.GetFromPool(this.muzzlePool, this.activeBulletPool, this.activeImpactPool);
|
||
}
|
||
}
|
||
|
||
if (bulletObj != null)
|
||
{
|
||
// 6. 激活对象
|
||
bulletObj.gameObject.SetActive(true);
|
||
// 10. 播放音效
|
||
if (!isNextLineShot && shotAudioClip != null)
|
||
{
|
||
AudioTool.AudioSource.PlayOneShot(shotAudioClip);
|
||
}
|
||
|
||
//// 1. 强制重置所有状态
|
||
//PoolTool.ResetTransform(bulletObj);
|
||
//PoolTool.ResetTransform(muzzleObj);
|
||
|
||
// 2. 计算发射位置(确保使用世界坐标)
|
||
Vector3 firePos = gun.position + gun.forward * shotOffsetZMul;
|
||
// 3. 设置初始位置(强制世界坐标)
|
||
if (initBulletPos == null)
|
||
{
|
||
bulletObj.position = new Vector3(transform.position.x, gun.position.y, transform.position.z);
|
||
}
|
||
else
|
||
{
|
||
bulletObj.position = initBulletPos.Value;
|
||
}
|
||
|
||
// 5. 计算目标位置(沿Y轴正方向)
|
||
//Vector3 targetPos = Vector3.zero;// new Vector3(firePos.x, firePos.y + shotDistance, 0);
|
||
// //targetPos.z = 0;
|
||
|
||
// 7. 获取子弹组件并重置状态
|
||
var bulletComp = bulletObj.GetComponent<Bullet>();
|
||
bulletComp.IsAutoLock = isAutoLock;
|
||
bulletComp.IsShoting = true;
|
||
if ((isAutoLock || initBulletPos != null) && lockEnemy != null && !lockEnemy.IsDestroyed())
|
||
{
|
||
bulletComp.LockTarget = lockEnemy;
|
||
bulletComp.ShotTargetPosition = bulletComp.LockTarget.transform.position;
|
||
//bulletObj.transform.LookAt(targetPos);
|
||
}
|
||
else
|
||
{
|
||
var pos = gun.position + gun.forward * shotDistance;
|
||
pos.y = gun.position.y;
|
||
bulletComp.ShotTargetPosition = pos;
|
||
}
|
||
var tmpTargetPos = bulletComp.ShotTargetPosition;
|
||
bulletObj.transform.eulerAngles = Vector3.zero;
|
||
|
||
|
||
if (muzzleObj != null)
|
||
{
|
||
muzzleObj.position = firePos;
|
||
// 4. 设置枪口火焰父对象(可选)
|
||
muzzleObj.SetParent(gun);
|
||
muzzleObj.gameObject.SetActive(true);
|
||
|
||
muzzleObj.transform.LookAt(bulletComp.ShotTargetPosition);
|
||
}
|
||
|
||
|
||
var worldTop = _cam.ScreenToWorldPoint(new Vector3(0, Screen.height, _cam.nearClipPlane));
|
||
//var yDistance = worldTop.z - transform.position.z;
|
||
//float shotDuration = 1f / shotSpeed;
|
||
// DOTween.To:动态更新弹体位置(每帧计算目标位置)
|
||
bulletComp.Tweener = DOTween.To(
|
||
() => bulletObj == null ? transform.position : bulletObj.position, // 当前位置(Getter)
|
||
(Vector3 pos) =>
|
||
{
|
||
if (bulletObj != null) { bulletObj.position = Vector3.MoveTowards(bulletObj.position, bulletComp.ShotTargetPosition, Time.deltaTime * shotSpeed); } // 更新位置(Setter)
|
||
},
|
||
tmpTargetPos,
|
||
shotSpeed
|
||
)
|
||
.SetSpeedBased()
|
||
.SetEase(Ease.Linear)
|
||
//.SetLoops(0) // 不循环
|
||
.OnUpdate(() =>
|
||
{
|
||
//// 每帧修正朝向(对准目标)
|
||
//if (lockTarget != null)
|
||
//{
|
||
// targetPos = lockTarget.transform.position;
|
||
//}
|
||
//bulletObj.transform.LookAt(targetPos);//跟踪导弹用
|
||
//if (bulletObj == null || bulletObj.IsDestroyed())
|
||
//{
|
||
// return;
|
||
//}
|
||
|
||
//if (bulletComp.IsLockedTarget)
|
||
//{
|
||
// if (bulletComp.LockTarget == null)
|
||
// {
|
||
// bulletObj.DOKill(true);
|
||
// return;
|
||
// }
|
||
//}
|
||
|
||
|
||
if (isAutoLock)
|
||
{
|
||
if (bulletComp.LockTarget == null)
|
||
{
|
||
bulletComp.LockTarget = GetNearestEnemyFromBullet(bullet);
|
||
//bulletComp.IsLockedTarget = true;
|
||
}
|
||
}
|
||
|
||
// 修正弹体朝向(对准目标,更真实)
|
||
if (bulletComp.LockTarget != null && bulletObj != null)
|
||
{
|
||
//var targetDir = (bulletComp.LockTarget.transform.position - bulletObj.transform.position).normalized;
|
||
bulletObj.LookAt(bulletComp.LockTarget.transform.position);
|
||
//bulletObj.forward = Vector3.Lerp(
|
||
// bulletObj.forward,
|
||
// targetDir,
|
||
// Time.deltaTime * lerpSpeed // 转向灵敏度
|
||
//);
|
||
//bulletComp.ShotTargetPosition = bulletComp.LockTarget.transform.position;
|
||
}
|
||
//非自动锁定状态,在轨迹更新过程中也判断是否击中
|
||
if (!isAutoLock)
|
||
{
|
||
CheckBulletBoom(bulletComp, bulletComp.LockTarget);
|
||
}
|
||
|
||
})
|
||
.OnPlay(() =>
|
||
{
|
||
})
|
||
.OnComplete(() =>
|
||
{
|
||
//if (currentShotCount > 0)
|
||
//{
|
||
//Debug.Log(currentShotCount);
|
||
//}
|
||
bulletComp.LockTarget = CheckBulletBoom(bulletComp, bulletComp.LockTarget);
|
||
|
||
if (bulletComp.LockTarget != null && hitAudioClip != null)
|
||
{
|
||
AudioTool.AudioSource.PlayOneShot(hitAudioClip);
|
||
}
|
||
|
||
currentShotCount++;
|
||
if (currentShotCount <= continueShotCount)
|
||
{
|
||
//bulletComp.currentShotCount = currentShotCount;
|
||
|
||
PoolTool.RecycleMuzzleToPool(muzzleObj, this.muzzlePool, this.activeImpactPool);
|
||
var nextLockTarget = GetNearestEnemyFromTarget(bulletComp.currentHitPosition, bulletComp.LockTarget);
|
||
if (nextLockTarget != null)
|
||
{
|
||
//SingleShot(nextLockTarget, bulletComp.transform.position, bulletComp.transform);//弹射
|
||
|
||
PoolTool.RecycleBulletToPool(bulletObj, this.bulletPool, this.activeBulletPool);
|
||
|
||
nextShotInfos.Enqueue(new NextShotInfo { CurrentShotCount = currentShotCount, Enemy = nextLockTarget, InitBulletPos = bulletObj.position });
|
||
//SingleShot(nextLockTarget, true, currentShotCount, bulletObj.position, null);//连线射击
|
||
}
|
||
else
|
||
{
|
||
PoolTool.RecycleBulletToPool(bulletObj, this.bulletPool, this.activeBulletPool);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
PoolTool.RecycleTarget(bulletObj, muzzleObj, this.bulletPool, this.activeBulletPool, this.muzzlePool, this.activeImpactPool);
|
||
}
|
||
});
|
||
|
||
//// 8. 子弹飞行(使用线性缓动,强制世界坐标)
|
||
//yield return bulletObj.DOMove(targetPos, shotSpeed)
|
||
// .SetEase(Ease.Linear)
|
||
// .OnUpdate(() =>
|
||
// {
|
||
// if (lockEnemy != null)
|
||
// {
|
||
// targetPos = lockEnemy.transform.position;
|
||
// }
|
||
// bulletObj.transform.LookAt(targetPos);//跟踪导弹用
|
||
// bulletObj.transform.eulerAngles = euler;
|
||
// })
|
||
// .SetSpeedBased()
|
||
// .OnPlay(() =>
|
||
// {
|
||
// //(0.1秒后)设置 子弹组件状态为发射中
|
||
// DOVirtual.DelayedCall(Time.deltaTime, () =>
|
||
// {
|
||
// if (bulletComp != null)
|
||
// {
|
||
// bulletComp.isShoting = true;
|
||
// }
|
||
// });
|
||
// })
|
||
// //.SetUpdate(true) // 忽略Time.timeScale影响
|
||
// .OnComplete(() =>
|
||
// {
|
||
// // 重置子弹组件状态
|
||
// if (bulletComp != null)
|
||
// {
|
||
// bulletComp.isShoting = false;
|
||
// }
|
||
// // 回收对象
|
||
// RecycleToPool(bulletObj, bulletPool, activeBullets);
|
||
// RecycleToPool(muzzleObj, muzzlePool, activeMuzzles);
|
||
// });
|
||
|
||
//// 9. 枪口火焰自动隐藏(0.2秒后)
|
||
//DOVirtual.DelayedCall(0.2f, () =>
|
||
//{
|
||
// if (muzzleObj != null)
|
||
// {
|
||
// RecycleToPool(muzzleObj, muzzlePool, activeMuzzles);
|
||
// }
|
||
//});
|
||
}
|
||
}
|
||
|
||
private Enemy CheckBulletBoom(Bullet bulletComp, Enemy lockTarget)
|
||
{
|
||
if (!isAutoLock)
|
||
{
|
||
if (bulletComp.hittedEnemy != null)
|
||
{
|
||
lockTarget = bulletComp.hittedEnemy;
|
||
}
|
||
}
|
||
// 命中检测(距离达标则触发效果)
|
||
if (bulletComp.gameObject != null)
|
||
{
|
||
bool isNeedDestroy = !VisibilityTool.IsObjectInFrustum(bulletComp.gameObject, _cam);
|
||
if (lockTarget == null)
|
||
{
|
||
if (isNeedDestroy)
|
||
{
|
||
if (bulletComp.Tweener != null)
|
||
{
|
||
bulletComp.Tweener.Complete(true);
|
||
}
|
||
bulletComp.transform.DOKill();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (!isNeedDestroy)
|
||
{
|
||
float currentDistance = Vector3.Distance(bulletComp.transform.position, lockTarget.transform.position);
|
||
isNeedDestroy = currentDistance < hitDistance;
|
||
}
|
||
var lockTargetPosition = lockTarget.transform.position;
|
||
bulletComp.currentHitPosition = lockTargetPosition;
|
||
bool isDestroyed = lockTarget.TakeDamage(bulletComp.power);//对敌方造成伤害
|
||
Vector3 targetDir = (bulletComp.transform.position - lockTargetPosition).normalized;
|
||
|
||
var impact = PoolTool.GetFromPool(this.impactPool, this.activeBulletPool, this.activeImpactPool); //Instantiate(impactPrefab, point, rot);
|
||
bulletComp.InitImpact(lockTargetPosition, targetDir, impact);//爆炸效果
|
||
if (isDestroyed)
|
||
{
|
||
if (bulletComp.Tweener != null)
|
||
{
|
||
bulletComp.Tweener.Complete(true);
|
||
}
|
||
bulletComp.transform.DOKill();
|
||
lockEnemy = null;
|
||
}
|
||
}
|
||
//if (isDestroyed)
|
||
//{
|
||
// bulletComp.Tweener.Kill(true);
|
||
// //bulletObj.DOKill(true); // 停止Tween
|
||
//}
|
||
////HitTarget(bulletObj, muzzleObj);
|
||
//}
|
||
}
|
||
return lockTarget;
|
||
}
|
||
|
||
// 计算弹体到目标的距离
|
||
private float GetDistanceToTarget(Transform bullet, Transform target)
|
||
{
|
||
if (target == null || target.IsDestroyed()) return float.MaxValue;
|
||
return Vector3.Distance(bullet.position, target.position);
|
||
}
|
||
|
||
private void OnDisable()
|
||
{
|
||
pressAction.action.performed -= PressAction_performed;
|
||
pressAction.action.canceled -= PressAction_canceled;
|
||
pressAction.action.Disable();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 此发炮弹锁定的对象
|
||
/// </summary>
|
||
private Enemy lockEnemy = null;
|
||
|
||
private void Update()
|
||
{
|
||
//if (isAutoLock)
|
||
//{
|
||
// LockEnemy();
|
||
//}
|
||
LockEnemy();
|
||
LerpTurretAngle();
|
||
LerpGunsAngle();
|
||
}
|
||
|
||
private void LockEnemy()
|
||
{
|
||
if (lockEnemy == null || lockEnemy.IsDestroyed())
|
||
{
|
||
//if (lockEnemy != null)
|
||
//{
|
||
// Debug.Log("aa:" + lockEnemy.GetHashCode());
|
||
// Global.LockedEnemies.Remove(lockEnemy.GetHashCode());
|
||
// lockEnemy = null;
|
||
// Debug.Log("cc:" + Global.LockedEnemies.Count);
|
||
//}
|
||
isStartAttack = false;
|
||
lockEnemy = GetNearestEnemyFromTarget(transform.position);
|
||
}
|
||
}
|
||
|
||
private Enemy GetNearestEnemyFromTarget(Vector3 targetPosition, Enemy currentHitTarget = null)
|
||
{
|
||
Enemy result = null;
|
||
float minDistance = float.MaxValue;
|
||
for (int i = 0; i < Global.Enemies.Count; i++)
|
||
{
|
||
var enemy = Global.Enemies[i];
|
||
if (enemy != null)
|
||
{
|
||
if (enemy == currentHitTarget)
|
||
{
|
||
continue;
|
||
}
|
||
float distance = Vector3.Distance(enemy.transform.position, targetPosition);
|
||
if (distance < minDistance && distance <= shotDistance)// && (ringManager == null || distance > ringManager.radius * 1.2f))// && !Global.LockedEnemies.ContainsKey(enemy.GetHashCode()))
|
||
{
|
||
if (_cam.orthographic)
|
||
{
|
||
if (VisibilityTool.IsObjectInFrustum(enemy.gameObject, _cam))
|
||
{
|
||
minDistance = distance;
|
||
result = enemy;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
minDistance = distance;
|
||
result = enemy;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
//if (result == null)
|
||
//{
|
||
// result = Global.Enemies.FirstOrDefault(m => !m.IsDestroyed());
|
||
//}
|
||
return result;
|
||
}
|
||
|
||
private Enemy GetNearestEnemyFromBullet(Transform bullet, Enemy currentHitTarget = null)
|
||
{
|
||
Enemy result = null;
|
||
float minDistance = float.MaxValue;
|
||
for (int i = 0; i < Global.Enemies.Count; i++)
|
||
{
|
||
var enemy = Global.Enemies[i];
|
||
if (enemy != null)
|
||
{
|
||
if (enemy == currentHitTarget)
|
||
{
|
||
continue;
|
||
}
|
||
float distance = GetDistanceToTarget(bullet, enemy.transform);
|
||
if (distance < minDistance && distance <= shotDistance)// && (ringManager == null || distance > ringManager.radius * 1.2f))// && !Global.LockedEnemies.ContainsKey(enemy.GetHashCode()))
|
||
{
|
||
if (_cam.orthographic)
|
||
{
|
||
if (VisibilityTool.IsObjectInFrustum(enemy.gameObject, _cam))
|
||
{
|
||
minDistance = distance;
|
||
result = enemy;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
minDistance = distance;
|
||
result = enemy;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
//if (result == null)
|
||
//{
|
||
// result = Global.Enemies.FirstOrDefault(m => !m.IsDestroyed());
|
||
//}
|
||
return result;
|
||
}
|
||
|
||
private void LerpTurretAngle()
|
||
{
|
||
if (isAutoLock && lockEnemy != null && !lockEnemy.IsDestroyed())
|
||
{
|
||
float speed = 1f;
|
||
float distance = Vector3.Distance(lockEnemy.transform.position, gun.position);
|
||
if (distance > shotDistance)
|
||
{
|
||
//isCanShot = false;
|
||
lockEnemy = null;
|
||
turretLerpEuler.Set(turret.eulerAngles.x, turret.eulerAngles.y, turret.eulerAngles.z);
|
||
//ResetTurretEuler();
|
||
}
|
||
else
|
||
{
|
||
if (!Global.LockedEnemies.ContainsKey(lockEnemy.GetHashCode()))
|
||
{
|
||
Global.LockedEnemies.Add(lockEnemy.GetHashCode(), lockEnemy);
|
||
}
|
||
Vector3 enemyPos = lockEnemy.transform.position;
|
||
turretLerpEuler.Set(turret.eulerAngles.x, turret.eulerAngles.y, turret.eulerAngles.z);
|
||
speed = this.lerpSpeed;
|
||
//turret.LookAt(enemyPos);
|
||
|
||
|
||
// 1. 计算炮塔到敌人的方向向量(仅考虑水平平面,忽略y轴高度差)
|
||
Vector3 directionToEnemy = enemyPos - turret.transform.position;
|
||
directionToEnemy.y = 0; // 只保留水平方向(x-z平面)
|
||
|
||
// 2. 计算方向向量对应的y轴欧拉角
|
||
float targetYAngle = Mathf.Atan2(directionToEnemy.x, directionToEnemy.z) * Mathf.Rad2Deg;
|
||
|
||
// 3. 赋值给turretLerpEuler的y轴
|
||
turretLerpEuler.y = targetYAngle;
|
||
turretLerpEuler.y = CalcTool.FixEulerAngle(turretLerpEuler.y, turret.eulerAngles.y);
|
||
|
||
//turretLerpEuler = new Vector3(turretBeforeEuler.x + shotFixAngleX, CalcTool.FixEulerAngle(tu
|
||
|
||
//if (!isCanShot)
|
||
//{
|
||
// isCanShot = Vector3.Distance(turret.eulerAngles, turretLerpEuler) < 3f;
|
||
// if (isCanShot)
|
||
// {
|
||
// isStartAttack = true;
|
||
// }
|
||
//}
|
||
}
|
||
turret.eulerAngles = Vector3.Lerp(turret.eulerAngles, turretLerpEuler, Time.deltaTime * speed);
|
||
}
|
||
else
|
||
{
|
||
turret.forward = transform.forward;
|
||
//isCanShot = isStartAttack && Global.Enemies.Any();
|
||
//ResetTurretEuler();
|
||
}
|
||
}
|
||
//private void ResetTurretEuler()
|
||
//{
|
||
// turretLerpEuler.Set(turret.eulerAngles.x, turret.eulerAngles.y + Time.deltaTime * this.lerpSpeed * 2, turret.eulerAngles.z);
|
||
// turret.eulerAngles = turretLerpEuler;
|
||
//}
|
||
|
||
private void LerpGunsAngle()
|
||
{
|
||
if (lockEnemy == null)
|
||
{
|
||
targetXAngle = 0;
|
||
}
|
||
else
|
||
{
|
||
Vector3 enemyPos = lockEnemy.transform.position;
|
||
// ========== 垂直索敌(炮管x轴旋转) ==========
|
||
// 1. 计算从炮管到敌人的方向向量(保留y轴高度差)
|
||
Vector3 gunToEnemy = gun.transform.position - enemyPos;
|
||
|
||
// 2. 将方向向量转换为炮管的局部坐标系(避免炮塔旋转影响计算)
|
||
Vector3 localDir = gun.transform.InverseTransformDirection(gunToEnemy);
|
||
|
||
// 3. 计算垂直旋转角度(x轴)
|
||
targetXAngle = Mathf.Atan2(localDir.y, Mathf.Sqrt(localDir.x * localDir.x + localDir.z * localDir.z)) * Mathf.Rad2Deg;
|
||
|
||
//// 4. 限制俯仰角度范围(防止炮管旋转过度)
|
||
//targetXAngle = Mathf.Clamp(targetXAngle, minXAngle, maxXAngle);
|
||
}
|
||
// 处理360度环绕问题
|
||
smoothXAngle = Mathf.LerpAngle(smoothXAngle, targetXAngle, Time.deltaTime * this.lerpSpeed);
|
||
gun.localEulerAngles = new Vector3(smoothXAngle, gun.localEulerAngles.y, gun.localEulerAngles.z);
|
||
}
|
||
|
||
|
||
//// 调试:在Scene视图中绘制发射位置
|
||
//private void OnDrawGizmosSelected()
|
||
//{
|
||
// if (gun != null)
|
||
// {
|
||
// Vector3 firePos = gun.TransformPoint(new Vector3(0, shotOffsetZ, 0));
|
||
// firePos.z = 0;
|
||
// Vector3 targetPos = new Vector3(firePos.x, firePos.y + shotDistance, 0);
|
||
|
||
// Gizmos.color = Color.green;
|
||
// Gizmos.DrawSphere(firePos, 0.1f);
|
||
|
||
// Gizmos.color = Color.red;
|
||
// Gizmos.DrawSphere(targetPos, 0.1f);
|
||
|
||
// Gizmos.color = Color.yellow;
|
||
// Gizmos.DrawLine(firePos, targetPos);
|
||
// }
|
||
//}
|
||
} |