This commit is contained in:
2026-07-05 17:39:30 +08:00
parent 6decb0b937
commit 43b29df7c0
8 changed files with 115 additions and 34 deletions
@@ -153,6 +153,9 @@ public class CreateFishManager : MonoSingleton<CreateFishManager>
private List<string> bossRecentList = new List<string>(); // 最近出现过的Boss(cool down)
private int bossRecentMax = 3; // N次内不重复出现
/// <summary>Boss鱼路线奇偶切换标记:true=下次选奇数路线,false=下次选偶数路线</summary>
private bool bossNextPathOdd = true;
private RoomFishProbabilityConfig yzCreateData; // 鱼阵创建数据
private float yzTimer = 999f; // 鱼阵刷新倒计时(秒)
private int yzCount; // 本轮同时激活阵型数量
@@ -996,9 +999,24 @@ public class CreateFishManager : MonoSingleton<CreateFishManager>
// 普通鱼(type==1)才从 pathList 随机选路径;鱼阵(type==2)由后续逻辑决定
if (fish.type == 1)
{
var pathList = FishDatas[fish.fishNumber].pathList;
var ft = FishDatas[fish.fishNumber];
var pathList = ft.pathList;
if (pathList != null && pathList.Count > 0)
pathId = pathList[Random.Range(0, pathList.Count)];
{
// Boss鱼(2)/特殊Boss(5):路线奇偶交替,避免同奇/同偶路径相互覆盖
if (ft != null && (ft.fishType == 2 || ft.fishType == 5))
{
var filtered = pathList.Where(p => (p % 2 != 0) == bossNextPathOdd).ToList();
if (filtered.Count == 0)
filtered = pathList;
pathId = filtered[Random.Range(0, filtered.Count)];
bossNextPathOdd = !bossNextPathOdd;
}
else
{
pathId = pathList[Random.Range(0, pathList.Count)];
}
}
}
else
{
@@ -185,6 +185,9 @@ public class GameManager : MonoSingleton<GameManager>
}
protected override void Awake()
{
// ⭐ botConfig 默认初始化,避免 null 导致技能系统不跑
if (botConfig == null) botConfig = new BotConfig();
// ⭐ 游戏启动时加载已保存的画质和帧率设置
int quality = PlayerPrefs.GetInt("GraphicsQuality", 1);
QualitySettings.SetQualityLevel(quality, true);
@@ -4,7 +4,6 @@ using UnityEngine;
/// <summary>
/// 机器人AI配置 — 本地测试用 Inspector 调节,后续从后端 GameConfig / RoomChangeResponse 下发
/// </summary>
[Serializable]
public class BotConfig
{
[Header("全局")]
@@ -21,49 +20,48 @@ public class BotConfig
public float stateRefreshInterval;
[Header("技能配置")]
public BotSkillConfig skills;
public BotSkillConfig skills = new BotSkillConfig();
[Header("active 状态参数")]
public BotBehaviorConfig activeBehavior;
public BotBehaviorConfig activeBehavior = new BotBehaviorConfig();
[Header("idle 状态参数")]
public BotBehaviorConfig idleBehavior;
public BotBehaviorConfig idleBehavior = new BotBehaviorConfig();
[Header("aggressive 状态参数")]
public BotBehaviorConfig aggressiveBehavior;
public BotBehaviorConfig aggressiveBehavior = new BotBehaviorConfig();
}
/// <summary>
/// 技能概率配置 — 每种技能独立随机,可以同时开多种
/// </summary>
[Serializable]
public class BotSkillConfig
{
[Header("刷新")]
[Tooltip("每隔多少秒重新掷骰子决定技能组合")]
public float skillRefreshInterval = 8f;
public float skillRefreshInterval = 1f;
[Space]
[Header("锁定 - 自动瞄准最近鱼")]
[Range(0f, 1f)] public float lockProb = 0.6f;
[Range(0f, 1f)] public float lockProb = 1f;
public float lockMinTime = 5f;
public float lockMaxTime = 20f;
[Space]
[Header("狂暴 - 开火速度翻倍")]
[Range(0f, 1f)] public float frenzyProb = 0.55f;
[Range(0f, 1f)] public float frenzyProb = 1f;
public float frenzyMinTime = 3f;
public float frenzyMaxTime = 8f;
[Space]
[Header("三发散弹 - 一次3发")]
[Range(0f, 1f)] public float tripleProb = 0.55f;
[Range(0f, 1f)] public float tripleProb = 1f;
public float tripleMinTime = 4f;
public float tripleMaxTime = 12f;
[Space]
[Header("双倍炮 - 消耗翻倍")]
[Range(0f, 1f)] public float doubleProb = 0.5f;
[Range(0f, 1f)] public float doubleProb = 1f;
public float doubleMinTime = 3f;
public float doubleMaxTime = 6f;
}
@@ -71,7 +69,6 @@ public class BotSkillConfig
/// <summary>
/// 行为参数 — 每个 aiState 一组
/// </summary>
[Serializable]
public class BotBehaviorConfig
{
[Header("开火")]
@@ -84,7 +81,7 @@ public class BotBehaviorConfig
[Header("旋转")]
[Tooltip("炮台旋转速度(度/秒)")]
public float rotateSpeed = 40f;
public float rotateSpeed = 80f;
[Header("目标")]
[Tooltip("锁定保持时间下限(秒)")]
@@ -104,7 +101,6 @@ public class BotBehaviorConfig
/// <summary>
/// 每个 bot 座位的技能运行时状态
/// </summary>
[Serializable]
public class BotSkillRuntime
{
public bool lockActive;
+1 -1
View File
@@ -18,7 +18,7 @@ public class BJEndUI : MonoBehaviour
public void Init(int beishu, int score)
{
tMP_beishu.text = beishu.ToString();
tMP_beishu.text = "<sprite=6>"+ beishu.ToString();
if (useScoreAnim)
StartCoroutine(ScoreAnim(score));
@@ -388,7 +388,7 @@ public class BJEventBase : MonoBehaviour
}
var endUI = Instantiate(bjEndUI, uiPos, Quaternion.identity, UIManager.Instance.uiRoot);
endUI.Init(paoBei, maxScore);
endUI.Init(bJbeishu, maxScore);
}
// 删除分数面板
@@ -129,7 +129,7 @@ public class FLXJEvent : BJEventBase
{
var uiPos = GetSeatPosition() + Vector3.up * bjEndUIOffsetY;
var endUI = Instantiate(bjEndUI, uiPos, Quaternion.identity, UIManager.Instance.uiRoot);
endUI.Init(paoBei, maxScore);
endUI.Init(bJbeishu, maxScore);
bjEndUI = null; // 防止 OnScoreEnd 重复创建
}
OnScoreEnd();
@@ -92,7 +92,7 @@ public class GSLDieEvent :BJEventBase
{
var uiPos = GetSeatPosition() + Vector3.up * bjEndUIOffsetY;
var endUI = Instantiate(bjEndUI, uiPos, Quaternion.identity, UIManager.Instance.uiRoot);
endUI.Init(paoBei, maxScore);
endUI.Init(bJbeishu, maxScore);
bjEndUI = null; // 防止 OnScoreEnd 重复创建
}
OnScoreEnd();
@@ -151,12 +151,16 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
[Header("攻击参数")]
public float attackTime = 0.5f;
[Header("人机参数")]
public float botRotateSpeed = 40f; // 兜底旋转速度(BotConfig 为空时用)
public float botRotateSpeed = 80f; // 兜底旋转速度(BotConfig 为空时用)
float clickTime = 0;
private Dictionary<int, float> botFireTimer = new Dictionary<int, float>();
private Dictionary<string, float> botLastAngle = new Dictionary<string, float>();
// bot 技能运行时
private Dictionary<int, BotSkillRuntime> botSkills = new Dictionary<int, BotSkillRuntime>();
// bot 目标记忆
private Dictionary<int, string> botTargetGuid = new Dictionary<int, string>();
// bot 状态计时:开火后持续 Attack 动画,空闲 0.3s 切回 Idle
private Dictionary<int, float> botIdleTimer = new Dictionary<int, float>();
public GameObject bullet_Test;
public GameObject bulletParent;
float changeTurretTime = 0f;
@@ -1926,15 +1930,35 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
RefreshBotSkills(sit, cfg.skills);
var skill = botSkills.ContainsKey(sit.posID) ? botSkills[sit.posID] : null;
// ====== 找目标:Boss > 黄金鱼 > 特殊鱼 > 普通鱼 ======
MonsterContral target = SelectBotTarget(sit, fishList, turretPos);
// ====== 目标选择(记住当前目标,死了/飞走了才换) ======
MonsterContral target = GetOrFindBotTarget(sit, fishList, turretPos, skill);
if (target != null)
if (target == null) continue;
// ====== 锁定技能:走真实锁定系统(显示锁定图标) ======
if (skill != null && skill.lockActive)
{
// 旋转朝向目标(持续旋转,不管发不发炮)
if (skill == null || !skill.lockActive)
RotateBotToTarget(sit, target.transform.position, rotateSpeed);
if (sit.monCotl_Lock != target)
{
sit.isLock = true;
sit.monCotl_Lock = target;
sit.monCotl_Lock_Position = target.colliderList != null && target.colliderList.Count > 0
? target.colliderList[0].transform : target.transform;
if (sit.lockFX != null) sit.lockFX.SetActive(true);
}
}
else
{
if (sit.isLock)
{
sit.isLock = false;
sit.monCotl_Lock = null;
if (sit.lockFX != null) sit.lockFX.SetActive(false);
}
}
// ====== 旋转(始终运行,Lerp自然减速) ======
RotateBotToTarget(sit, target.transform.position, rotateSpeed);
// ====== 持续射击 ======
if (!botFireTimer.ContainsKey(sit.posID))
@@ -1951,6 +1975,12 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
{
botFireTimer[sit.posID] = 0f;
// 攻击动画
sit.ChangeState(TurretState.Attack);
if (!botIdleTimer.ContainsKey(sit.posID))
botIdleTimer[sit.posID] = 0f;
botIdleTimer[sit.posID] = 0f;
float angle = sit.currentAngle;
botLastAngle[sit.user] = angle;
@@ -1967,14 +1997,50 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
sit.user, gold, count);
SyncFire_ForBot(sit, angle, gold, count);
}
// 空闲超时切回 Idle 动画
if (!botIdleTimer.ContainsKey(sit.posID))
botIdleTimer[sit.posID] = 0f;
botIdleTimer[sit.posID] += Time.deltaTime;
if (botIdleTimer[sit.posID] > 0.3f)
{
sit.ChangeState(TurretState.Idle);
botIdleTimer[sit.posID] = 0f;
}
}
}
/// <summary>
/// 获取或寻找目标:优先用记忆的目标(还在前方就沿用),死了/不在前方了再重新找
/// </summary>
private MonsterContral GetOrFindBotTarget(SitPosData sit, CreateFishManager fishList, Vector3 turretPos, BotSkillRuntime skill)
{
// 1. 检查记忆目标是否还有效
if (botTargetGuid.TryGetValue(sit.posID, out string guid))
{
if (fishList.fishDatas_hashCodekey.TryGetValue(guid, out var m)
&& m != null && m.gameObject.activeInHierarchy)
{
Vector3 fp = m.transform.position;
bool inFront = sit.IsFlipped ? fp.y < turretPos.y : fp.y > turretPos.y;
if (inFront) return m;
}
// 目标无效 → 清除记忆
botTargetGuid.Remove(sit.posID);
}
// 2. 重新找目标(Boss > 黄金鱼 > 特殊鱼 > 普通鱼,同类型打最近)
MonsterContral target = SelectBestTarget(sit, fishList, turretPos);
if (target != null)
botTargetGuid[sit.posID] = target.guid;
return target;
}
/// <summary>
/// 按优先级选目标:特殊Boss(5) > Boss(2) > 黄金鱼(3) > 特殊鱼(4) > 普通鱼(1)
/// 同一优先级内选最近的,只选炮台前方的鱼
/// </summary>
private MonsterContral SelectBotTarget(SitPosData sit, CreateFishManager fishList, Vector3 turretPos)
private MonsterContral SelectBestTarget(SitPosData sit, CreateFishManager fishList, Vector3 turretPos)
{
MonsterContral best = null;
float bestDist = float.MaxValue;
@@ -1985,14 +2051,11 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
var m = kv.Value;
if (m == null || !m.gameObject.activeInHierarchy) continue;
// 只选炮台前方的鱼
Vector3 fishPos = m.transform.position;
if (sit.IsFlipped && fishPos.y >= turretPos.y) continue;
if (!sit.IsFlipped && fishPos.y <= turretPos.y) continue;
float d = (fishPos - turretPos).sqrMagnitude;
// 优先级: 特殊Boss=5→0, Boss=2→1, 黄金鱼=3→2, 特殊鱼=4→3, 普通鱼=1→4
int pri = GetFishPriority((int)m.fishenum);
if (pri < bestPri || (pri == bestPri && d < bestDist))
@@ -2084,11 +2147,12 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
if (sit.IsFlipped) angle = (angle + 180f) % 360f;
// 平滑旋转:像真人一样缓慢转动,避免瞬间闪现
// Lerp 旋转:自然减速,速度由 rotateSpeed 控制
float current = sit.currentAngle;
float speed = rotateSpeed > 0f ? rotateSpeed : botRotateSpeed;
float maxDelta = speed * Time.deltaTime;
float smoothed = Mathf.MoveTowardsAngle(current, angle, maxDelta);
// t 越大旋转越快,clamp 防过冲
float t = Mathf.Min(speed * 0.15f * Time.deltaTime, 1f);
float smoothed = Mathf.LerpAngle(current, angle, t);
sit.SetRotation(smoothed);
}