1
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user