//============= Copyright (c) Ludic GmbH, All rights reserved. ============== // // Purpose: Part of the My Behaviour Tree Code // //============================================================================= using System.Collections; using System.Collections.Generic; using UnityEngine; namespace MyBT { #if UNITY_EDITOR using UnityEditor; using System; using System.Reflection; using System.Linq; [CustomEditor(typeof(TaskController)), InitializeOnLoadAttribute] public class TaskControllerInspector : Editor { public bool treeTaskViewer = false; public CodeInspector _codeInspector; #region editor gui repaint private int minUpdatesPerSecond = 1; private int maxUpdatesPerSecond = 10; private float myLastUiUpdateTime = 0f; private float nextUpdateDelay = 1.0f; // this is called from TaskController every time a tick happens public void InspectorRedrawCallback(bool forceUpdate = false) { //Debug.Log($"InspectorRedrawCallback {forceUpdate}"); TaskController _taskController = (TaskController)target; // reduce to min time, when chages in ui bool hasUiUpdate = ((TaskController)target).UiChangedThisFrame(); // LastUiChangeTimeEquals(_taskController.lastTickTime); if (hasUiUpdate) { nextUpdateDelay = 1.0f / maxUpdatesPerSecond; } if ((TaskController.currentUiTime > (myLastUiUpdateTime + nextUpdateDelay)) || forceUpdate) { ForceInspectorRedraw(); } } public void InspectorRegenerateCallback(bool forceUpdate = false) { //Debug.Log($"InspectorRegenerateCallback {forceUpdate}"); TaskController _taskController = (TaskController)target; _taskController.RegenerateBehaviourTreeIfRequired(forceUpdate); _codeInspector.RegenerateNodeLineNumberListIfRequired(forceUpdate); ForceInspectorRedraw(); } public void ForceInspectorRedraw () { //Debug.Log($"TaskControllerInspector.ForceInspectorRedraw: ui update delta {(TaskController.currentUiTime - myLastUiUpdateTime)}"); myLastUiUpdateTime = TaskController.currentUiTime; if (this != null) { EditorUtility.SetDirty(this); } if (target != null) { EditorUtility.SetDirty(target); } nextUpdateDelay = 1.0f / minUpdatesPerSecond; Repaint(); } public override bool RequiresConstantRepaint() { return false; } #endregion //TaskControllerInspector() { // //Debug.Log($"TaskControllerInspector.Constructor {this.GetInstanceID()}"); // //EditorApplication.playModeStateChanged += LogPlayModeState; //} //private static void LogPlayModeState(PlayModeStateChange state) { // switch (state) { // // start playing // case PlayModeStateChange.ExitingEditMode: // Debug.Log("ExitingEditMode"); // break; // case PlayModeStateChange.EnteredPlayMode: // Debug.Log("EnteredPlayMode"); // break; // // stopping playing // case PlayModeStateChange.ExitingPlayMode: // Debug.Log("ExitingPlayMode"); // break; // case PlayModeStateChange.EnteredEditMode: // Debug.Log("EnteredEditMode"); // break; // } //} // http://www.voidbred.com/blog/2014/12/auto-updating-custom-inspectors-for-unity/ public void SetInspectorCallbacks () { #if true TaskController _taskController = (TaskController)target; _taskController.InspectorRedraw = (val) => this.InspectorRedrawCallback(val); _taskController.InspectorRegenerate = (val) => this.InspectorRegenerateCallback(val); #endif } public void OnEnable() { TaskController _taskController = (TaskController)target; Debug.Log($"TaskControllerInspector.OnEnable {this.GetInstanceID()} {_taskController.name}"); // call this once to preload it (preventing error when called not in main thread) GUISkin myBtGuiSkin = MyBtResources.myBtGuiSkin; if (_codeInspector == null) { _codeInspector = ScriptableObject.CreateInstance(); _codeInspector.Init((TaskController)target); } // fixed version of generating the code after stopping the playmode //_taskController.ClearBinding(); //_codeInspector.ResetNodeLinenumberList(); EditorApplication.playModeStateChanged += PlayModeStateChanged; SetInspectorCallbacks(); // update if required (forced = false) InspectorRegenerateCallback(false); } public void OnDisable() { //Debug.Log($"TaskControllerInspector.OnDisable {this.GetInstanceID()}"); if (_codeInspector != null) { if (Application.isPlaying) { Destroy(_codeInspector); } else { DestroyImmediate(_codeInspector); } } _codeInspector = null; //if (target != null) { Debug.Log($"OnDisable {((TaskController)target).gameObject.name}"); } EditorApplication.playModeStateChanged -= PlayModeStateChanged; } // this is called when selecting the object //public TaskControllerInspector() { // Debug.Log($"TaskControllerInspector"); // EditorApplication.playModeStateChanged += PlayModeStateChanged; //} //~TaskControllerInspector() { // Debug.Log($"~TaskControllerInspector"); // EditorApplication.playModeStateChanged -= PlayModeStateChanged; //} #region generating and clearing node line dictionary private void PlayModeStateChanged(PlayModeStateChange playModeStateChange) { Debug.Log($"TaskControllerInspector.PlayModeStateChanged {playModeStateChange}"); // lets rely on awake instead for rebinding of nodes //_codeInspector.ResetNodeLinenumberList(); switch (playModeStateChange) { case PlayModeStateChange.EnteredEditMode: case PlayModeStateChange.EnteredPlayMode: InspectorRegenerateCallback(); break; case PlayModeStateChange.ExitingEditMode: case PlayModeStateChange.ExitingPlayMode: break; } } [SerializeField] FileHash btFileHash; public override void OnInspectorGUI() { //Debug.Log("TaskControllerInspector.OnInspectorGUI"); TaskController _taskController = (TaskController)target; // Testing // if (GUILayout.Button("Test")) { // _taskController.destroyNextFrame = true; // } // TODO: check, if this is needed here as well? // connection to TaskController calling the repaint function of this inspector SetInspectorCallbacks(); // end of connection to TaskController bool tick = false; bool regenerateRequest = false; bool restartRequest = false; DrawCustomInspector(_taskController, ref tick, ref regenerateRequest, ref restartRequest); // if manual reset requested if (regenerateRequest) { if (_taskController.runtimeLogging) Debug.Log("TaskControllerInspector.OnInspectorGUI: reset"); // clear _codeInspector.ResetNodeLinenumberList(); _taskController.FullReset(); // generate _taskController.FullGenerate(true); _codeInspector.GenerateNodeLineNumberList(); } if (restartRequest) { if (_taskController.runtimeLogging) Debug.Log("TaskControllerInspector.OnInspectorGUI: restart"); _taskController.Restart(true); } if (tick) { if (_taskController.runtimeLogging) Debug.Log("TaskControllerInspector.OnInspectorGUI: tick"); // because this can be run in edit (non playing) mode, we check if the bindings are setup in depth _taskController.VerifyBindings(); _codeInspector.CheckLineNumberHashOutdated(); _taskController.TickUpdate(); } if ((_codeInspector != null) && !_codeInspector.nodeLineNumberListError) { _codeInspector.DrawCodeInspector(); } } Rect stateIconRect; public void StateButton (string label, bool state, int textWidth=20) { EditorGUILayout.BeginHorizontal(EditorStyles.helpBox); stateIconRect = GUILayoutUtility.GetRect(GUIContent.none, new GUIStyle(), new GUILayoutOption[] {GUILayout.Width(16)}); stateIconRect.height += 2; stateIconRect.y += 1; stateIconRect.x += 1; if (state) { GUI.color = Color.green; GUI.DrawTexture(stateIconRect, MyBtResources.trueIcon, ScaleMode.ScaleToFit); } else { GUI.color = Color.red; GUI.DrawTexture(stateIconRect, MyBtResources.falseIcon, ScaleMode.ScaleToFit); } GUI.color = Color.white; GUI.color = Color.white; EditorGUILayout.LabelField(label, GUILayout.MinWidth(textWidth)); EditorGUILayout.EndHorizontal(); } bool displayUpdateInfo = false; public void DrawCustomInspector (TaskController _taskController, ref bool tick, ref bool regenerateRequest, ref bool restartRequest) { GUI.enabled = false; EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Script"), true, new GUILayoutOption[0]); GUI.enabled = true; bool btSettingsChanged = false; EditorGUILayout.Space(); EditorGUILayout.BeginVertical(EditorStyles.helpBox); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Behaviour Tree Script Files:"); if (GUILayout.Button("+", GUILayout.Width(40))) { _taskController.taskScripts.Add(null); btSettingsChanged |= true; } EditorGUILayout.EndHorizontal(); for (int i=0; i<_taskController.taskScripts.Count; i++) { TextAsset txtAss = _taskController.taskScripts[i]; EditorGUILayout.BeginHorizontal(); TextAsset newTxtAss = EditorGUILayout.ObjectField(txtAss, typeof(UnityEngine.TextAsset), false) as UnityEngine.TextAsset; if (newTxtAss != txtAss) { _taskController.taskScripts[i] = newTxtAss; regenerateRequest |= true; btSettingsChanged |= true; } if (GUILayout.Button("-", GUILayout.Width(40))) { _taskController.taskScripts.RemoveAt(i); btSettingsChanged |= true; break; } EditorGUILayout.EndHorizontal(); } EditorGUILayout.EndVertical(); EditorGUILayout.BeginVertical(EditorStyles.helpBox); EditorGUILayout.LabelField("Configuration:"); EditorGUILayout.BeginHorizontal(); bool newRuntimeLogging = GUILayout.Toggle(_taskController.runtimeLogging, "Runtime Debug"); if (newRuntimeLogging != _taskController.runtimeLogging) { _taskController.runtimeLogging = newRuntimeLogging; btSettingsChanged |= true; } bool newDebugLog = GUILayout.Toggle(_taskController.generatorLogging, "Generation Debug"); if (newDebugLog != _taskController.generatorLogging){ _taskController.generatorLogging = newDebugLog; btSettingsChanged |= true; } EditorGUILayout.EndHorizontal(); //EditorGUILayout.BeginHorizontal(); //bool newResetDataOnCodeChange = GUILayout.Toggle(_taskController.resetDataOnCodeChange, "Reset on Method Change"); //if (newResetDataOnCodeChange != _taskController.resetDataOnCodeChange){ // _taskController.resetDataOnCodeChange = newResetDataOnCodeChange; // btSettingsChanged |= true; //} //bool newAutoGen = GUILayout.Toggle(_taskController.runtimeAutomaticRegenerate, "Code Regeneration (at runtime)"); //if (newAutoGen != _taskController.runtimeAutomaticRegenerate) { // _taskController.runtimeAutomaticRegenerate = newAutoGen; // btSettingsChanged |= true; //} //EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); TickUpdateMode newTickMode = (TickUpdateMode)EditorGUILayout.EnumPopup(new GUIContent("Update Mode", "When is the BT Executed"), _taskController.tickMode); if (newTickMode != _taskController.tickMode) { _taskController.tickMode = newTickMode; btSettingsChanged |= true; } //GUI.color = lineNode.debugInternalActive ? Color.red : Color.white; displayUpdateInfo = EditorGUILayout.Toggle(displayUpdateInfo, MyBtResources.helpButtonGuiStyle, GUILayout.Width(MyBtResources.defaultGuiIconWidth), GUILayout.Height(MyBtResources.defaultGuiHeight)); EditorGUILayout.EndHorizontal(); if (displayUpdateInfo) { EditorGUILayout.BeginVertical(EditorStyles.helpBox); GUILayout.Label( "Update in Update or LateUpdate.\n" + "Standard: Run One Step\n" + "Delayed: Run One Step with Interval in Seconds\n" + "Maximum Time: Run Steps for a Maximum Time\n" + "Fullrun: Run Steps until Root Finishes in One Frame"); EditorGUILayout.EndVertical(); } switch (_taskController.tickMode) { case TickUpdateMode.Update: case TickUpdateMode.LateUpdate: break; case TickUpdateMode.DelayedUpdate: case TickUpdateMode.DelayedLateUpdate: EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Tick Update Interval"); float newTickDelay = EditorGUILayout.FloatField(_taskController.tickDelay); if (newTickDelay != _taskController.tickDelay) { _taskController.tickDelay = newTickDelay; btSettingsChanged |= true; } EditorGUILayout.EndHorizontal(); break; case TickUpdateMode.MaximumTimeLateUpdate: case TickUpdateMode.MaximumTimeUpdate: EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Maximum Runtime"); float newMaximumRuntime = EditorGUILayout.FloatField(_taskController.maximumRuntime); if (newMaximumRuntime != _taskController.maximumRuntime) { _taskController.maximumRuntime = newMaximumRuntime; btSettingsChanged |= true; } EditorGUILayout.EndHorizontal(); break; case TickUpdateMode.Manual: break; } EditorGUILayout.BeginHorizontal(); restartRequest |= GUILayout.Button("Restart", GUILayout.Width(80)); regenerateRequest |= GUILayout.Button("Regenerate", GUILayout.Width(80)); EditorGUILayout.Space(); GUI.enabled = (_taskController.tickMode == TickUpdateMode.Manual) || !Application.isPlaying; tick |= GUILayout.Button("Manual Tick", GUILayout.Width(80)); if (GUILayout.Button("Clear Console", GUILayout.Width(80))) { ClearLogConsole(); } GUI.enabled = true; EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); EditorGUILayout.EndVertical(); if (_taskController.runtimeLogging) { EditorGUILayout.BeginVertical(EditorStyles.helpBox); if (GUILayout.Button("Re-Draw", GUILayout.Width(80))) { _taskController.SetUiUpdate(); } bool newOverrideLogStringDisplay = EditorGUILayout.ToggleLeft("overrideLogStringDisplay (default off)", _taskController.overrideLogStringDisplay, GUILayout.MinWidth(60)); if (newOverrideLogStringDisplay != _taskController.overrideLogStringDisplay) { _taskController.overrideLogStringDisplay = newOverrideLogStringDisplay; btSettingsChanged |= true; } bool newOverrideDebugChangesActive = EditorGUILayout.ToggleLeft("overrideDebugChangesActive (default off)", _taskController.overrideDebugChangesActive, GUILayout.MinWidth(60)); if (newOverrideDebugChangesActive != _taskController.overrideDebugChangesActive) { _taskController.overrideDebugChangesActive = newOverrideDebugChangesActive; btSettingsChanged |= true; } bool newOverrideDebugInternalActive = EditorGUILayout.ToggleLeft("overrideDebugInternalActive (default off)", _taskController.overrideDebugInternalActive, GUILayout.MinWidth(60)); if (newOverrideDebugInternalActive != _taskController.overrideDebugInternalActive) { _taskController.overrideDebugInternalActive = newOverrideDebugInternalActive; btSettingsChanged |= true; } EditorGUILayout.EndVertical(); } if (!_taskController.internalAutomaticRegenerate || _taskController.generatorLogging) { EditorGUILayout.BeginVertical(EditorStyles.helpBox); GUILayout.BeginHorizontal(); GUI.color = Color.white; int numberOfNodes = _taskController.nodeCount; GUILayout.Label($"{numberOfNodes} nodes Generated"); // GUILayout.TextArea(NodeGenerator.GeneratedSize(_taskController.treeRootNodes).ToString(), GUILayout.Width(30)); if (_taskController.generationError || _taskController.bindingError) { GUI.color = Color.red; } //regenerateRequest |= GUILayout.Button("Re-Generate", GUILayout.Width(80)); //btSettingsChanged |= regenerateRequest; GUILayout.EndHorizontal(); bool newInternalAutomaticRegenerate = EditorGUILayout.ToggleLeft("internalAutomaticRegenerate (default on)", _taskController.internalAutomaticRegenerate, GUILayout.MinWidth(60)); if (newInternalAutomaticRegenerate != _taskController.internalAutomaticRegenerate) { _taskController.internalAutomaticRegenerate = newInternalAutomaticRegenerate; btSettingsChanged |= true; } EditorGUILayout.EndVertical(); } if (_taskController.generatorLogging) { EditorGUILayout.BeginVertical(EditorStyles.helpBox); float inspectorWidth = 0; inspectorWidth = EditorGUIUtility.currentViewWidth - 60; float halfWidth = (inspectorWidth - 1) / 2 - 2; float thirdWidth = 20; EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("BtScriptChangeDetected", GUILayout.MinWidth(thirdWidth))) { _taskController.BtScriptChangeDetected(); btSettingsChanged |= true; } if (GUILayout.Button("BtMethodChanged", GUILayout.MinWidth(thirdWidth))) { _taskController.ClearBindingsAndScanAndBindMethods(); btSettingsChanged |= true; } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("ResetNodesAndTokens", GUILayout.MinWidth(thirdWidth))) { _taskController.ResetNodesAndTokens(); btSettingsChanged |= true; } if (GUILayout.Button("GenerateTokensAndNodes", GUILayout.MinWidth(thirdWidth))) { _taskController.GenerateTokensAndNodes(true); btSettingsChanged |= true; } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("ClearBinding", GUILayout.MinWidth(thirdWidth))) { _taskController.ClearBinding(); btSettingsChanged |= true; } if (GUILayout.Button("Bind", GUILayout.MinWidth(thirdWidth))) { _taskController.ScanAndBindMethods(false); btSettingsChanged |= true; } if (GUILayout.Button("Bind Force", GUILayout.MinWidth(thirdWidth))) { _taskController.ScanAndBindMethods(true); btSettingsChanged |= true; } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("ResetNodeLinenumberList", GUILayout.MinWidth(thirdWidth))) { _codeInspector.ResetNodeLinenumberList(); btSettingsChanged |= true; } if (GUILayout.Button("GenerateNodeLineNumberList", GUILayout.MinWidth(thirdWidth))) { _codeInspector.GenerateNodeLineNumberList(); btSettingsChanged |= true; } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Set TaskController", GUILayout.MinWidth(thirdWidth))) { _taskController.SetTaskController(); btSettingsChanged |= true; } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); NodeGenerator.debugActive = EditorGUILayout.ToggleLeft("NodeGenerator debugActive", NodeGenerator.debugActive, GUILayout.MinWidth(60)); MethodScanner.debugActive = EditorGUILayout.ToggleLeft("MethodScanner debugActive", MethodScanner.debugActive, GUILayout.MinWidth(60)); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField($"MethodBinder:", GUILayout.MaxWidth(80)); MethodBinder.debug = EditorGUILayout.ToggleLeft("debug", MethodBinder.debug, GUILayout.MinWidth(40)); MethodBinder.debugIsBound = EditorGUILayout.ToggleLeft("debugIsBound", MethodBinder.debugIsBound, GUILayout.MinWidth(60)); MethodBinder.debugRunTreeBinding = EditorGUILayout.ToggleLeft("debugRunTreeBinding", MethodBinder.debugRunTreeBinding, GUILayout.MinWidth(60)); MethodBinder.debugActionBinding = EditorGUILayout.ToggleLeft("debugActionBinding", MethodBinder.debugActionBinding, GUILayout.MinWidth(60)); EditorGUILayout.EndHorizontal(); int hashMinWidth = 60; EditorGUILayout.BeginHorizontal(); StateButton("Has Script", _taskController.hasScript); StateButton("Has TickEnumerator", (_taskController.tickEnumerator != null)); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField($"Script Count: {_taskController.taskScripts.Count}", GUILayout.MinWidth(hashMinWidth)); GUILayout.FlexibleSpace(); EditorGUILayout.LabelField($"{(_taskController.textAssetHashes!=null?_taskController.textAssetHashes.ToString():"undefined")}", GUILayout.MinWidth(hashMinWidth)); EditorGUILayout.EndHorizontal(); // EditorGUILayout.BeginHorizontal(); // StateButton("Tokens Generated", _taskController.tokensGenerated); // StateButton("Nodes Generated", _taskController.nodesGenerated); // StateButton("isGenerated", _taskController.isGenerated); // EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); StateButton("Generation Succeeded", !_taskController.generationError); StateButton("Generation Updated", !_taskController.CheckGenerationOutdated(false)); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); int count = _taskController.treeRootNodes!=null?_taskController.treeRootNodes.Sum(o => o.Count):0; EditorGUILayout.LabelField($"TreeRootNode Count: {count}", GUILayout.MinWidth(hashMinWidth)); GUILayout.FlexibleSpace(); EditorGUILayout.LabelField($"{(_taskController.generatedCodeBtFileHashes!=null?_taskController.generatedCodeBtFileHashes.ToString():"undefined")}", GUILayout.MinWidth(hashMinWidth)); EditorGUILayout.EndHorizontal(); // EditorGUILayout.BeginHorizontal(); // StateButton("methodsScanned", _taskController.methodsScanned); // StateButton("methodsBound", _taskController.methodsBound); // StateButton("isBound", _taskController.methodsBound); // EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); StateButton("Binding Succeeded", !_taskController.bindingError); StateButton("Binding Updated", !_taskController.CheckBindingOutdated(false)); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField($"Method Count: {(_taskController.methods != null ? _taskController.methods.Count : 0)}", GUILayout.MinWidth(hashMinWidth)); GUILayout.FlexibleSpace(); EditorGUILayout.LabelField($"{_taskController.boundCodeBtFileHashes.ToString()}", GUILayout.MinWidth(hashMinWidth)); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); StateButton("NodeLineNumberList Generated", _codeInspector.nodeLineNumberListGenerated); StateButton("Has TaskController", _codeInspector.hasTaskControllerDefined); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); StateButton("Code Succeeded", !_codeInspector.nodeLineNumberListError); StateButton("LineNumberHash Updated", !_codeInspector.CheckLineNumberHashOutdated(false)); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField($"nodeLineCount: {_codeInspector.nodeLineCount}", GUILayout.MinWidth(hashMinWidth)); GUILayout.FlexibleSpace(); EditorGUILayout.LabelField($"{(_codeInspector.lineNumberListBtFileHashes !=null? _codeInspector.lineNumberListBtFileHashes.ToString():"undefined")}", GUILayout.MinWidth(hashMinWidth)); EditorGUILayout.EndHorizontal(); // SerializedObject sObj = new SerializedObject(_taskController); SerializedProperty sProp = _taskController.treeRootNodesAsSerializedProperty; if (sProp != null) { EditorGUILayout.PropertyField(sProp, true); } else { EditorGUILayout.LabelField($"treeRootNodes missing", GUILayout.MinWidth(hashMinWidth)); } SerializedProperty tProp = _codeInspector.openNodeLinesAsSerializedProperty; if (tProp != null) { EditorGUILayout.PropertyField(tProp, true); } else { EditorGUILayout.LabelField($"openNodeLinesAsSerializedProperty missing", GUILayout.MinWidth(hashMinWidth)); } EditorGUILayout.EndVertical(); } if (btSettingsChanged) { _codeInspector.SetDirty(); } } #endregion static MethodInfo _clearConsoleMethod; static MethodInfo clearConsoleMethod { get { if (_clearConsoleMethod == null) { Assembly assembly = Assembly.GetAssembly(typeof(SceneView)); Type logEntries = assembly.GetType("UnityEditor.LogEntries"); _clearConsoleMethod = logEntries.GetMethod("Clear"); } return _clearConsoleMethod; } } public static void ClearLogConsole() { clearConsoleMethod.Invoke(new object(), null); } public static void GameViewRepaint () { System.Reflection.Assembly assembly = typeof(UnityEditor.EditorWindow).Assembly; System.Type type = assembly.GetType("UnityEditor.GameView"); EditorWindow gameview = EditorWindow.GetWindow(type); gameview.Repaint(); } } #endif }