This commit is contained in:
2026-10-19 19:02:29 +08:00
parent 7b1695aa73
commit a71c6873e2
18 changed files with 523 additions and 91 deletions
@@ -79,9 +79,18 @@ public class CreateFishManager : MonoSingleton<CreateFishManager>
public GameObject dieFXPrefab; // 基础死亡特效
public GameObject dieFXPrefabAward; // 普通鱼金币奖励特效
public GameObject dieFXPrefabAwardProp; // 道具效果
public GameObject dieFXPrefabAwardTmp; // Boss 分数浮动文本
public GameObject dieFXPrefabAwardTmp_AddScore;
// ----------------------------------------------------------------
// 道具掉落配置(掉落数据由服务端 RoomKillNotice.drop 下发,客户端只负责展示)
// ----------------------------------------------------------------
[Header("道具掉落(服务端驱动)")]
[Tooltip("道具掉落预制体(挂 FishKillProp 脚本)。为空则不展示掉落特效")]
public GameObject propDropPrefab;
// ----------------------------------------------------------------
// 生成系统运行时数据
// ----------------------------------------------------------------
@@ -2015,17 +2024,15 @@ public class CreateFishManager : MonoSingleton<CreateFishManager>
// ⭐ 掉落道具推送去重:服务端把"击杀爆奖"和"掉落道具"拆成两条 RoomKillNotice 下发,
// 第二条与击杀消息同 fishId/killtime/gold,但只有 drop、没有 param。
// Boss 鱼 DieFish 是延迟回收(字典未即时移除),第二条若按击杀再处理一次:
// ① not-self 模式下 _pending[key] 被覆盖 → 前一个"爆奖中"BJEndUI 失去引用、永久残留在座位上;
// ② 重复克隆爆奖特效、重复走 DieFish → 特效/音效/爆奖双份(两个 Event 实例各出一次 bjEndUI)。
// 客户端目前没有掉落弹窗逻辑(见 RoomKillNotice.drop 注释),直接跳过;将来做掉落 UI 时在此处理。
// 此条消息不走击杀流程(避免重复爆奖),只创建道具掉落特效并飞向击杀者座位。
// ⚠️ param 是 JToken:JSON "param":null 会被反序列化成 JValue(Null)(Type==JTokenType.Null),
// 不能只判 C# null(与 BossKillParam.cs 的判空写法保持一致),否则第二条漏拦 → 重复爆奖。
if (killFish.drop != null
&& (killFish.param == null || killFish.param.Type == JTokenType.Null))
{
NetLog.Log($"[CreateFishManager] 掉落道具推送(跳过击杀处理):fishId={killFish.fishId} " +
NetLog.Log($"[CreateFishManager] 掉落道具推送:fishId={killFish.fishId} " +
$"prop={killFish.drop.propNumber}×{killFish.drop.count}");
HandleDropProp(killFish);
return;
}
@@ -2467,6 +2474,92 @@ public class CreateFishManager : MonoSingleton<CreateFishManager>
}
#endregion
// ================================================================
// 道具掉落(服务端驱动:RoomKillNotice.drop 下发掉落数据)
// ================================================================
/// <summary>
/// 处理服务端下发的道具掉落推送(RoomKillNotice.drop 不为空、param 为空的消息)。
/// 与金币流程对称:EffectPool 取预制体→转 Canvas 坐标→InitProp 设置编号和座位终点→
/// SetCoinCount→FlyToSeat 飞到座位回收。
/// 道具图标按 /uploads/{propNumber}.png 从服务端下载(由 FishKillProp 内部完成)。
/// </summary>
private void HandleDropProp(RoomKillNotice killFish)
{
if (propDropPrefab == null || killFish.drop == null) return;
if (string.IsNullOrEmpty(killFish.drop.propNumber)) return;
if (currGameType == null) return;
// 找击杀者座位
SitPosData sitData = null;
foreach (var item in currGameType.list_Sits)
{
if (item.user == killFish.userId)
{
sitData = item;
break;
}
}
if (sitData == null)
{
NetLog.Log($"[CreateFishManager] 掉落道具找不到座位:userId={killFish.userId}");
return;
}
// 找鱼位置(鱼可能已回收,找不到就用座位位置兜底)
Vector3 dropPos = sitData.transform.position;
if (fishDatas_hashCodekey.TryGetValue(killFish.fishId, out var monster) && monster != null)
dropPos = monster.transform.position;
// 创建道具特效(和金币一样 autoReturn=false,由 FlyToSeat 手动回池)
var propObj = EffectPool.Instance.Get(
propDropPrefab,
dropPos, Quaternion.identity,
currGameType.subPanelParent.parent,
autoReturn: false);
if (propObj == null) return;
// 坐标转换(和金币一致:世界坐标转 UI Canvas 坐标)
try
{
propObj.GetComponent<RectTransform>().anchoredPosition =
CoordinateTransformation.WorldPos_ToCanvas(dropPos, GetUiCanvas());
}
catch
{
propObj.transform.position = dropPos;
}
// 初始化道具:编号、数量、起点、终点(座位)、玩家信息
var fishKillProp = propObj.GetComponent<FishKillProp>();
if (fishKillProp == null)
{
// 预制体没挂 FishKillProp 脚本 → 回收并退出
EffectPool.Instance.Return(propObj);
return;
}
fishKillProp.InitProp(
killFish.drop.propNumber,
killFish.drop.count,
dropPos,
sitData.transform.position,
killFish.userId,
sitData.posID,
null);
fishKillProp.JiaFen = (audioID) =>
{
AudioManager.Instance.PlayerFX(audioID);
// TODO: 道具拾取后的业务逻辑(如刷新背包、弹获得面板等)
};
fishKillProp.SetCoinCount();
fishKillProp.FlyToSeat();
GameManager.LogFishFlow("DROP_PROP", killFish.fishId,
$"prop={killFish.drop.propName}×{killFish.drop.count} 座位={sitData.posID} 击杀者={killFish.userId}",
propObj.GetInstanceID());
}