2025-12-12 18:46:01 +08:00

836 lines
28 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 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);
// }
//}
}