UNPKG

@esengine/ai

Version:

用于Laya、Cocos Creator等JavaScript游戏引擎的高性能AI系统库:行为树、实用AI和有限状态机

1 lines 426 kB
{"version":3,"file":"index.mjs","sources":["../bin/core/Random.js","../bin/core/ArrayExt.js","../bin/core/Assert.js","../bin/core/Logger.js","../bin/core/ErrorHandler.js","../bin/behaviourTree/TaskStatus.js","../bin/behaviourTree/Blackboard.js","../bin/behaviourTree/composites/AbortTypes.js","../bin/behaviourTree/conditionals/BlackboardConditionals.js","../bin/behaviourTree/actions/BlackboardActions.js","../bin/behaviourTree/AdvancedObjectPool.js","../bin/core/TimeManager.js","../bin/core/EventManager.js","../bin/core/TypeGuards.js","../bin/behaviourTree/Behavior.js","../bin/behaviourTree/BehaviorTree.js","../bin/behaviourTree/conditionals/IConditional.js","../bin/behaviourTree/composites/Composite.js","../bin/behaviourTree/decorators/Decorator.js","../bin/behaviourTree/actions/ExecuteAction.js","../bin/behaviourTree/conditionals/ExecuteActionConditional.js","../bin/behaviourTree/factories/ConditionFactory.js","../bin/behaviourTree/actions/LogAction.js","../bin/behaviourTree/actions/WaitAction.js","../bin/behaviourTree/actions/BehaviorTreeReference.js","../bin/behaviourTree/decorators/ConditionalDecorator.js","../bin/behaviourTree/decorators/AlwaysFail.js","../bin/behaviourTree/decorators/AlwaysSucceed.js","../bin/behaviourTree/decorators/Inverter.js","../bin/behaviourTree/decorators/Repeater.js","../bin/behaviourTree/decorators/UntilFail.js","../bin/behaviourTree/decorators/UntilSuccess.js","../bin/behaviourTree/composites/Parallel.js","../bin/behaviourTree/composites/ParallelSelector.js","../bin/behaviourTree/composites/Selector.js","../bin/behaviourTree/composites/RandomSelector.js","../bin/behaviourTree/composites/Sequence.js","../bin/behaviourTree/composites/RandomSequence.js","../bin/behaviourTree/conditionals/GeneralConditionals.js","../bin/behaviourTree/decorators/CooldownDecorator.js","../bin/behaviourTree/decorators/TimeoutDecorator.js","../bin/behaviourTree/decorators/ChanceDecorator.js","../bin/behaviourTree/BehaviorTreeBuilder.js","../bin/behaviourTree/ObjectPool.js","../bin/behaviourTree/events/EventRegistry.js","../bin/behaviourTree/conditionals/RandomProbability.js","../bin/fsm/State.js","../bin/fsm/StateMachine.js","../bin/fsm/SimpleStateMachine.js","../bin/utilityAI/UtilityAI.js","../bin/utilityAI/considerations/FixedScoreConsideration.js","../bin/utilityAI/reasoners/Reasoner.js","../bin/utilityAI/reasoners/FirstScoreReasoner.js","../bin/utilityAI/reasoners/HighestScoreReasoner.js"],"sourcesContent":["/**\n * 高性能伪随机数生成器\n * 使用xorshift128算法,比原生Math.random()更快且质量更好\n *\n * @example\n * ```typescript\n * // 设置种子(可选,默认使用当前时间)\n * Random.setSeed(12345);\n *\n * // 生成0-1之间的随机数\n * const value = Random.value();\n *\n * // 生成指定范围的随机数\n * const rangeValue = Random.range(10, 20);\n *\n * // 生成随机整数\n * const intValue = Random.integer(1, 100);\n *\n * // 随机布尔值\n * const bool = Random.boolean();\n *\n * // 带概率的布尔值\n * const probBool = Random.chance(0.7); // 70%概率返回true\n * ```\n */\nexport class Random {\n /**\n * 设置随机数种子\n * @param seed 种子值,如果不提供则使用当前时间\n */\n static setSeed(seed) {\n if (seed === undefined) {\n seed = Date.now();\n }\n // 使用种子初始化四个状态变量\n this._x = seed >>> 0;\n this._y = (seed * 1812433253 + 1) >>> 0;\n this._z = (this._y * 1812433253 + 1) >>> 0;\n this._w = (this._z * 1812433253 + 1) >>> 0;\n // 确保所有状态变量都非零\n if (this._x === 0)\n this._x = 1;\n if (this._y === 0)\n this._y = 1;\n if (this._z === 0)\n this._z = 1;\n if (this._w === 0)\n this._w = 1;\n this._initialized = true;\n // 预热生成器\n for (let i = 0; i < 10; i++) {\n this.next();\n }\n }\n /**\n * 生成下一个32位无符号整数(内部使用)\n * 使用xorshift128算法\n */\n static next() {\n if (!this._initialized) {\n this.setSeed();\n }\n const t = this._x ^ (this._x << 11);\n this._x = this._y;\n this._y = this._z;\n this._z = this._w;\n this._w = (this._w ^ (this._w >>> 19)) ^ (t ^ (t >>> 8));\n return this._w >>> 0; // 确保返回无符号32位整数\n }\n /**\n * 生成0到1之间的随机浮点数(不包括1)\n * @returns 0 <= value < 1的随机数\n */\n static value() {\n return this.next() / 0x100000000; // 2^32\n }\n /**\n * 生成指定范围内的随机浮点数\n * @param min 最小值(包含)\n * @param max 最大值(不包含)\n * @returns min <= value < max的随机数\n */\n static range(min = 0, max = 1) {\n if (min >= max) {\n throw new Error(`最小值(${min})必须小于最大值(${max})`);\n }\n return min + (max - min) * this.value();\n }\n /**\n * 生成指定范围内的随机整数\n * @param min 最小值(包含)\n * @param max 最大值(包含)\n * @returns min <= value <= max的随机整数\n */\n static integer(min, max) {\n if (!Number.isInteger(min) || !Number.isInteger(max)) {\n throw new Error('最小值和最大值必须是整数');\n }\n if (min > max) {\n throw new Error(`最小值(${min})必须小于等于最大值(${max})`);\n }\n return Math.floor(this.range(min, max + 1));\n }\n /**\n * 生成随机布尔值\n * @returns 随机的true或false\n */\n static boolean() {\n return this.value() < 0.5;\n }\n /**\n * 根据概率生成布尔值\n * @param probability 返回true的概率(0-1之间)\n * @returns 根据概率返回的布尔值\n */\n static chance(probability) {\n if (probability < 0 || probability > 1) {\n throw new Error(`概率值必须在0-1之间,当前值: ${probability}`);\n }\n return this.value() < probability;\n }\n /**\n * 从数组中随机选择一个元素\n * @param array 要选择的数组\n * @returns 随机选中的元素\n */\n static choice(array) {\n if (array.length === 0) {\n throw new Error('数组不能为空');\n }\n const index = this.integer(0, array.length - 1);\n return array[index]; // 使用非空断言,因为我们已经检查了数组长度\n }\n /**\n * 从数组中随机选择多个不重复的元素\n * @param array 要选择的数组\n * @param count 选择的数量\n * @returns 随机选中的元素数组\n */\n static sample(array, count) {\n if (count < 0 || count > array.length) {\n throw new Error(`选择数量(${count})必须在0-${array.length}之间`);\n }\n if (count === 0) {\n return [];\n }\n if (count === array.length) {\n return [...array];\n }\n // 对于小的选择数量,使用Set避免重复\n if (count <= array.length / 2) {\n const result = [];\n const indices = new Set();\n while (result.length < count) {\n const index = this.integer(0, array.length - 1);\n if (!indices.has(index)) {\n indices.add(index);\n result.push(array[index]);\n }\n }\n return result;\n }\n else {\n // 对于大的选择数量,使用Fisher-Yates洗牌算法的部分版本\n const shuffled = [...array];\n for (let i = 0; i < count; i++) {\n const j = this.integer(i, shuffled.length - 1);\n [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];\n }\n return shuffled.slice(0, count);\n }\n }\n /**\n * 生成符合正态分布的随机数(Box-Muller变换)\n * @param mean 均值\n * @param standardDeviation 标准差\n * @returns 符合正态分布的随机数\n */\n static gaussian(mean = 0, standardDeviation = 1) {\n // 使用Box-Muller变换生成正态分布随机数\n const u1 = this.value();\n const u2 = this.value();\n const z0 = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n return z0 * standardDeviation + mean;\n }\n /**\n * 获取当前随机数生成器的状态(用于保存/恢复)\n * @returns 生成器状态对象\n */\n static getState() {\n if (!this._initialized) {\n this.setSeed();\n }\n return {\n x: this._x,\n y: this._y,\n z: this._z,\n w: this._w\n };\n }\n /**\n * 恢复随机数生成器的状态\n * @param state 要恢复的状态对象\n */\n static setState(state) {\n this._x = state.x;\n this._y = state.y;\n this._z = state.z;\n this._w = state.w;\n this._initialized = true;\n }\n}\nRandom._x = 123456789;\nRandom._y = 362436069;\nRandom._z = 521288629;\nRandom._w = 88675123;\nRandom._initialized = false;\n//# sourceMappingURL=Random.js.map","import { Random } from './Random';\n/**\n * 数组扩展器和高效数据结构工具\n * 提供栈、队列等数据结构的高效实现\n */\nexport class ArrayExt {\n /**\n * 将数组打乱顺序(Fisher-Yates洗牌算法)\n * 时间复杂度: O(n),空间复杂度: O(1)\n *\n * @param list 要打乱的数组\n * @throws {Error} 当数组为null或undefined时抛出错误\n */\n static shuffle(list) {\n if (!list) {\n throw new Error('数组不能为null或undefined');\n }\n // 优化:从后往前遍历,减少一次减法运算\n for (let i = list.length - 1; i > 0; i--) {\n const j = Random.integer(0, i);\n // 使用解构赋值进行交换,更简洁\n [list[i], list[j]] = [list[j], list[i]];\n }\n }\n /**\n * 取出数组第一个项(不移除)\n * @param list 目标数组\n * @returns 第一个元素\n * @throws {Error} 当数组为空时抛出错误\n */\n static peek(list) {\n if (list.length === 0) {\n throw new Error('无法从空数组中获取元素');\n }\n return list[0];\n }\n /**\n * 向数组头部添加一个项\n * @param list 目标数组\n * @param item 要添加的项\n */\n static unshift(list, item) {\n list.unshift(item);\n }\n /**\n * 移除数组第一个项并返回它\n * @param list 目标数组\n * @returns 移除的元素,如果数组为空则返回undefined\n */\n static pop(list) {\n return list.shift();\n }\n /**\n * 向数组尾部添加一个项\n * @param list 目标数组\n * @param item 要添加的项\n */\n static append(list, item) {\n list.push(item);\n }\n /**\n * 移除数组最后一个项并返回它\n * @param list 目标数组\n * @returns 移除的元素,如果数组为空则返回undefined\n */\n static removeLast(list) {\n return list.pop();\n }\n /**\n * 检查数组是否为空\n * @param list 目标数组\n * @returns 是否为空\n */\n static isEmpty(list) {\n return list.length === 0;\n }\n /**\n * 获取数组大小\n * @param list 目标数组\n * @returns 数组长度\n */\n static size(list) {\n return list.length;\n }\n /**\n * 清空数组\n * @param list 目标数组\n */\n static clear(list) {\n list.length = 0;\n }\n}\n/**\n * 高效的双端队列实现\n * 使用环形缓冲区,避免数组头部插入的性能问题\n *\n * @template T 队列中元素的类型\n *\n * @example\n * ```typescript\n * const deque = new Deque<number>(32);\n * deque.push(1);\n * deque.unshift(0);\n * console.log(deque.peekFirst()); // 0\n * console.log(deque.peekLast()); // 1\n * ```\n */\nexport class Deque {\n /**\n * 创建双端队列\n * @param initialCapacity 初始容量,必须大于0,默认16\n */\n constructor(initialCapacity = 16) {\n this._head = 0;\n this._tail = 0;\n this._size = 0;\n if (initialCapacity <= 0) {\n throw new Error('初始容量必须大于0');\n }\n this._capacity = Math.max(initialCapacity, 4);\n this._buffer = new Array(this._capacity);\n }\n /**\n * 向队列头部添加元素\n * @param item 要添加的元素\n */\n unshift(item) {\n if (this._size === this._capacity) {\n this._resize();\n }\n this._head = (this._head - 1 + this._capacity) % this._capacity;\n this._buffer[this._head] = item;\n this._size++;\n }\n /**\n * 向队列尾部添加元素\n * @param item 要添加的元素\n */\n push(item) {\n if (this._size === this._capacity) {\n this._resize();\n }\n this._buffer[this._tail] = item;\n this._tail = (this._tail + 1) % this._capacity;\n this._size++;\n }\n /**\n * 从队列头部移除元素\n * @returns 移除的元素,如果队列为空则返回undefined\n */\n shift() {\n if (this._size === 0) {\n return undefined;\n }\n const item = this._buffer[this._head];\n this._buffer[this._head] = undefined;\n this._head = (this._head + 1) % this._capacity;\n this._size--;\n return item;\n }\n /**\n * 从队列尾部移除元素\n * @returns 移除的元素,如果队列为空则返回undefined\n */\n pop() {\n if (this._size === 0) {\n return undefined;\n }\n this._tail = (this._tail - 1 + this._capacity) % this._capacity;\n const item = this._buffer[this._tail];\n this._buffer[this._tail] = undefined;\n this._size--;\n return item;\n }\n /**\n * 查看队列头部元素(不移除)\n * @returns 头部元素,如果队列为空则返回undefined\n */\n peekFirst() {\n return this._size > 0 ? this._buffer[this._head] : undefined;\n }\n /**\n * 查看队列尾部元素(不移除)\n * @returns 尾部元素,如果队列为空则返回undefined\n */\n peekLast() {\n if (this._size === 0) {\n return undefined;\n }\n const lastIndex = (this._tail - 1 + this._capacity) % this._capacity;\n return this._buffer[lastIndex];\n }\n /**\n * 获取队列大小\n */\n get size() {\n return this._size;\n }\n /**\n * 检查队列是否为空\n */\n get isEmpty() {\n return this._size === 0;\n }\n /**\n * 清空队列\n */\n clear() {\n for (let i = 0; i < this._capacity; i++) {\n this._buffer[i] = undefined;\n }\n this._head = 0;\n this._tail = 0;\n this._size = 0;\n }\n /**\n * 扩容队列(内部使用)\n * 当队列满时自动调用,容量翻倍\n */\n _resize() {\n const newCapacity = this._capacity * 2;\n const newBuffer = new Array(newCapacity);\n // 复制现有元素到新缓冲区\n for (let i = 0; i < this._size; i++) {\n newBuffer[i] = this._buffer[(this._head + i) % this._capacity];\n }\n this._buffer = newBuffer;\n this._head = 0;\n this._tail = this._size;\n this._capacity = newCapacity;\n }\n /**\n * 将队列转换为数组\n * @returns 包含队列所有元素的数组(从头到尾的顺序)\n */\n toArray() {\n const result = [];\n for (let i = 0; i < this._size; i++) {\n const item = this._buffer[(this._head + i) % this._capacity];\n if (item !== undefined) {\n result.push(item);\n }\n }\n return result;\n }\n}\n//# sourceMappingURL=ArrayExt.js.map","/**\n * 高性能断言工具类\n *\n * @description\n * 提供类型安全的断言方法,支持开发和生产环境的不同行为。\n * 在生产环境中可以禁用断言以提高性能。\n *\n * @example\n * ```typescript\n * // 基本断言\n * Assert.isTrue(player.health > 0, '玩家血量必须大于0');\n * Assert.isNotNull(gameObject, '游戏对象不能为空');\n *\n * // 类型安全的断言\n * const value: unknown = getData();\n * Assert.isNumber(value, '数据必须是数字');\n * // 现在 value 的类型被缩窄为 number\n *\n * // 配置断言行为\n * Assert.setEnabled(false); // 在生产环境中禁用\n * ```\n */\nexport class Assert {\n /**\n * 设置是否启用断言\n * @param enabled 是否启用\n */\n static setEnabled(enabled) {\n this._enabled = enabled;\n }\n /**\n * 设置断言失败时的行为\n * @param throwOnFailure 是否抛出异常,false则仅记录到控制台\n */\n static setThrowOnFailure(throwOnFailure) {\n this._throwOnFailure = throwOnFailure;\n }\n /**\n * 断言失败处理\n * @param message 错误消息\n * @param args 附加参数\n */\n static fail(message, ...args) {\n const errorMessage = message || '断言失败';\n if (this._throwOnFailure) {\n throw new Error(errorMessage);\n }\n else {\n console.assert(false, errorMessage, ...args);\n throw new Error(errorMessage); // 总是抛出错误,因为这是fail方法\n }\n }\n /**\n * 断言条件为真\n * @param condition 要检查的条件\n * @param message 失败时的错误消息\n * @param args 附加参数\n */\n static isTrue(condition, message, ...args) {\n if (!this._enabled)\n return;\n if (!condition) {\n this.fail(message || '条件必须为真', ...args);\n }\n }\n /**\n * 断言条件为假\n * @param condition 要检查的条件\n * @param message 失败时的错误消息\n * @param args 附加参数\n */\n static isFalse(condition, message, ...args) {\n if (!this._enabled)\n return;\n if (condition) {\n this.fail(message || '条件必须为假', ...args);\n }\n }\n /**\n * 断言对象不为null或undefined\n * @param obj 要检查的对象\n * @param message 失败时的错误消息\n * @param args 附加参数\n */\n static isNotNull(obj, message, ...args) {\n if (!this._enabled)\n return;\n if (obj == null) {\n this.fail(message || '对象不能为null或undefined', ...args);\n }\n }\n /**\n * 断言对象为null或undefined\n * @param obj 要检查的对象\n * @param message 失败时的错误消息\n * @param args 附加参数\n */\n static isNull(obj, message, ...args) {\n if (!this._enabled)\n return;\n if (obj != null) {\n this.fail(message || '对象必须为null或undefined', ...args);\n }\n }\n /**\n * 断言值为数字类型\n * @param value 要检查的值\n * @param message 失败时的错误消息\n * @param args 附加参数\n */\n static isNumber(value, message, ...args) {\n if (!this._enabled)\n return;\n if (typeof value !== 'number' || isNaN(value)) {\n this.fail(message || '值必须是有效数字', ...args);\n }\n }\n /**\n * 断言值为字符串类型\n * @param value 要检查的值\n * @param message 失败时的错误消息\n * @param args 附加参数\n */\n static isString(value, message, ...args) {\n if (!this._enabled)\n return;\n if (typeof value !== 'string') {\n this.fail(message || '值必须是字符串', ...args);\n }\n }\n /**\n * 断言值为布尔类型\n * @param value 要检查的值\n * @param message 失败时的错误消息\n * @param args 附加参数\n */\n static isBoolean(value, message, ...args) {\n if (!this._enabled)\n return;\n if (typeof value !== 'boolean') {\n this.fail(message || '值必须是布尔值', ...args);\n }\n }\n /**\n * 断言值为函数类型\n * @param value 要检查的值\n * @param message 失败时的错误消息\n * @param args 附加参数\n */\n static isFunction(value, message, ...args) {\n if (!this._enabled)\n return;\n if (typeof value !== 'function') {\n this.fail(message || '值必须是函数', ...args);\n }\n }\n /**\n * 断言值为对象类型(非null)\n * @param value 要检查的值\n * @param message 失败时的错误消息\n * @param args 附加参数\n */\n static isObject(value, message, ...args) {\n if (!this._enabled)\n return;\n if (typeof value !== 'object' || value === null) {\n this.fail(message || '值必须是对象', ...args);\n }\n }\n /**\n * 断言数组不为空\n * @param array 要检查的数组\n * @param message 失败时的错误消息\n * @param args 附加参数\n */\n static isNotEmpty(array, message, ...args) {\n if (!this._enabled)\n return;\n this.isNotNull(array, message, ...args);\n if (array.length === 0) {\n this.fail(message || '数组不能为空', ...args);\n }\n }\n /**\n * 断言字符串不为空\n * @param str 要检查的字符串\n * @param message 失败时的错误消息\n * @param args 附加参数\n */\n static isNotEmptyString(str, message, ...args) {\n if (!this._enabled)\n return;\n this.isNotNull(str, message, ...args);\n if (str.trim().length === 0) {\n this.fail(message || '字符串不能为空', ...args);\n }\n }\n /**\n * 断言数值在指定范围内\n * @param value 要检查的数值\n * @param min 最小值(包含)\n * @param max 最大值(包含)\n * @param message 失败时的错误消息\n * @param args 附加参数\n */\n static inRange(value, min, max, message, ...args) {\n if (!this._enabled)\n return;\n this.isNumber(value, message, ...args);\n if (value < min || value > max) {\n this.fail(message || `值必须在 ${min} 到 ${max} 之间`, ...args);\n }\n }\n /**\n * 断言值是指定类型的实例\n * @param value 要检查的值\n * @param constructor 构造函数\n * @param message 失败时的错误消息\n * @param args 附加参数\n */\n static isInstanceOf(value, constructor, message, ...args) {\n if (!this._enabled)\n return;\n if (!(value instanceof constructor)) {\n this.fail(message || `值必须是 ${constructor.name} 的实例`, ...args);\n }\n }\n /**\n * 断言数组包含指定元素\n * @param array 要检查的数组\n * @param element 要查找的元素\n * @param message 失败时的错误消息\n * @param args 附加参数\n */\n static contains(array, element, message, ...args) {\n if (!this._enabled)\n return;\n this.isNotNull(array, message, ...args);\n if (!array.includes(element)) {\n this.fail(message || '数组必须包含指定元素', ...args);\n }\n }\n /**\n * 获取当前断言配置\n * @returns 配置对象\n */\n static getConfig() {\n return {\n enabled: this._enabled,\n throwOnFailure: this._throwOnFailure\n };\n }\n}\n/** 是否启用断言检查 */\nAssert._enabled = true;\n/** 是否在断言失败时抛出异常而不是仅记录 */\nAssert._throwOnFailure = false;\n//# sourceMappingURL=Assert.js.map","/**\n * 日志级别枚举\n */\nexport var LogLevel;\n(function (LogLevel) {\n /** 调试信息 */\n LogLevel[LogLevel[\"Debug\"] = 0] = \"Debug\";\n /** 一般信息 */\n LogLevel[LogLevel[\"Info\"] = 1] = \"Info\";\n /** 警告信息 */\n LogLevel[LogLevel[\"Warn\"] = 2] = \"Warn\";\n /** 错误信息 */\n LogLevel[LogLevel[\"Error\"] = 3] = \"Error\";\n /** 关闭日志 */\n LogLevel[LogLevel[\"None\"] = 4] = \"None\";\n})(LogLevel || (LogLevel = {}));\n/**\n * 高性能日志系统\n *\n * @description\n * 提供分级日志记录功能,支持性能优化模式。\n * 在性能模式下,会跳过不必要的字符串格式化和时间戳计算。\n * 支持批量输出和延迟日志记录。\n *\n * @example\n * ```typescript\n * // 基本使用\n * Logger.info('游戏开始');\n * Logger.warn('玩家血量低', { health: 10 });\n * Logger.error('网络连接失败', error);\n *\n * // 配置日志系统\n * Logger.configure({\n * minLevel: LogLevel.Warn,\n * enableTimestamp: true,\n * performanceMode: false,\n * batchMode: true,\n * batchSize: 50\n * });\n *\n * // 性能敏感的代码中\n * Logger.setPerformanceMode(true);\n * ```\n */\nexport class Logger {\n /**\n * 配置日志系统\n * @param config 日志配置\n */\n static configure(config) {\n this._config = { ...this._config, ...config };\n // 配置批量模式\n if (config.batchMode !== undefined) {\n this._batchConfig.enabled = config.batchMode;\n }\n if (config.batchSize !== undefined) {\n this._batchConfig.maxSize = Math.max(1, config.batchSize);\n }\n if (config.batchFlushInterval !== undefined) {\n this._batchConfig.flushInterval = Math.max(100, config.batchFlushInterval);\n }\n // 初始化性能模式\n this._initializePerformanceMode();\n }\n /**\n * 初始化性能模式\n */\n static _initializePerformanceMode() {\n if (this._config.performanceMode) {\n // 在性能模式下,使用最简化的日志函数\n this._fastLog = (message, data) => {\n if (data !== undefined) {\n console.log(message, data);\n }\n else {\n console.log(message);\n }\n };\n }\n else {\n this._fastLog = null;\n }\n }\n /**\n * 设置最小日志级别\n * @param level 最小日志级别\n */\n static setMinLevel(level) {\n this._config.minLevel = level;\n }\n /**\n * 设置性能模式\n * @param enabled 是否启用性能模式\n */\n static setPerformanceMode(enabled) {\n this._config.performanceMode = enabled;\n this._initializePerformanceMode();\n }\n /**\n * 启用批量模式\n * @param enabled 是否启用\n * @param maxSize 批量大小\n * @param flushInterval 刷新间隔(毫秒)\n */\n static setBatchMode(enabled, maxSize = 50, flushInterval = 1000) {\n this._batchConfig.enabled = enabled;\n this._batchConfig.maxSize = Math.max(1, maxSize);\n this._batchConfig.flushInterval = Math.max(100, flushInterval);\n if (!enabled) {\n this.flushLogs(); // 禁用时立即刷新所有日志\n }\n }\n /**\n * 记录调试信息\n * @param message 消息\n * @param data 附加数据\n */\n static debug(message, data) {\n this._log(LogLevel.Debug, message, data);\n }\n /**\n * 记录一般信息\n * @param message 消息\n * @param data 附加数据\n */\n static info(message, data) {\n this._log(LogLevel.Info, message, data);\n }\n /**\n * 记录警告信息\n * @param message 消息\n * @param data 附加数据\n */\n static warn(message, data) {\n this._log(LogLevel.Warn, message, data);\n }\n /**\n * 记录错误信息\n * @param message 消息\n * @param error 错误对象或附加数据\n */\n static error(message, error) {\n this._log(LogLevel.Error, message, error);\n }\n /**\n * 内部日志记录方法\n * @param level 日志级别\n * @param message 消息\n * @param data 附加数据\n */\n static _log(level, message, data) {\n // 检查日志级别\n if (level < this._config.minLevel || this._config.minLevel === LogLevel.None) {\n return;\n }\n if (this._config.performanceMode && this._fastLog) {\n // 超高性能模式:跳过所有格式化\n this._fastLog(message, data);\n return;\n }\n if (this._batchConfig.enabled) {\n // 批量模式:添加到缓冲区\n this._addToBatch(level, message, data);\n }\n else if (this._config.performanceMode) {\n // 性能模式:简化输出\n this._performanceLog(level, message, data);\n }\n else {\n // 标准模式:完整格式化\n this._standardLog(level, message, data);\n }\n }\n /**\n * 添加日志到批量缓冲区\n */\n static _addToBatch(level, message, data) {\n const entry = {\n level,\n message,\n data,\n timestamp: Date.now(),\n prefix: this._config.prefix\n };\n this._logBuffer.push(entry);\n // 检查是否需要刷新\n const now = Date.now();\n const shouldFlushBySize = this._logBuffer.length >= this._batchConfig.maxSize;\n const shouldFlushByTime = (now - this._batchConfig.lastFlushTime) >= this._batchConfig.flushInterval;\n if (shouldFlushBySize || shouldFlushByTime) {\n this.flushLogs();\n }\n }\n /**\n * 刷新批量日志\n */\n static flushLogs() {\n if (this._logBuffer.length === 0) {\n return;\n }\n // 批量输出所有日志\n for (const entry of this._logBuffer) {\n if (this._config.performanceMode) {\n this._performanceLogEntry(entry);\n }\n else {\n this._standardLogEntry(entry);\n }\n }\n // 清空缓冲区\n this._logBuffer.length = 0;\n this._batchConfig.lastFlushTime = Date.now();\n }\n /**\n * 性能模式输出日志条目\n */\n static _performanceLogEntry(entry) {\n const levelName = this._levelNames[entry.level];\n const prefix = entry.prefix ? `[${entry.prefix}] ` : '';\n if (entry.data !== undefined) {\n console.log(`${prefix}[${levelName}] ${entry.message}`, entry.data);\n }\n else {\n console.log(`${prefix}[${levelName}] ${entry.message}`);\n }\n }\n /**\n * 标准模式输出日志条目\n */\n static _standardLogEntry(entry) {\n const timestamp = this._config.enableTimestamp ? this._formatTimestamp(entry.timestamp) : '';\n const levelName = this._levelNames[entry.level];\n const prefix = entry.prefix ? `[${entry.prefix}] ` : '';\n const style = this._levelStyles[entry.level];\n let logMessage = `${prefix}${timestamp}[${levelName}] ${entry.message}`;\n const consoleMethod = this._getConsoleMethod(entry.level);\n if (entry.data !== undefined) {\n if (style && typeof console.log === 'function') {\n consoleMethod(`%c${logMessage}`, style, entry.data);\n }\n else {\n consoleMethod(logMessage, entry.data);\n }\n }\n else {\n if (style && typeof console.log === 'function') {\n consoleMethod(`%c${logMessage}`, style);\n }\n else {\n consoleMethod(logMessage);\n }\n }\n // 错误级别且启用堆栈跟踪\n if (entry.level === LogLevel.Error && this._config.enableStackTrace && entry.data instanceof Error) {\n console.trace(entry.data);\n }\n }\n /**\n * 格式化时间戳\n */\n static _formatTimestamp(timestamp) {\n const date = new Date(timestamp);\n const hours = date.getHours().toString().padStart(2, '0');\n const minutes = date.getMinutes().toString().padStart(2, '0');\n const seconds = date.getSeconds().toString().padStart(2, '0');\n const milliseconds = date.getMilliseconds().toString().padStart(3, '0');\n return `[${hours}:${minutes}:${seconds}.${milliseconds}] `;\n }\n /**\n * 性能模式日志输出\n * @param level 日志级别\n * @param message 消息\n * @param data 附加数据\n */\n static _performanceLog(level, message, data) {\n const levelName = this._levelNames[level];\n const prefix = this._config.prefix ? `[${this._config.prefix}] ` : '';\n if (data !== undefined) {\n console.log(`${prefix}[${levelName}] ${message}`, data);\n }\n else {\n console.log(`${prefix}[${levelName}] ${message}`);\n }\n }\n /**\n * 标准模式日志输出\n * @param level 日志级别\n * @param message 消息\n * @param data 附加数据\n */\n static _standardLog(level, message, data) {\n const timestamp = this._config.enableTimestamp ? this._getTimestamp() : '';\n const levelName = this._levelNames[level];\n const prefix = this._config.prefix ? `[${this._config.prefix}] ` : '';\n const style = this._levelStyles[level];\n let logMessage = `${prefix}${timestamp}[${levelName}] ${message}`;\n // 根据日志级别选择合适的console方法\n const consoleMethod = this._getConsoleMethod(level);\n if (data !== undefined) {\n if (style && typeof console.log === 'function') {\n consoleMethod(`%c${logMessage}`, style, data);\n }\n else {\n consoleMethod(logMessage, data);\n }\n }\n else {\n if (style && typeof console.log === 'function') {\n consoleMethod(`%c${logMessage}`, style);\n }\n else {\n consoleMethod(logMessage);\n }\n }\n // 错误级别且启用堆栈跟踪\n if (level === LogLevel.Error && this._config.enableStackTrace && data instanceof Error) {\n console.trace(data);\n }\n }\n /**\n * 获取时间戳字符串\n * @returns 格式化的时间戳\n */\n static _getTimestamp() {\n const now = new Date();\n const hours = now.getHours().toString().padStart(2, '0');\n const minutes = now.getMinutes().toString().padStart(2, '0');\n const seconds = now.getSeconds().toString().padStart(2, '0');\n const milliseconds = now.getMilliseconds().toString().padStart(3, '0');\n return `[${hours}:${minutes}:${seconds}.${milliseconds}] `;\n }\n /**\n * 根据日志级别获取对应的console方法\n * @param level 日志级别\n * @returns console方法\n */\n static _getConsoleMethod(level) {\n switch (level) {\n case LogLevel.Debug:\n return console.debug || console.log;\n case LogLevel.Info:\n return console.info || console.log;\n case LogLevel.Warn:\n return console.warn || console.log;\n case LogLevel.Error:\n return console.error || console.log;\n default:\n return console.log;\n }\n }\n /**\n * 获取当前配置\n * @returns 当前日志配置的副本\n */\n static getConfig() {\n return { ...this._config };\n }\n /**\n * 创建带前缀的日志器\n * @param prefix 前缀\n * @returns 新的日志器实例\n */\n static createPrefixed(prefix) {\n return new PrefixedLogger(prefix);\n }\n}\nLogger._config = {\n minLevel: LogLevel.Debug,\n enableTimestamp: true,\n enableStackTrace: true,\n performanceMode: false,\n prefix: ''\n};\n/** 批量日志缓冲区 */\nLogger._logBuffer = [];\n/** 批量模式配置 */\nLogger._batchConfig = {\n enabled: false,\n maxSize: 50,\n flushInterval: 1000, // 1秒\n lastFlushTime: 0\n};\n/** 性能模式下的简化日志函数 */\nLogger._fastLog = null;\n/** 日志级别名称映射 */\nLogger._levelNames = {\n [LogLevel.Debug]: 'DEBUG',\n [LogLevel.Info]: 'INFO',\n [LogLevel.Warn]: 'WARN',\n [LogLevel.Error]: 'ERROR',\n [LogLevel.None]: 'NONE'\n};\n/** 日志级别样式映射(用于浏览器控制台) */\nLogger._levelStyles = {\n [LogLevel.Debug]: 'color: #888',\n [LogLevel.Info]: 'color: #007acc',\n [LogLevel.Warn]: 'color: #ff8c00',\n [LogLevel.Error]: 'color: #ff4444; font-weight: bold',\n [LogLevel.None]: ''\n};\n/**\n * 带前缀的日志器\n * 用于为特定模块或组件创建专用的日志器\n */\nexport class PrefixedLogger {\n constructor(_prefix) {\n this._prefix = _prefix;\n }\n debug(message, data) {\n Logger.debug(`[${this._prefix}] ${message}`, data);\n }\n info(message, data) {\n Logger.info(`[${this._prefix}] ${message}`, data);\n }\n warn(message, data) {\n Logger.warn(`[${this._prefix}] ${message}`, data);\n }\n error(message, error) {\n Logger.error(`[${this._prefix}] ${message}`, error);\n }\n}\n//# sourceMappingURL=Logger.js.map","/**\n* 错误处理级别枚举\n*/\nexport var ErrorLevel;\n(function (ErrorLevel) {\n /** 开发模式 - 严格检查,抛出所有错误 */\n ErrorLevel[ErrorLevel[\"Development\"] = 0] = \"Development\";\n /** 测试模式 - 记录错误但不中断执行 */\n ErrorLevel[ErrorLevel[\"Testing\"] = 1] = \"Testing\";\n /** 生产模式 - 最小化错误处理,优先性能 */\n ErrorLevel[ErrorLevel[\"Production\"] = 2] = \"Production\";\n /** 静默模式 - 完全禁用错误处理 */\n ErrorLevel[ErrorLevel[\"Silent\"] = 3] = \"Silent\";\n})(ErrorLevel || (ErrorLevel = {}));\n/**\n * 高性能错误处理系统\n *\n * @description\n * 提供可配置的错误处理策略,支持开发和生产环境的不同行为。\n * 在生产环境中可以完全禁用错误检查以提高性能。\n *\n * @example\n * ```typescript\n * // 配置错误处理器\n * ErrorHandler.configure({\n * level: ErrorLevel.Development,\n * enableAssertions: true,\n * enableTypeChecking: true\n * });\n *\n * // 使用断言\n * ErrorHandler.assert(player.health > 0, '玩家血量必须大于0');\n *\n * // 类型检查\n * ErrorHandler.checkType(value, 'number', '值必须是数字');\n *\n * // 性能监控\n * const result = ErrorHandler.monitor('expensiveFunction', () => {\n * return expensiveOperation();\n * });\n * ```\n */\nexport class ErrorHandler {\n /**\n * 配置错误处理器\n * @param config 配置选项\n */\n static configure(config) {\n this._config = { ...this._config, ...config };\n }\n /**\n * 设置错误处理级别\n * @param level 错误处理级别\n */\n static setLevel(level) {\n this._config.level = level;\n // 根据级别自动调整其他配置\n switch (level) {\n case ErrorLevel.Development:\n this._config.enableAssertions = true;\n this._config.enableTypeChecking = true;\n break;\n case ErrorLevel.Testing:\n this._config.enableAssertions = true;\n this._config.enableTypeChecking = false;\n break;\n case ErrorLevel.Production:\n this._config.enableAssertions = false;\n this._config.enableTypeChecking = false;\n break;\n case ErrorLevel.Silent:\n this._config.enableAssertions = false;\n this._config.enableTypeChecking = false;\n this._config.enablePerformanceMonitoring = false;\n break;\n }\n }\n /**\n * 断言检查\n * @param condition 条件\n * @param message 错误消息\n * @param context 上下文信息\n */\n static assert(condition, message, context) {\n if (!this._config.enableAssertions || this._config.level === ErrorLevel.Silent) {\n return;\n }\n this._errorStats.totalAssertions++;\n if (!condition) {\n const error = new Error(`断言失败: ${message}`);\n this._handleError(error, context);\n }\n }\n /**\n * 类型检查\n * @param value 要检查的值\n * @param expectedType 期望的类型\n * @param message 错误消息\n * @param context 上下文信息\n */\n static checkType(value, expectedType, message, context) {\n if (!this._config.enableTypeChecking || this._config.level === ErrorLevel.Silent) {\n return;\n }\n this._errorStats.totalTypeChecks++;\n const actualType = typeof value;\n if (actualType !== expectedType) {\n const errorMessage = message || `类型检查失败: 期望 ${expectedType}, 实际 ${actualType}`;\n const error = new Error(errorMessage);\n this._handleError(error, context);\n }\n }\n /**\n * 非空检查\n * @param value 要检查的值\n * @param message 错误消息\n * @param context 上下文信息\n */\n static checkNotNull(value, message, context) {\n if (!this._config.enableTypeChecking || this._config.level === ErrorLevel.Silent) {\n return;\n }\n this._errorStats.totalTypeChecks++;\n if (value == null) {\n const errorMessage = message || '值不能为null或undefined';\n const error = new Error(errorMessage);\n this._handleError(error, context);\n }\n }\n /**\n * 范围检查\n * @param value 要检查的值\n * @param min 最小值\n * @param max 最大值\n * @param message 错误消息\n * @param context 上下文信息\n */\n static checkRange(value, min, max, message, context) {\n if (!this._config.enableAssertions || this._config.level === ErrorLevel.Silent) {\n return;\n }\n this._errorStats.totalAssertions++;\n if (value < min || value > max) {\n const errorMessage = message || `值 ${value} 超出范围 [${min}, ${max}]`;\n const error = new Error(errorMessage);\n this._handleError(error, context);\n }\n }\n /**\n * 数组边界检查\n * @param array 数组\n * @param index 索引\n * @param message 错误消息\n * @param context 上下文信息\n */\n static checkArrayBounds(array, index, message, context) {\n if (!this._config.enableAssertions || this._config.level === ErrorLevel.Silent) {\n return;\n }\n this._errorStats.totalAssertions++;\n if (index < 0 || index >= array.length) {\n const errorMessage = message || `数组索引 ${index} 超出边界 [0, ${array.length - 1}]`;\n const error = new Error(errorMessage);\n this._handleError(error, context);\n }\n }\n /**\n * 性能监控装饰器\n * @param name 函数名称\n * @param fn 要监控的函数\n * @returns 函数执行结果\n */\n static monitor(name, fn) {\n if (!this._config.enablePerformanceMonitoring || this._config.level === ErrorLevel.Silent) {\n return fn();\n }\n const startTime = performance.now();\n try {\n const result = fn();\n const endTime = performance.now();\n this._recordPerformance(name, endTime - startTime);\n return result;\n }\n catch (error) {\n const endTime = performance.now();\n this._recordPerformance(name, endTime - startTime);\n throw error;\n }\n }\n /**\n * 异步性能监控\n * @param name 函数名称\n * @param fn 要监控的异步函数\n * @returns Promise结果\n */\n static async monitorAsync(name, fn) {\n if (!this._config.enablePerformanceMonitoring || this._config.level === ErrorLevel.Silent) {\n return fn();\n }\n const startTime = performance.now();\n try {\n const result = await fn();\n const endTime = performance.now();\n this._recordPerformance(name, endTime - startTime);\n return result;\n }\n catch (error) {\n const endTime = performance.now();\n this._recordPerformance(name, endTime - startTime);\n throw error;\n }\n }\n /**\n * 记录性能数据\n */\n static _recordPerformance(name, executionTime) {\n let data = this._performanceData.get(name);\n if (!data) {\n data = {\n functionName: name,\n executionTime: 0,\n callCount: 0,\n averageTime: 0,\n maxTime: 0,\n minTime: Infinity\n };\n this._performanceData.set(name, data);\n }\n data.callCount++;\n data.executionTime += executionTime;\n data.averageTime = data.executionTime / data.callCount;\n data.maxTime = Math.max(data.maxTime, executionTime);\n data.minTime = Math.min(data.minTime, executionTime);\n }\n /**\n * 处理错误\n */\n static _handleError(error, context) {\n this._errorStats.totalErrors++;\n // 调用错误回调\n if (this._config.onError) {\n try {\n this._config.onError(error, context);\n }\n catch (callbackError) {\n console.error('错误回调执行失败:', callbackError);\n }\n }\n // 根据错误级别决定行为\n switch (this._config.level) {\n case ErrorLevel.Development:\n throw error; // 开发模式:抛出错误\n case ErrorLevel.Testing:\n console.error('错误:', error.message, context);\n throw error; // 测试模式:记录并抛出\n case ErrorLevel.Production:\n console.warn('错误:', error.message);\n throw error; // 生产模式:警告并抛出\n case ErrorLevel.Silent:\n // 静默模式:什么都不做\n break;\n }\n throw error; // 默认行为\n }\n /**\n * 发出警告\n * @param message 警告消息\n * @param context 上下文信息\n */\n static warn(message, context) {\n if (this._config.level === ErrorLevel.Silent) {\n return;\n }\n this._errorStats.totalWarnings++;\n // 调用警告回调\n if (this._config.onWarning) {\n try {\n this._config.onWarning(message, context);\n }\n catch (callbackError) {\n console.error('警告回调执行失败:', callbackError);\n }\n }\n // 根据错误级别决定输出方式\n switch (this._config.level) {\n case ErrorLevel.Development:\n case ErrorLevel.Testing:\n console.warn('警告:', message, context);\n break;\n case ErrorLevel.Production:\n console.warn('警告:', message);\n break;\n }\n }\n /**\n * 获取性能统计信息\n */\n static getPerformanceStats() {\n return new Map(this._performanceData);\n }\n /**\n * 获取错误统计信息\n */\n static getErrorStats() {\n return { ...this._errorStats };\n }\n /**\n * 重置统计信息\n */\n static resetStats() {\n this._performanceData.clear();\n this._errorStats = {\n totalErrors: 0,\n totalWarnings: 0,\n totalAssertions: 0,\n totalTypeChecks: 0\n };\n }\n /**\n * 获取当前配置\n */\n static getConfig() {\n return { ...this._config };\n }\n /**\n * 创建带错误处理的函数包装器\n * @param fn 原函数\n * @param name 函数名称\n * @param enableMonitoring 是否启用性能监控\n * @returns 包装后的函数\n */\n static wrap(fn, name, enableMonitoring = false) {\n return (...args) => {\n try {\n if (enableMonitoring) {\n return this.monitor(name, () => fn(...args));\n }\n else {\n return fn(...args);\n }\n }\n catch (error) {\n this._handleError(error instanceof Error ? error : new Error(String(error)), { args, functionName: name });\n }\n };\n }\n}\nErrorHandler._config = {\n level: ErrorLevel.Development,\n enableAssertions: true,\n enableTypeChecking: true,\n enablePerformanceMonitoring: false\n};\n/** 性能监控数据 */\nErrorHandler._performanceData = new Map();\n/** 错误统计 */\nErrorHandler._errorStats = {\n totalErrors: 0,\n totalWarnings: 0,\n totalAssertions: 0,\n totalTypeChecks: 0\n};\n/**\n * 错误处理装饰器工厂\n * @param options 装饰器选项\n */\nexport function errorHandler(options = {}) {\n return function (target, propertyKey, descriptor) {\n const originalMethod = descriptor.value;\n const methodName = options.name || `${target.constructor.name}.${propertyKey}`;\n descriptor.value = function (...args) {\n // 类型检查\n if (options.enableTypeChecking) {\n for (let i = 0; i < args.length; i++) {\n if (args[i] == null) {\n ErrorHandler.warn(`方法 ${methodName} 的第 ${i + 1} 个参数为null或undefined`);\n }\n }\n }\n // 执行方法\n if (options.enableMonitoring) {\n return ErrorHandler.monitor(methodName, () => originalMethod.apply(this, args));\n }\n else {\n try {\n return originalMethod.apply(this, args);\n }\n catch (error) {\n const errorInstance = error instanceof Error ? error : new Error(String(error));\n ErrorHandler.warn(`方法 ${methodName} 执行失败`, { error: errorInstance, args, instance: this });\n throw errorInstance;\n }\n }\n };\n return descriptor;\n };\n}\n//# sourceMappingURL=ErrorHandler.js.map","/**\n * 行为树节点的执行状态枚举\n *\n * @description 定义了行为树中每个节点可能的执行状态\n */\nexport var TaskStatus;\n(function (TaskStatus) {\n /**\n * 无效状态 - 节点尚未执行或已被重置\n */\n TaskStatus[TaskStatus[\"Invalid\"] = 0] = \"Invalid\";\n /**\n * 成功状态 - 节点执行完成且成功\n */\n TaskStatus[TaskStatus[\"Success\"] = 1] = \"Success\";\n /**\n * 失败状态 - 节点执行完成但失败\n */\n TaskStatus[TaskStatus[\"Failure\"] = 2] = \"Failure\";\n /**\n * 运行中状态 - 节点正在执行,需要在下一帧继续\n */\n TaskStatus[TaskStatus[\"Running\"] = 3] = \"Running\";\n})(TaskStatus || (TaskStatus = {}));\n//# sourceMappingURL=TaskStatus.js.map","/**\n * 黑板变量类型枚举\n */\nexport var BlackboardValueType;\n(function (BlackboardValueType) {\n BlackboardValueType[\"String\"] = \"string\";\n BlackboardValueType[\"Number\"] = \"number\";\n BlackboardValueType[\"Boolean\"] = \"boolean\";\n BlackboardValueType[\"Vector2\"] = \"vector2\";\n BlackboardValueType[\"Vector3\"] = \"vector3\";\n BlackboardValueType[\"Object\"] = \"object\";\n BlackboardValueType[\"Array\"] = \"array\";\n})(BlackboardValueType || (BlackboardValueType = {}));\n/**\n * 行为树黑板系统\n *\n * @description\n * 提供类型安全的变量存储和访问机制,支持:\n * - 类型化变量定义和访问\n * - 变量监听和回调\n * - 序列化和反序列化\n * - 实时调试和编辑\n *\n * @example\n * ```typescript\n * // 创建黑板实例\n * const blackboard = new Blackboard();\n *\n * // 定义变量\n * blackboard.defineVariable('playerHealth', BlackboardValueType.Number, 100, {\n * description: '玩家生命值',\n * min: 0,\n * max: 100\n * });\n *\n * // 设置和获取值\n * blackboard.setValue('playerHealth', 80);\n * const health = blackboard.getValue<number>('playerHealth');\n *\n * // 监听变量变化\n * blackboard.addListener('playerHealth', (newVal, oldVal) => {\n * console.log(`玩家生命值从 ${oldVal} 变为 ${newVal}`);\n * });\n * ```\n */\nexport class Blackboard {\n constructor() {\n /** 变量定义存储 */\n this._variables = new Map();\n /** 变量监听器存储 */\n this._listeners = new Map();\n /** 监听器计数器 */\n this._listenerIdCounter = 0;\n /** 变量修改历史 */\n this._history = [];\n /** 是否启用历史记录 */\n this.enableHistory = false;\n }\n /**\n * 定义一个黑板变量\n *\n * @param name 变量名\n * @param type 变量类型\n * @param defaultValue 默认值\n * @param options 额外选项\n */\n defineVariable(name, type, defaultValue, options = {}) {\n if (!name || typeof name !== 'string') {\n throw new Error('变量名必须是非空字符串');\n }\n if (this._variables.has(name)) {\n console.warn(`黑板变量 \"${name}\" 已存在,将被重新定义`);\n }\n // 验证默认值类型\n if (!this._validateValueType(defaultValue, type)) {\n throw new Error(`默认值类型与变量类型 \"${type}\" 不匹配`);\n }\n const variable = {\n name,\n type,\n value: this._cloneValue(defaultValue),\n defaultValue: this._cloneValue(defaultValue),\n description: options.description || '',\n readonly: options.readonly || false,\n group: options.group || 'Default',\n min: options.min,\n max: options.max,\n options: options.options ? [...options.options] : undefined\n };\n this._variables.set(name, variable);\n }\n /**\n * 设置变量值\n *\n * @param name 变量名\n * @param value 新值\n * @param force 是否强制设置(忽略只读限制)\n */\n setValue(name, value, force = false) {\n const variable = this._variables.get(name);\n if (!variable) {\n console.warn(`尝试设置不存在的黑板变量 \"${name}\"`);\n return false;\n }\n if (variable.readonly && !force) {\n console.warn(`尝试修改只读黑板变量 \"${name}\"`);\n return false;\n }\n // 类型验证\n if (!this._validateValueType(value, variable.type)) {\n console.error(`设置的值类型与变量 \"${name}\" 的类型 \"${variable.type}\" 不匹配`);\n return false;\n }\n // 数值范围验证\n if (variable.type === BlackboardValueType.Number && typeof value === 'number') {\n if (variable.min !== undefined && value < variable.min) {\n console.warn(`变量 \"${name}\" 的值 ${value} 小于最小值 ${variable.min}`);\n return false;\n }\n if (variable.max !== undefined && value > variable.max) {\n console.warn(`变量 \"${name}\" 的值 ${value} 大于最大值 ${variable.max}`);\n return false;\n }\n }\n // 可选值验证\n if (variable.options && !variable.options.includes(value)) {\n console.warn(`变量 \"${n