227 lines
6.6 KiB
C#
227 lines
6.6 KiB
C#
/*
|
|
* @Author: 白哉
|
|
* @Description: AI 逻辑总模块
|
|
* @Date: 2025年04月01日 星期二 14:04:01
|
|
* @Modify:
|
|
*/
|
|
|
|
using System.Collections.Generic;
|
|
using Logic.Action;
|
|
using Logic.AI.Director;
|
|
using Logic.CrashSight;
|
|
using RuntimeData;
|
|
using TH1_Core.Managers;
|
|
using UnityEngine;
|
|
|
|
namespace Logic.AI
|
|
{
|
|
public enum AILogicState
|
|
{
|
|
Prepare,
|
|
Playing,
|
|
PrePlay,
|
|
Pausing,
|
|
Finished,
|
|
}
|
|
|
|
public enum AIActionType
|
|
{
|
|
Grid,
|
|
City,
|
|
Unit,
|
|
Tech,
|
|
Max,
|
|
}
|
|
|
|
public class AILogic
|
|
{
|
|
public AILogicState AILogicState;
|
|
public PlayerData PlayerData => _playerData;
|
|
|
|
private float _targetTime;
|
|
private IAIKernel _kernel;
|
|
private AILogicContext _context;
|
|
private MapData _mapData;
|
|
private PlayerData _playerData;
|
|
private int _actionCount;
|
|
private int _kernelVersion;
|
|
|
|
public static uint CurrentAIPlayerId;
|
|
public static Dictionary<uint, List<AIRecord>> AIRecordsDict;
|
|
|
|
public AILogic()
|
|
{
|
|
AIRecordsDict = new Dictionary<uint, List<AIRecord>>();
|
|
AILogicState = AILogicState.Prepare;
|
|
RebuildKernel();
|
|
}
|
|
|
|
public void RebuildKernel()
|
|
{
|
|
_context = new AILogicContext
|
|
{
|
|
Data = new AICalculatorData(),
|
|
Generator = new AIActionGenerator(),
|
|
ScoreCalculator = new AIActionScoreCalculator(),
|
|
Config = TH1Resource.ResourceLoader.Load<AIConfigAsset>("Export/AIConfig")
|
|
};
|
|
|
|
_kernel = AIKernelRegistry.Create();
|
|
_kernel.Initialize(_context);
|
|
_kernelVersion = AIKernelRegistry.Version;
|
|
}
|
|
|
|
public void StartAILogic(MapData mapData, PlayerData playerData)
|
|
{
|
|
if (_kernel == null || _kernel.KernelType != AIKernelRegistry.CurrentKernelType || _kernelVersion != AIKernelRegistry.Version) RebuildKernel();
|
|
|
|
AILogicState = AILogicState.Playing;
|
|
_actionCount = 0;
|
|
_mapData = mapData;
|
|
_playerData = playerData;
|
|
_kernel.StartTurn(_mapData, _playerData);
|
|
|
|
#if UNITY_EDITOR
|
|
CurrentAIPlayerId = _playerData.Id;
|
|
if (!AIRecordsDict.ContainsKey(CurrentAIPlayerId)) AIRecordsDict[CurrentAIPlayerId] = new List<AIRecord>();
|
|
AIRecordsDict[CurrentAIPlayerId].Clear();
|
|
#endif
|
|
}
|
|
|
|
public void FinishAILogic()
|
|
{
|
|
_kernel?.FinishTurn();
|
|
_playerData = null;
|
|
_mapData = null;
|
|
AILogicState = AILogicState.Prepare;
|
|
}
|
|
|
|
public void Update()
|
|
{
|
|
if (AILogicState == AILogicState.Finished || AILogicState == AILogicState.Prepare) return;
|
|
|
|
#if ENABLE_SPEEDUP
|
|
if (AILogicState == AILogicState.Pausing)
|
|
#else
|
|
if (AILogicState == AILogicState.Pausing && (AIDirectorBatchRuntime.SkipPresentationWait || !PresentationManager.Busy))
|
|
#endif
|
|
{
|
|
_targetTime -= Time.deltaTime;
|
|
if (_targetTime <= 0) AILogicState = AILogicState.Playing;
|
|
}
|
|
|
|
if (AILogicState != AILogicState.Playing) return;
|
|
|
|
if (_actionCount > 200)
|
|
{
|
|
var btNodeId = _kernel?.KernelType == AIKernelType.BehaviourTree ? MainEditor.Instance.BTNodeId : 0;
|
|
LogSystem.LogError($"AI 行为次数过多,可能进入死循环,强制结束 AI 逻辑 最终记录点为:{btNodeId}");
|
|
AILogicState = AILogicState.Finished;
|
|
return;
|
|
}
|
|
|
|
var update = _kernel.Update();
|
|
if (update.Result == AIKernelUpdateResult.None) return;
|
|
if (update.Result == AIKernelUpdateResult.Finished || update.Action == null)
|
|
{
|
|
AILogicState = AILogicState.Finished;
|
|
return;
|
|
}
|
|
|
|
ExecuteAction(update.Action);
|
|
}
|
|
|
|
private void ExecuteAction(AIActionBase action)
|
|
{
|
|
action.Param.MapData = _mapData;
|
|
action.Param.RefreshParams();
|
|
#if TH1_AI_DIRECTOR_DIAGNOSTICS || UNITY_EDITOR
|
|
var actionIndex = _actionCount + 1;
|
|
var before = AIDirectorDiagnostics.CaptureOutcomeProbe(_mapData, _playerData, action);
|
|
var executed = action.ActionLogic.CompleteExecute(action.Param);
|
|
var after = AIDirectorDiagnostics.CaptureOutcomeProbe(_mapData, _playerData, action);
|
|
if (_kernel?.KernelType == AIKernelType.Director)
|
|
{
|
|
AIDirectorDiagnostics.RecordExecution(
|
|
_mapData,
|
|
_playerData,
|
|
actionIndex,
|
|
action,
|
|
executed,
|
|
before,
|
|
after);
|
|
}
|
|
#else
|
|
action.ActionLogic.CompleteExecute(action.Param);
|
|
#endif
|
|
|
|
action.CheckIsActionDuration();
|
|
_targetTime = Mathf.Max(action.Duration, 0f);
|
|
|
|
#if UNITY_EDITOR
|
|
var records = GetCurrentAIRecords();
|
|
if (records != null && records.Count != 0)
|
|
{
|
|
var record = records[^1];
|
|
record.Action = action;
|
|
}
|
|
#endif
|
|
|
|
_actionCount++;
|
|
if (_kernel?.KernelType == AIKernelType.BehaviourTree) MainEditor.Instance.OnActionExcuted();
|
|
AILogicState = AILogicState.Pausing;
|
|
}
|
|
|
|
public static void RegisterKernel(AIKernelType kernelType)
|
|
{
|
|
AIKernelRegistry.Register(kernelType);
|
|
}
|
|
|
|
public static void UseBehaviourTreeKernel()
|
|
{
|
|
AIKernelRegistry.Register(AIKernelType.BehaviourTree);
|
|
}
|
|
|
|
public static void UseDirectorKernel()
|
|
{
|
|
AIKernelRegistry.Register(AIKernelType.Director);
|
|
}
|
|
|
|
public static List<AIRecord> GetCurrentAIRecords()
|
|
{
|
|
return AIRecordsDict.GetValueOrDefault(CurrentAIPlayerId);
|
|
}
|
|
|
|
public static List<AIRecord> GetAIRecords(uint playerId)
|
|
{
|
|
return AIRecordsDict.GetValueOrDefault(playerId);
|
|
}
|
|
}
|
|
|
|
public class AIStateRecord
|
|
{
|
|
public uint ID;
|
|
public string Desc;
|
|
public bool Result;
|
|
}
|
|
|
|
public class AIRecord
|
|
{
|
|
public List<AIStateRecord> StateRecords;
|
|
public AIActionBase Action;
|
|
public bool IsFoldout;
|
|
|
|
public AIRecord()
|
|
{
|
|
IsFoldout = false;
|
|
StateRecords = new List<AIStateRecord>();
|
|
}
|
|
|
|
public string GetDesc()
|
|
{
|
|
if (Action == null) return "暂无动作";
|
|
return Action.ActionLogic.GetType().ToString();
|
|
}
|
|
}
|
|
}
|