Files
rougelike-demo/client/assets/scripts/Framework/UI/UIBase.ts
2025-12-14 23:35:54 +08:00

188 lines
4.9 KiB
TypeScript

import { Node, Component, Button } from 'cc';
/**
* UI基类
* 职责:
* - 定义UI的基础接口和生命周期
* - 管理UI根节点
* - 提供显示/隐藏控制
*
* 所有UI必须继承此类并实现onGetUrl方法
*/
export abstract class UIBase {
/** UI根节点 */
protected _node: Node | null = null;
/** UI是否已加载 */
protected _isLoaded: boolean = false;
/** UI是否显示中 */
protected _isShowing: boolean = false;
/**
* 获取UI资源路径 (必须重载)
* @returns UI预制体路径
* @example
* onGetUrl(): string {
* return 'UI/Login/UILogin';
* }
*/
abstract onGetUrl(): string;
/**
* UI开始时调用 (可选重载)
* 在UI预制体加载完成后调用
* 可以在此方法中初始化UI数据、绑定事件等
* @param params 传入的参数
* @example
* onStart(params?: any): void {
* console.log('UI初始化', params);
* // 绑定按钮事件
* this.bindEvents();
* }
*/
onStart?(params?: any): void | Promise<void>;
/**
* UI结束时调用 (可选重载)
* 在UI被卸载前调用
* 可以在此方法中清理资源、解绑事件等
* @example
* onEnd(): void {
* console.log('UI清理');
* // 解绑按钮事件
* this.unbindEvents();
* }
*/
onEnd?(): void;
/**
* UI更新 (可选重载)
* 在每帧调用(仅当UI显示时)
* @param dt 距离上一帧的时间(秒)
* @example
* onUpdate(dt: number): void {
* // 更新倒计时
* this.updateTimer(dt);
* }
*/
onUpdate?(dt: number): void;
/**
* 设置UI根节点
* 由UIMgr在加载完成后调用
* @param node UI根节点
*/
setNode(node: Node): void {
this._node = node;
this._isLoaded = true;
}
/**
* 获取UI根节点
* @returns UI根节点,如果未加载则返回null
*/
getNode(): Node | null {
return this._node;
}
/**
* 显示UI
*/
show(): void {
if (this._node) {
this._node.active = true;
this._isShowing = true;
}
}
/**
* 隐藏UI
*/
hide(): void {
if (this._node) {
this._node.active = false;
this._isShowing = false;
}
}
/**
* 检查UI是否显示中
* @returns true表示正在显示,false表示已隐藏
*/
isShowing(): boolean {
return this._isShowing;
}
/**
* 检查UI是否已加载
* @returns true表示已加载,false表示未加载
*/
isLoaded(): boolean {
return this._isLoaded;
}
/**
* 查找子节点并获取组件
* @param path 节点路径,支持多层级查找(如: 'mid/input_account')
* @param componentType 组件类型
* @returns 组件实例,未找到则返回null
* @example
* const editBox = this.GetChild('mid/input_account', EditBox);
* const button = this.GetChild('btn_login', Button);
*/
protected GetChild<T extends Component>(path: string, componentType: { new(): T }): T | null {
if (!this._node) {
console.error('[UIBase] GetChild失败: UI根节点不存在');
return null;
}
const childNode = this._node.getChildByPath(path);
if (!childNode) {
console.error(`[UIBase] GetChild失败: 未找到节点 ${path}`);
return null;
}
const component = childNode.getComponent(componentType);
if (!component) {
console.error(`[UIBase] GetChild失败: 节点 ${path} 未挂载 ${componentType.name} 组件`);
return null;
}
return component;
}
/**
* 设置按钮点击事件
* @param button Button组件或包含Button组件的Node
* @param callback 点击回调函数
* @param target 回调函数的this指向
* @example
* const btn = this.GetChild('btn_login', Button);
* this.SetClick(btn, this.onLoginClick, this);
*/
protected SetClick(button: Button | Node | null, callback: () => void, target?: any): void {
if (!button) {
console.error('[UIBase] SetClick失败: button为null');
return;
}
let targetNode: Node | null = null;
// 判断是Button组件还是Node
if (button instanceof Button) {
targetNode = button.node;
} else if (button instanceof Node) {
targetNode = button;
}
if (!targetNode) {
console.error('[UIBase] SetClick失败: 无法获取目标节点');
return;
}
// 绑定点击事件
targetNode.on(Node.EventType.TOUCH_END, callback, target);
}
}