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-05-04 17:21:52 +08:00
|
|
|
|
public GameObject bulletPrefab_Two;
|
|
|
|
|
|
public GameObject bulletPrefab;
|
2026-05-02 23:52:48 +08:00
|
|
|
|
|
2026-08-29 01:02:09 +08:00
|
|
|
|
[Header("战神激光(锁定时显示,普通炮台不配置即无此效果)")]
|
|
|
|
|
|
[Tooltip("激光预制体(带 LineRenderer)。为空时自动创建一个 GameObject 并加 LineRenderer")]
|
|
|
|
|
|
public GameObject laserPrefab;
|
|
|
|
|
|
[Tooltip("自动创建激光时的线宽")]
|
|
|
|
|
|
public float laserWidth = 0.3f;
|
|
|
|
|
|
[Tooltip("自动创建激光时的颜色")]
|
|
|
|
|
|
public Color laserColor = new Color(1f, 0.3f, 0.3f, 0.9f);
|
|
|
|
|
|
|
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-05-02 23:52:48 +08:00
|
|
|
|
public void ChangeState(TurretState newState)
|
|
|
|
|
|
{
|
|
|
|
|
|
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
|
|
|
|
}
|