初次提交

This commit is contained in:
dgflash
2021-07-03 16:38:47 +08:00
parent 9fcf367e10
commit 7a405193fa
497 changed files with 55178 additions and 71 deletions

View File

@@ -0,0 +1,9 @@
{
"ver": "0.0.1",
"importer": "*",
"imported": true,
"uuid": "b60ccc66-5f47-4d5b-a7ba-8ec751c0c7e9",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,42 @@
import { AudioManager } from "./common/audio/AudioManager";
import { TimerManager } from "./common/manager/TimerManager";
import { GameManager } from "./game/GameManager";
import { LanguageManager } from "./gui/language/Language";
import { LayerManager } from "./gui/layer/LayerManager";
import { HttpRequest } from "./network/HttpRequest";
import { Root } from "./Root";
export class engine {
/** 多语言模块 */
public static i18n: LanguageManager;
/** 游戏时间管理 */
public static timer: TimerManager;
/** 游戏音乐管理 */
public static audio: AudioManager;
/** 二维界面管理 */
public static gui: LayerManager;
/** 三维游戏世界管理 */
public static game: GameManager;
/** HTTP */
public static http: HttpRequest;
/** 在Root.ts中初始化引导模块 */
private static init(root: Root) {
engine.i18n = new LanguageManager()
engine.timer = new TimerManager(root);
engine.audio = AudioManager.instance;
engine.gui = new LayerManager(root.gui!);
engine.game = root.addComponent(GameManager)!;
engine.http = new HttpRequest();
}
/** 修改引擎全局游戏速度 k-cocos.js */
public static get speed(): number {
// @ts-ignore
return cc.kGetSpeed();
}
public static set speed(value: number) {
// @ts-ignore
cc.kSetSpeed(value);
}
}

View File

@@ -0,0 +1,15 @@
{
"ver": "2.0.14",
"importer": "typescript",
"imported": true,
"uuid": "6eae6c9f-a531-46fe-b718-065c687941e2",
"files": [
".js",
".trans"
],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/Global.js"
}
}

View File

@@ -0,0 +1,16 @@
{
"ver": "4.0.1",
"importer": "typescript",
"imported": true,
"uuid": "0ff6ee3c-42c8-4101-bc2e-25a9252d27ff",
"files": [
".js",
".trans"
],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/Initialize.js",
"importerSettings": 3
}
}

View File

@@ -0,0 +1,66 @@
import { Component, director, game, Game, log, Node, view, _decorator } from "cc";
import { config } from "../game/config/Config";
import { EngineMessage } from "./common/event/EngineMessage";
import { Message } from "./common/event/MessageManager";
import { engine } from "./Engine";
import { GUI } from "./gui/GUI";
const { ccclass, property } = _decorator;
@ccclass('Root')
export class Root extends Component {
@property({
type: Node,
tooltip: "游戏层"
})
public game: Node | null = null;
@property({
type: Node,
tooltip: "界面层"
})
public gui: Node | null = null;
onLoad() {
//@ts-ignore
engine.init(this);
// 加载游戏配置
config.init(this.run);
}
/** 加载完引擎配置文件后执行 */
protected run() {
}
protected init() {
var c_gui = this.gui?.addComponent(GUI)!;
// 游戏显示事件
game.on(Game.EVENT_SHOW, () => {
log("Game.EVENT_SHOW");
engine.timer.load(); // 平台不需要在退出时精准计算时间,直接暂时游戏时间
engine.audio.resumeAll();
director.resume();
game.resume();
Message.dispatchEvent(EngineMessage.GAME_ENTER);
});
// 游戏隐藏事件
game.on(Game.EVENT_HIDE, () => {
log("Game.EVENT_HIDE");
engine.timer.save(); // 平台不需要在退出时精准计算时间,直接暂时游戏时间
engine.audio.pauseAll();
director.pause();
game.pause();
Message.dispatchEvent(EngineMessage.GAME_EXIT);
});
// 游戏尺寸修改事件
view.setResizeCallback(() => {
c_gui.resize();
Message.dispatchEvent(EngineMessage.GAME_RESIZE);
});
}
}

View File

@@ -0,0 +1,14 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "90b9c3a6-9660-429e-90a9-12982f76b42b",
"files": [],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/Root.js",
"importerSettings": 4,
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "c0dd7e2e-f8c5-4520-9fda-21942756751a",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "a643bc0f-eea9-46d6-8771-708dbdda9583",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,16 @@
{
"ver": "4.0.1",
"importer": "typescript",
"imported": true,
"uuid": "679627e3-65b0-484e-8fb0-f3120b0657b2",
"files": [
".js",
".trans"
],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/world/audio/Audio.js",
"importerSettings": 3
}
}

View File

@@ -0,0 +1,45 @@
// import { AudioSourceComponent, _decorator } from 'cc';
// const { ccclass, menu } = _decorator;
// /**
// * 3D声音通过发音对象与玩家对象之间的距离来动态调整音乐的音量
// * 注:
// * 组件自动加到音乐管理器中统一管理状态
// */
// @ccclass('AudioComponent')
// @menu('game/audio/AudioComponent')
// export class AudioComponent extends AudioSourceComponent {
// private speed: number = 10; // 数值越大越慢
// private startTime: number = 0; // 开始时间
// start() {
// this.startTime = performance.now();
// this.clip!.once('started', () => {
// });
// }
// /**
// * 设置音乐当前播放进度
// * @param progress 进度百分比
// */
// public setCurrentTime(progress: number) {
// this.currentTime = progress * this.duration;
// }
// /**
// * 正玄线性插值
// * @param start 音量初始值
// * @param end 音量结束值
// * @param t 时间变量
// */
// private sineLerp(start: number, end: number) {
// var t = (performance.now() - this.startTime) / (this.speed * 1000);
// return start + (end - start) * (Math.sin((t - 0.5) * Math.PI) + 1) * 0.5;
// }
// update(dt: number) {
// this.volume = this.sineLerp(1, 0.1);
// }
// }

View File

@@ -0,0 +1,14 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "98f16974-9ec5-430e-8494-6ff96bf8616e",
"files": [],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/game/audio/AudioComponent.js",
"importerSettings": 4,
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,33 @@
import { AudioClip, AudioSource, error, _decorator } from 'cc';
import { resLoader } from '../../utils/ResLoader';
const { ccclass, menu } = _decorator;
/**
* 注用playOneShot播放的音乐效果在播放期间暂时没办法即时关闭音乐
*/
/** 游戏音效 */
@ccclass('AudioEffect')
export class AudioEffect extends AudioSource {
private effects: Map<string, AudioClip> = new Map<string, AudioClip>();
public load(url: string, callback?: Function) {
resLoader.load(url, AudioClip, (err: Error | null, data: AudioClip) => {
if (err) {
error(err);
}
this.effects.set(url, data);
this.playOneShot(data, this.volume);
callback && callback();
});
}
release() {
for (let key in this.effects) {
resLoader.release(key);
}
this.effects.clear();
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "e52d2cac-6350-446e-9713-d82cf45b0b2c",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,159 @@
import { Component, game, Node, sys } from "cc";
import { AudioEffect } from "./AudioEffect";
import { AudioMusic } from "./AudioMusic";
const LOCAL_STORE_KEY = "game_audio";
export class AudioManager extends Component {
private static _instance: AudioManager;
public static get instance(): AudioManager {
if (this._instance == null) {
var node = new Node("UIAudioManager");
game.addPersistRootNode(node);
this._instance = node.addComponent(AudioManager);
this._instance.init();
var music = new Node("UIMusic");
music.parent = node;
this._instance.music = music.addComponent(AudioMusic);
var effect = new Node("UIEffect");
effect.parent = node;
this._instance.effect = effect.addComponent(AudioEffect);
}
return this._instance;
}
private local_data: any = {};
private music!: AudioMusic;
private effect!: AudioEffect;
private _volume_music: number = 1;
private _volume_effect: number = 1;
private _switch_music: boolean = true;
private _switch_effect: boolean = true;
private _uuid: string = "10000"; // 玩家唯一标识,一般为玩家数据库唯一编号
private _localStorageTag: string = ""; // 本地存储标签名
private init() {
let data = sys.localStorage.getItem(this._localStorageTag);
if (data) {
try {
this.local_data = JSON.parse(data);
this._volume_music = this.local_data.volume_music;
this._volume_effect = this.local_data.volume_effect;
this._switch_music = this.local_data.switch_music;
this._switch_effect = this.local_data.switch_effect;
}
catch (e) {
this.local_data = {};
this._volume_music = 1;
this._volume_effect = 1;
this._switch_music = true;
this._switch_effect = true;
}
this.music.volume = this._volume_music;
this.effect.volume = this._volume_effect;
}
}
/** 设置玩家唯一标识 */
public setUuid(value: string) {
this._uuid = value;
this._localStorageTag = `${LOCAL_STORE_KEY}_${this._uuid}`;
}
/**
* 播放背景音乐
* @param url 资源地址
* @param callback 音乐播放完成事件
*/
playMusic(url: string, callback: Function | null = null) {
if (this._switch_music) {
this.music.load(url);
this.music.onComplete = callback;
}
}
/**
* 播放音效
* @param url 资源地址
*/
playEffect(url: string) {
if (this._switch_effect) {
this.effect.load(url);
}
}
/** 背景音乐音量 */
public get musicVolume(): number {
return this._volume_music;
}
public set musicVolume(value: number) {
this._volume_music = value;
this.music.volume = value;
}
/** 音效音量 */
public get effectVolume(): number {
return this._volume_effect;
}
public set effectVolume(value: number) {
this._volume_effect = value;
this.effect.volume = value;
}
/** 音乐开关 */
public getSwitchMusic(): boolean {
return this._switch_music;
}
public setSwitchMusic(value: boolean) {
this._switch_music = value;
if (value == false)
this.music.stop();
}
/** 音效开关 */
public getSwitchEffect(): boolean {
return this._switch_effect;
}
public setSwitchEffect(value: boolean) {
this._switch_effect = value;
if (value == false)
this.effect.stop();
}
public resumeAll() {
if (this.music) {
this.music.play();
this.effect.play();
}
}
public pauseAll() {
if (this.music) {
this.music.pause();
this.effect.pause();
}
}
public stopAll() {
if (this.music) {
this.music.stop();
this.effect.stop();
}
}
public save() {
this.local_data.volume_music = this._volume_music;
this.local_data.volume_effect = this._volume_effect;
this.local_data.switch_music = this._switch_music;
this.local_data.switch_effect = this._switch_effect;
let data = JSON.stringify(this.local_data);
sys.localStorage.setItem(this._localStorageTag, data);
}
}

View File

@@ -0,0 +1,14 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "252f0cfe-bcf3-4bf1-8fe3-b0b626a26b70",
"files": [],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/game/audio/AudioManager.js",
"importerSettings": 4,
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,65 @@
import { AudioClip, AudioSource, error, _decorator } from 'cc';
import { resLoader } from '../../utils/ResLoader';
const { ccclass, menu } = _decorator;
/** 背景音乐 */
@ccclass('AudioMusic')
export class AudioMusic extends AudioSource {
public onComplete: Function | null = null;
private _progress: number = 0;
private _url: string | null = null;
private _isPlay: boolean = false;
/**
* 设置音乐当前播放进度
* @param progress 进度百分比(0~1)
*/
public get progress() {
this._progress = this.currentTime / this.duration;
return this._progress;
}
public set progress(value: number) {
this._progress = value;
this.currentTime = value * this.duration;
}
public load(url: string, callback?: Function) {
resLoader.load(url, AudioClip, (err: Error | null, data: AudioClip) => {
if (err) {
error(err);
}
if (this.playing) {
this.stop();
resLoader.release(this._url!);
}
this.clip = data;
this.currentTime = 0;
this.play();
callback && callback();
this._url = url;
});
}
update(dt: number) {
if (this.currentTime > 0) {
this._isPlay = true;
}
if (this._isPlay && this.playing == false) {
this._isPlay = false;
this.onComplete && this.onComplete();
}
}
release() {
if (this._url) {
resLoader.release(this._url);
this._url = null;
}
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "5c1f392a-19eb-4188-8bf8-f3f0afb9a42f",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "cc263b2f-8b27-4e4e-bad4-3860c593e0b3",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,82 @@
import { Component, _decorator } from "cc";
import { EventDispatcher } from "../event/EventDispatcher";
const { ccclass } = _decorator;
@ccclass("GameComponent")
export class GameComponent extends Component {
private _eventDispatcher: EventDispatcher | null = null;
public get eventDispatcher(): EventDispatcher {
if (!this._eventDispatcher) {
this._eventDispatcher = new EventDispatcher();
}
return this._eventDispatcher;
}
// 事件是否绑定node的active
private _isBindMessageActive: boolean = false;
/** 绑定node active属性即只有active为true才会响应事件 */
public bindMessageActive() {
this._isBindMessageActive = true;
}
/** 解绑node active属性无论node是否可见都会响应事件 */
public unbindMessageActive() {
this._isBindMessageActive = false;
}
/**
* 注册全局事件
* @param event(string) 事件名
* @param listener(function) 处理事件的侦听器函数
* @param thisObj(object) 侦听函数绑定的this对象
*/
public on(event: string, listener: Function, thisObj: any) {
this.eventDispatcher.on(event, (event, args) => {
if (!this.isValid) {
if (this._eventDispatcher) {
this._eventDispatcher.destroy();
this._eventDispatcher = null;
}
return;
}
if (this._isBindMessageActive) {
if (this.node.active) {
listener.call(thisObj, event, args);
}
}
else {
listener.call(thisObj, event, args);
}
}, thisObj);
}
/**
* 移除全局事件
* @param event(string) 事件名
*/
public off(event: string) {
if (this._eventDispatcher) {
this._eventDispatcher.off(event);
}
}
/**
* 触发全局事件
* @param event(string) 事件名
* @param arg(Array) 事件参数
*/
public dispatchEvent(event: string, arg = null) {
this.eventDispatcher.dispatchEvent(event, arg);
}
onDestroy() {
if (this._eventDispatcher) {
this._eventDispatcher.destroy();
this._eventDispatcher = null;
}
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "ac536f92-9564-4e5d-bcf0-cc66fb91e8aa",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "fd6e96ff-96ce-44f0-8eda-24aaf8c2e936",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,99 @@
import { Component, gfx, macro, Material, MeshRenderer, utils, Vec3, _decorator } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('DrawSectorMesh')
/**
* 绘制扇形网格
*/
export class DrawSectorMesh extends Component {
@property({ type: Material })
public mat: Material | null = null;
@property({
tooltip: "外圈半径"
})
public radius: number = 5;
@property({
tooltip: "内圈半径"
})
public innerRadius: number = 1;
@property({
tooltip: "扇形角度"
})
public angledegree: number = 60;
start() {
this.createMesh()
}
createMesh() {
const model = this.addComponent(MeshRenderer)!;
const segments: number = Math.floor(this.angledegree / 4) + 1; // 三角形个数(平滑度)
var positions: number[] = []; // 顶点位置数据
// 组装顶点数据
var vertices_count: number = segments * 2 + 2; // vertices(顶点)的个数与triangles索引三角形顶点数匹配
var vertices: Array<Vec3> = new Array<Vec3>(vertices_count);
var angleRad: number = this.angledegree * macro.RAD; // 角度转弧度
var angleCur: number = angleRad;
var angledelta: number = angleRad / segments; // 每个三角形的弧度
for (var i = 0; i < vertices_count; i += 2) { // 扇形每二个三角形之间共用2个顶点所有生成时每次循环生成二个顶点
var cosA: number = Math.cos(angleCur);
var sinA: number = Math.sin(angleCur);
vertices[i] = new Vec3(this.radius * cosA, 0, this.radius * sinA); // 已知扇形外圈半径为斜边求x、z值得到第一个顶点位置外圈半径顶点
vertices[i + 1] = new Vec3(this.innerRadius * cosA, 0, this.innerRadius * sinA); // 已知扇形内圈半径为斜边求x、z值得到第二个顶点位置内圈半径顶点
angleCur -= angledelta;
positions.push(vertices[i].x);
positions.push(vertices[i].y);
positions.push(vertices[i].z);
positions.push(vertices[i + 1].x);
positions.push(vertices[i + 1].y);
positions.push(vertices[i + 1].z);
}
// 组装三角形数据
var indice_count: number = segments * 6; // 扇形外圈与扇形内圈会生成一个四边形即二个三角形6个顶点索引
var indices: Array<number> = new Array<number>(indice_count);
for (var i = 0, vi = 0; i < indice_count; i += 6, vi += 2) { // i为三角形顶点索引号vi为顶点位置索引
indices[i] = vi;
indices[i + 1] = vi + 3;
indices[i + 2] = vi + 1;
indices[i + 3] = vi + 2;
indices[i + 4] = vi + 3;
indices[i + 5] = vi;
}
// 组装UV数据
var uvs: number[] = [];
for (var i = 0; i < vertices_count; i++) {
var u = vertices[i].x / this.radius / 2 + 0.5
var v = vertices[i].z / this.radius / 2 + 0.5
uvs.push(u, v);
}
const primitiveMode = gfx.PrimitiveMode.TRIANGLE_FAN;
const attributes: any[] = [{
name: gfx.AttributeName.ATTR_NORMAL,
format: gfx.Format.RGB32F,
}];
var IGeometry = {
positions: positions,
indices: indices,
uvs: uvs,
primitiveMode: primitiveMode, // 默认值效果一样,需要研究作用
attributes: attributes // 默认值效果一样,需要研究作用
}
const mesh = utils.createMesh(IGeometry);
model.mesh = mesh;
model.material = this.mat;
}
}

View File

@@ -0,0 +1,11 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "16f74800-b991-41f0-9345-1e8ef3d4c7b1",
"files": [],
"subMetas": {},
"userData": {
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "1e590879-0446-4a14-b8c2-0929fe3b3b19",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,16 @@
{
"ver": "4.0.1",
"importer": "typescript",
"imported": true,
"uuid": "5d11cc2e-27a7-4103-b91b-03989400c84e",
"files": [
".js",
".trans"
],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/event/AnimationEvent.js",
"importerSettings": 3
}
}

View File

@@ -0,0 +1,13 @@
/** ---------- 全局消息事件 ---------- */
export enum EngineMessage {
/** 中途退出游戏后,再进入游戏 */
GAME_ENTER = "EngineMessage.GAME_ENTER",
/** 中途退出游戏 */
GAME_EXIT = "EngineMessage.GAME_EXIT",
/** 游戏尺寸修改事件 */
GAME_RESIZE = "EngineMessage.GAME_RESIZE",
/** engin开始事件 */
ENGINE_START = "EngineMessage.ENGINE_START",
/** 玩家登陆成功 */
LOGIN_SUCCESS = 'EngineMessage.LOGIN_SUCCESS',
}

View File

@@ -0,0 +1,14 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "7f50140e-d51c-4c81-aefb-4f89562e25ba",
"files": [],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/event/EngineMessage.js",
"importerSettings": 4,
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,54 @@
import { MessageEventData } from "./MessageManager";
/*
* 事件对象基类,继承该类将拥有发送和接送事件的能力
*/
export class EventDispatcher {
protected _msg: MessageEventData | null = null;
/**
* 注册全局事件
* @param event(string) 事件名
* @param listener(function) 处理事件的侦听器函数
* @param thisObj(object) 侦听函数绑定的this对象
*/
public on(event: string, listener: (event: string, args: any) => void, thisObj: any) {
if (this._msg == null) {
this._msg = new MessageEventData();
}
this._msg.on(event, listener, thisObj);
}
/**
* 移除全局事件
* @param event(string) 事件名
* @param listener(function) 处理事件的侦听器函数
* @param thisObj(object) 侦听函数绑定的this对象
*/
public off(event: string) {
if (this._msg) {
this._msg.off(event);
}
}
/**
* 触发全局事件
* @param event(string) 事件名
* @param arg(Array) 事件参数
*/
public dispatchEvent(event: string, arg: any = null) {
if (this._msg == null) {
this._msg = new MessageEventData();
}
this._msg.dispatchEvent(event, arg);
}
/**
* 销毁事件对象
*/
public destroy() {
if (this._msg) {
this._msg.removes();
}
this._msg = null;
}
}

View File

@@ -0,0 +1,14 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "e718e259-c64b-49fb-b01f-9b2a2f2b0e6c",
"files": [],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/event/EventDispatcher.js",
"importerSettings": 4,
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,154 @@
import { log, warn } from "cc";
class EventData {
public event!: string;
public listener!: (event: string, args: any) => void;
public obj: any;
}
/**
* 批量注册、移除全局事件对象
*/
export class MessageEventData {
private events: any = {};
on(event: string, listener: (event: string, args: any) => void, thisObj: object) {
let list: Array<EventData> = this.events[event];
if (list == null) {
list = [];
this.events[event] = list;
}
let data: EventData = new EventData();
data.event = event;
data.listener = listener;
data.obj = thisObj;
list.push(data);
Message.on(event, listener, thisObj);
}
off(event: string) {
let ebs: Array<EventData> = this.events[event];
if (!ebs) {
return;
}
for (let eb of ebs) {
Message.off(event, eb.listener, eb.obj);
}
delete this.events[event];
}
dispatchEvent(event: string, arg: any = null) {
Message.dispatchEvent(event, arg);
}
removes() {
for (let event in this.events) {
this.off(event);
}
}
}
class MessageManager {
public static readonly Instance: MessageManager = new MessageManager();
private events: any = {};
/**
* 注册全局事件
* @param event(string) 事件名
* @param listener(function) 处理事件的侦听器函数
* @param thisObj(object) 侦听函数绑定的this对象
*/
on(event: string, listener: (event: string, args: any) => void, thisObj: object) {
if (!event || !listener) {
warn(`注册【${event}】事件的侦听器函数为空`);
return;
}
let list: Array<EventData> = this.events[event];
if (list == null) {
list = [];
this.events[event] = list;
}
let length = list.length;
for (let i = 0; i < length; i++) {
let bin = list[i];
if (bin.listener == listener && bin.obj == thisObj) {
warn(`名为【${event}】的事件重复注册侦听器`);
}
}
let data: EventData = new EventData();
data.event = event;
data.listener = listener;
data.obj = thisObj;
list.push(data);
}
/**
* 监听一次事件,事件响应后,该监听自动移除
* @param event
* @param listener
* @param thisObj
*/
once(event: string, listener: (event: string, args: any) => void, thisObj: object) {
let _listener: any = ($event: string, $args: any) => {
this.off(event, _listener, thisObj);
_listener = null;
listener.call(thisObj, $event, $args);
}
this.on(event, _listener, thisObj);
}
/**
* 移除全局事件
* @param event(string) 事件名
* @param listener(function) 处理事件的侦听器函数
* @param thisObj(object) 侦听函数绑定的this对象
*/
off(event: string, listener: Function, thisObj: object) {
let list: Array<EventData> = this.events[event];
if (!list) {
log(`名为【${event}】的事件不存在`);
return;
}
let length = list.length;
for (let i = 0; i < length; i++) {
let bin: EventData = list[i];
if (bin.listener == listener && bin.obj == thisObj) {
list.splice(i, 1);
break;
}
}
if (list.length == 0) {
delete this.events[event];
}
}
/**
* 触发全局事件
* @param event(string) 事件名
* @param arg(any) 事件参数
*/
dispatchEvent(event: string, arg: any = null) {
let list: Array<EventData> = this.events[event];
if (list != null) {
let temp: Array<EventData> = list.concat();
let length = temp.length;
for (let i = 0; i < length; i++) {
let eventBin = temp[i];
eventBin.listener.call(eventBin.obj, event, arg);
}
}
}
}
export const Message = MessageManager.Instance;

View File

@@ -0,0 +1,14 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "6ace47f5-502c-428e-a7b2-bdfbc4260c8d",
"files": [],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/event/MessageManager.js",
"importerSettings": 4,
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "257c907c-f9fd-452e-9406-7ecdb360cd45",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,114 @@
/** 引擎 utils.ts 中有一些基础数学方法 */
/** 随机管理 */
export class RandomManager {
// constructor() {
// this.setSeed(1);
// for (let index = 0; index < 10; index++) {
// console.log(this.getRandomInt(0, 100));
// }
// var a = this.getRandomByMinMaxList(50, 100, 5)
// console.log("随机的数字", a);
// var b = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// var r = this.getRandomByObjectList(b, 5);
// console.log("原始的对象", b);
// console.log("随机的对象", r);
// var c = this.getRandomBySumList(5, -100,);
// console.log("定和随机分配", c);
// }
private seedrandom!: any;
private getRandom(): number {
if (this.seedrandom)
return this.seedrandom.quick();
return Math.random();
}
/** 设置随机种子 */
setSeed(seed: number) {
//@ts-ignore
this.seedrandom = new Math.seedrandom(seed);
}
/**
* 生成指定范围的随机整数
* @param min 最小值
* @param max 最大值
* @param type 类型
*/
getRandomInt(min: number, max: number, type: number = 2): number {
min = Math.ceil(min);
max = Math.floor(max);
switch (type) {
case 1: // [min,max) 得到一个两数之间的随机整数,这个值不小于min如果min不是整数的话得到一个向上取整的 min并且小于但不等于max
return Math.floor(this.getRandom() * (max - min)) + min;
case 2: // [min,max] 得到一个两数之间的随机整数,包括两个数在内,这个值比min大如果min不是整数那就不小于比min大的整数但小于但不等于max
return Math.floor(this.getRandom() * (max - min + 1)) + min;
case 3: // (min,max) 得到一个两数之间的随机整数
return Math.floor(this.getRandom() * (max - min - 1)) + min + 1;
}
return 0;
}
/**
* 根据最大值,最小值范围 生成随机数数组
* @param min 最小值
* @param max 最大值
* @param n 随机个数
* @param type 类型
* @returns
*/
getRandomByMinMaxList(min: number, max: number, n: number, type: number = 2): Array<number> {
var result: Array<number> = [];
for (let i = 0; i < n; i++) {
result.push(this.getRandomInt(min, max))
}
return result;
}
/**
* 获取数组中随机对象
* @param objects 对象数组
* @param n 随机个数
* @returns
*/
getRandomByObjectList<T>(objects: Array<T>, n: number): Array<T> {
var temp: Array<T> = objects.slice();
var result: Array<T> = [];
for (let i = 0; i < n; i++) {
let index = this.getRandomInt(0, n, 1);
result.push(temp.splice(index, 1)[0]);
}
return result;
}
/**
* 定和随机分配
* @param n 随机数量
* @param sum 随机元素合
* @returns
*/
getRandomBySumList(n: number, sum: number) {
var residue = sum;
var value = 0;
var result: Array<number> = [];
for (let i = 0; i < n; i++) {
value = this.getRandomInt(0, residue, 3);
if (i == n - 1) {
value = residue;
}
else {
residue -= value;
}
result.push(value);
}
return result;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "b843ea76-f1d0-4ed5-b9e7-98b0509b6be8",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,236 @@
import { Component } from "cc";
import { EventDispatcher } from "../event/EventDispatcher";
const guid = function () {
let guid: string = "";
for (let i = 1; i <= 32; i++) {
let n = Math.floor(Math.random() * 16.0).toString(16);
guid += n;
if ((i == 8) || (i == 12) || (i == 16) || (i == 20))
guid += "-";
}
return guid;
}
export class TimerManager extends EventDispatcher {
private static times: any = {};
private schedules: any = {};
private _scheduleCount: number = 1;
private initTime: number = (new Date()).getTime(); // 当前游戏进入的时间毫秒值
private component: Component;
// 服务器时间与本地时间间隔
private _$serverTimeElasped: number = 0;
constructor(component: Component) {
super();
this.component = component;
this.schedule(this.onUpdate, 1);
}
/**
* 设置服务器时间与本地时间间隔
* @param val
*/
public serverTimeElasped(val?: number): number {
if (val) {
this._$serverTimeElasped = val;
}
return this._$serverTimeElasped;
}
/**
* 格式化日期显示 format= "yyyy-MM-dd hh:mm:ss";
* @param format
* @param date
*/
public format(format: string, date: Date): string {
let o: any = {
"M+": date.getMonth() + 1, // month
"d+": date.getDate(), // day
"h+": date.getHours(), // hour
"m+": date.getMinutes(), // minute
"s+": date.getSeconds(), // second
"q+": Math.floor((date.getMonth() + 3) / 3), // quarter
"S": date.getMilliseconds() // millisecond
}
if (/(y+)/.test(format)) {
format = format.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
}
for (let k in o) {
if (new RegExp("(" + k + ")").test(format)) {
format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length));
}
}
return format;
}
/** 获取游戏开始到现在逝去的时间 */
public getTime(): number {
return this.getLocalTime() - this.initTime;
}
/** 获取本地时间刻度 */
public getLocalTime(): number {
return Date.now();
}
public schedule(callback: Function, interval: number): string {
let UUID = `schedule_${this._scheduleCount++}`
this.schedules[UUID] = callback;
this.component.schedule(callback, interval);
return UUID;
}
public scheduleOnce(callback: Function, delay: number = 0): string {
let UUID = `scheduleOnce_${this._scheduleCount++}`;
this.schedules[UUID] = callback;
this.component.scheduleOnce(() => {
let cb = this.schedules[UUID];
if (cb) {
cb();
}
this.unschedule(UUID);
}, Math.max(delay, 0));
return UUID;
}
public unschedule(uuid: string) {
let cb = this.schedules[uuid];
if (cb) {
this.component.unschedule(cb);
delete this.schedules[uuid];
}
}
public unscheduleAllCallbacks() {
for (let k in this.schedules) {
this.component.unschedule(this.schedules[k]);
}
this.schedules = {};
}
onUpdate(dt: number) {
// 后台管理倒计时完成事件
for (let key in TimerManager.times) {
let data = TimerManager.times[key];
if (data.object[data.field] > 0) {
data.object[data.field]--;
if (data.object[data.field] == 0) {
if (data.onComplete) data.onComplete.call(data.object); // 触发倒计时完成事件
if (data.event) this.dispatchEvent(data.event);
}
else { // 修改是否完成状态
if (data.onSecond) {
data.onSecond.call(data.object); // 触发每秒回调事件
}
}
}
}
}
/** 游戏最小划时记录时间数据 */
public save() {
for (let key in TimerManager.times) {
TimerManager.times[key].startTime = this.getTime();
}
}
/** 游戏最大化时回复时间数据 */
public load() {
for (let key in TimerManager.times) {
let interval = Math.floor((this.getTime() - (TimerManager.times[key].startTime || this.getTime())) / 1000);
let data = TimerManager.times[key];
data.object[data.field] = data.object[data.field] - interval;
if (data.object[data.field] < 0) {
data.object[data.field] = 0;
}
TimerManager.times[key].startTime = null;
}
}
/** 注册指定对象的倒计时属性更新 */
public registerObject(object: any, field: string, onSecond: Function, onComplete: Function) {
let data: any = {};
data.id = guid();
data.object = object; // 管理对象
data.field = field; // 时间字段
data.onSecond = onSecond; // 每秒事件
data.onComplete = onComplete; // 倒计时完成事件
TimerManager.times[data.id] = data;
return data.id;
}
/** 注消指定对象的倒计时属性更新 */
public unRegisterObject(id: string) {
if (TimerManager.times[id])
delete TimerManager.times[id];
}
/**
* 注册事件需要管理的实时变化的时间对象
* @param event(String) 时间为零时触发的事件
* @param object(object) 需要管理的数据结构对象
* @param field(Array) 需要管理的字段
*/
public register(event: string, object: any, field: Array<string>) {
let data: any = {};
data.id = event;
data.event = event; // 倒计时完成事件
data.object = object; // 管理对象
data.field = field; // 时间字段
TimerManager.times[data.id] = data;
}
/** 注销定时器 */
public unRegister(event: string) {
if (TimerManager.times[event])
delete TimerManager.times[event];
}
}
/** 定时跳动组件 */
export class Timer {
public callback: Function | null = null;
private _elapsedTime: number = 0;
public get elapsedTime(): number {
return this._elapsedTime;
}
private _step: number = 0;
/** 触发间隔时间(秒) */
get step(): number {
return this._step;
}
set step(step: number) {
this._step = step; // 每次修改时间
this._elapsedTime = 0; // 逝去时间
}
public get progress(): number {
return this._elapsedTime / this._step;
}
constructor(step: number = 0) {
this.step = step;
}
public update(dt: number) {
this._elapsedTime += dt;
if (this._elapsedTime >= this._step) {
this._elapsedTime -= this._step;
this.callback?.call(this);
return true;
}
return false;
}
public reset() {
this._elapsedTime = 0;
}
}

View File

@@ -0,0 +1,14 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "246133c2-f8da-42af-9a59-dda24e690801",
"files": [],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/utils/TimerManager.js",
"importerSettings": 4,
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "40b31022-d422-493f-a40d-2621c96ec9f2",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,47 @@
/**
*
* @file EncryptUtil.ts
* @author dream
* @description 一些加密解密方法
*
*/
export module EncryptUtil {
/**
* AES 加密
* @param msg
* @param key
* @param iv
* @returns
*/
export function aesEncrypt(msg: string, key: string, iv: string): string {
let encrypt = CryptoJS.AES.encrypt(msg, utf8Parse(key), {
iv: utf8Parse(iv),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return encrypt.toString();
}
/**
* AES 解密
* @param str
* @param key
* @param iv
* @returns
*/
export function aesDecrypt(str: string, key: string, iv: string): string {
let decrypt = CryptoJS.AES.decrypt(str, utf8Parse(key), {
iv: utf8Parse(iv),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return CryptoJS.enc.Utf8.stringify(decrypt);
}
function utf8Parse(utf8Str: string): string {
return CryptoJS.enc.Utf8.parse(utf8Str);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "a74a14e9-aeda-4bc7-9e16-f2fee1a4b927",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,202 @@
export function md5(md5str: string) {
var createMD5String = function (string: string) {
var x = Array()
var k, AA, BB, CC, DD, a, b, c, d
var S11 = 7,
S12 = 12,
S13 = 17,
S14 = 22
var S21 = 5,
S22 = 9,
S23 = 14,
S24 = 20
var S31 = 4,
S32 = 11,
S33 = 16,
S34 = 23
var S41 = 6,
S42 = 10,
S43 = 15,
S44 = 21
string = uTF8Encode(string)
x = convertToWordArray(string)
a = 0x67452301
b = 0xEFCDAB89
c = 0x98BADCFE
d = 0x10325476
for (k = 0; k < x.length; k += 16) {
AA = a
BB = b
CC = c
DD = d
a = FF(a, b, c, d, x[k + 0], S11, 0xD76AA478)
d = FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756)
c = FF(c, d, a, b, x[k + 2], S13, 0x242070DB)
b = FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE)
a = FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF)
d = FF(d, a, b, c, x[k + 5], S12, 0x4787C62A)
c = FF(c, d, a, b, x[k + 6], S13, 0xA8304613)
b = FF(b, c, d, a, x[k + 7], S14, 0xFD469501)
a = FF(a, b, c, d, x[k + 8], S11, 0x698098D8)
d = FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF)
c = FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1)
b = FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE)
a = FF(a, b, c, d, x[k + 12], S11, 0x6B901122)
d = FF(d, a, b, c, x[k + 13], S12, 0xFD987193)
c = FF(c, d, a, b, x[k + 14], S13, 0xA679438E)
b = FF(b, c, d, a, x[k + 15], S14, 0x49B40821)
a = GG(a, b, c, d, x[k + 1], S21, 0xF61E2562)
d = GG(d, a, b, c, x[k + 6], S22, 0xC040B340)
c = GG(c, d, a, b, x[k + 11], S23, 0x265E5A51)
b = GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA)
a = GG(a, b, c, d, x[k + 5], S21, 0xD62F105D)
d = GG(d, a, b, c, x[k + 10], S22, 0x2441453)
c = GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681)
b = GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8)
a = GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6)
d = GG(d, a, b, c, x[k + 14], S22, 0xC33707D6)
c = GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87)
b = GG(b, c, d, a, x[k + 8], S24, 0x455A14ED)
a = GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905)
d = GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8)
c = GG(c, d, a, b, x[k + 7], S23, 0x676F02D9)
b = GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A)
a = HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942)
d = HH(d, a, b, c, x[k + 8], S32, 0x8771F681)
c = HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122)
b = HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C)
a = HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44)
d = HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9)
c = HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60)
b = HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70)
a = HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6)
d = HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA)
c = HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085)
b = HH(b, c, d, a, x[k + 6], S34, 0x4881D05)
a = HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039)
d = HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5)
c = HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8)
b = HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665)
a = II(a, b, c, d, x[k + 0], S41, 0xF4292244)
d = II(d, a, b, c, x[k + 7], S42, 0x432AFF97)
c = II(c, d, a, b, x[k + 14], S43, 0xAB9423A7)
b = II(b, c, d, a, x[k + 5], S44, 0xFC93A039)
a = II(a, b, c, d, x[k + 12], S41, 0x655B59C3)
d = II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92)
c = II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D)
b = II(b, c, d, a, x[k + 1], S44, 0x85845DD1)
a = II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F)
d = II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0)
c = II(c, d, a, b, x[k + 6], S43, 0xA3014314)
b = II(b, c, d, a, x[k + 13], S44, 0x4E0811A1)
a = II(a, b, c, d, x[k + 4], S41, 0xF7537E82)
d = II(d, a, b, c, x[k + 11], S42, 0xBD3AF235)
c = II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB)
b = II(b, c, d, a, x[k + 9], S44, 0xEB86D391)
a = addUnsigned(a, AA)
b = addUnsigned(b, BB)
c = addUnsigned(c, CC)
d = addUnsigned(d, DD)
}
var tempValue = wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d)
return tempValue.toLowerCase()
}
var rotateLeft = function (lValue: number, iShiftBits: number) {
return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits))
}
var addUnsigned = function (lX: number, lY: number) {
var lX4, lY4, lX8, lY8, lResult
lX8 = (lX & 0x80000000)
lY8 = (lY & 0x80000000)
lX4 = (lX & 0x40000000)
lY4 = (lY & 0x40000000)
lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF)
if (lX4 & lY4) return (lResult ^ 0x80000000 ^ lX8 ^ lY8)
if (lX4 | lY4) {
if (lResult & 0x40000000) return (lResult ^ 0xC0000000 ^ lX8 ^ lY8)
else return (lResult ^ 0x40000000 ^ lX8 ^ lY8)
} else {
return (lResult ^ lX8 ^ lY8)
}
}
var F = function (x: number, y: number, z: number) {
return (x & y) | ((~x) & z)
}
var G = function (x: number, y: number, z: number) {
return (x & z) | (y & (~z))
}
var H = function (x: number, y: number, z: number) {
return (x ^ y ^ z)
}
var I = function (x: number, y: number, z: number) {
return (y ^ (x | (~z)))
}
var FF = function (a: number, b: number, c: number, d: number, x: number, s: number, ac: number) {
a = addUnsigned(a, addUnsigned(addUnsigned(F(b, c, d), x), ac))
return addUnsigned(rotateLeft(a, s), b)
}
var GG = function (a: number, b: number, c: number, d: number, x: number, s: number, ac: number) {
a = addUnsigned(a, addUnsigned(addUnsigned(G(b, c, d), x), ac))
return addUnsigned(rotateLeft(a, s), b)
}
var HH = function (a: number, b: number, c: number, d: number, x: number, s: number, ac: number) {
a = addUnsigned(a, addUnsigned(addUnsigned(H(b, c, d), x), ac))
return addUnsigned(rotateLeft(a, s), b)
}
var II = function (a: number, b: number, c: number, d: number, x: number, s: number, ac: number) {
a = addUnsigned(a, addUnsigned(addUnsigned(I(b, c, d), x), ac))
return addUnsigned(rotateLeft(a, s), b)
}
var convertToWordArray = function (string: string) {
var lWordCount
var lMessageLength = string.length
var lNumberOfWordsTempOne = lMessageLength + 8
var lNumberOfWordsTempTwo = (lNumberOfWordsTempOne - (lNumberOfWordsTempOne % 64)) / 64
var lNumberOfWords = (lNumberOfWordsTempTwo + 1) * 16
var lWordArray = Array(lNumberOfWords - 1)
var lBytePosition = 0
var lByteCount = 0
while (lByteCount < lMessageLength) {
lWordCount = (lByteCount - (lByteCount % 4)) / 4
lBytePosition = (lByteCount % 4) * 8
lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition))
lByteCount++
}
lWordCount = (lByteCount - (lByteCount % 4)) / 4
lBytePosition = (lByteCount % 4) * 8
lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition)
lWordArray[lNumberOfWords - 2] = lMessageLength << 3
lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29
return lWordArray
}
var wordToHex = function (lValue: number) {
var WordToHexValue = '',
WordToHexValueTemp = '',
lByte, lCount
for (lCount = 0; lCount <= 3; lCount++) {
lByte = (lValue >>> (lCount * 8)) & 255
WordToHexValueTemp = '0' + lByte.toString(16)
WordToHexValue = WordToHexValue + WordToHexValueTemp.substr(WordToHexValueTemp.length - 2, 2)
}
return WordToHexValue
}
var uTF8Encode = function (string: string) {
string = string.toString().replace(/\x0d\x0a/g, '\x0a')
var output = ''
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n)
if (c < 128) {
output += String.fromCharCode(c)
} else if ((c > 127) && (c < 2048)) {
output += String.fromCharCode((c >> 6) | 192)
output += String.fromCharCode((c & 63) | 128)
} else {
output += String.fromCharCode((c >> 12) | 224)
output += String.fromCharCode(((c >> 6) & 63) | 128)
output += String.fromCharCode((c & 63) | 128)
}
}
return output
}
return createMD5String(md5str)
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "0c84ecf9-5446-4322-8073-3e74289d8d1a",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,143 @@
/**
* @file SqlUtil.ts
* @author dream
* @description 本地化存储方案
*
*/
import { sys } from "cc";
import { PREVIEW } from "cc/env";
import { EncryptUtil } from "./EncryptUtil";
import { md5 } from "./Md5";
export module SqlUtil {
let _key: string | null = null;
let _iv: string | null = null;
/**
* 初始化密钥
* @param key aes加密的key
* @param iv aes加密的iv
*/
export function init(key: string, iv: string) {
_key = md5(key);
_iv = md5(iv);
}
/**
* 存储
* @param key 存储key
* @param value 存储值
* @returns
*/
export function set(key: string, value: any) {
if (null == key) {
console.error("存储的key不能为空");
return;
}
if (!PREVIEW) {
key = md5(key);
}
if (null == value) {
console.warn("存储的值为空,则直接移除该存储");
remove(key);
return;
}
if (typeof value === 'function') {
console.error("储存的值不能为方法");
return;
}
if (typeof value === 'object') {
try {
value = JSON.stringify(value);
}
catch (e) {
console.error(`解析失败str=${value}`);
return;
}
}
else if (typeof value === 'number') {
value = value + "";
}
if (!PREVIEW && null != _key && null != _iv) {
try {
value = EncryptUtil.aesEncrypt(value, _key, _iv);
}
catch (e) {
value = null;
}
}
sys.localStorage.setItem(key, value);
}
/**
* 获取
* @param key 获取的key
* @param defaultValue 获取的默认值
* @returns
*/
export function get(key: string, defaultValue?: any) {
if (null == key) {
console.error("存储的key不能为空");
return;
}
if (!PREVIEW) {
key = md5(key);
}
let str: string | null = sys.localStorage.getItem(key);
if (null != str && '' !== str && !PREVIEW && null != _key && null != _iv) {
try {
str = EncryptUtil.aesDecrypt(str, _key, _iv);
}
catch (e) {
str = null;
}
}
if (null == defaultValue || typeof defaultValue === 'string') {
return str;
}
if (null === str) {
return defaultValue;
}
if (typeof defaultValue === 'number') {
return Number(str) || 0;
}
if (typeof defaultValue === 'boolean') {
return "true" == str; // 不要使用Boolean("false");
}
if (typeof defaultValue === 'object') {
try {
return JSON.parse(str);
}
catch (e) {
console.error("解析数据失败,str=" + str);
return defaultValue;
}
}
return str;
}
/**
* 移除某个值
* @param key 需要移除的key
* @returns
*/
export function remove(key: string) {
if (null == key) {
console.error("存储的key不能为空");
return;
}
if (!PREVIEW) {
key = md5(key);
}
sys.localStorage.removeItem(key);
}
/**
* 清空整个本地存储
*/
export function clear() {
sys.localStorage.clear();
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "85c76a54-4620-4425-b443-b657fe7fb087",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,29 @@
export const global: any;
declare global {
class WordArray { }
namespace CryptoJS {
namespace AES {
export function encrypt(message: string, key: string, cfg: { iv: string, mode: mode, padding: pad }): WordArray;
export function decrypt(text: string, key: string, cfg: { iv: string, mode: mode, padding: pad }): WordArray;
}
namespace enc {
namespace Utf8 {
export function parse(key: WordArray): string;
export function stringify(word: WordArray): string;
}
}
export enum mode {
CBC
}
export enum pad {
Pkcs7
}
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "3c1c0c1d-c9a4-4dd2-9249-254305a4c73b",
"files": [],
"subMetas": {},
"userData": {}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.22",
"importer": "javascript",
"imported": true,
"uuid": "83485788-0873-4c9f-a03f-c9f9b5ee44ca",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "7f7d89cd-a784-4243-824c-5c0a97c5ca9f",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,35 @@
import { Camera, Component, gfx, MeshRenderer, RenderTexture, view, _decorator } from 'cc';
const { ccclass, property } = _decorator;
/** 三维摄像机内容显示到模型上 */
@ccclass('RtToModel')
export class RtToModel extends Component {
@property(Camera)
camara: Camera = null!;
@property(MeshRenderer)
model: MeshRenderer = null!;
private rt: RenderTexture = new RenderTexture();
start() {
const size = view.getVisibleSize();
const colorAttachment = new gfx.ColorAttachment();
const depthStencilAttachment = new gfx.DepthStencilAttachment();
const pi = new gfx.RenderPassInfo([colorAttachment], depthStencilAttachment, []);
this.rt.reset({
width: size.width,
height: size.height,
passInfo: pi
});
this.camara.targetTexture = this.rt;
const mat = this.model.material!;
mat.setProperty('mainTexture', this.rt);
}
onDestroy() {
this.rt.destroy();
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "ed72a01b-9451-4614-828a-9a41072a27ea",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,105 @@
import { Camera, Component, EventTouch, gfx, Node, RenderTexture, Sprite, SpriteFrame, SystemEventType, UITransform, Vec3, _decorator } from 'cc';
const { ccclass, property } = _decorator;
/** 三维模型显示到二维精灵上 */
@ccclass('RtToSprite')
export class RtToSprite extends Component {
@property({
type: Camera,
tooltip: "渲染模型的三维摄像机"
})
camera: Camera | null = null;
@property({
type: Sprite,
tooltip: "显示模型的二维精灵组件"
})
sprite: Sprite | null = null;
@property({
tooltip: "是否触摸控制旋转"
})
rotation: boolean = false;
@property({
type: Node,
tooltip: "三维模型",
visible: function () {
//@ts-ignore
return this.rotation === true;
},
})
model: Node | null = null;
private rt: RenderTexture = new RenderTexture();
private touched = false; // 是否触摸节点
start() {
let size = this.sprite!.getComponent(UITransform)!;
this.refreshRenderTexture(size.width, size.height);
if (this.rotation) {
this.sprite!.node.on(SystemEventType.TOUCH_START, this.onTouchStart, this);
this.sprite!.node.on(SystemEventType.TOUCH_MOVE, this.onTouchMove, this);
this.sprite!.node.on(SystemEventType.TOUCH_END, this.onTouchEnd, this);
this.sprite!.node.on(SystemEventType.TOUCH_CANCEL, this.onTouchEnd, this);
}
}
protected onTouchStart(event: EventTouch) {
this.touched = true;
}
protected onTouchMove(event: EventTouch) {
if (this.touched) {
let eulerAngles: Vec3 = this.model!.eulerAngles;
let deltaX = event.touch!.getDelta().x!;
eulerAngles.y += -deltaX;
this.model!.eulerAngles = eulerAngles;
}
}
protected onTouchEnd(event: EventTouch) {
this.touched = false;
}
/** 刷新纹理内容 */
refreshRenderTexture(w: number, h: number): void {
const colorAttachment = new gfx.ColorAttachment();
const depthStencilAttachment = new gfx.DepthStencilAttachment();
const pi = new gfx.RenderPassInfo([colorAttachment], depthStencilAttachment, []);
this.rt.reset({
width: w,
height: h,
passInfo: pi
});
let spriteframe: SpriteFrame = this.sprite!.spriteFrame!;
let sp: SpriteFrame = new SpriteFrame();
sp.reset({
originalSize: spriteframe.originalSize,
rect: spriteframe.rect,
offset: spriteframe.offset,
isRotate: spriteframe.rotated,
borderTop: spriteframe.insetTop,
borderLeft: spriteframe.insetLeft,
borderBottom: spriteframe.insetBottom,
borderRight: spriteframe.insetRight,
});
this.camera!.targetTexture = this.rt;
sp.texture = this.rt;
this.sprite!.spriteFrame = sp;
}
onDestroy() {
if (this.rotation) {
this.sprite!.node.off(SystemEventType.TOUCH_START, this.onTouchStart, this);
this.sprite!.node.off(SystemEventType.TOUCH_MOVE, this.onTouchMove, this);
this.sprite!.node.off(SystemEventType.TOUCH_END, this.onTouchEnd, this);
this.sprite!.node.off(SystemEventType.TOUCH_CANCEL, this.onTouchEnd, this);
}
this.rt.destroy();
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "a266c942-aa61-45ac-9dcd-7ad398427989",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,14 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "9f0f8674-5ea3-45dd-98bb-dd163a68fa03",
"files": [],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/Engine.js",
"importerSettings": 4,
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "8e279ff4-2659-4fc1-8026-816ebfa0a20e",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,8 @@
import { Component } from 'cc';
/** 游戏世界管理 */
export class GameManager extends Component {
onLoad() {
}
}

View File

@@ -0,0 +1,14 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "0f970a25-bb06-40a6-a1ae-95a474a2ea8f",
"files": [],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/game/GameWorld.js",
"importerSettings": 4,
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,15 @@
{
"ver": "2.0.14",
"importer": "typescript",
"imported": true,
"uuid": "4f7621b2-4583-4085-96e7-b186c5ce78b8",
"files": [
".js",
".trans"
],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/world/PlayerController.js"
}
}

View File

@@ -0,0 +1,15 @@
{
"ver": "2.0.14",
"importer": "typescript",
"imported": true,
"uuid": "4944b3c7-53b3-4008-8a34-36c482ac3a89",
"files": [
".js",
".trans"
],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/world/World.js"
}
}

View File

@@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "02c1592d-2be3-47fe-9f64-557dbd901fd6",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,157 @@
/**
* 自由飞行摄像机
* 使用方式:
* 1、组件绑定到任意一设想机上
* 2、通过W(上、S(下)、A(左、D(右、Q(Y轴向下、E(Y轴向上来操作摄像机移动
* 3、按住SHIFT键会加速飞行
* 4、鼠标左或右键按下滑动控制摄像机视角原地旋转
* 5、鼠标滚轮键滑动摄像机拉近或拉远
* 6、只支持PC上使用
*/
import { CCFloat, Component, EventTouch, game, macro, math, systemEvent, SystemEvent, _decorator } from 'cc';
const { ccclass, property,menu } = _decorator;
const { Vec2, Vec3, Quat } = math;
const v2_1 = new Vec2();
const v2_2 = new Vec2();
const v3_1 = new Vec3();
const qt_1 = new Quat();
const KEYCODE = {
W: 'W'.charCodeAt(0),
S: 'S'.charCodeAt(0),
A: 'A'.charCodeAt(0),
D: 'D'.charCodeAt(0),
Q: 'Q'.charCodeAt(0),
E: 'E'.charCodeAt(0),
SHIFT: macro.KEY.shift,
};
@ccclass("FreeFlightCamera")
@menu('game/camera/FreeFlightCamera')
export class FreeFlightCamera extends Component {
@property({
type: CCFloat,
tooltip: "移动速度"
})
public moveSpeed: number = 1;
@property({
type: CCFloat,
tooltip: "按Shift键后的速度"
})
public moveSpeedShiftScale: number = 5;
@property({
type: CCFloat,
slide: true,
range: [0.05, 0.5, 0.01],
tooltip: "移动后惯性效果"
})
public damp: number = 0.2;
@property({
type: CCFloat,
tooltip: "旋转速度"
})
public rotateSpeed: number = 1;
public _euler = new Vec3();
public _velocity = new Vec3();
public _position = new Vec3();
public _speedScale = 1;
public onLoad() {
systemEvent.on(SystemEvent.EventType.MOUSE_WHEEL, this.onMouseWheel, this);
systemEvent.on(SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);
systemEvent.on(SystemEvent.EventType.KEY_UP, this.onKeyUp, this);
systemEvent.on(SystemEvent.EventType.TOUCH_START, this.onTouchStart, this);
systemEvent.on(SystemEvent.EventType.TOUCH_MOVE, this.onTouchMove, this);
systemEvent.on(SystemEvent.EventType.TOUCH_END, this.onTouchEnd, this);
Vec3.copy(this._euler, this.node.eulerAngles);
Vec3.copy(this._position, this.node.position);
}
public onDestroy() {
systemEvent.off(SystemEvent.EventType.MOUSE_WHEEL, this.onMouseWheel, this);
systemEvent.off(SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);
systemEvent.off(SystemEvent.EventType.KEY_UP, this.onKeyUp, this);
systemEvent.off(SystemEvent.EventType.TOUCH_START, this.onTouchStart, this);
systemEvent.off(SystemEvent.EventType.TOUCH_MOVE, this.onTouchMove, this);
systemEvent.off(SystemEvent.EventType.TOUCH_END, this.onTouchEnd, this);
}
public update(dt) {
// position
Vec3.transformQuat(v3_1, this._velocity, this.node.rotation);
Vec3.scaleAndAdd(this._position, this._position, v3_1, this.moveSpeed * this._speedScale);
Vec3.lerp(v3_1, this.node.position, this._position, dt / this.damp); // 向量线性插值产生位移惯性效果
this.node.setPosition(v3_1);
// rotation
Quat.fromEuler(qt_1, this._euler.x, this._euler.y, this._euler.z);
Quat.slerp(qt_1, this.node.rotation, qt_1, dt / this.damp); // 四元素线性插值产生旋转惯性效果
this.node.setRotation(qt_1);
}
public onMouseWheel(e) {
const delta = -e.getScrollY() * this.moveSpeed * 0.1; // 向下滚动时增量为正
Vec3.transformQuat(v3_1, Vec3.UNIT_Z, this.node.rotation);
Vec3.scaleAndAdd(this._position, this.node.position, v3_1, delta);
}
public onKeyDown(e) {
const v = this._velocity;
if (e.keyCode === KEYCODE.SHIFT) { this._speedScale = this.moveSpeedShiftScale; }
else if (e.keyCode === KEYCODE.W) { if (v.z === 0) { v.z = -1; } }
else if (e.keyCode === KEYCODE.S) { if (v.z === 0) { v.z = 1; } }
else if (e.keyCode === KEYCODE.A) { if (v.x === 0) { v.x = -1; } }
else if (e.keyCode === KEYCODE.D) { if (v.x === 0) { v.x = 1; } }
else if (e.keyCode === KEYCODE.Q) { if (v.y === 0) { v.y = -1; } }
else if (e.keyCode === KEYCODE.E) { if (v.y === 0) { v.y = 1; } }
}
public onKeyUp(e) {
const v = this._velocity;
if (e.keyCode === KEYCODE.SHIFT) { this._speedScale = 1; }
else if (e.keyCode === KEYCODE.W) { if (v.z < 0) { v.z = 0; } }
else if (e.keyCode === KEYCODE.S) { if (v.z > 0) { v.z = 0; } }
else if (e.keyCode === KEYCODE.A) { if (v.x < 0) { v.x = 0; } }
else if (e.keyCode === KEYCODE.D) { if (v.x > 0) { v.x = 0; } }
else if (e.keyCode === KEYCODE.Q) { if (v.y < 0) { v.y = 0; } }
else if (e.keyCode === KEYCODE.E) { if (v.y > 0) { v.y = 0; } }
}
private onTouchStart(e: EventTouch) {
if (game.canvas.requestPointerLock) { game.canvas.requestPointerLock(); }
}
private onTouchMove(e: EventTouch) {
e.getStartLocation(v2_1);
if (v2_1.x > game.canvas.width * 0.4) { // rotation
e.getDelta(v2_2);
this._euler.y -= v2_2.x * this.rotateSpeed * 0.1; // 上下旋转
this._euler.x += v2_2.y * this.rotateSpeed * 0.1; // 左右旋转
}
else { // position
e.getLocation(v2_2);
Vec2.subtract(v2_2, v2_2, v2_1);
this._velocity.x = v2_2.x * 0.01;
this._velocity.z = -v2_2.y * 0.01;
}
}
private onTouchEnd(e: EventTouch) {
if (document.exitPointerLock) {
document.exitPointerLock();
}
e.getStartLocation(v2_1);
if (v2_1.x < game.canvas.width * 0.4) { // position
this._velocity.x = 0;
this._velocity.z = 0;
}
}
}

View File

@@ -0,0 +1,14 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "6c841994-fb82-4dbe-ac07-cb9b49b09874",
"files": [],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/game/camera/FreeFlightCamera.js",
"importerSettings": 4,
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,15 @@
{
"ver": "2.0.14",
"importer": "typescript",
"imported": true,
"uuid": "b6242caf-dd5c-407f-881d-cbac87ff982f",
"files": [
".js",
".trans"
],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/world/first-person-camera.js"
}
}

View File

@@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "dedee4cf-1d22-402a-833a-0bdc09ebbdea",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,51 @@
import { Camera, Component, ResolutionPolicy, UITransform, view, _decorator } from "cc";
import { Logger } from "../utils/Logger";
const { ccclass, menu } = _decorator;
/** 引擎对外接口 */
@ccclass('GUI')
export class GUI extends Component {
/** 界面层矩形信息组件 */
public transform!: UITransform;
public camera!: Camera;
onLoad() {
this.init();
}
/** 初始化引擎 */
protected init() {
this.transform = this.getComponent(UITransform)!;
this.camera = this.getComponentInChildren(Camera)!;
this.resize();
}
public resize() {
let dr = view.getDesignResolutionSize();
var s = view.getFrameSize();
var rw = s.width;
var rh = s.height;
var finalW = rw;
var finalH = rh;
if ((rw / rh) > (dr.width / dr.height)) {
// 如果更长,则用定高
finalH = dr.height;
finalW = finalH * rw / rh;
}
else {
// 如果更短,则用定宽
finalW = dr.width;
finalH = finalW * rh / rw;
}
// 手工修改canvas和设计分辨率这样反复调用也能生效。
view.setDesignResolutionSize(finalW, finalH, ResolutionPolicy.UNKNOWN);
this.transform!.width = finalW;
this.transform!.height = finalH;
Logger.trace(dr, "设计尺寸");
Logger.trace(s, "屏幕尺寸");
}
}

View File

@@ -0,0 +1,14 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "9461c77f-7463-4418-ae48-e49db8c58e8b",
"files": [],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/gui/GUI.js",
"importerSettings": 4,
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "df85f52f-c354-483d-af86-9cd0b96cdc96",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,133 @@
import { CCInteger, CCString, color, Component, Label, Layers, Mask, math, Node, Overflow, Sprite, SpriteFrame, UITransform, Vec3, _decorator } from 'cc';
import { RoundRectMask } from './RoundRectMask';
const { ccclass, property } = _decorator;
// 徽标位置
export enum Position {
// 左上角
TOP_LEFT = 0,
// 右上角
TOP_RIGHT = 1,
}
@ccclass('Badge')
export class Badge extends Component {
@property({ type: SpriteFrame, tooltip: '背景' })
bg: SpriteFrame | null = null;
@property({ tooltip: '内容' })
string: string = '6';
@property({ type: CCInteger, tooltip: '宽' })
width: number = 30;
@property({ type: CCInteger, tooltip: '高' })
height: number = 26;
@property({ type: CCInteger, tooltip: '圆角' })
radius: number = 6;
@property({ type: CCInteger, tooltip: '位置\n 0: 左上角 \n 1: 右上角' })
position: Position = Position.TOP_LEFT;
// 徽标位置
public static POSITION: Position;
private badgeNode: Node = null!;
private color = color(214, 30, 30, 240);
private textColor = color(255, 255, 255, 255);
private labelNode: Node = null!;
public get text(): string {
return this.string;
}
public set text(text: string) {
this.string = text;
this.setText(text);
}
onLoad() {
this.initBadge();
}
// 初始化badge插入节点等操作
initBadge() {
const badgeNode = this.createBadge();
this.node.addChild(badgeNode);
console.log('badgeNode:', badgeNode);
}
// 设置位置
setPosition(position: Position) {
const parentSize = this.node.getComponent(UITransform)?.contentSize as math.Size;
const badgeSize = this.badgeNode.getComponent(UITransform)?.contentSize as math.Size;
switch (position) {
case Position.TOP_LEFT: {
const x = -parentSize.width / 2;
const y = parentSize.height / 2;
this.badgeNode.setPosition(new Vec3(x, y, 0));
break;
}
case Position.TOP_RIGHT: {
const x = parentSize.width / 2;
const y = parentSize.height / 2;
this.badgeNode.setPosition(new Vec3(x, y, 0));
break;
}
}
}
// 设置文字
setText(text: string) {
this.text = text;
const label = this.labelNode.getComponent(Label);
if (label) {
label.string = this.text;
label.color = this.textColor;
}
return this;
}
createBadge() {
this.badgeNode = new Node('BadgeNode');
const backgroundNode = new Node('backgroundNode');
this.labelNode = new Node('labelNode');
this.badgeNode.layer = Layers.Enum.UI_2D;
backgroundNode.layer = Layers.Enum.UI_2D;
this.labelNode.layer = Layers.Enum.UI_2D;
// 设置mask
this.badgeNode.addComponent(Mask);
this.badgeNode.addComponent(RoundRectMask).radius = this.radius;
this.badgeNode.getComponent(UITransform)?.setContentSize(this.width, this.height);
this.badgeNode.active = true;
// 设置背景
const _sprite = backgroundNode.addComponent(Sprite);
_sprite.type = Sprite.Type.SIMPLE;
_sprite.color = this.color;
_sprite.spriteFrame = this.bg;
backgroundNode.getComponent(UITransform)?.setContentSize(this.width, this.height);
// 设置label信息
const _label = this.labelNode.addComponent(Label);
_label.getComponent(UITransform)?.setContentSize(this.width, this.height);
_label.string = this.text;
_label.color = this.textColor;
_label.fontSize = 14;
_label.lineHeight = 0;
_label.overflow = Overflow.SHRINK;
_label.enableWrapText = false;
// 添加节点
this.badgeNode.addChild(backgroundNode);
this.badgeNode.addChild(this.labelNode);
this.setPosition(this.position);
return this.badgeNode;
}
start() { }
}

View File

@@ -0,0 +1,11 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "bac76a30-f785-48f8-8a40-0750d050a7ea",
"files": [],
"subMetas": {},
"userData": {
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,78 @@
import { CCFloat, Component, game, Game, Graphics, Mask, UITransform, _decorator } from 'cc';
const { ccclass, property, executeInEditMode, disallowMultiple, requireComponent, menu } = _decorator;
@ccclass('RoundRectMask')
@executeInEditMode(true)
@disallowMultiple(true)
@requireComponent(Mask)
@menu('渲染组件/圆角遮罩')
export class RoundRectMask extends Component {
// 圆角半径
@property({ type: CCFloat, tooltip: '圆角半径:\n0-1之间为最小边长比例值, \n>1为具体像素值' })
private propRadius: number = 50;
mask: Mask | null = null;
public get radius(): number {
return this.propRadius;
}
public set radius(r: number) {
this.propRadius = r;
this.updateMask(r);
}
protected onEnable(): void {
this.mask = this.getComponent(Mask);
this.updateMask(this.radius);
}
private updateMask(r: number) {
let _radius = r >= 0 ? r : 0;
if (_radius < 1) {
const uiTransform = this.node.getComponent(UITransform);
_radius = Math.min(uiTransform?.width || 0, uiTransform?.width || 0) * _radius;
}
if (this.mask) {
// @ts-ignore.
this.mask['radius'] = _radius;
// @ts-ignore.
this.mask['onDraw'] = this.onDraw.bind(this.mask);
this.mask['_updateGraphics'] = this._updateGraphics.bind(this.mask);
this.mask.type = Mask.Type.RECT;
}
}
private _updateGraphics() {
// @ts-ignore.
let graphics = this._graphics;
if (!graphics) {
return;
}
this.onDraw(graphics);
}
/**
* mask 用于绘制罩子的函数.
* this 指向mask 对象,需要特别注意.
* @param graphics
*/
protected onDraw(graphics: Graphics) {
const uiTransform = this.node.getComponent(UITransform) as UITransform;
graphics.clear();
let width = uiTransform.width;
let height = uiTransform.height;
let x = -width * uiTransform.anchorX;
let y = -height * uiTransform.anchorY;
graphics.roundRect(x, y, width, height, this.radius || 0);
if (game.renderType === Game.RENDER_TYPE_CANVAS) {
graphics.stroke();
}
else {
graphics.fill();
}
}
}

View File

@@ -0,0 +1,11 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "da0f8081-4246-43a9-a7a4-36e111a8a8c0",
"files": [],
"subMetas": {},
"userData": {
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "c35d33a8-9290-47c3-8f06-35fb298a5556",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,42 @@
import { Animation, AnimationClip, EventTouch, _decorator } from "cc";
import { resLoader } from "../../utils/ResLoader";
import ButtonSimple from "./ButtonSimple";
const { ccclass, property, menu } = _decorator;
@ccclass("ButtonEffect")
@menu('ui/button/ButtonEffect')
export default class ButtonEffect extends ButtonSimple {
@property({
tooltip: "是否开启"
})
disabledEffect: boolean = false;
private anim!: Animation;
onLoad() {
this.anim = this.node.addComponent(Animation);
var ac_start = resLoader.get("common/anim/button_scale_start", AnimationClip)!;
var ac_end = resLoader.get("common/anim/button_scale_end", AnimationClip)!;
this.anim.defaultClip = ac_start;
this.anim.createState(ac_start, ac_start?.name);
this.anim.createState(ac_end, ac_end?.name);
super.onLoad();
}
protected onTouchtStart(event: EventTouch) {
if (!this.disabledEffect) {
this.anim.play("button_scale_start");
}
}
protected onTouchEnd(event: EventTouch) {
if (!this.disabledEffect) {
this.anim.play("button_scale_end");
}
super.onTouchEnd(event);
}
}

View File

@@ -0,0 +1,11 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "1be3686b-18ee-4ecf-a11a-a60eb2816d37",
"files": [],
"subMetas": {},
"userData": {
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,54 @@
import { Component, director, EventTouch, Node, _decorator } from "cc";
const { ccclass, property, menu } = _decorator;
@ccclass("ButtonSimple")
@menu('ui/button/ButtonSimple')
export default class ButtonSimple extends Component {
@property({
tooltip: "是否只能触发一次"
})
private once: boolean = false;
@property({
tooltip: "每次触发间隔"
})
private interval: number = 500;
private touchCount = 0;
private touchtEndTime = 0;
onLoad() {
this.node.on(Node.EventType.TOUCH_START, this.onTouchtStart, this);
this.node.on(Node.EventType.TOUCH_END, this.onTouchEnd, this);
this.node.on(Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
}
/** 触摸开始 */
protected onTouchtStart(event: EventTouch) { }
/** 触摸结束 */
protected onTouchEnd(event: EventTouch) {
if (this.once) {
if (this.touchCount > 0) {
event.propagationStopped = true;
return;
}
this.touchCount++;
}
// 防连点500毫秒出发一次事件
if (this.touchtEndTime && director.getTotalTime() - this.touchtEndTime < this.interval) {
event.propagationStopped = true;
}
else {
this.touchtEndTime = director.getTotalTime();
}
}
onDestroy() {
this.node.off(Node.EventType.TOUCH_START, this.onTouchtStart, this);
this.node.off(Node.EventType.TOUCH_END, this.onTouchEnd, this);
this.node.off(Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
}
}

View File

@@ -0,0 +1,11 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "8d645c8e-6d7d-45bc-97e4-dac6e2d2bc69",
"files": [],
"subMetas": {},
"userData": {
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,61 @@
import { EventTouch, _decorator } from "cc";
import ButtonEffect from "./ButtonEffect";
const { ccclass, property, menu } = _decorator;
@ccclass("ButtonTouchLong")
@menu('ui/button/ButtonTouchLong')
export class ButtonTouchLong extends ButtonEffect {
@property({
tooltip: "长按时间(秒)"
})
time: number = 1;
protected _passTime = 0;
protected _isTouchLong: boolean = true;
protected _event: EventTouch | null = null;
public onLongTouchCallback!: Function | null;
onLoad() {
this._isTouchLong = false;
super.onLoad();
}
/** 触摸开始 */
onTouchtStart(event: EventTouch) {
this._event = event;
this._passTime = 0;
super.onTouchtStart(event);
}
/** 触摸结束 */
onTouchEnd(event: EventTouch) {
if (this._passTime > this.time) {
event.propagationStopped = true;
}
this._event = null;
this._passTime = 0;
this._isTouchLong = false;
super.onTouchEnd(event);
}
removeTouchLong() {
this._event = null;
this._isTouchLong = false;
}
/** 引擎更新事件 */
update(dt: number) {
if (this._event && !this._isTouchLong) {
this._passTime += dt;
if (this._passTime >= this.time) {
this._isTouchLong = true;
if (this.onLongTouchCallback)
this.onLongTouchCallback(this._event);
this.removeTouchLong();
}
}
}
}

View File

@@ -0,0 +1,11 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "da96e9fb-598a-4f4d-a3c8-90ed65fcdbb1",
"files": [],
"subMetas": {},
"userData": {
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "bff487ed-c8aa-446b-a2a4-24408ef0480c",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,95 @@
import { _decorator } from "cc";
import LabelNumber from "./LabelNumber";
const { ccclass, property, menu } = _decorator;
@ccclass("LabelChange")
@menu('ui/label/LabelChange')
export class LabelChange extends LabelNumber {
@property
isInteger: boolean = false;
private duration: number = 0; // 持续时间
private callback: Function | undefined; // 完成回调
private isBegin: boolean = false; // 是否开始
private speed: number = 0; // 变化速度
private end: number = 0; // 最终值
/**
* 变化到某值,如果从当前开始的begin传入null
* @param {number} duration
* @param {number} end
* @param {Function} [callback]
*/
public changeTo(duration: number, end: number, callback?: Function) {
if (duration == 0) {
if (callback) callback();
return;
}
this.playAnim(duration, this.num, end, callback);
}
/**
* 变化值,如果从当前开始的begin传入null
* @param {number} duration
* @param {number} value
* @param {Function} [callback]
* @memberof LabelChange
*/
public changeBy(duration: number, value: number, callback?: Function) {
if (duration == 0) {
if (callback) callback();
return;
}
this.playAnim(duration, this.num, this.num + value, callback);
}
/** 立刻停止 */
public stop(excCallback: boolean = true) {
this.num = this.end;
this.isBegin = false;
if (excCallback && this.callback) this.callback();
}
/** 播放动画 */
private playAnim(duration: number, begin: number, end: number, callback?: Function) {
this.duration = duration;
this.end = end;
this.callback = callback;
this.speed = (end - begin) / duration;
this.num = begin;
this.isBegin = true;
}
/** 是否已经结束 */
private isEnd(num: number): boolean {
if (this.speed > 0) {
return num >= this.end;
}
else {
return num <= this.end;
}
}
update(dt: number) {
if (this.isBegin) {
if (this.num == this.end) {
this.isBegin = false;
if (this.callback) this.callback();
return;
}
let num = this.num + dt * this.speed;
if (this.isInteger) num = Math.ceil(num);
/** 变化完成 */
if (this.isEnd(num)) {
num = this.end;
this.isBegin = false;
if (this.callback) this.callback();
}
this.num = num;
}
}
}

View File

@@ -0,0 +1,14 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "fff0f2f0-54d8-4d7b-9f55-8ab59308f149",
"files": [],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/gui/label/LabelChange.js",
"importerSettings": 4,
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,43 @@
import { Color, Label, _decorator } from 'cc';
const { ccclass, property } = _decorator;
/** 渐变颜色字体 */
@ccclass('LabelColor')
export class LabelColor extends Label {
@property({ type: [Color] })
private _colors: Color[] = [];
@property({ type: [Color] })
public get colors(): Color[] {
return this._colors;
}
public set colors(value: Color[]) {
this._colors = value;
this._colorDirty = true;
}
protected _updateColor() {
const colorDirty = this._colorDirty;
super._updateColor();
if (colorDirty) {
const vData = this.renderData!.vData;
let colorOffset = 5;
for (let i = 0; i < 4; i++) {
const color = this.colors[i] || this.color;
const colorR = color.r / 255;
const colorG = color.g / 255;
const colorB = color.b / 255;
const colorA = this.node._uiProps.opacity;
vData![colorOffset] = colorR;
vData![colorOffset + 1] = colorG;
vData![colorOffset + 2] = colorB;
vData![colorOffset + 3] = colorA;
colorOffset += 9;
}
}
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "c2a31250-e8a8-459d-a40b-ebd3f3498b78",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,48 @@
import { error, Label, _decorator } from "cc";
const { ccclass, property, menu } = _decorator;
@ccclass("LabelNumber")
@menu('ui/label/LabelNumber')
export default class LabelNumber extends Label {
@property
_num: number = 0;
@property({
tooltip: "是否显示货币符号"
})
_showSym: string = "";
@property
public set num(value: number) {
this._num = value;
this.updateLabel();
}
public get num(): number {
return this._num;
}
@property
public set showSym(value: string) {
if (value) {
this._showSym = value;
this.updateLabel();
}
}
public get showSym(): string {
return this._showSym;
}
@property
useFix: boolean = true;
/** 刷新lab */
protected updateLabel() {
if (typeof (this._num) != "number") {
error("[LabelNumber] num不是一个合法数字");
}
this.string = this.num.toString();
}
}

View File

@@ -0,0 +1,14 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "f7b4317b-d018-4f96-bcf8-20f8591a6b0b",
"files": [],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/gui/label/LabelNumber.js",
"importerSettings": 4,
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,135 @@
import { Label, _decorator } from "cc";
import { EDITOR } from "cc/env";
import { engine } from "../../Engine";
const { ccclass, property, menu } = _decorator;
@ccclass("LabelTime")
@menu('ui/label/LabelTime')
export default class LabelTime extends Label {
@property({
tooltip: "到计时间总时间(单位秒)"
})
countDown: number = 1000;
@property({
tooltip: "天数数据格式化"
})
dayFormat: string = "{0} day";
@property({
tooltip: "时间格式化"
})
timeFormat: string = "{0}:{1}:{2}";
@property({
tooltip: "是否有00"
})
zeroize: boolean = true;
private dateDisable!: boolean;
private result!: string;
private timeId!: string;
public second!: Function;
public complete!: Function;
private replace(value: string, ...args: any): string {
return value.replace(/\{(\d+)\}/g,
function (m, i) {
return args[i];
});
}
/** 格式化字符串 */
private format() {
let c: number = this.countDown;
let date: number = Math.floor(c / 86400);
c = c - date * 86400;
let hours: number = Math.floor(c / 3600);
c = c - hours * 3600;
let minutes: number = Math.floor(c / 60);
c = c - minutes * 60;
let seconds: number = c;
this.dateDisable = this.dateDisable || false;
if (date == 0 && hours == 0 && minutes == 0 && seconds == 0) {
if (this.zeroize) {
this.result = this.replace(this.timeFormat, "00", "00", "00");
}
else {
this.result = this.replace(this.timeFormat, "0", "0", "0");
}
}
else if (date > 0 && !this.dateDisable) {
let dataFormat = this.dayFormat;
let index = dataFormat.indexOf("{1}");
if (hours == 0 && index > -1) {
dataFormat = dataFormat.substring(0, index - 1);
}
let df = dataFormat;
if (date > 1 && dataFormat.indexOf("days") < 0) {
df = df.replace("day", "days");
}
if (date < 2) {
df = df.replace("days", "day");
}
this.result = this.replace(df, date, hours); // 如果天大于1则显示 "1 Day..."
}
else {
hours += date * 24;
if (this.zeroize) {
this.result = this.replace(
this.timeFormat,
this.coverString(hours),
this.coverString(minutes),
this.coverString(seconds)); // 否则显示 "01:12:24"
}
else {
this.result = this.replace(
this.timeFormat,
hours,
minutes,
seconds);
}
}
this.string = this.result;
}
/** 个位数的时间数据将字符串补位 */
private coverString(value: number) {
if (value < 10)
return "0" + value;
return value.toString();
}
/** 设置时间能否由天数显示 */
public setDateDisable(flag: boolean) {
this.dateDisable = flag;
}
public setCdTime(second: number) {
this.countDown = second; // 倒计时,初始化显示字符串
this.format();
}
start() {
this.format();
if (!EDITOR)
this.timeId = engine.timer.registerObject(this, "countDown", this.onSecond, this.onComplete);
}
onDestroy() {
if (!EDITOR)
engine.timer.unRegisterObject(this.timeId);
}
private onSecond() {
this.format();
if (this.second) this.second(this.node);
}
private onComplete() {
if (this.complete) this.complete(this.node);
}
}

View File

@@ -0,0 +1,14 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "5778618c-37a4-4f6d-a013-f5bf51997d7c",
"files": [],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/gui/label/LabelTime.js",
"importerSettings": 4,
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "4d72408b-5cb6-4ca1-aad9-d73b890db1a0",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,127 @@
import { error, log, warn } from "cc";
import { EventDispatcher } from "../../common/event/EventDispatcher";
import { Logger } from "../../utils/Logger";
import { LanguageLabel } from "./LanguageLabel";
import LanguagePack from "./LanguagePack";
export enum LanguageEvent {
/** 语种变化事件 */
CHANGE = 'LanguageEvent.CHANGE',
/** 语种资源释放事件 */
RELEASE_RES = "LanguageEvent.RELEASE_RES"
}
const DEFAULT_LANGUAGE = "zh";
export class LanguageManager extends EventDispatcher {
/** Label修改之前的回调 */
public beforeChangeLabel: ((comp: LanguageLabel, content: string, dataID: string) => void) | null = null;
private _currentLang: string = ""; // 当前语言
private _supportLanguages: Array<string> = ["zh", "en", "tr"]; // 支持的语言
private _languagePack: LanguagePack = new LanguagePack(); // 语言包
/** 设置多语言系统支持哪些语种 */
public set supportLanguages(supportLanguages: Array<string>) {
this._supportLanguages = supportLanguages;
}
/**
* 获取当前语种
*/
public get currentLanguage(): string {
return this._currentLang;
}
/**
* 获取支持的多语种数组
*/
public get languages(): string[] {
return this._supportLanguages;
}
public isExist(lang: string): boolean {
return this.languages.indexOf(lang) > -1;
}
/**
* 获取下一个语种
*/
public getNextLang(): string {
let supportLangs = this.languages;
let index = supportLangs.indexOf(this._currentLang);
let newLanguage = supportLangs[(index + 1) % supportLangs.length];
return newLanguage;
}
/**
* 改变语种,会自动下载对应的语种,下载完成回调
* @param language
*/
public setLanguage(language: string, callback: (success: boolean) => void) {
if (!language) {
language = DEFAULT_LANGUAGE;
}
language = language.toLowerCase();
let index = this.languages.indexOf(language);
if (index < 0) {
warn("当前不支持该语种" + language + " 将自动切换到 zh 语种!");
language = DEFAULT_LANGUAGE;
}
if (language === this._currentLang) {
callback(false);
return;
}
this.loadLanguageAssets(language, (err: any, lang: string) => {
if (err) {
error("语言资源包下载失败", err);
callback(false);
return;
}
Logger.logBusiness(`当前语言为【${language}`);
this._currentLang = language;
this._languagePack.updateLanguage(language);
this.dispatchEvent(LanguageEvent.CHANGE, lang);
callback(true);
});
}
/**
* 设置多语言资源目录
* @param langjsonPath 多语言json目录
* @param langTexturePath 多语言图片目录
*/
public setAssetsPath(langjsonPath: string, langTexturePath: string) {
this._languagePack.setAssetsPath(langjsonPath, langTexturePath);
}
/**
* 根据data获取对应语种的字符
* @param labId
* @param arr
*/
public getLangByID(labId: string): string {
return this._languagePack.getLangByID(labId);
}
/**
* 下载语言包素材资源
* 包括语言json配置和语言纹理包
* @param lang
* @param callback
*/
public loadLanguageAssets(lang: string, callback: Function) {
lang = lang.toLowerCase();
return this._languagePack.loadLanguageAssets(lang, callback);
}
/**
* 释放不需要的语言包资源
* @param lang
*/
public releaseLanguageAssets(lang: string) {
lang = lang.toLowerCase();
this._languagePack.releaseLanguageAssets(lang);
this.dispatchEvent(LanguageEvent.RELEASE_RES, lang);
}
}

View File

@@ -0,0 +1,14 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "3757cc59-a8b6-4e6c-876f-3ff810e98ba3",
"files": [],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/gui/language/Language.js",
"importerSettings": 4,
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,140 @@
import { CCString, Component, error, Label, warn, _decorator } from "cc";
import { EDITOR } from "cc/env";
import { engine } from "../../Engine";
const { ccclass, property, menu } = _decorator;
@ccclass("LangLabelParamsItem")
export class LangLabelParamsItem {
@property
key: string = "";
@property
value: string = "";
}
@ccclass("LanguageLabel")
@menu('ui/language/LanguageLabel')
export class LanguageLabel extends Component {
@property({
type: LangLabelParamsItem,
displayName: "params"
})
private _params: Array<LangLabelParamsItem> = [];
@property({
type: LangLabelParamsItem,
displayName: "params"
})
set params(value: Array<LangLabelParamsItem>) {
this._params = value;
if (!EDITOR) {
this._needUpdate = true;
}
}
get params(): Array<LangLabelParamsItem> {
return this._params || [];
}
@property({ serializable: true })
private _dataID: string = "";
@property({ type: CCString, serializable: true })
get dataID(): string {
return this._dataID || "";
}
set dataID(value: string) {
this._dataID = value;
if (!EDITOR) {
this._needUpdate = true;
}
}
get string(): string {
let _string = engine.i18n.getLangByID(this._dataID);
if (engine.i18n.beforeChangeLabel) {
engine.i18n.beforeChangeLabel(this, _string, this._dataID);
}
if (_string && this._params.length > 0) {
this._params.forEach((item: LangLabelParamsItem) => {
_string = _string.replace(`%{${item.key}}`, item.value)
})
}
if (!_string) {
warn("[LanguageLabel] 未找到语言标识使用dataID替换");
_string = this._dataID;
}
return _string;
}
set language(lang: string) {
this._needUpdate = true;
}
initFontSize: number = 0;
onLoad() {
this._needUpdate = true;
if (!this.getComponent(Label)) error(this.node.name, this._dataID);
this.initFontSize = this.getComponent(Label)!.fontSize;
}
/**
* 默认文本的系统字体名字
*/
public getLabelFont(lang: string): string {
switch (lang) {
case "zh":
case "tr": {
return "SimHei";
}
}
return "Helvetica";
}
/**
* 修改多语言参数,采用惰性求值策略
* @param key 对于i18n表里面的key值
* @param value 替换的文本
*/
setVars(key: string, value: string) {
let haskey = false;
for (let i = 0; i < this._params.length; i++) {
let element: LangLabelParamsItem = this._params[i];
if (element.key === key) {
element.value = value;
haskey = true;
}
}
if (!haskey) {
let ii = new LangLabelParamsItem();
ii.key = key;
ii.value = value;
this._params.push(ii);
}
this._needUpdate = true;
}
private _needUpdate: boolean = false;
update() {
if (this._needUpdate) {
this.updateLabel();
this._needUpdate = false;
}
}
updateLabel() {
do {
if (!this._dataID) {
break;
}
let spcomp = this.getComponent(Label);
if (!spcomp) {
warn("[LanguageLabel], 该节点没有cc.Label组件");
break;
}
spcomp.fontFamily = this.getLabelFont(engine.i18n.currentLanguage);
spcomp.string = this.string;
}
while (false);
}
}

View File

@@ -0,0 +1,14 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "110c8bc4-7793-443c-bfcd-f66786427697",
"files": [],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/gui/language/LanguageLabel.js",
"importerSettings": 4,
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,102 @@
import { director, error, JsonAsset, resources, warn } from "cc";
import { Logger } from "../../utils/Logger";
import { resLoader } from "../../utils/ResLoader";
import { LanguageLabel } from "./LanguageLabel";
import { LanguageSprite } from "./LanguageSprite";
export default class LanguagePack {
private _languageLabels: any = {};
// 默认资源文件目录
private _langjsonPath: string = "lang_json";
private _langTexturePath: string = "lang_texture";
/**
* 设置多语言资源目录
* @param langjsonPath 多语言json目录
* @param langTexturePath 多语言图片目录
*/
public setAssetsPath(langjsonPath: string, langTexturePath: string) {
if (langjsonPath) {
this._langjsonPath = langjsonPath;
}
if (langTexturePath) {
this._langTexturePath = langTexturePath;
}
}
/**
* 刷新语言文字
* @param lang
*/
public updateLanguage(lang: string) {
let lanjson = resLoader.get(`${this._langjsonPath}/${lang}`, JsonAsset);
if (lanjson && lanjson.json) {
this._languageLabels = lanjson.json;
let rootNodes = director.getScene()!.children;
for (let i = 0; i < rootNodes.length; ++i) {
// 更新所有的LanguageLabel节点
let languagelabels = rootNodes[i].getComponentsInChildren(LanguageLabel);
for (let j = 0; j < languagelabels.length; j++) {
languagelabels[j].language = lang;
}
// 更新所有的LanguageSprite节点
let languagesprites = rootNodes[i].getComponentsInChildren(LanguageSprite);
for (let j = 0; j < languagesprites.length; j++) {
languagesprites[j].language = lang;
}
}
}
else {
warn("没有找到指定语言内容配置", lang);
}
}
/**
* 根据dataID获取对应语言的字符
* @param uuid
*/
public getLangByID(labId: string): string {
return this._languageLabels[labId] || "";
}
/**
* 下载对应语言包资源
* @param lang 语言标识
* @param callback 下载完成回调
*/
public loadLanguageAssets(lang: string, callback: Function) {
let lang_texture_path = `${this._langTexturePath}/${lang}`;
let lang_json_path = `${this._langjsonPath}/${lang}`;
resLoader.loadDir(lang_texture_path, (err: any) => {
if (err) {
error(err);
callback(err);
return;
}
Logger.logBusiness(lang_texture_path, "下载语言包 textures 资源");
resLoader.load(lang_json_path, JsonAsset, (err) => {
if (err) {
error(err);
callback(err);
return;
}
Logger.trace(lang_json_path, "下载语言包 json 资源");
callback(err, lang);
})
})
}
/**
* 释放某个语言的语言包资源包括json
* @param lang
*/
public releaseLanguageAssets(lang: string) {
let langpath = `${this._langTexturePath}/${lang}`;
resLoader.releaseDir(langpath);
Logger.logBusiness(langpath, "释放语言图片资源");
let langjsonpath = `${this._langjsonPath}/${lang}`;
resLoader.release(langjsonpath);
Logger.logBusiness(langjsonpath, "释放语言文字资源");
}
}

View File

@@ -0,0 +1,14 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "2ffebca3-e7dc-4873-8bf8-059b72f120e6",
"files": [],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/gui/language/LanguagePack.js",
"importerSettings": 4,
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,16 @@
{
"ver": "4.0.1",
"importer": "typescript",
"imported": true,
"uuid": "a1bd53a3-66a7-4477-8c18-9254dc5983bf",
"files": [
".js",
".trans"
],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/gui/language/LanguagePointLabel.js",
"importerSettings": 3
}
}

View File

@@ -0,0 +1,46 @@
import { Component, Size, Sprite, SpriteFrame, UITransform, _decorator } from "cc";
import { engine } from "../../Engine";
import { Logger } from "../../utils/Logger";
import { resLoader } from "../../utils/ResLoader";
const { ccclass, property, menu } = _decorator;
@ccclass("LanguageSprite")
@menu('ui/language/LanguageSprite')
export class LanguageSprite extends Component {
@property({
tooltip: "资源路径language/texture/内的相对路径)"
})
public path: string = "";
@property({
tooltip: "是否设置为图片原始资源大小"
})
private isRawSize: boolean = true;
start() {
this.language = engine.i18n.currentLanguage;
}
set language(lang: string) {
this.updateSprite(lang);
}
updateSprite(lang: string) {
let spcomp: Sprite = this.getComponent(Sprite)!;
// 获取语言标记
let path = `language/texture/${lang}/${this.path}/spriteFrame`;
let res = resLoader.get(path, SpriteFrame);
if (!res) {
Logger.erroring("[LanguageSprite] 资源不存在 " + path);
}
spcomp.spriteFrame = res;
/** 修改节点为原始图片资源大小 */
if (this.isRawSize) {
//@ts-ignore
let rawSize = res._originalSize as Size;
spcomp.getComponent(UITransform)?.setContentSize(rawSize);
}
}
}

View File

@@ -0,0 +1,14 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "11b9693f-4486-45e7-b2e8-7a1c7297a1ec",
"files": [],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/gui/language/LanguageSprite.js",
"importerSettings": 4,
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "7ebfb1c8-171b-4e1b-94e3-9921824f003c",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,53 @@
/*
* Gui模块常用类型定义
*/
import { Node } from "cc";
/*** 回调参数对象定义 */
export interface UICallbacks {
/** 节点添加到层级以后的回调 */
onAdded?: (node: Node, params: any) => void,
/**
* destroy之后回调
*/
onRemoved?: (node: Node | null, params: any) => void,
/**
* 注意:调用`delete`或`$delete`才会触发此回调,如果`this.node.destroy()`,该回调将直接忽略。
*
* 如果指定onBeforeRemoved则next必须调用否则节点不会被正常删除。
*
* 比如希望节点做一个FadeOut然后删除则可以在`onBeforeRemoved`当中播放action动画动画结束后调用next
*
* */
onBeforeRemove?: (node: Node, next: Function) => void
}
/** gui.popup.add 弹框层回调对象定义 */
export interface PopViewParams extends UICallbacks {
/** 是否显示暗色背景 */
modal?: boolean,
/** 是否触摸背景关闭弹窗 */
touchClose?: boolean,
/** 控制暗色背景的透明度 默认为190*/
opacity?: number;
}
/** 本类型仅供gui模块内部使用请勿在功能逻辑中使用 */
export class ViewParams {
/** 界面唯一标识 */
public uuid!: string;
/** 预制路径 */
public prefabPath!: string;
/** 传递给打开界面的参数 */
public params: any | null;
/** 窗口事件 */
public callbacks!: UICallbacks | null;
/** 是否在使用状态 */
public valid: boolean = true;
/** 界面根节点 */
public node: Node | null = null;;
}

View File

@@ -0,0 +1,14 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "82d3af5c-ef52-4490-8f79-777aac7079bc",
"files": [],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/script/core/gui/layer/Defines.js",
"importerSettings": 4,
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,80 @@
import { Component, Node, _decorator } from "cc";
import { ViewParams } from "./Defines";
const { ccclass } = _decorator;
/** 窗口事件触发组件 */
@ccclass('DelegateComponent')
export class DelegateComponent extends Component {
viewParams: ViewParams | null = null;
add() {
let viewParams = this.viewParams!;
// 触发窗口组件上添加到父节点后的事件
this.applyComponentsFunction(this.node, "onAdded", viewParams.params);
if (typeof viewParams.callbacks!.onAdded === "function") {
viewParams.callbacks!.onAdded(this.node, viewParams.params);
}
}
/** 删除节点该方法只能调用一次将会触发onBeforeRemoved回调 */
remove(isDestroy: boolean) {
let viewParams = this.viewParams!;
if (viewParams.valid) {
// 触发窗口组件上移除之前的事件
this.applyComponentsFunction(this.node, "onBeforeRemove", viewParams.params);
// 通知外部对象窗口组件上移除之前的事件(关闭窗口前的关闭动画处理)
if (typeof viewParams.callbacks!.onBeforeRemove === "function") {
viewParams.callbacks!.onBeforeRemove(
this.node,
() => {
this.removed(viewParams, isDestroy);
});
}
else {
this.removed(viewParams, isDestroy);
}
}
}
/** 窗口组件中触发移除事件与释放窗口对象 */
private removed(viewParams: ViewParams, isDestroy: boolean) {
viewParams.valid = false;
if (typeof viewParams.callbacks!.onRemoved === "function") {
viewParams.callbacks!.onRemoved(this.node, viewParams.params);
}
if (isDestroy)
this.node.destroy();
else
this.node.removeFromParent();
}
onDestroy() {
let viewParams = this.viewParams!;
// 触发窗口组件上窗口移除之后的事件
this.applyComponentsFunction(this.node, "onRemoved", viewParams.params);
// 通知外部对象窗口移除之后的事件
if (typeof viewParams.callbacks!.onRemoved === "function") {
viewParams.callbacks!.onRemoved(this.node, viewParams.params);
}
this.viewParams = null;
}
protected applyComponentsFunction(node: Node, funName: string, params: any) {
for (let i = 0; i < node.components.length; i++) {
let component: any = node.components[i];
let func = component[funName];
if (func) {
func.call(component, params);
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More