1
This commit is contained in:
@@ -23,9 +23,10 @@ using Random = UnityEngine.Random;
|
||||
/// num < 炮倍×100 绿,< 炮倍×200 蓝,< 炮倍×500 紫,≥ 炮倍×500 红(金色不用),
|
||||
/// 每张卡下方显示本份金币数量。
|
||||
///
|
||||
/// 流程(用户确认版,全程 UI 层):
|
||||
/// 死亡位置一次出现 8 颗宝石 → 停留1秒 → 飞到屏幕中心两排(每排4个)排好
|
||||
/// 流程(用户确认版,全程 UI 层;lua 对应 PW_Effect.txt DropTenCardEffect):
|
||||
/// 屏幕靠上位置一次出现 N 颗宝石 → 飞到屏幕中心两排排好
|
||||
/// → 一张一张翻牌(全是金币)→ 卡牌飞到座位上方 → 显示 BJEndUI + 加分 + 回收
|
||||
/// 音效对齐 lua:宝石掉落 FreeCardDrop / 翻牌 FreeCardBomb / 分数滚动 FreeCardEarnRoll(+Enlarge/Reduce) / 领取 FreeGet
|
||||
///
|
||||
/// ⭐ 资源配置:所有预制体/图片/名称集中在 JiXieXieConfig 资产(ScriptableObject),
|
||||
/// 只需拖一个 config 字段。资产用菜单 Tools → 机械蝎 → 生成八连抽配置资产 一键生成。
|
||||
@@ -386,8 +387,9 @@ public class JiXieXieEvent : BJEventBase
|
||||
Vector2 baseUi = CoordinateTransformation.ScreenPoint_ToCanvas(
|
||||
new Vector2(Screen.width / 2f, Screen.height * spawnYRatio), _uiCanvas);
|
||||
|
||||
// 音效:宝石掉落(PW_FreeCardDrop,只播一次,宝石间隔太密不逐颗播)
|
||||
AudioManager.Instance.PlayerFXByName("PW_FreeCardDrop");
|
||||
// 音效:宝石掉落(lua OnCreate 仅 isSelf 播 FreeCardDrop,只播一次,宝石间隔太密不逐颗播)
|
||||
if (isSelf)
|
||||
AudioManager.Instance.PlayerFXByName("PW_FreeCardDrop");
|
||||
|
||||
_chipInstances.Clear();
|
||||
|
||||
@@ -523,12 +525,19 @@ public class JiXieXieEvent : BJEventBase
|
||||
{
|
||||
Debug.Log($"[JiXieXie] 八连抽完成,显示 BJEndUI 倍数={bJbeishu} 总分={maxScore} isSelf={isSelf}");
|
||||
|
||||
// 音效:分数滚动开始(lua RollScoreAim 用 FreeCardEarnRoll)
|
||||
AudioManager.Instance.PlayerFXByName("PW_FreeCardEarnRoll");
|
||||
|
||||
Vector3 uiPos = GetSeatPosition() + Vector3.up * bjEndUIOffsetY;
|
||||
if (bjEndUI != null)
|
||||
{
|
||||
var endUI = Instantiate(bjEndUI, uiPos, Quaternion.identity,
|
||||
CreateFishManager.Instance != null ? CreateFishManager.Instance.bjEffectRoot : null);
|
||||
endUI.Init(bJbeishu, maxScore, isSelf);
|
||||
|
||||
// 音效:分数数字放大→还原(lua RollScoreAim 用 FreeCardEarnEnlarge / FreeCardEarnReduce)
|
||||
AudioManager.Instance.PlayerFXByName("PW_FreeCardEarnEnlarge");
|
||||
DOVirtual.DelayedCall(0.4f, () => AudioManager.Instance.PlayerFXByName("PW_FreeCardEarnReduce"));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -538,6 +547,9 @@ public class JiXieXieEvent : BJEventBase
|
||||
// 回加金币到玩家
|
||||
JiaFen?.Invoke();
|
||||
|
||||
// 音效:领取成功(lua MoveEndAim 最后一张卡 i==10 用 FreeGet)
|
||||
AudioManager.Instance.PlayerFXByName("PW_FreeGet");
|
||||
|
||||
// 回收爆奖预制体 + 销毁 chip
|
||||
die();
|
||||
}
|
||||
@@ -561,8 +573,8 @@ public class JiXieXieEvent : BJEventBase
|
||||
/// <summary>翻单张卡:隐藏宝石 → 显示卡牌背面 → 翻转显示正面(分级色)→ 特效</summary>
|
||||
private void FlipOneCard(GameObject chip, DropItemInfo info, int index)
|
||||
{
|
||||
// 音效:打开卡片(PW_OpenCard)
|
||||
AudioManager.Instance.PlayerFXByName("PW_OpenCard");
|
||||
// 音效:翻牌/爆开(lua BombToAmi 用 FreeCardBomb;原 PW_OpenCard 为旧口径)
|
||||
AudioManager.Instance.PlayerFXByName("PW_FreeCardBomb");
|
||||
|
||||
// 隐藏宝石相关子物体
|
||||
Transform gemstone = chip.transform.Find(gemstoneName);
|
||||
|
||||
@@ -1,28 +1,77 @@
|
||||
using Assets.MyGame.Scripts.Net.APIResponses.Enum;
|
||||
using Assets.MyGame.Scripts.Net.WebResponses;
|
||||
using DG.Tweening;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// 机械蝎 Boss。
|
||||
/// 死亡流程:正常打死 → 在死亡位置爆炸 → 范围内普通鱼死亡 → 加分滚动。
|
||||
/// 通用爆奖模式,Boss 本体走标准 MonsterControl_BossBase.DieFish。
|
||||
///
|
||||
/// lua 对应:pw_enemy/PW_Scorpion.txt(本体生命周期)+ pw_effect/PW_Effect.txt DropTenCardEffect(八连抽)。
|
||||
/// 本体 Spine 状态机(PW_Scorpion.txt):in(出生) → stand(站立,2.2s后) ⇄ attack(随机10~25s,需倒计时>5)
|
||||
/// → out(离场/OnMoveFinish) → 爆炸(OnExplosion)。碰撞体由 MonsterContral 基类驱动。
|
||||
/// 八连抽(DropTenCardEffect)见 JiXieXieEvent.cs。
|
||||
///
|
||||
/// C# 侧已对齐 lua 的 Spine 动画切换(OnMoveEnter 驱动 in→stand + 启动攻击循环 StartAttackCycle;DieFish 驱动 out),
|
||||
/// 全部经 PlaySpineSafe 守卫:动画名缺失或 isSpine=0 时自动跳过(回退原 stand2),绝不抛异常。
|
||||
/// ⚠️ 当前预制体 PW_Enemy_3000 的 isSpine=0,且 Spine 资源需含 in/stand/attack/out 动画名;
|
||||
/// 美术接入 Spine 骨骼(isSpine=1 + 对应动画名)后此逻辑立即生效。
|
||||
/// 音效时序(FreeCardBomb/FreeGet 等)已在 JiXieXieEvent.cs 对齐;死亡音效 isSelf 判定见 DieFish。
|
||||
/// </summary>
|
||||
public class JiXieXie_BOSS : MonsterControl_BossBase
|
||||
{
|
||||
// ---- Spine 动画名(需与美术 Spine 资源动画名一致;不一致改这里即可)----
|
||||
private const string AnimIn = "in";
|
||||
private const string AnimStand = "stand";
|
||||
private const string AnimAttack = "attack";
|
||||
private const string AnimOut = "out";
|
||||
|
||||
// ---- 攻击节奏(对齐 lua:随机 10~25s 间隔,attack 持续 2s,出生 2.2s 后转 stand)----
|
||||
// ⚠️ 攻击循环用 DOTween 递归计时驱动,从 OnMoveEnter 启动(进入 move 状态即触发,不依赖 isMove)。
|
||||
// 基类 MonsterContral.OnUpdate 对 isMove=false 的固定 Boss 会在分发处直接 return,
|
||||
// 导致 OnMoveUpdata 永不被调用——故攻击计时不能放在 OnMoveUpdata 里。
|
||||
private Tween _attackTween;
|
||||
private bool _attackRunning;
|
||||
|
||||
// ---- 自动退出(对齐 lua OnMoveFinish:未被击杀也到点离场)----
|
||||
// ⚠️ 机械蝎 isMove=false 固定停在 (0,0,0),不走路线、OnMoveUpdata 永不触发路线终点检测,
|
||||
// 且服务端 SyncGameData.remainingSeconds 未接上销毁逻辑 → 不被打死就永远不消失。
|
||||
// 故用可配置存活计时器兜底:到点播 out(若 Spine 含)后回收。<=0 表示永不离场(纯靠击杀)。
|
||||
[Header("自动退出:未被击杀时的存活时长(秒),<=0 永不自动离场")]
|
||||
public float bossLifeTime = 30f;
|
||||
private Tween _exitTween;
|
||||
private bool _isExiting;
|
||||
private const float AttackIntervalMin = 10f;
|
||||
private const float AttackIntervalMax = 25f;
|
||||
private const float AttackDuration = 2f;
|
||||
private const float BornToStandDelay = 2.2f;
|
||||
|
||||
public override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
_attackRunning = false;
|
||||
// 音效:蝎子语音(PW_TextScorpion)
|
||||
AudioManager.Instance.PlayerFXByName("PW_TextScorpion");
|
||||
}
|
||||
|
||||
public override void DieFish(RoomKillNotice obj = null)
|
||||
{
|
||||
StopAttackCycle(); // 死亡即停止攻击循环,避免对已销毁对象触发动画
|
||||
StopExitTimer(); // 取消自动退出计时,避免击杀回收后定时器再触发一次回收
|
||||
base.DieFish(obj);
|
||||
|
||||
// 离场/死亡动画(对齐 lua out;非 Spine 或无 out 动画时自动跳过)
|
||||
PlaySpineSafe(AnimOut, false);
|
||||
|
||||
// 音效:蝎子死亡 + 死亡语音(PW_ScorpionDie / PW_ScorpionDieVoice)
|
||||
AudioManager.Instance.PlayerFXByName("PW_ScorpionDie");
|
||||
AudioManager.Instance.PlayerFXByName("PW_ScorpionDieVoice");
|
||||
// lua OnExplosion:仅当击杀者为自己(isSelf)才播;本地用户不可用时回退为必播(保留原行为)
|
||||
bool isSelfKill = GameManager.Instance != null && obj != null && obj.userId == GameManager.Instance.user.id;
|
||||
if (isSelfKill || GameManager.Instance == null)
|
||||
{
|
||||
AudioManager.Instance.PlayerFXByName("PW_ScorpionDie");
|
||||
AudioManager.Instance.PlayerFXByName("PW_ScorpionDieVoice");
|
||||
}
|
||||
|
||||
// 标准爆奖事件初始化
|
||||
action_effect_DieAward = (o) =>
|
||||
@@ -62,4 +111,134 @@ public class JiXieXie_BOSS : MonsterControl_BossBase
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// Spine 动画切换(对齐 lua PW_Scorpion.txt 的 SkeletonAnimation 状态机)
|
||||
// 全部经 PlaySpineSafe / HasSpineAnim 守卫:动画名缺失或 isSpine=0 时自动跳过
|
||||
// (回退原 stand2 或保持现状),绝不抛 Spine ArgumentException。
|
||||
// ⚠️ 当前预制体 PW_Enemy_3000 的 isSpine=0 且 Spine 资源需含 in/stand/attack/out 动画名;
|
||||
// 美术接入 Spine 骨骼(isSpine=1 + 对应动画名)后此逻辑立即生效。
|
||||
// ================================================================
|
||||
|
||||
/// <summary>出生动画序列:in(一次) → 2.2s 后 stand(循环)。无 in 动画时回退基类 stand2。
|
||||
/// 同时启动攻击循环(OnMoveEnter 在状态切到 move 时必被调用,不受 isMove 闸门影响)。</summary>
|
||||
public override void OnMoveEnter()
|
||||
{
|
||||
if (HasSpineAnim(AnimIn))
|
||||
{
|
||||
PlaySpineSafe(AnimIn, false);
|
||||
DOVirtual.DelayedCall(BornToStandDelay, () => PlaySpineSafe(AnimStand, true));
|
||||
}
|
||||
else
|
||||
{
|
||||
base.OnMoveEnter();
|
||||
}
|
||||
StartAttackCycle();
|
||||
StartExitTimer();
|
||||
}
|
||||
|
||||
/// <summary>对象回收/离场时停止攻击与退出计时,避免对已销毁对象触发(DOTween 回调安全守卫)。</summary>
|
||||
public override void OnDisable()
|
||||
{
|
||||
StopAttackCycle();
|
||||
StopExitTimer();
|
||||
base.OnDisable();
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// 自动退出(对齐 lua OnMoveFinish:未被击杀也到点离场)
|
||||
// ================================================================
|
||||
|
||||
/// <summary>启动存活计时:到点触发 AutoExit。bossLifeTime<=0 不自动离场(纯靠击杀)。</summary>
|
||||
private void StartExitTimer()
|
||||
{
|
||||
if (bossLifeTime <= 0f) return;
|
||||
_isExiting = false;
|
||||
if (_exitTween != null && _exitTween.IsActive()) _exitTween.Kill();
|
||||
_exitTween = DOVirtual.DelayedCall(bossLifeTime, AutoExit);
|
||||
}
|
||||
|
||||
/// <summary>到点离场:停攻击循环 → 播 out(若 Spine 含)→ 延迟回收本体(同 lua OnMoveFinish→DestroyEnemy)。</summary>
|
||||
private void AutoExit()
|
||||
{
|
||||
if (_isExiting) return;
|
||||
_isExiting = true;
|
||||
StopAttackCycle();
|
||||
|
||||
float delay = 0f;
|
||||
if (HasSpineAnim(AnimOut))
|
||||
{
|
||||
PlaySpineSafe(AnimOut, false);
|
||||
delay = 2f; // 对齐 lua PW_Effect_Scorpion_Out 的 time=2
|
||||
}
|
||||
DOVirtual.DelayedCall(delay, () =>
|
||||
{
|
||||
if (this == null) return;
|
||||
if (CreateFishManager.Instance != null)
|
||||
CreateFishManager.Instance.Id_DieFish_NotFX(guid);
|
||||
});
|
||||
}
|
||||
|
||||
private void StopExitTimer()
|
||||
{
|
||||
_isExiting = false;
|
||||
if (_exitTween != null && _exitTween.IsActive())
|
||||
_exitTween.Kill();
|
||||
_exitTween = null;
|
||||
}
|
||||
|
||||
/// <summary>攻击循环:随机 10~25s 间隔播放 attack(一次,2s) 后回 stand(对齐 lua stand⇄attack)。
|
||||
/// 用 DOTween 递归计时,不依赖 OnMoveUpdata / isMove。
|
||||
/// lua 原逻辑 attack 还需“出场倒计时>5”才触发,C# 侧无该倒计时阶段,故简化为纯随机间隔。</summary>
|
||||
private void StartAttackCycle()
|
||||
{
|
||||
if (!HasSpineAnim(AnimAttack)) return;
|
||||
if (_attackRunning) return; // 防止 OnMoveEnter 重复触发导致多套循环
|
||||
_attackRunning = true;
|
||||
ScheduleNextAttack();
|
||||
}
|
||||
|
||||
private void ScheduleNextAttack()
|
||||
{
|
||||
if (!_attackRunning) return;
|
||||
float delay = UnityEngine.Random.Range(AttackIntervalMin, AttackIntervalMax);
|
||||
_attackTween = DOVirtual.DelayedCall(delay, PlayAttackOnce);
|
||||
}
|
||||
|
||||
private void PlayAttackOnce()
|
||||
{
|
||||
if (!_attackRunning) return;
|
||||
PlaySpineSafe(AnimAttack, false);
|
||||
_attackTween = DOVirtual.DelayedCall(AttackDuration, () =>
|
||||
{
|
||||
if (!_attackRunning) return;
|
||||
PlaySpineSafe(AnimStand, true);
|
||||
ScheduleNextAttack();
|
||||
});
|
||||
}
|
||||
|
||||
private void StopAttackCycle()
|
||||
{
|
||||
_attackRunning = false;
|
||||
if (_attackTween != null && _attackTween.IsActive())
|
||||
_attackTween.Kill();
|
||||
_attackTween = null;
|
||||
}
|
||||
|
||||
/// <summary>Spine 动画是否存在(动画名或 Skeleton 缺失时返回 false)。</summary>
|
||||
private bool HasSpineAnim(string name)
|
||||
{
|
||||
if (skeleton == null) return false;
|
||||
var data = skeleton.SkeletonDataAsset?.GetSkeletonData(false);
|
||||
return data != null && data.FindAnimation(name) != null;
|
||||
}
|
||||
|
||||
/// <summary>安全播放 Spine 动画:动画名不存在 / 非 Spine 时静默跳过,避免 Spine 抛 ArgumentException。</summary>
|
||||
private void PlaySpineSafe(string name, bool loop)
|
||||
{
|
||||
if (skeleton == null) return;
|
||||
var data = skeleton.SkeletonDataAsset?.GetSkeletonData(false);
|
||||
if (data == null || data.FindAnimation(name) == null) return;
|
||||
skeleton.AnimationState.SetAnimation(0, name, loop);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ MonoBehaviour:
|
||||
moveSpeed: 0
|
||||
cachedCamera: {fileID: 0}
|
||||
cachedUirootRect: {fileID: 0}
|
||||
skeleton: {fileID: 0}
|
||||
skeleton: {fileID: 8745352577998363756}
|
||||
isSpine: 0
|
||||
fishTypeID: 12028
|
||||
currState: 0
|
||||
@@ -137,7 +137,7 @@ MonoBehaviour:
|
||||
isMove: 0
|
||||
is3D: 0
|
||||
forwordXYZ:
|
||||
fishenum: 0
|
||||
fishenum: 2
|
||||
moveTime: 0
|
||||
isyz: 0
|
||||
yzID:
|
||||
@@ -167,3 +167,15 @@ MonoBehaviour:
|
||||
v3: {x: 0, y: 0, z: 0}
|
||||
isLocalCoin: 0
|
||||
killerSitPos: -1
|
||||
--- !u!114 &8745352577998363756 stripped
|
||||
MonoBehaviour:
|
||||
m_CorrespondingSourceObject: {fileID: 114517790449158321, guid: 75d387735e7c6cb4d806a8603178a855,
|
||||
type: 3}
|
||||
m_PrefabInstance: {fileID: 8704159038579725533}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: d247ba06193faa74d9335f5481b2b56c, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
|
||||
Reference in New Issue
Block a user