mirror of
http://junepve.top:3000/chicrobot/CINC.git
synced 2026-10-29 19:51:29 +08:00
1141 lines
42 KiB
JavaScript
1141 lines
42 KiB
JavaScript
/**
|
|
* 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) {
|
|
// 这里可以添加真实的状态更新逻辑
|
|
// 例如:根据游戏中的实际数据更新角色状态信息
|
|
}
|
|
}
|
|
} |