368 lines
11 KiB
C#
368 lines
11 KiB
C#
using Spine.Unity;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
|
||
public enum TurretAnimType
|
||
{
|
||
None = 0,
|
||
skeletonAnimation = 1,
|
||
animation = 2,
|
||
animator = 3
|
||
}
|
||
|
||
|
||
public enum TurretState
|
||
{
|
||
None = 0,
|
||
Idle = 1,
|
||
Attack = 2,
|
||
|
||
LaserStart = 3,
|
||
LaserLoop = 4,
|
||
LaserEnd = 5
|
||
}
|
||
|
||
|
||
public class TurretData : MonoBehaviour
|
||
{
|
||
[Header("基础")]
|
||
public string turretID;
|
||
public SitPosData setPosData;
|
||
public Transform tra_BulletStartPosition;
|
||
public GameObject bulletPrefab_Two;
|
||
public GameObject bulletPrefab;
|
||
|
||
[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;
|
||
|
||
[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;
|
||
// 新增:狂暴专用攻击动画(已经是二倍速帧动画)
|
||
public string frenzyAttackAnim;
|
||
private bool useFrenzyAnim = false;
|
||
|
||
|
||
public string laserStartAnim;
|
||
public string laserLoopAnim;
|
||
public string laserEndAnim;
|
||
|
||
private TurretState currState;
|
||
|
||
/// <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;
|
||
}
|
||
|
||
private void Awake()
|
||
{
|
||
//if (setPosData != null)
|
||
// setPosData.OnStateChange = ChangeState;
|
||
|
||
// 自动判断:如果狂暴攻击动画名不为空,就启用切换动画模式
|
||
useFrenzyAnim = !string.IsNullOrEmpty(frenzyAttackAnim);
|
||
}
|
||
|
||
private void OnEnable()
|
||
{
|
||
PlayState(TurretState.Idle);
|
||
}
|
||
//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;
|
||
//}
|
||
public Vector2 GetBulletStartWorldPos()
|
||
{
|
||
if (tra_BulletStartPosition == null)
|
||
return Vector2.zero;
|
||
|
||
// 如果是 UI(UGUI)
|
||
RectTransform rect = tra_BulletStartPosition.GetComponent<RectTransform>();
|
||
if (rect != null)
|
||
{
|
||
Canvas canvas = UIManager.Instance.uiRoot.GetComponent<Canvas>();
|
||
Camera cam = Camera.main;
|
||
|
||
// 方式1:通过屏幕坐标直接转世界坐标(推荐)
|
||
Vector3 screenPos = RectTransformUtility.WorldToScreenPoint(
|
||
canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera,
|
||
rect.position
|
||
);
|
||
// 需要计算 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;
|
||
}
|
||
|
||
// 非 UI(3D 物体)
|
||
return tra_BulletStartPosition.position;
|
||
}
|
||
public void SetFrenzy(bool value)
|
||
{
|
||
isFrenzy = value;
|
||
|
||
if (!useFrenzyAnim) // 没有狂暴动画 → 用速度加倍
|
||
{
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
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;
|
||
}
|
||
}
|
||
|
||
//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);
|
||
// }
|
||
//}
|
||
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
|
||
};
|
||
|
||
// 如果动画名为空 → 停止当前动画(暂停),不再播放任何动画
|
||
if (string.IsNullOrEmpty(anim))
|
||
{
|
||
skeletonAnimation.AnimationState.ClearTrack(0);
|
||
skeletonAnimation.AnimationState.SetEmptyAnimation(0, 0f);
|
||
return;
|
||
}
|
||
|
||
bool loop = state == TurretState.Attack || state == TurretState.LaserLoop;
|
||
skeletonAnimation.AnimationState.SetAnimation(0, anim, loop);
|
||
|
||
if (state == TurretState.LaserStart && !string.IsNullOrEmpty(laserLoopAnim))
|
||
{
|
||
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
|
||
};
|
||
|
||
if (string.IsNullOrEmpty(anim))
|
||
{
|
||
old_CurrAnimation.Stop();
|
||
return;
|
||
}
|
||
|
||
old_CurrAnimation.Play(anim);
|
||
}
|
||
|
||
private void PlayAnimator(TurretState state)
|
||
{
|
||
if (new_CurrAnimation == null) return;
|
||
|
||
// 如果缺少对应动画,暂停 animator(速度为0)或者设置一个空状态
|
||
if (state == TurretState.Attack && isFrenzy && useFrenzyAnim && !string.IsNullOrEmpty(frenzyAttackAnim))
|
||
{
|
||
new_CurrAnimation.SetTrigger("FrenzyAttack");
|
||
}
|
||
else if (!string.IsNullOrEmpty(attackAnim) || state == TurretState.Idle && !string.IsNullOrEmpty(idleAnim))
|
||
{
|
||
new_CurrAnimation.SetInteger("State", (int)state);
|
||
}
|
||
else
|
||
{
|
||
// 没有可用动画时暂停 animator,避免残留动作
|
||
new_CurrAnimation.speed = 0f;
|
||
}
|
||
}
|
||
|
||
} |