添中在多语言切换时,自动释放前一个语言使用的所有资源

This commit is contained in:
dgflash
2024-07-16 16:42:21 +08:00
parent e68b8a74ec
commit 2563837ce6
14 changed files with 151 additions and 134 deletions

View File

@@ -5,6 +5,7 @@
* @LastEditTime: 2023-08-21 15:19:56
*/
import { DEBUG } from "cc/env";
import { EffectSingleCase } from "../libs/animator-effect/EffectSingleCase";
import { ecs } from "../libs/ecs/ECS";
import { ECSRootSystem } from "../libs/ecs/ECSSystem";
import { LanguageManager } from "../libs/gui/language/Language";
@@ -33,12 +34,14 @@ export class oops {
static log = Logger;
/** 游戏配置 */
static config = new Config();
/** 本地存储 */
static storage: StorageManager;
/** 资源管理 */
static res: ResLoader;
/** 全局消息 */
static message: MessageManager = MessageManager.Instance;
static message: MessageManager;
/** 随机工具 */
static random = RandomManager.instance;
/** 本地存储 */
static storage: StorageManager = new StorageManager();
/** 游戏时间管理 */
static timer: TimerManager;
/** 游戏音乐管理 */
@@ -47,21 +50,21 @@ export class oops {
static gui: LayerManager;
/** 三维游戏世界管理 */
static game: GameManager;
/** 资源管理 */
static res = new ResLoader();
/** ----------可选模块---------- */
/** 多语言模块 */
static language: LanguageManager;
static language: LanguageManager = new LanguageManager();
/** HTTP */
static http: HttpRequest = new HttpRequest();
static http: HttpRequest = new HttpRequest(); // 使用流程文档可参考、简化与服务器对接、使用新版API体验可进入下面地址获取新版本替换network目录中的内容(https://store.cocos.com/app/detail/5877)
/** WebSocket */
static tcp: NetManager = new NetManager();
static tcp: NetManager = new NetManager(); // 使用流程文档可参考、简化与服务器对接、使用新版API体验可进入下面地址获取新版本替换network目录中的内容(https://store.cocos.com/app/detail/5877)
/** ECS */
static ecs: ECSRootSystem = new ecs.RootSystem();
/** MVVM */
static mvvm = VM;
/** 对象池 */
static pool = EffectSingleCase.instance;
}
// 引入oops全局变量以方便调试

View File

@@ -5,12 +5,14 @@
* @LastEditTime: 2023-08-28 10:02:57
*/
import { Component, Game, JsonAsset, Node, _decorator, director, game, screen, sys } from "cc";
import { LanguageManager } from "../libs/gui/language/Language";
import { GameConfig } from "../module/config/GameConfig";
import { GameQueryConfig } from "../module/config/GameQueryConfig";
import { oops, version } from "./Oops";
import { AudioManager } from "./common/audio/AudioManager";
import { EventMessage } from "./common/event/EventMessage";
import { message } from "./common/event/MessageManager";
import { resLoader } from "./common/loader/ResLoader";
import { StorageManager } from "./common/storage/StorageManager";
import { TimerManager } from "./common/timer/TimerManager";
import { GameManager } from "./game/GameManager";
import { GUI } from "./gui/GUI";
@@ -49,20 +51,45 @@ export class Root extends Component {
}
}
private loadConfig() {
private async loadConfig() {
// 创建持久根节点
this.persistRootNode = new Node("PersistRootNode");
director.addPersistRootNode(this.persistRootNode);
// 资源管理模块
oops.res = resLoader;
const config_name = "config";
oops.res.load(config_name, JsonAsset, () => {
var config = oops.res.get(config_name);
if (config == null) {
this.loadConfig();
return;
}
const config = await oops.res.loadAsync(config_name, JsonAsset);
if (config) {
// oops.config.btc = new BuildTimeConstants();
oops.config.query = new GameQueryConfig();
oops.config.game = new GameConfig(config);
// 本地存储模块
oops.storage = new StorageManager();
oops.storage.init(oops.config.game.localDataKey, oops.config.game.localDataIv); // 初始化本地存储加密
// 全局消息
oops.message = message;
// 创建音频模块
oops.audio = this.persistRootNode.addComponent(AudioManager);
oops.audio.load();
// 创建时间模块
oops.timer = this.persistRootNode.addComponent(TimerManager)!;
// 游戏场景管理
oops.game = new GameManager(this.game);
// 游戏界面管理
oops.gui = new LayerManager(this.gui);
// 网络模块
oops.http.server = oops.config.game.httpServer; // Http 服务器地址
oops.http.timeout = oops.config.game.httpTimeout; // Http 请求超时时间
oops.storage.init(oops.config.game.localDataKey, oops.config.game.localDataIv); // 初始化本地存储加密
game.frameRate = oops.config.game.frameRate; // 初始化每秒传输帧数
this.enabled = true;
@@ -70,7 +97,10 @@ export class Root extends Component {
this.run();
oops.res.release(config_name);
});
}
else {
this.loadConfig();
}
}
update(dt: number) {
@@ -93,20 +123,6 @@ export class Root extends Component {
}
protected init() {
// 创建持久根节点
this.persistRootNode = new Node("PersistRootNode");
director.addPersistRootNode(this.persistRootNode);
// 创建音频模块
oops.audio = this.persistRootNode.addComponent(AudioManager);
oops.audio.load();
// 创建时间模块
oops.timer = this.persistRootNode.addComponent(TimerManager)!;
oops.language = new LanguageManager();
oops.game = new GameManager(this.game);
oops.gui = new LayerManager(this.gui);
this.initGui();
this.initEcsSystem();
oops.ecs.init();

View File

@@ -7,9 +7,7 @@ class EventData {
public object: any;
}
/**
* 批量注册、移除全局事件对象
*/
/** 批量注册、移除全局事件对象 */
export class MessageEventData {
private events: Map<string, Array<EventData>> = new Map();
@@ -31,7 +29,7 @@ export class MessageEventData {
ed.object = object;
eds.push(ed);
MessageManager.Instance.on(event, listener, object);
message.on(event, listener, object);
}
/**
@@ -43,7 +41,7 @@ export class MessageEventData {
if (!eds) return;
for (let eb of eds) {
MessageManager.Instance.off(event, eb.listener, eb.object);
message.off(event, eb.listener, eb.object);
}
this.events.delete(event);
}
@@ -54,7 +52,7 @@ export class MessageEventData {
* @param args(any) 事件参数
*/
dispatchEvent(event: string, ...args: any) {
MessageManager.Instance.dispatchEvent(event, ...args);
message.dispatchEvent(event, ...args);
}
/** 清除所有的全局事件监听 */
@@ -106,8 +104,6 @@ export class RoleViewComp extends Component{
}
*/
export class MessageManager {
static readonly Instance: MessageManager = new MessageManager();
private events: Map<string, Array<EventData>> = new Map();
/**
@@ -204,4 +200,6 @@ export class MessageManager {
}
}
}
}
}
export const message = new MessageManager();

View File

@@ -1,4 +1,4 @@
import { Asset, AssetManager, Constructor, __private, assetManager, error, js, resources } from "cc";
import { Asset, AssetManager, Constructor, __private, assetManager, error, js, resources, warn } from "cc";
export type ProgressCallback = __private._cocos_asset_asset_manager_deprecated__LoadProgressCallback;
export type CompleteCallback<T = any> = any; // (error: Error | null, asset: T) => void; (error: Error | null, asset: T[], urls: string[]) => void;
@@ -130,7 +130,7 @@ oops.res.load("spine_path", sp.SkeletonData, (err: Error | null, sd: sp.Skeleton
return new Promise((resolve, reject) => {
this.load(bundleName, paths, type, (err: Error | null, asset: T) => {
if (err) {
error(err.message);
warn(err.message);
}
resolve(asset);
});
@@ -333,4 +333,6 @@ oops.res.loadDir("game", onProgressCallback, onCompleteCallback);
this.loadByBundleAndArgs(resources, args);
}
}
}
}
export const resLoader = new ResLoader();

View File

@@ -6,7 +6,7 @@
*/
import { JsonAsset } from "cc";
import { oops } from "../Oops";
import { resLoader } from "../common/loader/ResLoader";
/** 资源路径 */
var path: string = "config/game/";
@@ -35,7 +35,7 @@ export class JsonUtil {
callback(data.get(name));
else {
var url = path + name;
oops.res.load(url, JsonAsset, (err: Error | null, content: JsonAsset) => {
resLoader.load(url, JsonAsset, (err: Error | null, content: JsonAsset) => {
if (err) {
console.warn(err.message);
callback(null);
@@ -59,7 +59,7 @@ export class JsonUtil {
}
else {
var url = path + name;
oops.res.load(url, JsonAsset, (err: Error | null, content: JsonAsset) => {
resLoader.load(url, JsonAsset, (err: Error | null, content: JsonAsset) => {
if (err) {
console.warn(err.message);
resolve(null);
@@ -80,6 +80,6 @@ export class JsonUtil {
static release(name: string) {
var url = path + name;
data.delete(name);
oops.res.release(url);
resLoader.release(url);
}
}

View File

@@ -5,7 +5,7 @@
* @LastEditTime: 2023-01-19 14:52:12
*/
import { Animation, AnimationClip, EventTouch, instantiate, Node, Prefab, Size, UITransform, v3, Vec3 } from "cc";
import { oops } from "../Oops";
import { resLoader } from "../common/loader/ResLoader";
/** 显示对象工具 */
export class ViewUtil {
@@ -92,7 +92,7 @@ export class ViewUtil {
* @param path 资源路径
*/
static createPrefabNode(path: string): Node {
var p: Prefab = oops.res.get(path, Prefab)!;
var p: Prefab = resLoader.get(path, Prefab)!;
var n = instantiate(p);
return n;
}
@@ -103,7 +103,7 @@ export class ViewUtil {
*/
static createPrefabNodeAsync(path: string): Promise<Node> {
return new Promise(async (resolve, reject) => {
oops.res.load(path, Prefab, (err: Error | null, content: Prefab) => {
resLoader.load(path, Prefab, (err: Error | null, content: Prefab) => {
if (err) {
console.error(`名为【${path}】的资源加载失败`);
return;
@@ -132,7 +132,7 @@ export class ViewUtil {
anim = node.addComponent(Animation);
}
var clip = oops.res.get(path, AnimationClip) as AnimationClip;
var clip = resLoader.get(path, AnimationClip) as AnimationClip;
if (!clip) {
return;
}

View File

@@ -6,8 +6,8 @@
*/
import { Animation, Component, ParticleSystem, _decorator, sp } from 'cc';
import { oops } from '../../core/Oops';
import { EffectEvent } from './EffectEvent';
import { message } from '../../core/common/event/MessageManager';
const { ccclass, property } = _decorator;
@@ -63,6 +63,6 @@ export class EffectFinishedRelease extends Component {
}
private onRecovery() {
if (this.node.parent) oops.message.dispatchEvent(EffectEvent.Put, this.node);
if (this.node.parent) message.dispatchEvent(EffectEvent.Put, this.node);
}
}

View File

@@ -4,8 +4,9 @@
* @LastEditors: dgflash
* @LastEditTime: 2023-03-06 14:40:34
*/
import { Animation, Component, Node, NodePool, ParticleSystem, Prefab, Vec3, sp } from 'cc';
import { oops } from '../../core/Oops';
import { Animation, Component, Node, NodePool, ParticleSystem, Prefab, sp, Vec3 } from 'cc';
import { message } from '../../core/common/event/MessageManager';
import { resLoader } from '../../core/common/loader/ResLoader';
import { ViewUtil } from '../../core/utils/ViewUtil';
import { EffectEvent } from './EffectEvent';
import { EffectFinishedRelease } from './EffectFinishedRelease';
@@ -59,7 +60,7 @@ export class EffectSingleCase {
private res: Map<string, boolean> = new Map();
constructor() {
oops.message.on(EffectEvent.Put, this.onPut, this);
message.on(EffectEvent.Put, this.onPut, this);
}
private onPut(event: string, node: Node) {
@@ -72,25 +73,19 @@ export class EffectSingleCase {
* @param parent 父节点
* @param pos 位置
*/
loadAndShow(path: string, parent?: Node, params?: IEffectParams): Promise<Node> {
async loadAndShow(path: string, parent?: Node, params?: IEffectParams): Promise<Node> {
return new Promise(async (resolve, reject) => {
var np = this.effects.get(path);
if (np == undefined) {
// 记录显示对象资源
this.res.set(path, true);
oops.res.load(path, Prefab, (err: Error | null, prefab: Prefab) => {
if (err) {
console.error(`名为【${path}】的特效资源加载失败`);
return;
}
var node = this.show(path, parent, params);
resolve(node);
});
await resLoader.loadAsync(path, Prefab);
const node = this.show(path, parent, params);
resolve(node);
}
else {
var node = this.show(path, parent, params);
const node = this.show(path, parent, params);
resolve(node);
}
});
@@ -180,12 +175,12 @@ export class EffectSingleCase {
release(path?: string) {
if (path) {
this.clear(path);
oops.res.release(path);
resLoader.release(path);
}
else {
this.clear();
this.res.forEach((value: boolean, path: string) => {
oops.res.release(path);
resLoader.release(path);
});
}
}

View File

@@ -1,17 +1,10 @@
import { EventDispatcher } from "../../../core/common/event/EventDispatcher";
import { Logger } from "../../../core/common/log/Logger";
import { LanguageData } from "./LanguageData";
import { LanguagePack } from "./LanguagePack";
export enum LanguageEvent {
/** 语种变化事件 */
CHANGE = 'LanguageEvent.CHANGE',
/** 语种资源释放事件 */
RELEASE_RES = "LanguageEvent.RELEASE_RES"
}
export class LanguageManager extends EventDispatcher {
private _languages: Array<string> = ["zh", "en", "tr"]; // 支持的语言
/** 多语言管理器 */
export class LanguageManager {
private _languages: Array<string> = ["zh", "en", "tr"]; // 支持的语言
private _languagePack: LanguagePack = new LanguagePack(); // 语言包
private _defaultLanguage: string = "zh"; // 默认语言
@@ -38,13 +31,16 @@ export class LanguageManager extends EventDispatcher {
return this._languagePack;
}
/**
* 是否存在指定语言
* @param lang 语言名
* @returns 存在返回true,则否false
*/
isExist(lang: string): boolean {
return this.languages.indexOf(lang) > -1;
}
/**
* 获取下一个语种
*/
/** 获取下一个语种 */
getNextLang(): string {
let supportLangs = this.languages;
let index = supportLangs.indexOf(LanguageData.current);
@@ -53,8 +49,9 @@ export class LanguageManager extends EventDispatcher {
}
/**
* 改变语种,会自动下载对应的语种,下载完成回调
* @param language
* 改变语种,会自动下载对应的语种
* @param language 语言名
* @param callback 多语言资源数据加载完成回调
*/
setLanguage(language: string, callback: (success: boolean) => void) {
if (language == null || language == "") {
@@ -77,9 +74,10 @@ export class LanguageManager extends EventDispatcher {
this.loadLanguageAssets(language, (lang: string) => {
Logger.logConfig(`当前语言为【${language}`);
var oldLanguage = LanguageData.current;
LanguageData.current = language;
this._languagePack.updateLanguage(language);
this.dispatchEvent(LanguageEvent.CHANGE, lang);
this._languagePack.releaseLanguageAssets(oldLanguage);
callback(true);
});
}
@@ -111,6 +109,5 @@ export class LanguageManager extends EventDispatcher {
releaseLanguageAssets(lang: string) {
lang = lang.toLowerCase();
this._languagePack.releaseLanguageAssets(lang);
this.dispatchEvent(LanguageEvent.RELEASE_RES, lang);
}
}

View File

@@ -1,3 +1,5 @@
import { TTFFont } from "cc";
/*
* @Author: dgflash
* @Date: 2022-02-11 09:31:52
@@ -5,12 +7,21 @@
* @LastEditTime: 2023-08-22 16:37:40
*/
export class LanguageData {
/** JSON资源目录 */
static path_json: string = "language/json";
/** 纹理资源目录 */
static path_texture: string = "language/texture";
/** SPINE资源目录 */
static path_spine: string = "language/spine";
/** 当前语言 */
static current: string = "";
/** 语言JSON配置数据 */
static json: any = {}
/** 语言EXCEL中的配置数据 */
static excel: any = null!;
/** TTF字体 */
static font: TTFFont = null!;
/**
* 通过多语言关键字获取语言文本

View File

@@ -1,6 +1,5 @@
import { CCString, Component, Label, RichText, TTFFont, _decorator, warn } from "cc";
import { EDITOR } from "cc/env";
import { oops } from "../../../core/Oops";
import { LanguageData } from "./LanguageData";
const { ccclass, property, menu } = _decorator;
@@ -109,8 +108,7 @@ export class LanguageLabel extends Component {
updateContent() {
var label = this.getComponent(Label);
var richtext = this.getComponent(RichText);
var path = oops.language.pack.json + "/" + oops.language.current;
var font: TTFFont | null = oops.res.get(path, TTFFont);
var font: TTFFont | null = LanguageData.font
if (label) {
if (font && !label.useSystemFont) {

View File

@@ -4,9 +4,9 @@
* @LastEditors: dgflash
* @LastEditTime: 2023-08-22 16:34:28
*/
import { director, error, JsonAsset, TTFFont, warn } from "cc";
import { director, error, JsonAsset, TTFFont } from "cc";
import { resLoader } from "../../../core/common/loader/ResLoader";
import { Logger } from "../../../core/common/log/Logger";
import { oops } from "../../../core/Oops";
import { JsonUtil } from "../../../core/utils/JsonUtil";
import { LanguageData } from "./LanguageData";
import { LanguageLabel } from "./LanguageLabel";
@@ -14,13 +14,6 @@ import { LanguageSpine } from "./LanguageSpine";
import { LanguageSprite } from "./LanguageSprite";
export class LanguagePack {
/** JSON资源目录 */
json: string = "language/json";
/** 纹理资源目录 */
texture: string = "language/texture";
/** SPINE资源目录 */
spine: string = "language/spine";
/**
* 刷新语言文字
* @param lang
@@ -76,8 +69,8 @@ export class LanguagePack {
/** 纹理多语言资源 */
private loadTexture(lang: string) {
return new Promise((resolve, reject) => {
let path = `${this.texture}/${lang}`;
oops.res.loadDir(path, (err: any, assets: any) => {
const path = `${LanguageData.path_texture}/${lang}`;
resLoader.loadDir(path, (err: any, assets: any) => {
if (err) {
error(err);
resolve(null);
@@ -85,38 +78,37 @@ export class LanguagePack {
}
Logger.logConfig(path, "下载语言包 textures 资源");
resolve(null);
})
});
});
}
/** Json格式多语言资源 */
private loadJson(lang: string) {
return new Promise((resolve, reject) => {
let path = `${this.json}/${lang}`;
oops.res.load(path, JsonAsset, (err: Error | null, asste: JsonAsset) => {
if (err) {
error(err);
resolve(null);
return;
}
LanguageData.json = asste.json;
return new Promise(async (resolve, reject) => {
const path = `${LanguageData.path_json}/${lang}`;
const jsonAsset = await resLoader.loadAsync(path, JsonAsset);
if (jsonAsset) {
LanguageData.json = jsonAsset.json;
Logger.logConfig(path, "下载语言包 json 资源");
}
else {
resolve(null);
return;
}
oops.res.load(path, TTFFont, (err: Error | null) => {
if (err == null) Logger.logConfig(path, "下载语言包 ttf 资源");
resolve(null);
});
})
resLoader.load(path, TTFFont, (err: Error | null, font: TTFFont) => {
if (err == null) Logger.logConfig(path, "下载语言包 ttf 资源");
LanguageData.font = font;
resolve(null);
});
});
}
/** SPINE动画多语言资源 */
private loadSpine(lang: string) {
return new Promise((resolve, reject) => {
let path = `${this.spine}/${lang}`;
oops.res.loadDir(path, (err: any, assets: any) => {
return new Promise(async (resolve, reject) => {
const path = `${LanguageData.path_spine}/${lang}`;
resLoader.loadDir(path, (err: any, assets: any) => {
if (err) {
error(err);
resolve(null);
@@ -133,16 +125,21 @@ export class LanguagePack {
* @param lang
*/
releaseLanguageAssets(lang: string) {
let langTexture = `${this.texture}/${lang}`;
oops.res.releaseDir(langTexture);
Logger.logView(langTexture, "释放语言 texture 资源");
let langTexture = `${LanguageData.path_texture}/${lang}`;
resLoader.releaseDir(langTexture);
let langJson = `${this.json}/${lang}`;
oops.res.release(langJson);
Logger.logView(langJson, "释放语言文字资源");
let langJson = `${LanguageData.path_json}/${lang}`;
let json = resLoader.get(langJson, JsonAsset);
if (json) {
json.decRef();
}
let langSpine = `${this.spine}/${lang}`;
oops.res.release(langSpine);
Logger.logView(langSpine, "释放语言 spine 资源");
let font = resLoader.get(langJson, TTFFont);
if (font) {
font.decRef();
}
let langSpine = `${LanguageData.path_spine}/${lang}`;
resLoader.release(langSpine);
}
}

View File

@@ -6,7 +6,7 @@
*/
import { CCString, Component, _decorator, sp } from "cc";
import { EDITOR } from "cc/env";
import { oops } from "../../../core/Oops";
import { resLoader } from "../../../core/common/loader/ResLoader";
import { LanguageData } from "./LanguageData";
const { ccclass, property, menu } = _decorator;
@@ -47,7 +47,7 @@ export class LanguageSpine extends Component {
private updateSpine() {
// 获取语言标记
let path = `language/spine/${LanguageData.current}/${this.dataID}`;
let res: sp.SkeletonData | null = oops.res.get(path, sp.SkeletonData);
let res: sp.SkeletonData | null = resLoader.get(path, sp.SkeletonData);
if (res) {
let spine: sp.Skeleton = this.getComponent(sp.Skeleton)!;
spine.skeletonData = res;

View File

@@ -6,7 +6,7 @@
*/
import { CCString, Component, Size, Sprite, SpriteFrame, UITransform, _decorator } from "cc";
import { EDITOR } from "cc/env";
import { oops } from "../../../core/Oops";
import { resLoader } from "../../../core/common/loader/ResLoader";
import { LanguageData } from "./LanguageData";
const { ccclass, property, menu } = _decorator;
@@ -44,7 +44,7 @@ export class LanguageSprite extends Component {
private updateSprite() {
// 获取语言标记
let path = `language/texture/${LanguageData.current}/${this.dataID}/spriteFrame`;
let res: SpriteFrame | null = oops.res.get(path, SpriteFrame);
let res: SpriteFrame | null = resLoader.get(path, SpriteFrame);
if (res) {
let spcomp: Sprite = this.getComponent(Sprite)!;
spcomp.spriteFrame = res;