UP-Viagg-io/Viagg-io/Assets/Packages/MyBT/BT/TaskController.cs

481 lines
17 KiB
C#
Raw Normal View History

2024-01-19 16:30:05 +01:00
//============= Copyright (c) Ludic GmbH, All rights reserved. ==============
//
// Purpose: Part of the My Behaviour Tree Code
//
//=============================================================================
using UnityEngine;
using System;
using System.Linq;
//using System.Runtime.Serialization.Configuration;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.Xml.Serialization;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace MyBT {
[System.Serializable] public class DictionaryOfStringAndMethodImplementation : SerializableDictionary<string, MethodImplementation> {}
[System.Serializable] public class DictionaryOfStringAndDictionaryOfStringAndMethodImplementation : SerializableDictionary<string, DictionaryOfStringAndMethodImplementation> {}
public enum TickUpdateMode {
// run in update / lateUpdate
Update,
LateUpdate,
// run in an interval for 1 step
DelayedUpdate,
DelayedLateUpdate,
// run for a maximum time
MaximumTimeUpdate,
MaximumTimeLateUpdate,
// run a full cycle
FullrunUpdate,
FullrunLateUpdate,
// run manually
Manual
}
[ExecuteInEditMode, System.Serializable]
public partial class TaskController : MonoBehaviour {
#region unity inspector updater (only in editor)
// handling changes of the bt -> requires redrawing of the editor ui
[NonSerialized]
public Action<bool> InspectorRedraw;
[NonSerialized]
public Action<bool> InspectorRegenerate;
// called from lateUpdate
[System.Diagnostics.Conditional("UNITY_EDITOR")]
void CallInspectorRedraw() {
// makes unity editor very slow!!!
//UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
lastTickTime = currentUiTime;
if (InspectorRedraw != null) InspectorRedraw.Invoke(false);
}
// called from lateUpdate
[System.Diagnostics.Conditional("UNITY_EDITOR")]
void CallInspectorRegenerate() {
if (InspectorRegenerate != null) InspectorRegenerate.Invoke(false);
}
// when was the tick executed last time
public float lastTickTime = 0;
// when was the behaviour tree changed last time
private float lastUiChangedTime = 0;
// has the behaviour tree changed in this frame?
// it is called from TaskControllerInspector
public bool UiChangedThisFrame () {
return (lastTickTime == lastUiChangedTime);
}
public bool UiChangedAt (float time) {
return lastTickTime == time;
}
// called to set behaviour tree changed
public void SetUiUpdate() {
// time at beginning of frame
lastUiChangedTime = TaskController.currentUiTime;
}
public static float currentUiTime {
get { return Time.time; }
}
#endregion
#region settings
// tick settings
public float tickDelay = 0f;
public float maximumRuntime = 0.1f;
public TickUpdateMode tickMode = TickUpdateMode.Update;
// generation settings
public bool runtimeAutomaticRegenerate = true;
public bool resetDataOnCodeChange = false;
// logging
public bool runtimeLogging = false;
public bool overrideLogStringDisplay = false;
public bool overrideDebugInternalActive = false;
public bool overrideDebugChangesActive = false;
// timer for tick delay calculation
private float tickDelayTimer = 0f;
NodeResult rootResult = NodeResult.Succeeded;
#if false
public IEnumerator<NodeResult> _tickEnumerator;
public IEnumerator<NodeResult> tickEnumerator {
get {
return _tickEnumerator;
}
set {
if (runtimeLogging)
Debug.LogWarning($"setting tickEnumerator to '{value}'");
_tickEnumerator = value;
}
}
#else
public IEnumerator<NodeResult> tickEnumerator;
#endif
private bool tickSuccess = false;
#endregion
#region duplicate watcher
// #if UNITY_EDITOR
// Catch duplication of this GameObject
[SerializeField]
private int instanceID = 0;
/// <summary>
/// Detect when the GameObject has been duplicated
/// if so make sure then dont share the same instance of the behaviour tree generator
/// </summary>
void AwakeDuplicateCatcher() {
if (instanceID != GetInstanceID()) {
if (generatorLogging) Debug.Log($"Detected Duplicate! ( {instanceID} != {GetInstanceID()} )");
// Do whatever you need to set up the duplicate
// copy old contents
// List<UnityEngine.TextAsset> oldTaskScripts = new List<UnityEngine.TextAsset>();
// for (int i=0; i<oldTaskScripts.Count; i++) { //(TextAsset txtAss in oldTaskScripts) {
// taskScripts.Add(oldTaskScripts[i]);
// }
// clear current data
__behaviourTreeGenerator = null;
// assign scripts
// read scripts
// for (int i=0; i<oldTaskScripts.Count; i++) { //(TextAsset txtAss in oldTaskScripts) {
// taskScripts.Add(oldTaskScripts[i]);
// }
// works, but regenerates everything, breaks when unity builds the project
// BtScriptChangeDetected(true);
// FullReset();
instanceID = GetInstanceID();
#if UNITY_EDITOR
EditorUtility.SetDirty(this);
#endif
}
}
// #endif
private void Awake() {
AwakeDuplicateCatcher();
if (Application.isPlaying) {
VerifyBindings();
RegenerateBehaviourTreeIfRequired(false);
}
}
public void OnDestroy () {
#if !UNITY_EDITOR
// calling this when building causes it to fail
ClearBinding();
#endif
}
#endregion
// public bool destroyNextFrame = false;
// public bool breakNextFrame = false;
#region tick functions
public void Update() {
// if (breakNextFrame) {
// Debug.Break();
// }
// if (destroyNextFrame) {
// OnDestroy();
// breakNextFrame = true;
// }
// Debug.Log($"Update {Time.frameCount} {Time.time}");
if (Application.isPlaying) {
if (tickMode == TickUpdateMode.Update) {
TickUpdateStep();
}
else if (tickMode == TickUpdateMode.DelayedUpdate) {
tickDelayTimer -= Time.deltaTime;
if (tickDelayTimer < 0) {
tickDelayTimer += tickDelay;
TickUpdateStep();
}
}
else if (tickMode == TickUpdateMode.MaximumTimeUpdate) {
float startTime = Time.realtimeSinceStartup;
while ((Time.realtimeSinceStartup - startTime) < maximumRuntime) {
TickUpdateStep();
}
}
else if (tickMode == TickUpdateMode.FullrunUpdate) {
TickUpdateFull();
}
}
}
public void LateUpdate () {
if (Application.isPlaying) {
if (tickMode == TickUpdateMode.LateUpdate) {
TickUpdateStep();
}
else if (tickMode == TickUpdateMode.DelayedLateUpdate) {
tickDelayTimer -= Time.deltaTime;
if (tickDelayTimer < 0) {
tickDelayTimer += tickDelay;
TickUpdateStep();
}
}
else if (tickMode == TickUpdateMode.MaximumTimeLateUpdate) {
float startTime = Time.realtimeSinceStartup;
while ((Time.realtimeSinceStartup - startTime) < maximumRuntime) {
TickUpdateStep();
}
}
else if (tickMode == TickUpdateMode.FullrunLateUpdate) {
TickUpdateFull();
}
}
}
public void TickUpdate () {
TickUpdateStep();
}
public void TickUpdateStep () {
if (restartBehaviourTreeNextFrame) {
RestartNow();
}
if (hasRootNode) {
// if null or looped trough all items
if ((!tickSuccess) || (tickEnumerator == null)) {
// restart tick
tickEnumerator = rootNode.Tick(null, 0).GetEnumerator();
if (runtimeLogging)
Debug.Log($"Assigned new Iterator {(tickEnumerator != null)}");
}
// run as usual
tickSuccess = tickEnumerator.MoveNext();
rootResult = tickEnumerator.Current;
if (runtimeLogging)
Debug.Log($"--- Ticked --- s: {tickSuccess} r:{rootResult} --- t:{Time.frameCount} ----------------------------------------------------------------------------------------------------------------------");
}
else {
Debug.LogError("cannot tick, missing root node! disabling this gameObject !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
gameObject.SetActive(false);
}
CallInspectorRedraw();
}
public void TickUpdateFull() {
if (restartBehaviourTreeNextFrame) {
RestartNow();
}
tickDelayTimer = tickDelay;
//behaviourTreeGenerator.SetTaskControllerAndGenerateCache(this);
if (hasRootNode) {
// reset the bt
foreach (var result in rootNode.Tick(null, 0)) {
rootResult = result;
}
rootResult = NodeResult.Continue;
}
if (runtimeLogging)
Debug.Log($"--- Restart --- r:{rootResult} --- t:{Time.frameCount} ----------------------------------------------------------------------------------------------------------------------");
CallInspectorRedraw();
}
#endregion
}
public partial class TaskController {
#region behaviourtree generator & exposing of methods and variables
[SerializeField]
private BehaviourTreeGenerator __behaviourTreeGenerator;
[HideInInspector, SerializeField]
private BehaviourTreeGenerator behaviourTreeGenerator {
get {
if (__behaviourTreeGenerator == null) { __behaviourTreeGenerator = BehaviourTreeGenerator.CreateInstance<BehaviourTreeGenerator>(); }
if (__behaviourTreeGenerator == null) { Debug.LogError($"behaviourTreeGenerator is null... how? {behaviourTreeGenerator}"); }
return __behaviourTreeGenerator;
}
}
// expose parts of the behaviourTreeGenerator
#endregion
#region functions and variables from behaviourTreeGenerator
// configuration
[SerializeField]
public List<TextAsset> taskScripts;
public bool generatorLogging {
get { return behaviourTreeGenerator.generatorLogging; }
set { behaviourTreeGenerator.generatorLogging = value; }
}
public bool internalAutomaticRegenerate {
get { return behaviourTreeGenerator.internalAutomaticRegenerate; }
set { behaviourTreeGenerator.internalAutomaticRegenerate = value; }
}
public TreeNode rootNode {
get { return behaviourTreeGenerator.rootNode; }
}
public bool hasRootNode {
get { return (behaviourTreeGenerator.rootNode != null); }
}
public void FullGenerate (bool force) {
behaviourTreeGenerator.FullGenerate(this, force);
}
public void FullReset () {
behaviourTreeGenerator.FullReset();
}
private bool restartBehaviourTreeNextFrame = false;
public void Restart(bool forceImmediate=false) {
restartBehaviourTreeNextFrame = true;
if (forceImmediate) {
RestartNow();
}
}
private void RestartNow () {
behaviourTreeGenerator.Restart();
restartBehaviourTreeNextFrame = false;
}
public void VerifyBindings() {
behaviourTreeGenerator.VerifyBinding(this);
}
public void RegenerateBehaviourTreeIfRequired (bool forceUpdate) {
behaviourTreeGenerator.RegenerateBehaviourTreeIfRequired(this, forceUpdate);
}
/// <summary>
/// the methods called have changed (or might have...)
/// we dont yet filter which scripts are changed
/// </summary>
public void ClearBindingsAndScanAndBindMethods(bool forceUpdate = false) {
// forcefully clear and regenerate
behaviourTreeGenerator.ClearBindingsAndScanAndBindMethods(this, forceUpdate);
}
/// <summary>
/// is called by DetectBtChanges
/// </summary>
/// <param name="forceUpdate"></param>
public void BtScriptChangeDetected(bool forceUpdate = false) {
behaviourTreeGenerator.BtScriptChangeDetected(this, forceUpdate);
}
public void ResetNodesAndTokens() {
behaviourTreeGenerator.ResetNodesAndTokens();
}
public void GenerateTokensAndNodes(bool forceUpdate = false) {
Debug.Log("GenerateTokensAndNodes");
behaviourTreeGenerator.GenerateTokensAndNodes(this, forceUpdate);
}
public void ClearBinding() {
behaviourTreeGenerator.ClearBinding();
}
public void ScanAndBindMethods(bool forceUpdate = false) {
behaviourTreeGenerator.ScanAndBindMethods(this, forceUpdate);
}
public void SetTaskController() {
behaviourTreeGenerator.SetTaskControllerAndGenerateCache(this);
}
public void SetBehaviourTreeDirty() {
#if UNITY_EDITOR
EditorUtility.SetDirty(behaviourTreeGenerator);
#endif
}
public bool CheckBindingOutdated(bool allowNull = true) {
return behaviourTreeGenerator.CheckBindingOutdated(this, allowNull);
}
public bool CheckGenerationOutdated(bool allowNull = true) {
return behaviourTreeGenerator.CheckGenerationOutdated(allowNull);
}
// status
// hasScript && tokensGenerated && nodesGenerated
// public bool isGenerated {
// get { return behaviourTreeGenerator.isGenerated; }
// }
// public string generationState {
// get { return behaviourTreeGenerator.generationState; }
// }
public bool hasScript {
get { return behaviourTreeGenerator.HasScripts(this); }
}
// public bool tokensGenerated {
// get { return behaviourTreeGenerator.tokensGenerated; }
// }
// public bool nodesGenerated {
// get { return behaviourTreeGenerator.nodesGenerated; }
// }
public FileHash textAssetHashes {
get { return behaviourTreeGenerator.textAssetHashes; }
}
public FileHash generatedCodeBtFileHashes {
get { return behaviourTreeGenerator.generatedCodeBtFileHashes; }
}
public FileHash boundCodeBtFileHashes {
get { return behaviourTreeGenerator.boundCodeBtFileHashes; }
}
public List<TokenList> tokens {
get { return behaviourTreeGenerator.tokens; }
}
public List<NodeList> treeRootNodes {
get { return behaviourTreeGenerator.treeRootNodes; }
}
#if UNITY_EDITOR
public SerializedProperty treeRootNodesAsSerializedProperty {
get {
SerializedObject sObj = new SerializedObject(behaviourTreeGenerator);
return sObj.FindProperty("treeRootNodes");
}
}
#endif
public int nodeCount {
get { return behaviourTreeGenerator.nodeCount; }
}
public bool generationError {
get { return behaviourTreeGenerator.generationError; }
}
public bool bindingError {
get { return behaviourTreeGenerator.bindingError; }
}
// public bool methodsScanned {
// get { return behaviourTreeGenerator.methodsScanned; }
// }
// public bool methodsBound {
// get { return behaviourTreeGenerator.methodsBound; }
// }
public DictionaryOfStringAndDictionaryOfStringAndMethodImplementation methods {
get { return behaviourTreeGenerator.methods; }
}
#endregion
}
}