From b32c550b15fea0a5baf03bbbb55ace2d05fcde1b Mon Sep 17 00:00:00 2001 From: dgflash Date: Sun, 5 Apr 2026 11:05:58 +0800 Subject: [PATCH] =?UTF-8?q?1.=E8=A7=84=E8=8C=83=E5=8C=96=E6=A1=86=E6=9E=B6?= =?UTF-8?q?=E4=B8=9A=E5=8A=A1=E6=A8=A1=E6=9D=BF=E7=9A=84=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=202.=E5=BA=9F=E5=BC=83GameCollision.ts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/module/common/CCBusiness.ts | 73 +++++++++++++++++- assets/module/common/CCEntity.ts | 16 ++-- assets/module/common/CCView.ts | 32 +++----- assets/module/common/GameCollision.ts | 87 ---------------------- assets/module/common/GameCollision.ts.meta | 9 --- 5 files changed, 88 insertions(+), 129 deletions(-) delete mode 100644 assets/module/common/GameCollision.ts delete mode 100644 assets/module/common/GameCollision.ts.meta diff --git a/assets/module/common/CCBusiness.ts b/assets/module/common/CCBusiness.ts index d9afc34..6cf1dfc 100644 --- a/assets/module/common/CCBusiness.ts +++ b/assets/module/common/CCBusiness.ts @@ -11,7 +11,26 @@ import type { CCEntity } from './CCEntity'; /** 业务逻辑 */ export class CCBusiness { - ent!: T; + private _destroyed: boolean = false; + + /** 当前业务逻辑是否有效(未销毁) */ + get isValid(): boolean { + return !this._destroyed; + } + + private _ent: T | null = null; + + /** 所属实体引用 */ + get ent(): T { + if (this._destroyed) { + console.warn('[OopsFramework]', '尝试访问已销毁的业务逻辑的实体引用'); + } + return this._ent!; + } + + set ent(value: T) { + this._ent = value; + } /** 业务逻辑初始化(由 CCEntity.addBusiness 自动调用) */ protected init() { @@ -19,6 +38,13 @@ export class CCBusiness { } destroy() { + if (this._destroyed) { + console.warn('[OopsFramework]', '业务逻辑已销毁,无需重复销毁'); + return; + } + + this._destroyed = true; + // 释放消息对象 if (this._event) { this._event.clear(); @@ -26,7 +52,7 @@ export class CCBusiness { } // 清空实体引用,避免循环引用导致的内存泄漏 - this.ent = null!; + this._ent = null; } //#region 全局事件管理 @@ -34,6 +60,9 @@ export class CCBusiness { private _event: EventDispatcher | null = null; /** 全局事件管理器 */ private get event(): EventDispatcher { + if (this._destroyed) { + console.warn('[OopsFramework]', '尝试访问已销毁的业务逻辑的事件管理器'); + } if (this._event == null) this._event = new EventDispatcher(); return this._event; } @@ -47,6 +76,10 @@ export class CCBusiness { * @param object 侦听函数绑定的this对象 */ watch(event: K, listener: ListenerFuncTyped, object: any): void { + if (this._destroyed) { + console.warn('[OopsFramework]', '尝试在已销毁的业务逻辑上注册事件'); + return; + } this.event.on(event as string, listener as ListenerFunc, object); } @@ -57,6 +90,10 @@ export class CCBusiness { * @param object 侦听函数绑定的this对象 */ watchOnce(event: K, listener: ListenerFuncTyped, object: any): void { + if (this._destroyed) { + console.warn('[OopsFramework]', '尝试在已销毁的业务逻辑上注册一次性事件'); + return; + } this.event.once(event as string, listener as ListenerFunc, object); } @@ -67,6 +104,7 @@ export class CCBusiness { * @param object 侦听函数绑定的this对象(可选) */ unwatch(event: K, listener?: ListenerFuncTyped, object?: any): void { + if (this._destroyed) return; this.event.off(event as string, listener as ListenerFunc, object); } @@ -76,6 +114,10 @@ export class CCBusiness { * @param data 事件数据 */ emit(event: K, data: OopsFramework.TypedEventMap[K]): void { + if (this._destroyed) { + console.warn('[OopsFramework]', '尝试在已销毁的业务逻辑上触发事件'); + return; + } this.event.emit(event, data); } @@ -85,6 +127,10 @@ export class CCBusiness { * @param data 事件数据(必须完全匹配类型定义) */ emitAsync(event: K, data: OopsFramework.TypedEventMap[K]): Promise { + if (this._destroyed) { + console.warn('[OopsFramework]', '尝试在已销毁的业务逻辑上触发异步事件'); + return Promise.resolve(); + } return this.event.emitAsync(event, data); } @@ -99,6 +145,10 @@ export class CCBusiness { * @param object 侦听函数绑定的this对象 */ on(event: string, listener: ListenerFunc, object: object) { + if (this._destroyed) { + console.warn('[OopsFramework]', '尝试在已销毁的业务逻辑上注册事件'); + return; + } this.event.on(event, listener, object); } @@ -109,6 +159,10 @@ export class CCBusiness { * @param object 侦听函数绑定的this对象 */ once(event: string, listener: ListenerFunc, object: object) { + if (this._destroyed) { + console.warn('[OopsFramework]', '尝试在已销毁的业务逻辑上注册一次性事件'); + return; + } this.event.once(event, listener, object); } @@ -119,6 +173,7 @@ export class CCBusiness { * @param object 侦听函数绑定的this对象(可选) */ off(event: string, listener?: ListenerFunc, object?: object) { + if (this._destroyed) return; this.event.off(event, listener, object); } @@ -128,6 +183,10 @@ export class CCBusiness { * @param args 事件参数 */ dispatchEvent(event: string, ...args: unknown[]) { + if (this._destroyed) { + console.warn('[OopsFramework]', '尝试在已销毁的业务逻辑上触发事件'); + return; + } this.event.dispatchEvent(event, ...args); } @@ -137,6 +196,10 @@ export class CCBusiness { * @param args 事件参数 */ dispatchEventAsync(event: string, ...args: unknown[]): Promise { + if (this._destroyed) { + console.warn('[OopsFramework]', '尝试在已销毁的业务逻辑上触发异步事件'); + return Promise.resolve(); + } return this.event.dispatchEventAsync(event, ...args); } @@ -149,13 +212,17 @@ export class CCBusiness { * onGlobal(event: string, args: unknown) { console.log(args) }; */ protected setEvent(...args: string[]) { + if (this._destroyed) { + console.warn('[OopsFramework]', '尝试在已销毁的业务逻辑上批量设置事件'); + return; + } for (const name of args) { const func = (this as Record)[name]; if (typeof func === 'function') { this.on(name, func as ListenerFunc, this); } else { - console.error(`名为【${name}】的全局事方法不存在`); + console.error('[OopsFramework]', `名为【${name}】的全局事方法不存在`); } } } diff --git a/assets/module/common/CCEntity.ts b/assets/module/common/CCEntity.ts index 73f32b9..4124923 100644 --- a/assets/module/common/CCEntity.ts +++ b/assets/module/common/CCEntity.ts @@ -35,7 +35,7 @@ export abstract class CCEntity extends ecs.Entity { addChildSingleton(cls: OopsFramework.EntityCtor): T { if (this.singletons == null) this.singletons = new Map(); if (this.singletons.has(cls)) { - console.error(`${cls.name} 单例子实体已存在`); + console.error('[OopsFramework]', `${cls.name} 单例子实体已存在`); return null!; } const entity = ecs.getEntity(cls); @@ -105,7 +105,7 @@ export abstract class CCEntity extends ecs.Entity { // 检查实体是否已销毁 if (!this.isValid) { - console.warn(`实体已销毁,取消添加预制体组件: ${(ctor as any).name}`); + console.warn('[OopsFramework]', `实体已销毁,取消添加预制体组件: ${(ctor as any).name}`); node.destroy(); return null; } @@ -120,7 +120,7 @@ export abstract class CCEntity extends ecs.Entity { // 检查实体是否已销毁 if (!this.isValid) { - console.warn(`实体已销毁,取消添加预制体组件: ${(ctor as any).name}`); + console.warn('[OopsFramework]', `实体已销毁,取消添加预制体组件: ${(ctor as any).name}`); node.destroy(); return null; } @@ -166,7 +166,7 @@ export abstract class CCEntity extends ecs.Entity { } if (oops.gui.has(key)) { - console.warn(`${key} 界面已存在`); + console.warn('[OopsFramework]', `${key} 界面已存在`); return oops.gui.get(key); } @@ -174,7 +174,7 @@ export abstract class CCEntity extends ecs.Entity { // 检查实体是否已销毁 if (!this.isValid) { - console.warn(`实体已销毁,取消添加界面组件: ${key}`); + console.warn('[OopsFramework]', `实体已销毁,取消添加界面组件: ${key}`); oops.gui.remove(key); return null; } @@ -196,7 +196,7 @@ export abstract class CCEntity extends ecs.Entity { if (key) { const node = oops.gui.get(key); if (node == null) { - console.warn(`${key} 界面不存在或已关闭`); + console.warn('[OopsFramework]', `${key} 界面不存在或已关闭`); return; } @@ -209,7 +209,7 @@ export abstract class CCEntity extends ecs.Entity { if (view) this.remove(ctor as unknown as CompType); } catch (error) { - console.error(`移除界面组件失败: ${key}`, error); + console.error('[OopsFramework]', `移除界面组件失败: ${key}`, error); } }; oops.gui.remove(key); @@ -248,7 +248,7 @@ export abstract class CCEntity extends ecs.Entity { addBusiness>(cls: OopsFramework.BusinessCtor): T { if (this.businesss == null) this.businesss = new Map(); if (this.businesss.has(cls)) { - console.error(`${cls.name} 业务逻辑组件已存在`); + console.error('[OopsFramework]', `${cls.name} 业务逻辑组件已存在`); return null!; } const business = new cls(); diff --git a/assets/module/common/CCView.ts b/assets/module/common/CCView.ts index f000c0f..d3cdb33 100644 --- a/assets/module/common/CCView.ts +++ b/assets/module/common/CCView.ts @@ -5,13 +5,11 @@ * @LastEditTime: 2022-09-06 17:20:51 */ -import type { Component } from 'cc'; import type { ecs } from '../../libs/ecs/ECS'; import { ECSModel } from '../../libs/ecs/ECSModel'; import { VM } from '../../libs/model-view/ViewModel'; import { VMBase } from '../../libs/model-view/VMBase'; import type { CCEntity } from './CCEntity'; -import type { UICtor } from '../../types/Module'; import { GameComponent } from './GameComponent'; /** @@ -90,15 +88,13 @@ export abstract class CCView extends GameComponent implement * 注意:如果子类需要覆盖此方法,必须调用 super.onLoad() */ onLoad() { - // 提前返回,避免不必要的检查 if (!this.mvvm) return; - // onBind 语义为"绑定初始化",与 data 是否存在解耦,始终调用 this.onBind(); const data = this.data; if (data === undefined || data === null) { - console.warn(`[CCView] ${this.constructor.name}: mvvm=true 但 data 未定义,VM 绑定已跳过`); + console.warn('[OopsFramework]', `${this.constructor.name}: mvvm=true 但 data 未定义,VM 绑定已跳过`); return; } @@ -110,7 +106,6 @@ export abstract class CCView extends GameComponent implement * @private */ private initializeVM() { - // 使用正则替换所有的点,避免 VM.add 内部校验失败 const uuid = this.node.uuid.replace(/\./g, ''); this.tag = `_temp<${uuid}>`; VM.add(this.data!, this.tag); @@ -161,35 +156,30 @@ export abstract class CCView extends GameComponent implement private getVMComponents(): VMBase[] { const result: VMBase[] = []; const myUuid = this.uuid; - - // 深度优先遍历,遇到嵌套的 MVVM CCView 时剪枝 + const traverse = (node: any) => { - // 检查当前节点的组件 const vmComps = node.getComponents(VMBase); const vmLen = vmComps.length; for (let i = 0; i < vmLen; i++) { result.push(vmComps[i]); } - - // 检查是否有嵌套的 MVVM CCView(排除自己) + const ccViews = node.getComponents(CCView); const ccLen = ccViews.length; for (let i = 0; i < ccLen; i++) { const view = ccViews[i]; if (view.uuid !== myUuid && view.mvvm) { - // 遇到嵌套的 MVVM CCView,剪枝,不再遍历其子节点 return; } } - - // 递归遍历子节点 + const children = node.children; const childLen = children.length; for (let i = 0; i < childLen; i++) { traverse(children[i]); } }; - + traverse(this.node); return result; } @@ -199,24 +189,24 @@ export abstract class CCView extends GameComponent implement remove() { const ent = this.ent; if (!ent) { - console.error(`组件 ${this.name} 移除失败,实体不存在`); + console.error('[OopsFramework]', `组件 ${this.name} 移除失败,实体不存在`); return; } const tid = this.tid; if (tid < 0) { - console.error(`组件 ${this.name} 移除失败,组件未注册 (tid=${tid})`); + console.error('[OopsFramework]', `组件 ${this.name} 移除失败,组件未注册 (tid=${tid})`); return; } const cct = ECSModel.compCtors[tid]; if (!cct) { - console.error(`组件 ${this.name} 移除失败,组件构造函数不存在 (tid=${tid})`); + console.error('[OopsFramework]', `组件 ${this.name} 移除失败,组件构造函数不存在 (tid=${tid})`); return; } - ent.removeUi(cct as unknown as UICtor); - this.ent = null!; // 清空引用,避免内存泄漏 + ent.removeUi(cct as unknown as OopsFramework.UICtor); + this.ent = null!; } /** @@ -224,11 +214,9 @@ export abstract class CCView extends GameComponent implement * 注意:如果子类需要覆盖此方法,必须调用 super.onDestroy() */ protected onDestroy() { - // 只有启用了 MVVM 时才执行清理 if (this.mvvm) { this.onUnBind(); - // 解除全部引用 const tag = this.tag; if (tag) { VM.remove(tag); diff --git a/assets/module/common/GameCollision.ts b/assets/module/common/GameCollision.ts deleted file mode 100644 index 82a4452..0000000 --- a/assets/module/common/GameCollision.ts +++ /dev/null @@ -1,87 +0,0 @@ -/* - * @Author: dgflash - * @Date: 2022-03-29 17:08:08 - * @LastEditors: dgflash - * @LastEditTime: 2022-09-02 09:45:41 - */ -import type { ICollisionEvent, ITriggerEvent } from 'cc'; -import { _decorator, ccenum, Collider, Component } from 'cc'; - -const { ccclass, property } = _decorator; - -/** 碰撞物体类型 */ -export enum CollisionType { - /** 角色类 */ - Role, - /** 飞弹类体*/ - Ballistic, - /** 墙体类 */ - Wall -} -ccenum(CollisionType); - -const Event_TriggerEnter = 'onTriggerEnter'; -const Event_TriggerStay = 'onTriggerStay'; -const Event_TriggerExit = 'onTriggerExit'; -const Event_CollisionEnter = 'onCollisionEnter'; -const Event_CollisionStay = 'onCollisionStay'; -const Event_CollisionExit = 'onCollisionExit'; - -/** 碰撞器与触发器 */ -@ccclass('GameCollision') -export class GameCollision extends Component { - protected collider: Collider = null!; - - @property({ type: CollisionType, tooltip: '碰撞物体类型' }) - type: CollisionType = CollisionType.Ballistic; - - onLoad() { - this.collider = this.getComponent(Collider)!; - if (this.collider.isTrigger) { - this.collider.on(Event_TriggerEnter, this.onTrigger, this); - this.collider.on(Event_TriggerStay, this.onTrigger, this); - this.collider.on(Event_TriggerExit, this.onTrigger, this); - } - else { - this.collider.on(Event_CollisionEnter, this.onCollision, this); - this.collider.on(Event_CollisionStay, this.onCollision, this); - this.collider.on(Event_CollisionExit, this.onCollision, this); - } - } - - private onTrigger(event: ITriggerEvent) { - switch (event.type) { - case Event_TriggerEnter: - this.onTriggerEnter(event); - break; - case Event_TriggerStay: - this.onTriggerStay(event); - break; - case Event_TriggerExit: - this.onTriggerExit(event); - break; - } - } - - protected onTriggerEnter(event: ITriggerEvent) { } - protected onTriggerStay(event: ITriggerEvent) { } - protected onTriggerExit(event: ITriggerEvent) { } - - private onCollision(event: ICollisionEvent) { - switch (event.type) { - case Event_CollisionEnter: - this.onCollisionEnter(event); - break; - case Event_CollisionStay: - this.onCollisionStay(event); - break; - case Event_CollisionExit: - this.onCollisionExit(event); - break; - } - } - - protected onCollisionEnter(event: ICollisionEvent) { } - protected onCollisionStay(event: ICollisionEvent) { } - protected onCollisionExit(event: ICollisionEvent) { } -} diff --git a/assets/module/common/GameCollision.ts.meta b/assets/module/common/GameCollision.ts.meta deleted file mode 100644 index c334c05..0000000 --- a/assets/module/common/GameCollision.ts.meta +++ /dev/null @@ -1,9 +0,0 @@ -{ - "ver": "4.0.24", - "importer": "typescript", - "imported": true, - "uuid": "7fa3eab3-0e4a-4152-af0e-50daf4e55261", - "files": [], - "subMetas": {}, - "userData": {} -}