/** * TmpScene 类 - 示例场景,展示如何创建一个带有可展开信息栏的Phaser场景 * 继承自Phaser.Scene,是Phaser游戏中场景的基本结构 */ export default class TmpScene extends Phaser.Scene { /** * 构造函数 - 初始化场景 * @constructor */ constructor() { // 调用父类构造函数,设置场景名称为'TmpScene' // 场景名称用于在Phaser中切换场景时使用 super('TmpScene') // 定义类成员变量 // 信息栏可见性状态 - 初始为隐藏 this.infoBarVisible = false; // 信息栏对象引用 - 用于存储信息栏的所有相关元素 this.infoBar = null; // 信息栏展开/收起按钮引用 this.infoBarToggle = null; // 信息栏主容器引用 - 包含整个信息栏的所有元素 this.infoBarContainer = null; // 滚动容器引用 - 包含所有可滚动的组件 this.infoBarScrollContainer = null; // 组件管理对象 - 存储所有添加到信息栏的组件实例 this.infoBarComponents = {}; // 信息栏配置对象 - 控制信息栏的行为和外观 this.infoBarConfig = { width: 0, // 信息栏宽度,将在create方法中根据屏幕宽度计算 height: 0, // 信息栏高度,将在create方法中根据屏幕高度计算 components: {}, // 存储组件配置信息 autoCleanup: true, // 是否自动清理资源密集型组件,优化性能 scrollable: true // 是否支持滚动,当组件总高度超过屏幕高度时 }; // 滚动相关变量 this.scrollY = 0; // 当前滚动位置 this.maxScrollY = 0; // 最大滚动距离 this.isDragging = false; // 是否正在拖动滚动 this.lastDragY = 0; // 上一次拖动的Y坐标 } /** * create方法 - 场景创建时调用的方法,用于初始化场景中的所有元素 * 在Phaser中,场景启动后会自动调用此方法 */ create() { // 设置场景背景色为深灰色 // cameras.main 是场景的主相机,控制场景的渲染区域 this.cameras.main.setBackgroundColor('#333333'); // 获取屏幕宽度和高度,存储为实例变量供后续使用 // this.scale.width 和 this.scale.height 是Phaser提供的获取当前游戏窗口大小的属性 this.screenWidth = this.scale.width; this.screenHeight = this.scale.height; // 调用方法创建右侧信息栏 this.createRightInfoBar(); // 示例:添加不同类型的组件到信息栏,带固定高度和自定义顺序 // 添加角色状态组件 - 文本类型 this.addInfoBarComponent('playerStats', { type: 'text', // 组件类型:文本 title: '角色状态', // 组件标题 content: ['生命值: 100/100', '魔法值: 50/50', '等级: 1'], // 组件内容 enabled: true, // 组件是否启用 resourceIntensive: false, // 是否为资源密集型组件 height: 120, // 固定高度 order: 1 // 自定义顺序(越小越靠前) }); // 添加小地图组件 - 占位符类型 this.addInfoBarComponent('miniMap', { type: 'placeholder', // 占位符类型,实际项目中替换为真实小地图 title: '小地图', // 组件标题 content: ['小地图区域'], // 组件内容 enabled: true, // 组件是否启用 resourceIntensive: true, // 标记为资源密集型组件,会在信息栏收起时自动清理 height: 180, // 固定高度 order: 2 // 自定义顺序 }); // 添加游戏日志组件 - 文本类型 this.addInfoBarComponent('gameLog', { type: 'text', // 组件类型:文本 title: '游戏日志', // 组件标题 content: ['系统: 欢迎来到游戏', '提示: 按H查看帮助', '新任务: 探索未知区域', '提示: 按I打开背包'], // 组件内容 enabled: true, // 组件是否启用 resourceIntensive: false, // 是否为资源密集型组件 height: 160, // 固定高度 order: 3 // 自定义顺序 }); // 初始化时,如果信息栏可见,创建所有启用的组件 // 由于初始状态下infoBarVisible为false,这里不会执行 if (this.infoBarVisible) { this.createAllEnabledComponents(); } } /** * 创建所有启用的组件 * 按自定义顺序创建组件实例,并重新排列和更新滚动区域 */ createAllEnabledComponents() { // 获取按自定义顺序排序的组件列表 const orderedComponents = this.getOrderedComponents(); // 遍历排序后的组件,创建启用的组件实例 orderedComponents.forEach(({id, config}) => { if (config.enabled) { this.createComponentInstance(id, config.config); } }); // 重新排列组件位置 this.rearrangeComponents(); // 更新滚动区域大小 this.updateScrollArea(); } /** * 获取按自定义顺序排序的组件 * @returns {Array} 排序后的组件数组,每个元素包含id和config */ getOrderedComponents() { // 将组件配置对象转换为数组 // Object.entries() 将对象转换为[key, value]数组 return Object.entries(this.infoBarConfig.components) // 映射为包含id和config的对象数组 .map(([id, config]) => ({id, config})) // 按order属性排序,order值越小越靠前 .sort((a, b) => { // 获取每个组件的order值,如果没有设置则默认为0 const orderA = a.config.config.order || 0; const orderB = b.config.config.order || 0; return orderA - orderB; }); } /** * 创建右侧信息栏 * 包括容器、背景、滚动容器和展开/收起按钮 */ createRightInfoBar() { // 定义信息栏宽度为屏幕宽度的20% this.infoBarConfig.width = this.screenWidth * 0.2; this.infoBarConfig.height = this.screenHeight * 0.8; // 创建信息栏主容器 // this.add.container() 创建一个新的容器,用于组织和管理多个游戏对象 this.infoBarContainer = this.add.container( this.screenWidth, // 初始位置在屏幕上侧外 0 - this.infoBarConfig.height // Y坐标 ); // 创建信息栏背景 // this.add.rectangle() 创建一个矩形游戏对象 const infoBarBg = this.add.rectangle( 0, // 相对于容器的X位置 0, // 相对于容器的Y位置 this.infoBarConfig.width, // 宽度 this.screenHeight, // 高度 0x222222, // 背景色(深灰色) 0.9 // 透明度 ).setOrigin(1, 0); // 设置锚点在右上角,便于定位 // 创建滚动容器,用于容纳所有组件 this.infoBarScrollContainer = this.add.container( -this.infoBarConfig.width, // 相对于信息栏容器的X位置 0 // Y坐标为0 ); // 将背景和滚动容器添加到信息栏容器 // 容器的add方法可以添加多个游戏对象 this.infoBarContainer.add([infoBarBg, this.infoBarScrollContainer]); // 如果配置了滚动功能,设置滚动 if (this.infoBarConfig.scrollable) { this.setupScrolling(); } // 创建信息栏展开/收起按钮,固定在屏幕右上角 // this.add.text() 创建文本游戏对象 this.infoBarToggle = this.add.text( this.screenWidth, // 按钮X位置 0, // 按钮Y位置 '+', // 初始文本 { fontSize: '16px', fill: '#ffffff', backgroundColor: '#444444', padding: { x: 10, y: 5 } } ).setOrigin(1, 0) // 设置锚点在右上角 .setInteractive(); // 设置为可交互,以便接收鼠标事件 // 添加点击事件监听器 // on('pointerdown', callback) 监听鼠标按下事件 this.infoBarToggle.on('pointerdown', () => { this.toggleInfoBar(); // 调用方法切换信息栏显示/隐藏 }); // 保存信息栏对象,便于其他系统更新和访问 this.infoBar = { container: this.infoBarContainer, // 信息栏主容器 scrollContainer: this.infoBarScrollContainer, // 滚动容器 toggle: this.infoBarToggle, // 展开/收起按钮 bg: infoBarBg, // 背景 config: this.infoBarConfig, // 配置 components: this.infoBarComponents // 组件 }; } /** * 设置滚动功能 * 添加鼠标和触摸事件监听,实现信息栏的滚动效果 */ setupScrolling() { // 移除遮罩,使用简单的滚动方式 // 添加鼠标事件监听 // on('pointerdown', callback) 监听鼠标按下事件 this.input.on('pointerdown', (pointer) => { // 检查点击是否在信息栏区域内 const infoBarRect = new Phaser.Geom.Rectangle( this.screenWidth - this.infoBarConfig.width, // 信息栏左边界 0, // 上边界 this.infoBarConfig.width, // 宽度 this.screenHeight // 高度 ); // 检查点击位置是否在信息栏内且信息栏可见 if (Phaser.Geom.Rectangle.ContainsPoint(infoBarRect, pointer) && this.infoBarVisible) { this.isDragging = true; // 开始拖动 this.lastDragY = pointer.y; // 记录初始拖动位置 } }); // 监听鼠标移动事件 this.input.on('pointermove', (pointer) => { if (this.isDragging) { // 计算拖动距离 const deltaY = pointer.y - this.lastDragY; // 调用滚动方法 this.scroll(deltaY); // 更新最后拖动位置 this.lastDragY = pointer.y; } }); // 监听鼠标释放事件 this.input.on('pointerup', () => { this.isDragging = false; // 结束拖动 }); // 添加触摸事件支持,使滚动在移动设备上也能工作 this.input.on('touchstart', (pointer) => { this.input.emit('pointerdown', pointer); // 触发pointerdown事件 }); this.input.on('touchmove', (pointer) => { this.input.emit('pointermove', pointer); // 触发pointermove事件 }); this.input.on('touchend', () => { this.input.emit('pointerup'); // 触发pointerup事件 }); } /** * 滚动逻辑 * 处理滚动位置计算和应用 * @param {number} deltaY - 滚动距离 */ scroll(deltaY) { // 计算新的滚动位置 const newScrollY = this.scrollY + deltaY; // 限制滚动范围,使用Phaser.Math.Clamp确保值在指定范围内 this.scrollY = Phaser.Math.Clamp(newScrollY, 0, this.maxScrollY); // 应用滚动:通过设置滚动容器的Y位置实现 this.infoBarScrollContainer.setY(-this.scrollY); } /** * 更新滚动区域 * 计算所有组件的总高度,更新最大滚动距离 */ updateScrollArea() { // 计算所有组件的总高度 let totalHeight = 20; // 顶部边距 const orderedComponents = this.getOrderedComponents(); // 遍历所有组件,累加高度 orderedComponents.forEach(({id}) => { if (this.infoBarComponents[id]) { totalHeight += this.infoBarComponents[id].height + 10; // 组件高度 + 间距 } }); // 更新最大滚动距离:总高度减去屏幕高度,加上底部边距 this.maxScrollY = Math.max(0, totalHeight - this.screenHeight + 20); // +20为底部边距 } /** * 切换信息栏显示/隐藏 * 使用Phaser的tweens系统实现平滑的动画效果 */ toggleInfoBar() { // 切换信息栏可见性状态 this.infoBarVisible = !this.infoBarVisible; if (this.infoBarVisible) { // 展开信息栏 // 使用tweens.add()创建动画 this.tweens.add({ targets: this.infoBarContainer, // 动画目标:信息栏容器 x: this.screenWidth, // 目标X位置:屏幕右侧减去信息栏宽度 duration: 300, // 动画持续时间:300毫秒 ease: 'Power1' // 缓动函数:Power1(线性) }); // 更新按钮文本为'-'(收起) this.infoBarToggle.setText('-'); // 创建所有启用的组件 this.createAllEnabledComponents(); } else { // 收起信息栏 this.tweens.add({ targets: this.infoBarContainer, // 动画目标:信息栏容器 x: this.screenWidth + this.infoBarConfig.width, // 目标X位置:屏幕右侧外 duration: 300, // 动画持续时间:300毫秒 ease: 'Power1' // 缓动函数:Power1(线性) }); // 更新按钮文本为'+'(展开) this.infoBarToggle.setText('+'); // 如果配置了自动清理,清理资源密集型组件 if (this.infoBarConfig.autoCleanup) { this.cleanupResourceIntensiveComponents(); } } } /** * 添加信息栏组件 * @param {string} id - 组件唯一标识符 * @param {Object} componentConfig - 组件配置对象 * @returns {Object} 组件实例 */ addInfoBarComponent(id, componentConfig) { // 保存组件配置到信息栏配置对象 this.infoBarConfig.components[id] = { enabled: componentConfig.enabled !== false, // 默认为启用 resourceIntensive: componentConfig.resourceIntensive || false, // 默认为非资源密集型 config: componentConfig // 保存原始配置 }; // 如果信息栏可见且组件启用,则创建组件实例 if (this.infoBarVisible && this.infoBarConfig.components[id].enabled) { this.createComponentInstance(id, componentConfig); // 重新排列组件位置 this.rearrangeComponents(); } // 返回组件实例 return this.infoBarComponents[id]; } /** * 创建组件实例 * 根据组件类型创建不同的实例 * @param {string} id - 组件唯一标识符 * @param {Object} componentConfig - 组件配置对象 * @returns {Object} 组件实例 */ createComponentInstance(id, componentConfig) { // 根据组件类型创建不同的实例 let componentInstance; switch (componentConfig.type) { case 'text': // 创建文本组件 componentInstance = this.createTextComponent(id, componentConfig); break; case 'miniMap': // 创建小地图组件 componentInstance = this.createMiniMapComponent(id, componentConfig); break; case 'placeholder': default: // 创建占位符组件 componentInstance = this.createPlaceholderComponent(id, componentConfig); break; } // 保存组件实例到组件管理对象 this.infoBarComponents[id] = componentInstance; return componentInstance; } /** * 创建文本组件 * @param {string} id - 组件唯一标识符 * @param {Object} config - 组件配置对象 * @returns {Object} 文本组件实例 */ createTextComponent(id, config) { // 创建组件组容器 const componentGroup = this.add.container( 10, // 相对于滚动容器的X位置,左内边距10px 0 // 相对于滚动容器的Y位置 ); // 创建组件内容容器,用于容纳标题栏和内容 const componentContent = this.add.container(0, 0); // 创建标题栏背景 const titleBar = this.add.rectangle( 0, // X位置 0, // Y位置 this.infoBarConfig.width - 20, // 宽度:信息栏宽度减去左右内边距 30, // 高度 0x444444, // 颜色 0.5 // 透明度 ).setOrigin(0, 0); // 锚点在左上角 // 创建组件标题 const title = this.add.text( 10, // X位置 5, // Y位置 config.title, // 标题文本 { fontSize: '14px', fill: '#ffffff', fontWeight: 'bold' } // 样式 ).setOrigin(0, 0); // 锚点在左上角 // 创建展开/收起按钮 const toggleBtn = this.add.text( this.infoBarConfig.width - 30, // X位置 5, // Y位置 '-', // 初始文本 { fontSize: '16px', fill: '#ffffff' } // 样式 ).setOrigin(0, 0) // 锚点在左上角 .setInteractive(); // 设置为可交互 // 创建分隔线 const separator = this.add.line( 0, // X位置 30, // Y位置 0, 0, // 起始点 this.infoBarConfig.width - 20, 0, // 结束点 0x555555 // 颜色 ).setOrigin(0, 0); // 锚点在左上角 // 创建内容容器,用于动态管理内容 const contentContainer = this.add.container(0, 30); // 创建内容文本 const contentTexts = []; config.content.forEach((line, index) => { const text = this.add.text( 10, // X位置 index * 25, // Y位置:每行25像素 line, // 文本内容 { fontSize: '12px', fill: '#cccccc' } // 样式 ).setOrigin(0, 0); // 锚点在左上角 contentTexts.push(text); }); // 将内容添加到内容容器 contentContainer.add(contentTexts); // 添加所有元素到组件内容 componentContent.add([titleBar, title, toggleBtn, separator, contentContainer]); // 添加组件内容到组件组 componentGroup.add(componentContent); // 添加到滚动容器 this.infoBarScrollContainer.add(componentGroup); // 保存组件状态 const componentState = { expanded: true, // 是否展开 contentVisible: true // 内容是否可见 }; // 添加展开/收起点击事件 toggleBtn.on('pointerdown', () => { this.toggleComponent(id, componentState, contentContainer, toggleBtn); }); // 计算高度 const collapsedHeight = 40; // 收起时的高度 const expandedHeight = config.height || (30 + config.content.length * 25) + 10; // 展开时的高度 // 返回组件实例对象 return { group: componentGroup, componentContent: componentContent, title: title, titleBar: titleBar, toggleBtn: toggleBtn, separator: separator, contentContainer: contentContainer, content: contentTexts, type: config.type, config: config, state: componentState, collapsedHeight: collapsedHeight, expandedHeight: expandedHeight, height: expandedHeight }; } /** * 创建小地图组件(示例) * @param {string} id - 组件唯一标识符 * @param {Object} config - 组件配置对象 * @returns {Object} 小地图组件实例 */ createMiniMapComponent(id, config) { // 创建组件组容器 const componentGroup = this.add.container( 10, // 相对于滚动容器的X位置,左内边距10px 0 // 相对于滚动容器的Y位置 ); // 创建组件内容容器,用于容纳标题栏和内容 const componentContent = this.add.container(0, 0); // 创建标题栏背景 const titleBar = this.add.rectangle( 0, // X位置 0, // Y位置 this.infoBarConfig.width - 20, // 宽度:信息栏宽度减去左右内边距 30, // 高度 0x444444, // 颜色 0.5 // 透明度 ).setOrigin(0, 0); // 锚点在左上角 // 创建组件标题 const title = this.add.text( 10, // X位置 5, // Y位置 config.title, // 标题文本 { fontSize: '14px', fill: '#ffffff', fontWeight: 'bold' } // 样式 ).setOrigin(0, 0); // 锚点在左上角 // 创建展开/收起按钮 const toggleBtn = this.add.text( this.infoBarConfig.width - 30, // X位置 5, // Y位置 '-', // 初始文本 { fontSize: '16px', fill: '#ffffff' } // 样式 ).setOrigin(0, 0) // 锚点在左上角 .setInteractive(); // 设置为可交互 // 创建分隔线 const separator = this.add.line( 0, // X位置 30, // Y位置 0, 0, // 起始点 this.infoBarConfig.width - 20, 0, // 结束点 0x555555 // 颜色 ).setOrigin(0, 0); // 锚点在左上角 // 创建内容容器,用于动态管理内容 const contentContainer = this.add.container(0, 30); // 创建小地图占位符(实际项目中替换为真实小地图) const miniMapBg = this.add.rectangle( (this.infoBarConfig.width - 20) / 2, // X位置:水平居中 (config.height - 40) / 2, // Y位置:垂直居中,减去标题栏高度 this.infoBarConfig.width - 40, // 宽度:信息栏宽度减去内边距 config.height - 50, // 高度:固定高度减去标题和分隔线高度 0x111111, // 颜色 1 // 透明度 ).setOrigin(0.5, 0.5); // 锚点在中心 // 创建小地图文本 const miniMapText = this.add.text( (this.infoBarConfig.width - 20) / 2, // X位置:水平居中 (config.height - 40) / 2, // Y位置:垂直居中 '小地图', // 文本 { fontSize: '12px', fill: '#cccccc' } // 样式 ).setOrigin(0.5, 0.5); // 锚点在中心 // 将内容添加到内容容器 contentContainer.add([miniMapBg, miniMapText]); // 添加所有元素到组件内容 componentContent.add([titleBar, title, toggleBtn, separator, contentContainer]); // 添加组件内容到组件组 componentGroup.add(componentContent); // 添加到滚动容器 this.infoBarScrollContainer.add(componentGroup); // 保存组件状态 const componentState = { expanded: true, // 是否展开 contentVisible: true, // 内容是否可见 contentCreated: true // 标记内容已创建 }; // 添加展开/收起点击事件 toggleBtn.on('pointerdown', () => { this.toggleComponent(id, componentState, contentContainer, toggleBtn, true); }); // 计算高度 const collapsedHeight = 40; // 收起时的高度 const expandedHeight = config.height || 160; // 展开时的高度 // 返回组件实例对象 return { group: componentGroup, componentContent: componentContent, title: title, titleBar: titleBar, toggleBtn: toggleBtn, separator: separator, contentContainer: contentContainer, map: miniMapBg, mapText: miniMapText, type: config.type, config: config, state: componentState, collapsedHeight: collapsedHeight, expandedHeight: expandedHeight, height: expandedHeight }; } /** * 创建占位符组件 * @param {string} id - 组件唯一标识符 * @param {Object} config - 组件配置对象 * @returns {Object} 占位符组件实例 */ createPlaceholderComponent(id, config) { // 创建组件组容器 const componentGroup = this.add.container( 10, // 相对于滚动容器的X位置,左内边距10px 0 // 相对于滚动容器的Y位置 ); // 创建组件内容容器,用于容纳标题栏和内容 const componentContent = this.add.container(0, 0); // 创建标题栏背景 const titleBar = this.add.rectangle( 0, // X位置 0, // Y位置 this.infoBarConfig.width - 20, // 宽度:信息栏宽度减去左右内边距 30, // 高度 0x444444, // 颜色 0.5 // 透明度 ).setOrigin(0, 0); // 锚点在左上角 // 创建组件标题 const title = this.add.text( 10, // X位置 5, // Y位置 config.title, // 标题文本 { fontSize: '14px', fill: '#ffffff', fontWeight: 'bold' } // 样式 ).setOrigin(0, 0); // 锚点在左上角 // 创建展开/收起按钮 const toggleBtn = this.add.text( this.infoBarConfig.width - 30, // X位置 5, // Y位置 '-', // 初始文本 { fontSize: '16px', fill: '#ffffff' } // 样式 ).setOrigin(0, 0) // 锚点在左上角 .setInteractive(); // 设置为可交互 // 创建分隔线 const separator = this.add.line( 0, // X位置 30, // Y位置 0, 0, // 起始点 this.infoBarConfig.width - 20, 0, // 结束点 0x555555 // 颜色 ).setOrigin(0, 0); // 锚点在左上角 // 创建内容容器,用于动态管理内容 const contentContainer = this.add.container(0, 30); // 创建内容 const contentText = this.add.text( 10, // X位置 0, // Y位置 config.content.join('\n'), // 内容文本,将数组元素用换行符连接 { fontSize: '12px', fill: '#cccccc', wordWrap: { width: this.infoBarConfig.width - 40 } // 自动换行 } ).setOrigin(0, 0); // 锚点在左上角 // 将内容添加到内容容器 contentContainer.add([contentText]); // 添加所有元素到组件内容 componentContent.add([titleBar, title, toggleBtn, separator, contentContainer]); // 添加组件内容到组件组 componentGroup.add(componentContent); // 添加到滚动容器 this.infoBarScrollContainer.add(componentGroup); // 保存组件状态 const componentState = { expanded: true, // 是否展开 contentVisible: true // 内容是否可见 }; // 添加展开/收起点击事件 toggleBtn.on('pointerdown', () => { this.toggleComponent(id, componentState, contentContainer, toggleBtn); }); // 计算高度 const collapsedHeight = 40; // 收起时的高度 const expandedHeight = config.height || (50 + contentText.height) + 10; // 展开时的高度 // 返回组件实例对象 return { group: componentGroup, componentContent: componentContent, title: title, titleBar: titleBar, toggleBtn: toggleBtn, separator: separator, contentContainer: contentContainer, content: [contentText], type: config.type, config: config, state: componentState, collapsedHeight: collapsedHeight, expandedHeight: expandedHeight, height: expandedHeight }; } /** * 更新信息栏组件内容 * 目前仅支持更新文本类型组件的内容 * @param {string} id - 组件唯一标识符 * @param {Array} newContent - 新的内容数组 */ updateInfoBarComponent(id, newContent) { // 获取组件实例 const component = this.infoBarComponents[id]; // 检查组件是否存在且为文本类型 if (component && component.type === 'text') { // 清空现有内容 component.content.forEach(text => text.destroy()); component.content = []; // 添加新内容 newContent.forEach((line, index) => { const text = this.add.text( 0, // X位置 30 + index * 25, // Y位置:标题栏高度 + 行索引 * 行高 line, // 文本内容 { fontSize: '12px', fill: '#cccccc' } // 样式 ).setOrigin(0, 0); // 锚点在左上角 component.group.add(text); component.content.push(text); }); // 重新排列组件(保持固定高度) this.rearrangeComponents(); } } /** * 重新排列组件 * 按自定义顺序排列组件,并更新它们的Y位置 */ rearrangeComponents() { let currentY = 20; // 起始Y位置,顶部边距20px // 获取按自定义顺序排序的组件列表 const orderedComponents = this.getOrderedComponents(); // 遍历组件,设置它们的Y位置 orderedComponents.forEach(({id}) => { const component = this.infoBarComponents[id]; if (component && component.group) { component.group.setY(currentY); // 设置组件的Y位置 currentY += component.height + 10; // 更新当前Y位置:组件高度 + 间距10px } }); // 更新滚动区域大小 this.updateScrollArea(); } /** * 切换组件显示/隐藏 * @param {string} id - 组件唯一标识符 */ toggleInfoBarComponent(id) { // 获取组件配置 const componentConfig = this.infoBarConfig.components[id]; if (componentConfig) { // 切换组件启用状态 componentConfig.enabled = !componentConfig.enabled; if (componentConfig.enabled) { // 启用组件,创建实例 if (this.infoBarVisible) { this.createComponentInstance(id, componentConfig.config); this.rearrangeComponents(); } } else { // 禁用组件,销毁实例 this.destroyComponentInstance(id); this.rearrangeComponents(); } } } /** * 销毁组件实例 * @param {string} id - 组件唯一标识符 */ destroyComponentInstance(id) { if (this.infoBarComponents[id]) { // 销毁组件的组容器 this.infoBarComponents[id].group.destroy(); // 从组件管理对象中删除 delete this.infoBarComponents[id]; } } /** * 清理资源密集型组件 * 在信息栏收起时调用,释放资源 */ cleanupResourceIntensiveComponents() { // 遍历所有组件配置 for (const id in this.infoBarConfig.components) { const componentConfig = this.infoBarConfig.components[id]; // 检查是否为资源密集型组件且存在实例 if (componentConfig.resourceIntensive && this.infoBarComponents[id]) { this.destroyComponentInstance(id); } } // 收起时重新排列剩余组件,确保位置正确 this.rearrangeComponents(); } /** * 重新创建资源密集型组件 * 在信息栏展开时调用,重新加载资源 */ recreateResourceIntensiveComponents() { // 遍历所有组件配置 for (const id in this.infoBarConfig.components) { const componentConfig = this.infoBarConfig.components[id]; // 检查是否为资源密集型组件且已启用 if (componentConfig.resourceIntensive && componentConfig.enabled) { this.createComponentInstance(id, componentConfig.config); } } } /** * 切换信息栏显示/隐藏(优化版) * 使用Phaser的tweens系统实现平滑的动画效果 */ toggleInfoBar() { // 切换信息栏可见性状态 this.infoBarVisible = !this.infoBarVisible; if (this.infoBarVisible) { // 展开信息栏 this.tweens.add({ targets: this.infoBarContainer, // 动画目标:信息栏容器 x: this.screenWidth, // 目标X位置 duration: 300, // 动画持续时间 ease: 'Power1' // 缓动函数 }); // 更新按钮文本 this.infoBarToggle.setText('-'); // 只创建缺失的组件(主要是资源密集型组件) this.createMissingComponents(); // 确保所有组件正确排列 this.rearrangeComponents(); } else { // 收起信息栏 this.tweens.add({ targets: this.infoBarContainer, // 动画目标:信息栏容器 x: this.screenWidth + this.infoBarConfig.width, // 目标X位置 duration: 300, // 动画持续时间 ease: 'Power1' // 缓动函数 }); // 更新按钮文本 this.infoBarToggle.setText('+'); // 只清理资源密集型组件 this.cleanupResourceIntensiveComponents(); } } /** * 创建缺失的组件 * 只创建已启用但尚未创建实例的组件 */ createMissingComponents() { // 获取按自定义顺序排序的组件列表 const orderedComponents = this.getOrderedComponents(); // 遍历组件,创建缺失的实例 orderedComponents.forEach(({id, config}) => { if (config.enabled && !this.infoBarComponents[id]) { this.createComponentInstance(id, config.config); } }); } /** * 创建所有启用的组件(完整重建) * 先销毁所有现有组件,然后重新创建 */ createAllEnabledComponents() { // 先销毁所有现有组件,避免重叠 this.destroyAllComponents(); // 然后创建所有启用的组件 const orderedComponents = this.getOrderedComponents(); orderedComponents.forEach(({id, config}) => { if (config.enabled) { this.createComponentInstance(id, config.config); } }); // 排列组件 this.rearrangeComponents(); // 更新滚动区域 this.updateScrollArea(); } /** * 销毁所有组件 * 清理所有组件实例 */ destroyAllComponents() { // 遍历所有组件实例 for (const id in this.infoBarComponents) { this.destroyComponentInstance(id); } } /** * 切换组件展开/收起 * @param {string} id - 组件唯一标识符 * @param {Object} state - 组件状态对象 * @param {Phaser.GameObjects.Container} contentContainer - 内容容器 * @param {Phaser.GameObjects.Text} toggleBtn - 切换按钮 * @param {boolean} isResourceIntensive - 是否为资源密集型组件 */ toggleComponent(id, state, contentContainer, toggleBtn, isResourceIntensive = false) { // 切换展开状态 state.expanded = !state.expanded; if (state.expanded) { // 展开组件 toggleBtn.setText('-'); if (isResourceIntensive) { // 资源密集型组件,需要重新创建内容 if (!state.contentCreated) { this.recreateComponentContent(id); state.contentCreated = true; } } // 显示内容容器 contentContainer.visible = true; // 更新组件高度 this.infoBarComponents[id].height = this.infoBarComponents[id].expandedHeight; } else { // 收起组件 toggleBtn.setText('+'); if (isResourceIntensive) { // 资源密集型组件,清理内容 this.destroyComponentContent(id); state.contentCreated = false; } // 隐藏内容容器 contentContainer.visible = false; // 更新组件高度 this.infoBarComponents[id].height = this.infoBarComponents[id].collapsedHeight; } // 重新排列所有组件 this.rearrangeComponents(); // 更新滚动区域 this.updateScrollArea(); } /** * 销毁组件内容 * 清理组件的内容容器中的所有子元素 * @param {string} id - 组件唯一标识符 */ destroyComponentContent(id) { // 获取组件实例 const component = this.infoBarComponents[id]; // 检查组件和内容容器是否存在 if (component && component.contentContainer) { // 销毁内容容器中的所有子元素 // removeAll(true) 中的true表示同时销毁这些元素 component.contentContainer.removeAll(true); } } /** * 重新创建组件内容 * 为资源密集型组件重新创建内容 * @param {string} id - 组件唯一标识符 */ recreateComponentContent(id) { // 获取组件实例 const component = this.infoBarComponents[id]; // 检查组件和内容容器是否存在 if (!component || !component.contentContainer) return; // 获取组件配置 const config = component.config; // 根据组件类型重新创建内容 switch (config.type) { case 'miniMap': case 'placeholder': // 重新创建小地图内容 const miniMapBg = this.add.rectangle( (this.infoBarConfig.width - 20) / 2, // 水平居中 (config.height - 40) / 2, // 垂直居中 this.infoBarConfig.width - 40, // 宽度 config.height - 50, // 高度 0x111111, // 颜色 1 // 透明度 ).setOrigin(0.5, 0.5); // 锚点在中心 const miniMapText = this.add.text( (this.infoBarConfig.width - 20) / 2, // 水平居中 (config.height - 40) / 2, // 垂直居中 '小地图', // 文本 { fontSize: '12px', fill: '#cccccc' } // 样式 ).setOrigin(0.5, 0.5); // 锚点在中心 // 添加到内容容器 component.contentContainer.add([miniMapBg, miniMapText]); // 更新组件引用 component.map = miniMapBg; component.mapText = miniMapText; break; case 'text': // 重新创建文本内容 const contentTexts = []; config.content.forEach((line, index) => { const text = this.add.text( 10, // X位置 index * 25, // Y位置 line, // 文本 { fontSize: '12px', fill: '#cccccc' } // 样式 ).setOrigin(0, 0); // 锚点在左上角 contentTexts.push(text); }); // 添加到内容容器 component.contentContainer.add(contentTexts); // 更新组件引用 component.content = contentTexts; break; } } /** * 设置信息栏自动清理策略 * @param {boolean} enabled - 是否启用自动清理 */ setInfoBarAutoCleanup(enabled) { this.infoBarConfig.autoCleanup = enabled; } /** * 更新信息栏内容的方法(向后兼容) * 兼容旧接口,更新第一个文本组件 * @param {Array} newContent - 新的内容数组 */ updateInfoBarContent(newContent) { // 查找第一个文本类型组件 const firstTextComponentId = Object.keys(this.infoBarComponents).find( id => this.infoBarComponents[id] && this.infoBarComponents[id].type === 'text' ); // 如果找到,更新其内容 if (firstTextComponentId) { this.updateInfoBarComponent(firstTextComponentId, newContent); } } /** * update方法 - 场景更新方法 * 在Phaser中,每帧都会调用此方法 */ update() { // 示例:模拟动态更新角色状态 if (this.infoBarVisible && this.infoBarComponents.playerStats) { // 这里可以添加真实的状态更新逻辑 // 例如:根据游戏中的实际数据更新角色状态信息 } } }