Files
5.24BuYu/Assets/MyGame/Scripts/Turret/TurretData.cs
T

512 lines
18 KiB
C#
Raw Normal View History

2026-06-08 21:23:40 +08:00
using Spine.Unity;
using System.Collections.Generic;
2026-02-09 10:44:38 +08:00
using UnityEngine;
2026-05-02 23:52:48 +08:00
public enum TurretAnimType
{
None = 0,
skeletonAnimation = 1,
animation = 2,
animator = 3
}
2026-05-22 19:30:02 +08:00
2026-05-02 23:52:48 +08:00
public enum TurretState
{
None = 0,
Idle = 1,
Attack = 2,
LaserStart = 3,
LaserLoop = 4,
LaserEnd = 5
}
2026-02-09 10:44:38 +08:00
2026-05-22 19:30:02 +08:00
2026-02-09 10:44:38 +08:00
public class TurretData : MonoBehaviour
{
2026-05-02 23:52:48 +08:00
[Header("基础")]
2026-02-09 10:44:38 +08:00
public string turretID;
public SitPosData setPosData;
2026-03-31 17:00:05 +08:00
public Transform tra_BulletStartPosition;
2026-07-28 21:11:57 +08:00
[Tooltip("炮台旋转节点。拖入后 SitPosData.tra_CurrTurret 用此节点旋转;不拖则用炮台自身的父节点(兼容旧逻辑)")]
public Transform tra_RotationPivot;
2026-09-02 16:27:35 +08:00
public GameObject bulletPrefab_Two;
2026-05-04 17:21:52 +08:00
public GameObject bulletPrefab;
2026-05-02 23:52:48 +08:00
2026-08-29 01:02:09 +08:00
[Header("战神激光(锁定时显示,普通炮台不配置即无此效果)")]
2026-08-30 15:29:02 +08:00
[Tooltip("激光预制体。非空=预制体形态(JiGuangControl 工具类驱动);为空=LineRenderer 兜底形态(自动创建)")]
2026-08-29 01:02:09 +08:00
public GameObject laserPrefab;
2026-08-30 15:29:02 +08:00
[Tooltip("LineRenderer 兜底形态的线宽")]
2026-08-29 01:02:09 +08:00
public float laserWidth = 0.3f;
2026-08-30 15:29:02 +08:00
[Tooltip("LineRenderer 兜底形态的颜色")]
2026-08-29 01:02:09 +08:00
public Color laserColor = new Color(1f, 0.3f, 0.3f, 0.9f);
2026-08-30 15:29:02 +08:00
[Tooltip("预制体形态激光的长度校正值(不同激光预制体设置不同值)")]
public float laserLengthCorrect = 1f;
2026-08-29 01:02:09 +08:00
2026-06-08 21:23:40 +08:00
[Header("UI面板属性")]
[Tooltip("炮台编号,对应 TurretResourcesData.turretNumber")]
public int turretNumber;
[Tooltip("1=普通炮台 2=战神 3=翅膀")]
public int turretType = 1;
[Tooltip("UI 展示名称")]
public string displayName;
[Tooltip("品质 1=普通 2=稀有 3=史诗 4=传说")]
[Range(1, 5)]
public int quality = 1;
[Tooltip("解锁所需等级(0=默认拥有)")]
public int unlockLevel;
[Tooltip("UI 列表/详情用的缩略图")]
public Sprite uiIconSprite;
[TextArea(2, 4)]
[Tooltip("描述文字")]
public string description;
// ---- 运行时状态(由服务器/UI面板读写) ----
[System.NonSerialized] public bool isUnlocked;
[System.NonSerialized] public bool isEquipped;
[System.NonSerialized] public int ownStarLevel;
2026-05-02 23:52:48 +08:00
[Header("动画组件")]
public SkeletonGraphic skeletonAnimation;
public Animation old_CurrAnimation;
public Animator new_CurrAnimation;
public TurretAnimType currTurretType;
public bool isFrenzy=false;
[Header("动画名")]
public string idleAnim;
public string attackAnim;
2026-05-22 19:30:02 +08:00
// 新增:狂暴专用攻击动画(已经是二倍速帧动画)
public string frenzyAttackAnim;
private bool useFrenzyAnim = false;
2026-05-02 23:52:48 +08:00
public string laserStartAnim;
public string laserLoopAnim;
public string laserEndAnim;
2026-08-28 22:33:35 +08:00
[Header("战神 Animator 参数")]
[Tooltip("战神状态机开火参数名(Bool:Idle0 --PlayFire--> Fire状态机,Fire --PlayFire=false--> Exit回Idle)。" +
"检测到此参数自动启用战神模式,无需手动配置")]
public string playFireParam = "PlayFire";
2026-08-29 01:02:09 +08:00
[Tooltip("开火动画变体参数名(Int):0=Fire0(正常子弹) 1=Fire1(激光)。锁定时自动切1,非锁定开火用0")]
2026-08-28 22:33:35 +08:00
public string fireIndexParam = "FireIndex";
[Tooltip("待机动画变体参数名(Int:IdleIndex=0选Idle0...)")]
public string idleIndexParam = "IdleIndex";
2026-08-29 01:02:09 +08:00
[Tooltip("开火动画变体索引默认值:0=Fire0(正常子弹) 1=Fire1(激光)。运行时按锁定状态自动切换,此值仅为无锁定信息时的兜底")]
2026-08-28 22:33:35 +08:00
public int fireIndex = 0;
[Tooltip("待机动画变体索引:0=Idle0 1=Idle1...")]
public int idleIndex = 0;
// ---- 战神模式运行时缓存(首次 PlayAnimator 时检测)----
private bool _warGodAnimChecked; // 是否已检测过参数
private bool _warGodAnimMode; // true=战神 PlayFire 约定
private bool _playFireIsTrigger; // PlayFire 实际类型是否 Trigger(个别控制器用触发器)
private bool _hasFireIndex;
private bool _hasIdleIndex;
private AnimatorControllerParameterType _fireIndexType = AnimatorControllerParameterType.Int;
private AnimatorControllerParameterType _idleIndexType = AnimatorControllerParameterType.Int;
2026-05-02 23:52:48 +08:00
private TurretState currState;
2026-06-08 21:23:40 +08:00
/// <summary>
/// 从资源表查询 TurretResourcesData。表里没有则返回 null,调用方应使用 TurretData 自身的 Inspector 默认值。
/// </summary>
public TurretResourcesData GetResourcesData()
{
return TurretResourcesData.GetTurretData(turretNumber);
}
/// <summary>安全获取攻击音效 ID:表里有就用,没有则返回 0(静音)。</summary>
public int GetAttackAudioID()
{
var res = GetResourcesData();
return res != null ? res.attackAudioID : 0;
}
/// <summary>安全获取子弹 ID 列表:表里有就用,没有则返回空列表。</summary>
public List<int> GetBulletIDs()
{
var res = GetResourcesData();
return res?.bulletID ?? new List<int>();
}
/// <summary>安全获取动画类型:表里有就用,没有则用 Inspector 配置的 currTurretType。</summary>
public TurretAnimType GetAnimationType()
{
var res = GetResourcesData();
return res != null ? (TurretAnimType)res.animationType : currTurretType;
}
2026-05-02 23:52:48 +08:00
private void Awake()
2026-02-09 10:44:38 +08:00
{
2026-05-27 19:47:06 +08:00
//if (setPosData != null)
// setPosData.OnStateChange = ChangeState;
2026-05-22 19:30:02 +08:00
// 自动判断:如果狂暴攻击动画名不为空,就启用切换动画模式
useFrenzyAnim = !string.IsNullOrEmpty(frenzyAttackAnim);
2026-05-02 23:52:48 +08:00
}
private void OnEnable()
{
PlayState(TurretState.Idle);
}
2026-06-15 18:37:06 +08:00
//public Vector2 GetBulletStartWorldPos()
//{
// if (tra_BulletStartPosition == null)
// return Vector2.zero;
// // 如果是 UI RectTransform(UGUI)
// RectTransform rect = tra_BulletStartPosition.GetComponent<RectTransform>();
// if (rect != null)
// {
// // 1. UI 本地坐标 → Canvas 坐标
// Vector2 canvasPos = CoordinateTransformation.AnchoredPos_ToCanvas(
// rect,
// UIManager.Instance.uiRoot.GetComponent<Canvas>()
// );
// // 2. Canvas 坐标 → 世界坐标
// return CoordinateTransformation.CanvasToWorld(
// canvasPos,
// UIManager.Instance.uiRoot.GetComponent<RectTransform>(),
// Camera.main
// );
// }
// // 如果不是 UI(比如 3D Transform)
// return tra_BulletStartPosition.position;
//}
2026-05-02 23:52:48 +08:00
public Vector2 GetBulletStartWorldPos()
{
if (tra_BulletStartPosition == null)
return Vector2.zero;
2026-06-15 18:37:06 +08:00
// 如果是 UI(UGUI)
2026-05-02 23:52:48 +08:00
RectTransform rect = tra_BulletStartPosition.GetComponent<RectTransform>();
if (rect != null)
2026-02-09 10:44:38 +08:00
{
2026-06-15 18:37:06 +08:00
Canvas canvas = UIManager.Instance.uiRoot.GetComponent<Canvas>();
Camera cam = Camera.main;
2026-05-02 23:52:48 +08:00
2026-06-15 18:37:06 +08:00
// 方式1:通过屏幕坐标直接转世界坐标(推荐)
Vector3 screenPos = RectTransformUtility.WorldToScreenPoint(
canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera,
rect.position
2026-05-02 23:52:48 +08:00
);
2026-06-15 18:37:06 +08:00
// 需要计算 Z 轴距离(物体到相机的距离),这里假设子弹打在 Z=0 平面
float distance = Mathf.Abs(cam.transform.position.z - 0f);
Vector3 worldPos = cam.ScreenToWorldPoint(new Vector3(screenPos.x, screenPos.y, distance));
worldPos.z = 0f;
return worldPos;
2026-05-02 23:52:48 +08:00
}
2026-06-15 18:37:06 +08:00
// 非 UI(3D 物体)
2026-05-02 23:52:48 +08:00
return tra_BulletStartPosition.position;
}
public void SetFrenzy(bool value)
{
isFrenzy = value;
2026-05-22 19:30:02 +08:00
if (!useFrenzyAnim) // 没有狂暴动画 → 用速度加倍
2026-05-02 23:52:48 +08:00
{
2026-05-22 19:30:02 +08:00
float speed = isFrenzy ? 2f : 1f;
if (skeletonAnimation != null)
skeletonAnimation.timeScale = speed;
if (new_CurrAnimation != null)
new_CurrAnimation.speed = speed;
if (old_CurrAnimation != null)
{
foreach (AnimationState state in old_CurrAnimation)
state.speed = speed;
}
}
else
{
// 有狂暴动画 → 不做速度调整,保持 1 倍速
if (skeletonAnimation != null)
skeletonAnimation.timeScale = 1f;
if (new_CurrAnimation != null)
new_CurrAnimation.speed = 1f;
if (old_CurrAnimation != null)
{
foreach (AnimationState state in old_CurrAnimation)
state.speed = 1f;
}
2026-02-09 10:44:38 +08:00
}
}
2026-05-22 19:30:02 +08:00
2026-09-02 16:27:35 +08:00
public virtual void ChangeState(TurretState newState)
2026-05-02 23:52:48 +08:00
{
if (currState == newState) return;
currState = newState;
PlayState(newState);
}
private void PlayState(TurretState state)
{
switch (currTurretType)
{
case TurretAnimType.skeletonAnimation:
PlaySkeleton(state);
break;
case TurretAnimType.animation:
PlayLegacy(state);
break;
case TurretAnimType.animator:
PlayAnimator(state);
break;
}
}
2026-06-15 18:37:06 +08:00
//private void PlaySkeleton(TurretState state)
//{
// if (skeletonAnimation == null) return;
// string anim = state switch
// {
// TurretState.Idle => idleAnim,
// TurretState.Attack => (isFrenzy && useFrenzyAnim) ? frenzyAttackAnim : attackAnim,
// TurretState.LaserStart => laserStartAnim,
// TurretState.LaserLoop => laserLoopAnim,
// TurretState.LaserEnd => laserEndAnim,
// _ => idleAnim
// };
// bool loop = state == TurretState.Attack || state == TurretState.LaserLoop;
// skeletonAnimation.AnimationState.SetAnimation(0, anim, loop);
// if (state == TurretState.LaserStart)
// {
// skeletonAnimation.AnimationState.AddAnimation(0, laserLoopAnim, true, 0f);
// }
//}
//private void PlayLegacy(TurretState state)
//{
// if (old_CurrAnimation == null) return;
// string anim = state switch
// {
// TurretState.Idle => idleAnim,
// TurretState.Attack => (isFrenzy && useFrenzyAnim) ? frenzyAttackAnim : attackAnim,
// TurretState.LaserStart => laserStartAnim,
// TurretState.LaserLoop => laserLoopAnim,
// TurretState.LaserEnd => laserEndAnim,
// _ => idleAnim
// };
// old_CurrAnimation.Play(anim);
//}
//private void PlayAnimator(TurretState state)
//{
// if (new_CurrAnimation == null) return;
// if (state == TurretState.Attack && isFrenzy && useFrenzyAnim)
// {
// new_CurrAnimation.SetTrigger("FrenzyAttack");
// }
// else
// {
// new_CurrAnimation.SetInteger("State", (int)state);
// }
//}
2026-05-02 23:52:48 +08:00
private void PlaySkeleton(TurretState state)
{
if (skeletonAnimation == null) return;
string anim = state switch
{
TurretState.Idle => idleAnim,
2026-05-22 19:30:02 +08:00
TurretState.Attack => (isFrenzy && useFrenzyAnim) ? frenzyAttackAnim : attackAnim,
2026-05-02 23:52:48 +08:00
TurretState.LaserStart => laserStartAnim,
TurretState.LaserLoop => laserLoopAnim,
TurretState.LaserEnd => laserEndAnim,
_ => idleAnim
};
2026-06-15 18:37:06 +08:00
// 如果动画名为空 → 停止当前动画(暂停),不再播放任何动画
if (string.IsNullOrEmpty(anim))
{
skeletonAnimation.AnimationState.ClearTrack(0);
skeletonAnimation.AnimationState.SetEmptyAnimation(0, 0f);
return;
}
2026-05-02 23:52:48 +08:00
bool loop = state == TurretState.Attack || state == TurretState.LaserLoop;
skeletonAnimation.AnimationState.SetAnimation(0, anim, loop);
2026-06-15 18:37:06 +08:00
if (state == TurretState.LaserStart && !string.IsNullOrEmpty(laserLoopAnim))
2026-05-02 23:52:48 +08:00
{
skeletonAnimation.AnimationState.AddAnimation(0, laserLoopAnim, true, 0f);
}
}
private void PlayLegacy(TurretState state)
{
if (old_CurrAnimation == null) return;
string anim = state switch
{
TurretState.Idle => idleAnim,
2026-05-22 19:30:02 +08:00
TurretState.Attack => (isFrenzy && useFrenzyAnim) ? frenzyAttackAnim : attackAnim,
2026-05-02 23:52:48 +08:00
TurretState.LaserStart => laserStartAnim,
TurretState.LaserLoop => laserLoopAnim,
TurretState.LaserEnd => laserEndAnim,
_ => idleAnim
};
2026-06-15 18:37:06 +08:00
if (string.IsNullOrEmpty(anim))
{
old_CurrAnimation.Stop();
return;
}
2026-05-02 23:52:48 +08:00
old_CurrAnimation.Play(anim);
}
private void PlayAnimator(TurretState state)
{
if (new_CurrAnimation == null) return;
2026-08-28 22:33:35 +08:00
// 战神约定(PlayFire/FireIndex/IdleIndex):控制器带 PlayFire 参数时自动启用
if (!_warGodAnimChecked) DetectWarGodAnimator();
if (_warGodAnimMode)
{
PlayWarGodAnimator(state);
return;
}
// ---- 旧约定(State 整数 / FrenzyAttack 触发器),保持不变 ----
2026-06-15 18:37:06 +08:00
// 如果缺少对应动画,暂停 animator(速度为0)或者设置一个空状态
if (state == TurretState.Attack && isFrenzy && useFrenzyAnim && !string.IsNullOrEmpty(frenzyAttackAnim))
2026-05-22 19:30:02 +08:00
{
new_CurrAnimation.SetTrigger("FrenzyAttack");
}
2026-06-15 18:37:06 +08:00
else if (!string.IsNullOrEmpty(attackAnim) || state == TurretState.Idle && !string.IsNullOrEmpty(idleAnim))
2026-05-22 19:30:02 +08:00
{
new_CurrAnimation.SetInteger("State", (int)state);
}
2026-06-15 18:37:06 +08:00
else
{
// 没有可用动画时暂停 animator,避免残留动作
new_CurrAnimation.speed = 0f;
}
2026-05-02 23:52:48 +08:00
}
2026-05-22 19:30:02 +08:00
2026-08-28 22:33:35 +08:00
/// <summary>
/// 战神 Animator 约定检测:控制器带 PlayFire 参数(14082/14053 等战神状态机)即启用战神模式。
/// 参数实际类型以运行时 Animator.parameters 为准,不写死(个别控制器 PlayFire 可能是 Trigger)。
/// 变体索引(fireIndex/idleIndex)检测完一次性写入。
/// </summary>
private void DetectWarGodAnimator()
{
// 控制器未就绪时不标记,下次 PlayAnimator 再检测
if (new_CurrAnimation.runtimeAnimatorController == null) return;
_warGodAnimChecked = true;
_warGodAnimMode = false;
foreach (var p in new_CurrAnimation.parameters)
{
if (p.name == playFireParam)
{
_warGodAnimMode = true;
_playFireIsTrigger = p.type == AnimatorControllerParameterType.Trigger;
}
else if (p.name == fireIndexParam)
{
_hasFireIndex = true;
_fireIndexType = p.type;
}
else if (p.name == idleIndexParam)
{
_hasIdleIndex = true;
_idleIndexType = p.type;
}
}
if (!_warGodAnimMode) return;
// 变体索引一次性写入(Inspector 配置,运行中不变)
if (_hasFireIndex) SetNumericParam(fireIndexParam, _fireIndexType, fireIndex);
if (_hasIdleIndex) SetNumericParam(idleIndexParam, _idleIndexType, idleIndex);
Debug.Log($"[TurretData] 战神Animator模式 #{turretNumber}: PlayFire={(_playFireIsTrigger ? "Trigger" : "Bool")}, " +
$"FireIndex={(_hasFireIndex ? fireIndex.ToString() : "无")}, IdleIndex={(_hasIdleIndex ? idleIndex.ToString() : "无")}");
}
/// <summary>
/// 战神状态切换(14082 等状态机结构):
/// Idle0 --PlayFire=true--> Fire状态机;Fire0/Fire1 --PlayFire=false--> Exit 回 Idle。
/// - Bool 型(标准):Attack 置 true 持续开火,Idle 置 false 停火退出;
/// - Trigger 型(兜底):Attack 设触发器,Idle 复位。
/// LaserStart/LaserLoop 视为开火中,None/LaserEnd 视为待机。
2026-08-29 01:02:09 +08:00
/// ⭐ FireIndex 动态切换:锁定中(激光开启)=1 走 Fire1(激光动画);非锁定=0 走 Fire0(正常子弹动画)。
2026-08-28 22:33:35 +08:00
/// </summary>
private void PlayWarGodAnimator(TurretState state)
{
bool attacking = state == TurretState.Attack
|| state == TurretState.LaserStart
|| state == TurretState.LaserLoop;
2026-08-29 01:02:09 +08:00
// ⭐ 开火变体:0=正常子弹 1=激光(锁定中)。进 Fire 状态机前先设好值
if (attacking && _hasFireIndex)
SetNumericParam(fireIndexParam, _fireIndexType, IsLaserActive() ? 1 : 0);
2026-08-28 22:33:35 +08:00
if (_playFireIsTrigger)
{
if (attacking) new_CurrAnimation.SetTrigger(playFireParam);
else new_CurrAnimation.ResetTrigger(playFireParam);
}
else
{
new_CurrAnimation.SetBool(playFireParam, attacking);
}
}
2026-08-29 01:02:09 +08:00
/// <summary>
/// 激光是否开启:战神锁定中即激光开启(与 SitPosData.UpdateZhanShenLaser 的显示条件一致)。
/// setPosData 由 SitPosData.LoadZhanShen 注入;远程座位的锁定经 SetLockTarget 同步,同样生效。
/// </summary>
private bool IsLaserActive()
{
var sit = setPosData;
return sit != null
&& sit.isLock
&& sit.monCotl_Lock_Position != null
&& sit.monCotl_Lock_Position.gameObject.activeInHierarchy;
}
2026-08-28 22:33:35 +08:00
/// <summary>按参数实际类型写数值(Int/Bool/Float 通吃)</summary>
private void SetNumericParam(string paramName, AnimatorControllerParameterType type, int value)
{
switch (type)
{
case AnimatorControllerParameterType.Int:
new_CurrAnimation.SetInteger(paramName, value);
break;
case AnimatorControllerParameterType.Bool:
new_CurrAnimation.SetBool(paramName, value != 0);
break;
case AnimatorControllerParameterType.Float:
new_CurrAnimation.SetFloat(paramName, value);
break;
}
}
2026-06-08 21:23:40 +08:00
}