mirror of
https://gitee.com/dgflash/oops-framework.git
synced 2026-06-18 19:52:10 +08:00
初次提交
This commit is contained in:
9
assets/script/core/2D.meta
Normal file
9
assets/script/core/2D.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "0.0.1",
|
||||
"importer": "*",
|
||||
"imported": true,
|
||||
"uuid": "b60ccc66-5f47-4d5b-a7ba-8ec751c0c7e9",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
42
assets/script/core/Engine.ts
Normal file
42
assets/script/core/Engine.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
15
assets/script/core/Global.ts.meta
Normal file
15
assets/script/core/Global.ts.meta
Normal 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"
|
||||
}
|
||||
}
|
||||
16
assets/script/core/Initialize.ts.meta
Normal file
16
assets/script/core/Initialize.ts.meta
Normal 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
|
||||
}
|
||||
}
|
||||
66
assets/script/core/Root.ts
Normal file
66
assets/script/core/Root.ts
Normal 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
14
assets/script/core/Root.ts.meta
Normal file
14
assets/script/core/Root.ts.meta
Normal 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": []
|
||||
}
|
||||
}
|
||||
12
assets/script/core/common.meta
Normal file
12
assets/script/core/common.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "c0dd7e2e-f8c5-4520-9fda-21942756751a",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
12
assets/script/core/common/audio.meta
Normal file
12
assets/script/core/common/audio.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "a643bc0f-eea9-46d6-8771-708dbdda9583",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
16
assets/script/core/common/audio/Audio.ts.meta
Normal file
16
assets/script/core/common/audio/Audio.ts.meta
Normal 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
|
||||
}
|
||||
}
|
||||
45
assets/script/core/common/audio/AudioComponent.ts
Normal file
45
assets/script/core/common/audio/AudioComponent.ts
Normal 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);
|
||||
// }
|
||||
// }
|
||||
14
assets/script/core/common/audio/AudioComponent.ts.meta
Normal file
14
assets/script/core/common/audio/AudioComponent.ts.meta
Normal 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": []
|
||||
}
|
||||
}
|
||||
33
assets/script/core/common/audio/AudioEffect.ts
Normal file
33
assets/script/core/common/audio/AudioEffect.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
9
assets/script/core/common/audio/AudioEffect.ts.meta
Normal file
9
assets/script/core/common/audio/AudioEffect.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.22",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "e52d2cac-6350-446e-9713-d82cf45b0b2c",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
159
assets/script/core/common/audio/AudioManager.ts
Normal file
159
assets/script/core/common/audio/AudioManager.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
14
assets/script/core/common/audio/AudioManager.ts.meta
Normal file
14
assets/script/core/common/audio/AudioManager.ts.meta
Normal 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": []
|
||||
}
|
||||
}
|
||||
65
assets/script/core/common/audio/AudioMusic.ts
Normal file
65
assets/script/core/common/audio/AudioMusic.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
9
assets/script/core/common/audio/AudioMusic.ts.meta
Normal file
9
assets/script/core/common/audio/AudioMusic.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.22",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "5c1f392a-19eb-4188-8bf8-f3f0afb9a42f",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
12
assets/script/core/common/component.meta
Normal file
12
assets/script/core/common/component.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "cc263b2f-8b27-4e4e-bad4-3860c593e0b3",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
82
assets/script/core/common/component/GameComponent.ts
Normal file
82
assets/script/core/common/component/GameComponent.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.22",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "ac536f92-9564-4e5d-bcf0-cc66fb91e8aa",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
12
assets/script/core/common/component/geometry.meta
Normal file
12
assets/script/core/common/component/geometry.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "fd6e96ff-96ce-44f0-8eda-24aaf8c2e936",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"ver": "4.0.22",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "16f74800-b991-41f0-9345-1e8ef3d4c7b1",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"simulateGlobals": []
|
||||
}
|
||||
}
|
||||
12
assets/script/core/common/event.meta
Normal file
12
assets/script/core/common/event.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "1e590879-0446-4a14-b8c2-0929fe3b3b19",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
16
assets/script/core/common/event/AnimationEvent.ts.meta
Normal file
16
assets/script/core/common/event/AnimationEvent.ts.meta
Normal 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
|
||||
}
|
||||
}
|
||||
13
assets/script/core/common/event/EngineMessage.ts
Normal file
13
assets/script/core/common/event/EngineMessage.ts
Normal 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',
|
||||
}
|
||||
14
assets/script/core/common/event/EngineMessage.ts.meta
Normal file
14
assets/script/core/common/event/EngineMessage.ts.meta
Normal 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": []
|
||||
}
|
||||
}
|
||||
54
assets/script/core/common/event/EventDispatcher.ts
Normal file
54
assets/script/core/common/event/EventDispatcher.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
14
assets/script/core/common/event/EventDispatcher.ts.meta
Normal file
14
assets/script/core/common/event/EventDispatcher.ts.meta
Normal 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": []
|
||||
}
|
||||
}
|
||||
154
assets/script/core/common/event/MessageManager.ts
Normal file
154
assets/script/core/common/event/MessageManager.ts
Normal 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;
|
||||
14
assets/script/core/common/event/MessageManager.ts.meta
Normal file
14
assets/script/core/common/event/MessageManager.ts.meta
Normal 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": []
|
||||
}
|
||||
}
|
||||
12
assets/script/core/common/manager.meta
Normal file
12
assets/script/core/common/manager.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "257c907c-f9fd-452e-9406-7ecdb360cd45",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
114
assets/script/core/common/manager/RandomManager.ts
Normal file
114
assets/script/core/common/manager/RandomManager.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
9
assets/script/core/common/manager/RandomManager.ts.meta
Normal file
9
assets/script/core/common/manager/RandomManager.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.22",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "b843ea76-f1d0-4ed5-b9e7-98b0509b6be8",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
236
assets/script/core/common/manager/TimerManager.ts
Normal file
236
assets/script/core/common/manager/TimerManager.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
14
assets/script/core/common/manager/TimerManager.ts.meta
Normal file
14
assets/script/core/common/manager/TimerManager.ts.meta
Normal 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": []
|
||||
}
|
||||
}
|
||||
12
assets/script/core/common/storage.meta
Normal file
12
assets/script/core/common/storage.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "40b31022-d422-493f-a40d-2621c96ec9f2",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
47
assets/script/core/common/storage/EncryptUtil.ts
Normal file
47
assets/script/core/common/storage/EncryptUtil.ts
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
9
assets/script/core/common/storage/EncryptUtil.ts.meta
Normal file
9
assets/script/core/common/storage/EncryptUtil.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.22",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "a74a14e9-aeda-4bc7-9e16-f2fee1a4b927",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
202
assets/script/core/common/storage/Md5.ts
Normal file
202
assets/script/core/common/storage/Md5.ts
Normal 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)
|
||||
}
|
||||
9
assets/script/core/common/storage/Md5.ts.meta
Normal file
9
assets/script/core/common/storage/Md5.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.22",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "0c84ecf9-5446-4322-8073-3e74289d8d1a",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
143
assets/script/core/common/storage/SqlUtil.ts
Normal file
143
assets/script/core/common/storage/SqlUtil.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
9
assets/script/core/common/storage/SqlUtil.ts.meta
Normal file
9
assets/script/core/common/storage/SqlUtil.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.22",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "85c76a54-4620-4425-b443-b657fe7fb087",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
29
assets/script/core/common/storage/crypto-js.d.ts
vendored
Normal file
29
assets/script/core/common/storage/crypto-js.d.ts
vendored
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
9
assets/script/core/common/storage/crypto-js.d.ts.meta
Normal file
9
assets/script/core/common/storage/crypto-js.d.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.22",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "3c1c0c1d-c9a4-4dd2-9249-254305a4c73b",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
6030
assets/script/core/common/storage/crypto-js.js
Normal file
6030
assets/script/core/common/storage/crypto-js.js
Normal file
File diff suppressed because it is too large
Load Diff
9
assets/script/core/common/storage/crypto-js.js.meta
Normal file
9
assets/script/core/common/storage/crypto-js.js.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.22",
|
||||
"importer": "javascript",
|
||||
"imported": true,
|
||||
"uuid": "83485788-0873-4c9f-a03f-c9f9b5ee44ca",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
12
assets/script/core/common/texture.meta
Normal file
12
assets/script/core/common/texture.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "7f7d89cd-a784-4243-824c-5c0a97c5ca9f",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
35
assets/script/core/common/texture/RtToModel.ts
Normal file
35
assets/script/core/common/texture/RtToModel.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
9
assets/script/core/common/texture/RtToModel.ts.meta
Normal file
9
assets/script/core/common/texture/RtToModel.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.22",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "ed72a01b-9451-4614-828a-9a41072a27ea",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
105
assets/script/core/common/texture/RtToSprite.ts
Normal file
105
assets/script/core/common/texture/RtToSprite.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
9
assets/script/core/common/texture/RtToSprite.ts.meta
Normal file
9
assets/script/core/common/texture/RtToSprite.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.22",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "a266c942-aa61-45ac-9dcd-7ad398427989",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
14
assets/script/core/engine.ts.meta
Normal file
14
assets/script/core/engine.ts.meta
Normal 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": []
|
||||
}
|
||||
}
|
||||
12
assets/script/core/game.meta
Normal file
12
assets/script/core/game.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "8e279ff4-2659-4fc1-8026-816ebfa0a20e",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
8
assets/script/core/game/GameManager.ts
Normal file
8
assets/script/core/game/GameManager.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Component } from 'cc';
|
||||
|
||||
/** 游戏世界管理 */
|
||||
export class GameManager extends Component {
|
||||
onLoad() {
|
||||
|
||||
}
|
||||
}
|
||||
14
assets/script/core/game/GameManager.ts.meta
Normal file
14
assets/script/core/game/GameManager.ts.meta
Normal 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": []
|
||||
}
|
||||
}
|
||||
15
assets/script/core/game/PlayerController.ts.meta
Normal file
15
assets/script/core/game/PlayerController.ts.meta
Normal 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"
|
||||
}
|
||||
}
|
||||
15
assets/script/core/game/World.ts.meta
Normal file
15
assets/script/core/game/World.ts.meta
Normal 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"
|
||||
}
|
||||
}
|
||||
12
assets/script/core/game/camera.meta
Normal file
12
assets/script/core/game/camera.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "02c1592d-2be3-47fe-9f64-557dbd901fd6",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
157
assets/script/core/game/camera/FreeFlightCamera.ts
Normal file
157
assets/script/core/game/camera/FreeFlightCamera.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
assets/script/core/game/camera/FreeFlightCamera.ts.meta
Normal file
14
assets/script/core/game/camera/FreeFlightCamera.ts.meta
Normal 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": []
|
||||
}
|
||||
}
|
||||
15
assets/script/core/game/first-person-camera.ts.meta
Normal file
15
assets/script/core/game/first-person-camera.ts.meta
Normal 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"
|
||||
}
|
||||
}
|
||||
12
assets/script/core/gui.meta
Normal file
12
assets/script/core/gui.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "dedee4cf-1d22-402a-833a-0bdc09ebbdea",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
51
assets/script/core/gui/GUI.ts
Normal file
51
assets/script/core/gui/GUI.ts
Normal 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, "屏幕尺寸");
|
||||
}
|
||||
}
|
||||
14
assets/script/core/gui/GUI.ts.meta
Normal file
14
assets/script/core/gui/GUI.ts.meta
Normal 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": []
|
||||
}
|
||||
}
|
||||
12
assets/script/core/gui/badge.meta
Normal file
12
assets/script/core/gui/badge.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "df85f52f-c354-483d-af86-9cd0b96cdc96",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
133
assets/script/core/gui/badge/Badge.ts
Normal file
133
assets/script/core/gui/badge/Badge.ts
Normal 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() { }
|
||||
}
|
||||
11
assets/script/core/gui/badge/Badge.ts.meta
Normal file
11
assets/script/core/gui/badge/Badge.ts.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"ver": "4.0.22",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "bac76a30-f785-48f8-8a40-0750d050a7ea",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"simulateGlobals": []
|
||||
}
|
||||
}
|
||||
78
assets/script/core/gui/badge/RoundRectMask.ts
Normal file
78
assets/script/core/gui/badge/RoundRectMask.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
assets/script/core/gui/badge/RoundRectMask.ts.meta
Normal file
11
assets/script/core/gui/badge/RoundRectMask.ts.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"ver": "4.0.22",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "da0f8081-4246-43a9-a7a4-36e111a8a8c0",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"simulateGlobals": []
|
||||
}
|
||||
}
|
||||
12
assets/script/core/gui/button.meta
Normal file
12
assets/script/core/gui/button.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "c35d33a8-9290-47c3-8f06-35fb298a5556",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
42
assets/script/core/gui/button/ButtonEffect.ts
Normal file
42
assets/script/core/gui/button/ButtonEffect.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
11
assets/script/core/gui/button/ButtonEffect.ts.meta
Normal file
11
assets/script/core/gui/button/ButtonEffect.ts.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"ver": "4.0.22",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "1be3686b-18ee-4ecf-a11a-a60eb2816d37",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"simulateGlobals": []
|
||||
}
|
||||
}
|
||||
54
assets/script/core/gui/button/ButtonSimple.ts
Normal file
54
assets/script/core/gui/button/ButtonSimple.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
11
assets/script/core/gui/button/ButtonSimple.ts.meta
Normal file
11
assets/script/core/gui/button/ButtonSimple.ts.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"ver": "4.0.22",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "8d645c8e-6d7d-45bc-97e4-dac6e2d2bc69",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"simulateGlobals": []
|
||||
}
|
||||
}
|
||||
61
assets/script/core/gui/button/ButtonTouchLong.ts
Normal file
61
assets/script/core/gui/button/ButtonTouchLong.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
assets/script/core/gui/button/ButtonTouchLong.ts.meta
Normal file
11
assets/script/core/gui/button/ButtonTouchLong.ts.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"ver": "4.0.22",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "da96e9fb-598a-4f4d-a3c8-90ed65fcdbb1",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"simulateGlobals": []
|
||||
}
|
||||
}
|
||||
12
assets/script/core/gui/label.meta
Normal file
12
assets/script/core/gui/label.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "bff487ed-c8aa-446b-a2a4-24408ef0480c",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
95
assets/script/core/gui/label/LabelChange.ts
Normal file
95
assets/script/core/gui/label/LabelChange.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
assets/script/core/gui/label/LabelChange.ts.meta
Normal file
14
assets/script/core/gui/label/LabelChange.ts.meta
Normal 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": []
|
||||
}
|
||||
}
|
||||
43
assets/script/core/gui/label/LabelColor.ts
Normal file
43
assets/script/core/gui/label/LabelColor.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
9
assets/script/core/gui/label/LabelColor.ts.meta
Normal file
9
assets/script/core/gui/label/LabelColor.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.22",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "c2a31250-e8a8-459d-a40b-ebd3f3498b78",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
48
assets/script/core/gui/label/LabelNumber.ts
Normal file
48
assets/script/core/gui/label/LabelNumber.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
14
assets/script/core/gui/label/LabelNumber.ts.meta
Normal file
14
assets/script/core/gui/label/LabelNumber.ts.meta
Normal 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": []
|
||||
}
|
||||
}
|
||||
135
assets/script/core/gui/label/LabelTime.ts
Normal file
135
assets/script/core/gui/label/LabelTime.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
14
assets/script/core/gui/label/LabelTime.ts.meta
Normal file
14
assets/script/core/gui/label/LabelTime.ts.meta
Normal 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": []
|
||||
}
|
||||
}
|
||||
12
assets/script/core/gui/language.meta
Normal file
12
assets/script/core/gui/language.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "4d72408b-5cb6-4ca1-aad9-d73b890db1a0",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
127
assets/script/core/gui/language/Language.ts
Normal file
127
assets/script/core/gui/language/Language.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
14
assets/script/core/gui/language/Language.ts.meta
Normal file
14
assets/script/core/gui/language/Language.ts.meta
Normal 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": []
|
||||
}
|
||||
}
|
||||
140
assets/script/core/gui/language/LanguageLabel.ts
Normal file
140
assets/script/core/gui/language/LanguageLabel.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
14
assets/script/core/gui/language/LanguageLabel.ts.meta
Normal file
14
assets/script/core/gui/language/LanguageLabel.ts.meta
Normal 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": []
|
||||
}
|
||||
}
|
||||
102
assets/script/core/gui/language/LanguagePack.ts
Normal file
102
assets/script/core/gui/language/LanguagePack.ts
Normal 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, "释放语言文字资源");
|
||||
}
|
||||
}
|
||||
14
assets/script/core/gui/language/LanguagePack.ts.meta
Normal file
14
assets/script/core/gui/language/LanguagePack.ts.meta
Normal 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": []
|
||||
}
|
||||
}
|
||||
16
assets/script/core/gui/language/LanguagePointLabel.ts.meta
Normal file
16
assets/script/core/gui/language/LanguagePointLabel.ts.meta
Normal 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
|
||||
}
|
||||
}
|
||||
46
assets/script/core/gui/language/LanguageSprite.ts
Normal file
46
assets/script/core/gui/language/LanguageSprite.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
14
assets/script/core/gui/language/LanguageSprite.ts.meta
Normal file
14
assets/script/core/gui/language/LanguageSprite.ts.meta
Normal 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": []
|
||||
}
|
||||
}
|
||||
12
assets/script/core/gui/layer.meta
Normal file
12
assets/script/core/gui/layer.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "7ebfb1c8-171b-4e1b-94e3-9921824f003c",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
53
assets/script/core/gui/layer/Defines.ts
Normal file
53
assets/script/core/gui/layer/Defines.ts
Normal 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;;
|
||||
}
|
||||
14
assets/script/core/gui/layer/Defines.ts.meta
Normal file
14
assets/script/core/gui/layer/Defines.ts.meta
Normal 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": []
|
||||
}
|
||||
}
|
||||
80
assets/script/core/gui/layer/DelegateComponent.ts
Normal file
80
assets/script/core/gui/layer/DelegateComponent.ts
Normal 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
Reference in New Issue
Block a user