[add] Engine
This commit is contained in:
		
							
								
								
									
										12
									
								
								assets/Script/Engine/Utils/Act.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								assets/Script/Engine/Utils/Act.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.1.2",
 | 
			
		||||
  "uuid": "b43bf5ea-67c5-4fc8-9893-1f406a52508f",
 | 
			
		||||
  "isBundle": false,
 | 
			
		||||
  "bundleName": "",
 | 
			
		||||
  "priority": 1,
 | 
			
		||||
  "compressionType": {},
 | 
			
		||||
  "optimizeHotUpdate": {},
 | 
			
		||||
  "inlineSpriteFrames": {},
 | 
			
		||||
  "isRemoteBundle": {},
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										55
									
								
								assets/Script/Engine/Utils/Act/Shake.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								assets/Script/Engine/Utils/Act/Shake.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
const { ccclass } = cc._decorator;
 | 
			
		||||
 | 
			
		||||
@ccclass
 | 
			
		||||
export default class Shake extends cc.ActionInterval {
 | 
			
		||||
    private _init: boolean = false;
 | 
			
		||||
    private _initial_x: number = 0;
 | 
			
		||||
    private _initial_y: number = 0;
 | 
			
		||||
    private _strength_x: number = 0;
 | 
			
		||||
    private _strength_y: number = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *  建立抖動動畫
 | 
			
		||||
     * @param {number} duration     動畫持續時長
 | 
			
		||||
     * @param {number} strength_x   抖動幅度: x方向
 | 
			
		||||
     * @param {number} strength_y   抖動幅度: y方向
 | 
			
		||||
     * @returns {Shake}
 | 
			
		||||
     */
 | 
			
		||||
    public static create(duration: number, strength_x: number, strength_y: number): Shake {
 | 
			
		||||
        let act: Shake = new Shake();
 | 
			
		||||
        act.initWithDuration(duration, strength_x, strength_y);
 | 
			
		||||
        return act;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public initWithDuration(duration: number, strength_x: number, strength_y: number): boolean {
 | 
			
		||||
        cc.ActionInterval.prototype['initWithDuration'].apply(this, arguments);
 | 
			
		||||
        this._strength_x = strength_x;
 | 
			
		||||
        this._strength_y = strength_y;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public fgRangeRand(min: number, max: number): number {
 | 
			
		||||
        let rnd: number = Math.random();
 | 
			
		||||
        return rnd * (max - min) + min;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public update(time: number): void {
 | 
			
		||||
        let randx = this.fgRangeRand(-this._strength_x, this._strength_x);
 | 
			
		||||
        let randy = this.fgRangeRand(-this._strength_y, this._strength_y);
 | 
			
		||||
        this.getTarget().setPosition(randx + this._initial_x, randy + this._initial_y);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public startWithTarget(target: cc.Node): void {
 | 
			
		||||
        cc.ActionInterval.prototype['startWithTarget'].apply(this, arguments);
 | 
			
		||||
        if (!this._init) {
 | 
			
		||||
            this._init = true;
 | 
			
		||||
            this._initial_x = target.x;
 | 
			
		||||
            this._initial_y = target.y;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public stop(): void {
 | 
			
		||||
        this.getTarget().setPosition(new cc.Vec2(this._initial_x, this._initial_y));
 | 
			
		||||
        cc.ActionInterval.prototype['stop'].apply(this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								assets/Script/Engine/Utils/Act/Shake.ts.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								assets/Script/Engine/Utils/Act/Shake.ts.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.0.8",
 | 
			
		||||
  "uuid": "c5872cc0-91a4-49cb-a055-e037accd801d",
 | 
			
		||||
  "isPlugin": false,
 | 
			
		||||
  "loadPluginInWeb": true,
 | 
			
		||||
  "loadPluginInNative": true,
 | 
			
		||||
  "loadPluginInEditor": false,
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								assets/Script/Engine/Utils/Audio.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								assets/Script/Engine/Utils/Audio.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.1.2",
 | 
			
		||||
  "uuid": "d042d487-d962-4d90-920e-70ab9b8b383c",
 | 
			
		||||
  "isBundle": false,
 | 
			
		||||
  "bundleName": "",
 | 
			
		||||
  "priority": 1,
 | 
			
		||||
  "compressionType": {},
 | 
			
		||||
  "optimizeHotUpdate": {},
 | 
			
		||||
  "inlineSpriteFrames": {},
 | 
			
		||||
  "isRemoteBundle": {},
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										184
									
								
								assets/Script/Engine/Utils/Audio/CSAudio.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								assets/Script/Engine/Utils/Audio/CSAudio.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,184 @@
 | 
			
		||||
import LocalStorageData from "../../Data/LocalStorageData";
 | 
			
		||||
 | 
			
		||||
export default class CSAudio {
 | 
			
		||||
	private static _instance: CSAudio = null;
 | 
			
		||||
	public static get Instance(): CSAudio { return this._instance; }
 | 
			
		||||
	private _idToClip: Map<number, cc.AudioClip> = new Map<number, cc.AudioClip>();
 | 
			
		||||
	private _idToPath: Map<number, string> = new Map<number, string>();
 | 
			
		||||
	private _idToAudioId: Map<number, number> = new Map<number, number>();
 | 
			
		||||
	private _currentMusic: number = -1;
 | 
			
		||||
	/**判斷音效播放太過頻繁不疊加 */
 | 
			
		||||
	private _lastPlaySoundTime: Map<string, number> = new Map<string, number>();
 | 
			
		||||
	private readonly _canPlaySoundCutTime: number = 10;
 | 
			
		||||
 | 
			
		||||
	constructor() {
 | 
			
		||||
		CSAudio._instance = this;
 | 
			
		||||
		if (LocalStorageData.Instance.MusicType) {
 | 
			
		||||
			this.SetMusicVolume(+LocalStorageData.Instance.MusicType);
 | 
			
		||||
		} else {
 | 
			
		||||
			this.SetMusicVolume(0.3);
 | 
			
		||||
		}
 | 
			
		||||
		if (LocalStorageData.Instance.SoundType) {
 | 
			
		||||
			this.SetSoundVolume(+LocalStorageData.Instance.SoundType);
 | 
			
		||||
		} else {
 | 
			
		||||
			this.SetSoundVolume(0.3);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public AddClipsInfo(clips: Map<number, cc.AudioClip>, pathes: Map<number, string>): void {
 | 
			
		||||
		this._idToClip = clips;
 | 
			
		||||
		this._idToPath = pathes;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 設定AudioID
 | 
			
		||||
	 * @param id
 | 
			
		||||
	 * @param audioId
 | 
			
		||||
	 */
 | 
			
		||||
	private _setAudioId(id: number, audioId: number): void {
 | 
			
		||||
		this._idToAudioId.set(id, audioId);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 取得AudioID
 | 
			
		||||
	 * @param id
 | 
			
		||||
	 */
 | 
			
		||||
	private _getAudioId(id: number): number {
 | 
			
		||||
		if (this._idToAudioId.has(id)) {
 | 
			
		||||
			return this._idToAudioId.get(id);
 | 
			
		||||
		} else {
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 打開音效音量
 | 
			
		||||
	 */
 | 
			
		||||
	public OpenSound(): void {
 | 
			
		||||
		this.SetSoundVolume(0.3);
 | 
			
		||||
	}
 | 
			
		||||
	/**
 | 
			
		||||
	 * 關閉音效音量
 | 
			
		||||
	 */
 | 
			
		||||
	public CloseSound(): void {
 | 
			
		||||
		this.SetSoundVolume(0.0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 設定音效音量
 | 
			
		||||
	 * @param volume
 | 
			
		||||
	 */
 | 
			
		||||
	public SetSoundVolume(volume: number): void {
 | 
			
		||||
		LocalStorageData.Instance.SoundType = volume.toString();
 | 
			
		||||
		cc.audioEngine.setEffectsVolume(volume);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 播放音效
 | 
			
		||||
	 * @param id
 | 
			
		||||
	 * @param loop
 | 
			
		||||
	 */
 | 
			
		||||
	public PlaySound(id: number, loop: boolean = false): void {
 | 
			
		||||
		// 靜音後有一禎仍然會撥放的問題:音量>0才撥放
 | 
			
		||||
		if (cc.audioEngine.getEffectsVolume() <= 0) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		if (this._idToClip.has(id)) {
 | 
			
		||||
			let path: string = this._idToPath.get(id);
 | 
			
		||||
			let timenum: number = new Date().getTime();
 | 
			
		||||
			if (!this._lastPlaySoundTime.has(path)) {
 | 
			
		||||
				this._lastPlaySoundTime.set(path, timenum);
 | 
			
		||||
				let audioId: number = cc.audioEngine.playEffect(this._idToClip.get(id), loop);
 | 
			
		||||
				this._setAudioId(id, audioId);
 | 
			
		||||
			} else {
 | 
			
		||||
				let lastTime: number = this._lastPlaySoundTime.get(path);
 | 
			
		||||
				if (timenum - lastTime > this._canPlaySoundCutTime) {
 | 
			
		||||
					this._lastPlaySoundTime.set(path, timenum);
 | 
			
		||||
					let audioId: number = cc.audioEngine.playEffect(this._idToClip.get(id), loop);
 | 
			
		||||
					this._setAudioId(id, audioId);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			cc.error("未知的Sound Id: ", id);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 停止音效
 | 
			
		||||
	 * @param id
 | 
			
		||||
	 */
 | 
			
		||||
	public StopSound(id: number): void {
 | 
			
		||||
		let audioId = this._getAudioId(id);
 | 
			
		||||
		if (audioId >= 0) {
 | 
			
		||||
			cc.audioEngine.stopEffect(audioId);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 打開音樂音量
 | 
			
		||||
	 */
 | 
			
		||||
	public OpenMusic(): void {
 | 
			
		||||
		this.SetMusicVolume(0.3);
 | 
			
		||||
	}
 | 
			
		||||
	/**
 | 
			
		||||
	 * 關閉音樂音量
 | 
			
		||||
	 */
 | 
			
		||||
	public CloseMusic(): void {
 | 
			
		||||
		this.SetMusicVolume(0.0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 設定音樂音量
 | 
			
		||||
	 * @param volume
 | 
			
		||||
	 */
 | 
			
		||||
	public SetMusicVolume(volume: number): void {
 | 
			
		||||
		cc.audioEngine.setMusicVolume(volume);
 | 
			
		||||
		LocalStorageData.Instance.MusicType = volume.toString();
 | 
			
		||||
		// 靜音後有一禎仍然會撥放的問題:背景音樂要回復
 | 
			
		||||
		if (this._currentMusic != -1 && volume > 0 && !cc.audioEngine.isMusicPlaying()) {
 | 
			
		||||
			this._ccMusicPlayId = cc.audioEngine.playMusic(this._idToClip.get(this._currentMusic), true);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 撥放音樂
 | 
			
		||||
	 * @param id
 | 
			
		||||
	 * @param loop
 | 
			
		||||
	 */
 | 
			
		||||
	public PlayMusic(id: number, loop: boolean = true): void {
 | 
			
		||||
		if (this._currentMusic != id) {
 | 
			
		||||
			if (this._idToClip.has(id)) {
 | 
			
		||||
				// 靜音後有一禎仍然會撥放的問題:音量>0才撥放
 | 
			
		||||
				if (cc.audioEngine.getMusicVolume() > 0) {
 | 
			
		||||
					this._ccMusicPlayId = cc.audioEngine.playMusic(this._idToClip.get(id), loop);
 | 
			
		||||
				}
 | 
			
		||||
				this._currentMusic = id;
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				cc.error("未知的Music Id: ", id);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	private _ccMusicPlayId: number = -1;
 | 
			
		||||
	private _currentMusicTime: number = -1;
 | 
			
		||||
	public pauseOrResume(isPause?: boolean) {
 | 
			
		||||
		if (isPause) {
 | 
			
		||||
			this._currentMusicTime = cc.audioEngine.getCurrentTime(this._ccMusicPlayId);
 | 
			
		||||
			cc.audioEngine.pauseAll();
 | 
			
		||||
			cc.audioEngine.stopMusic();
 | 
			
		||||
		} else {
 | 
			
		||||
			cc.audioEngine.resumeAll();
 | 
			
		||||
			cc.audioEngine.setCurrentTime(this._ccMusicPlayId, this._currentMusicTime);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 停止音樂
 | 
			
		||||
	 * @param id
 | 
			
		||||
	 */
 | 
			
		||||
	public StopMusic(): void {
 | 
			
		||||
		cc.audioEngine.stopMusic();
 | 
			
		||||
		this._currentMusic = -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								assets/Script/Engine/Utils/Audio/CSAudio.ts.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								assets/Script/Engine/Utils/Audio/CSAudio.ts.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.0.8",
 | 
			
		||||
  "uuid": "f3ba292a-ecad-4485-ab60-1cd3ee94979a",
 | 
			
		||||
  "isPlugin": false,
 | 
			
		||||
  "loadPluginInWeb": true,
 | 
			
		||||
  "loadPluginInNative": true,
 | 
			
		||||
  "loadPluginInEditor": false,
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										168
									
								
								assets/Script/Engine/Utils/Audio/CSCommonAudios.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								assets/Script/Engine/Utils/Audio/CSCommonAudios.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,168 @@
 | 
			
		||||
import CSAudio from "./CSAudio";
 | 
			
		||||
 | 
			
		||||
export default class CSAppAudios {
 | 
			
		||||
    private static _instanceApp: CSAppAudios = null;
 | 
			
		||||
    public static get InstanceApp(): CSAppAudios { return this._instanceApp; }
 | 
			
		||||
    private _idToAppClip: Map<number, cc.AudioClip> = new Map<number, cc.AudioClip>();
 | 
			
		||||
    private _idToAppPath: Map<number, string> = new Map<number, string>();
 | 
			
		||||
    private _idToAppAudioId: Map<number, number> = new Map<number, number>();
 | 
			
		||||
    private _currentAppMusic: number = -1;
 | 
			
		||||
    /** 判斷音效播放太過頻繁不疊加 */
 | 
			
		||||
    private _lastAppPlaySoundTime: Map<string, number> = new Map<string, number>();
 | 
			
		||||
    private readonly _appCanPlaySoundCutTime: number = 10;
 | 
			
		||||
    constructor() {
 | 
			
		||||
        CSAppAudios._instanceApp = this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AddAppClipsInfo(clips: Map<number, cc.AudioClip>, pathes: Map<number, string>): void {
 | 
			
		||||
        this._idToAppClip = clips;
 | 
			
		||||
        this._idToAppPath = pathes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 設定AudioID
 | 
			
		||||
     * @param id
 | 
			
		||||
     * @param audioId
 | 
			
		||||
     */
 | 
			
		||||
    private _setAppAudioId(id: number, audioId: number): void {
 | 
			
		||||
        this._idToAppAudioId.set(id, audioId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 打開音效音量
 | 
			
		||||
     */
 | 
			
		||||
    public OpenSound(): void {
 | 
			
		||||
        CSAudio.Instance.OpenSound();
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * 關閉音效音量
 | 
			
		||||
     */
 | 
			
		||||
    public CloseSound(): void {
 | 
			
		||||
        CSAudio.Instance.CloseSound();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 取得AudioID
 | 
			
		||||
     * @param id
 | 
			
		||||
     */
 | 
			
		||||
    private _getAppAudioId(id: number): number {
 | 
			
		||||
        if (this._idToAppAudioId.has(id)) {
 | 
			
		||||
            return this._idToAppAudioId.get(id);
 | 
			
		||||
        } else {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 播放音效
 | 
			
		||||
     * @param id
 | 
			
		||||
     * @param loop
 | 
			
		||||
     */
 | 
			
		||||
    public PlayAppSound(id: number, loop: boolean = false): void {
 | 
			
		||||
        // 靜音後有一禎仍然會撥放的問題:音量>0才撥放
 | 
			
		||||
        if (cc.audioEngine.getEffectsVolume() <= 0) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._idToAppClip.has(id)) {
 | 
			
		||||
            let path: string = this._idToAppPath.get(id);
 | 
			
		||||
            let timenum: number = new Date().getTime();
 | 
			
		||||
            if (!this._lastAppPlaySoundTime.has(path)) {
 | 
			
		||||
                this._lastAppPlaySoundTime.set(path, timenum);
 | 
			
		||||
                let audioId: number = cc.audioEngine.playEffect(this._idToAppClip.get(id), loop);
 | 
			
		||||
                this._setAppAudioId(id, audioId);
 | 
			
		||||
            } else {
 | 
			
		||||
                let lastTime: number = this._lastAppPlaySoundTime.get(path);
 | 
			
		||||
                if (timenum - lastTime > this._appCanPlaySoundCutTime) {
 | 
			
		||||
                    this._lastAppPlaySoundTime.set(path, timenum);
 | 
			
		||||
                    let audioId: number = cc.audioEngine.playEffect(this._idToAppClip.get(id), loop);
 | 
			
		||||
                    this._setAppAudioId(id, audioId);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            cc.error("未知的Sound Id: ", id);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 停止音效
 | 
			
		||||
     * @param id
 | 
			
		||||
     */
 | 
			
		||||
    public StopAppSound(id: number): void {
 | 
			
		||||
        let audioId: number = this._getAppAudioId(id);
 | 
			
		||||
        if (audioId >= 0) {
 | 
			
		||||
            cc.audioEngine.stopEffect(audioId);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 打開音樂音量
 | 
			
		||||
     */
 | 
			
		||||
    public OpenMusic(): void {
 | 
			
		||||
        this._setAppMusicVolume(0.3);
 | 
			
		||||
        CSAudio.Instance.OpenMusic();
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * 關閉音樂音量
 | 
			
		||||
     */
 | 
			
		||||
    public CloseMusic(): void {
 | 
			
		||||
        this._setAppMusicVolume(0.0); // 這邊再改改
 | 
			
		||||
        CSAudio.Instance.CloseMusic();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 設定音樂音量
 | 
			
		||||
     * @param volume
 | 
			
		||||
     */
 | 
			
		||||
    private _setAppMusicVolume(volume: number): void {
 | 
			
		||||
        // 靜音後有一禎仍然會撥放的問題:背景音樂要回復
 | 
			
		||||
        if (this._currentAppMusic != -1 && volume > 0 && !cc.audioEngine.isMusicPlaying()) {
 | 
			
		||||
            this._ccAppMusicPlayId = cc.audioEngine.playMusic(this._idToAppClip.get(this._currentAppMusic), true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 撥放音樂
 | 
			
		||||
     * @param id
 | 
			
		||||
     * @param loop
 | 
			
		||||
     */
 | 
			
		||||
    public PlayAppMusic(id: number, loop: boolean = true): void {
 | 
			
		||||
        if (this._currentAppMusic != id) {
 | 
			
		||||
            if (this._idToAppClip.has(id)) {
 | 
			
		||||
                // 靜音後有一禎仍然會撥放的問題:音量>0才撥放
 | 
			
		||||
                if (cc.audioEngine.getMusicVolume() > 0) {
 | 
			
		||||
                    this._ccAppMusicPlayId = cc.audioEngine.playMusic(this._idToAppClip.get(id), loop);
 | 
			
		||||
                }
 | 
			
		||||
                this._currentAppMusic = id;
 | 
			
		||||
            } else {
 | 
			
		||||
                cc.error("未知的Music Id: ", id);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            this.StopMusic();
 | 
			
		||||
            this.PlayAppMusic(id);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private _ccAppMusicPlayId: number = -1;
 | 
			
		||||
    private _currentAppMusicTime: number = -1;
 | 
			
		||||
 | 
			
		||||
    public pauseOrResume(isPause?: boolean): void {
 | 
			
		||||
        if (isPause) {
 | 
			
		||||
            this._currentAppMusicTime = cc.audioEngine.getCurrentTime(this._ccAppMusicPlayId);
 | 
			
		||||
            cc.audioEngine.pauseAll();
 | 
			
		||||
            cc.audioEngine.stopMusic();
 | 
			
		||||
        } else {
 | 
			
		||||
            cc.audioEngine.resumeAll();
 | 
			
		||||
            cc.audioEngine.setCurrentTime(this._ccAppMusicPlayId, this._currentAppMusicTime);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 停止音樂
 | 
			
		||||
     * @param id
 | 
			
		||||
     */
 | 
			
		||||
    public StopMusic(): void {
 | 
			
		||||
        cc.audioEngine.stopMusic();
 | 
			
		||||
        this._currentAppMusic = -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								assets/Script/Engine/Utils/Audio/CSCommonAudios.ts.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								assets/Script/Engine/Utils/Audio/CSCommonAudios.ts.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.0.8",
 | 
			
		||||
  "uuid": "ce946cad-16db-4383-a734-43bb8f14089e",
 | 
			
		||||
  "isPlugin": false,
 | 
			
		||||
  "loadPluginInWeb": true,
 | 
			
		||||
  "loadPluginInNative": true,
 | 
			
		||||
  "loadPluginInEditor": false,
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								assets/Script/Engine/Utils/Bezier.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								assets/Script/Engine/Utils/Bezier.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.1.2",
 | 
			
		||||
  "uuid": "4e2d4321-bbfb-46d8-87bc-15a7c99c000d",
 | 
			
		||||
  "isBundle": false,
 | 
			
		||||
  "bundleName": "",
 | 
			
		||||
  "priority": 1,
 | 
			
		||||
  "compressionType": {},
 | 
			
		||||
  "optimizeHotUpdate": {},
 | 
			
		||||
  "inlineSpriteFrames": {},
 | 
			
		||||
  "isRemoteBundle": {},
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								assets/Script/Engine/Utils/Bezier/Bezier.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								assets/Script/Engine/Utils/Bezier/Bezier.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
export module Bezier {
 | 
			
		||||
 | 
			
		||||
    export function GetPoint(p0: cc.Vec2, p1: cc.Vec2, p2: cc.Vec2, p3: cc.Vec2, t: number): cc.Vec2 {
 | 
			
		||||
        if (t < 0) {
 | 
			
		||||
            t = 0;
 | 
			
		||||
        }
 | 
			
		||||
        else if (t > 1) {
 | 
			
		||||
            t = 1
 | 
			
		||||
        }
 | 
			
		||||
        let OneMinusT = 1 - t;
 | 
			
		||||
        return p0.mul(OneMinusT * OneMinusT * OneMinusT)
 | 
			
		||||
            .add(p1.mul(3 * OneMinusT * OneMinusT * t))
 | 
			
		||||
            .add(p2.mul(3 * OneMinusT * t * t))
 | 
			
		||||
            .add(p3.mul(t * t * t));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								assets/Script/Engine/Utils/Bezier/Bezier.ts.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								assets/Script/Engine/Utils/Bezier/Bezier.ts.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.0.8",
 | 
			
		||||
  "uuid": "b47d81c4-01a1-45cd-94f8-19daf96f17a8",
 | 
			
		||||
  "isPlugin": false,
 | 
			
		||||
  "loadPluginInWeb": true,
 | 
			
		||||
  "loadPluginInNative": true,
 | 
			
		||||
  "loadPluginInEditor": false,
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								assets/Script/Engine/Utils/CCExtensions.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								assets/Script/Engine/Utils/CCExtensions.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.1.2",
 | 
			
		||||
  "uuid": "dcb480f5-98b4-4a48-9d82-e3e1fe837e8d",
 | 
			
		||||
  "isBundle": false,
 | 
			
		||||
  "bundleName": "",
 | 
			
		||||
  "priority": 1,
 | 
			
		||||
  "compressionType": {},
 | 
			
		||||
  "optimizeHotUpdate": {},
 | 
			
		||||
  "inlineSpriteFrames": {},
 | 
			
		||||
  "isRemoteBundle": {},
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										69
									
								
								assets/Script/Engine/Utils/CCExtensions/ArrayExtension.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								assets/Script/Engine/Utils/CCExtensions/ArrayExtension.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
			
		||||
declare interface Array<T> {
 | 
			
		||||
    /**
 | 
			
		||||
     * 移除一個值並且回傳
 | 
			
		||||
     * @param index
 | 
			
		||||
     */
 | 
			
		||||
    ExRemoveAt(index: number): T;
 | 
			
		||||
    /**
 | 
			
		||||
     * 物件陣列排序,asc&key陣列長度請一樣
 | 
			
		||||
     * PS. boolean 帶false是先true在false
 | 
			
		||||
     * @link JavaScript Object 排序 http://www.eion.com.tw/Blogger/?Pid=1170#:~:text=JavaScript%20Object%20排序
 | 
			
		||||
     * @param asc 是否升序排列(小到大)
 | 
			
		||||
     * @param key 需排序的key(優先順序左到右)(沒有就放空)
 | 
			
		||||
     */
 | 
			
		||||
    ObjectSort(asc?: boolean[], key?: string[]): any[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Array.prototype.ExRemoveAt || Object.defineProperty(Array.prototype, "ExRemoveAt", {
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    value: function (index: number): any {
 | 
			
		||||
        let item: any = this.splice(index, 1);
 | 
			
		||||
        return item[0];
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
Array.prototype.ObjectSort || Object.defineProperty(Array.prototype, "ObjectSort", {
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    /**
 | 
			
		||||
     * @param asc 是否升序排列(小到大)
 | 
			
		||||
     * @param key 需排序的key(優先順序左到右)(沒有就放空)
 | 
			
		||||
     */
 | 
			
		||||
    value: function (asc: boolean[] = [true], key?: string[]): any[] {
 | 
			
		||||
        if (this.length === 0) {
 | 
			
		||||
            return this;
 | 
			
		||||
        } else if (!key || key.length === 0) {
 | 
			
		||||
            console.error(`ObjectSort key error`);
 | 
			
		||||
            return this;
 | 
			
		||||
        } else if (asc.length !== key.length) {
 | 
			
		||||
            console.error(`ObjectSort key asc error asc.length: ${asc.length}, key.length: ${key.length}`);
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
        for (let i: number = 0; i < key.length; i++) {
 | 
			
		||||
            const keyname: string = key[i];
 | 
			
		||||
            if (this[0][keyname] === undefined) {
 | 
			
		||||
                console.error(`ObjectSort has not key[${i}]: ${keyname}`);
 | 
			
		||||
                return this;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        let count: number = key ? key.length : 1;
 | 
			
		||||
        let arr: any[];
 | 
			
		||||
        for (let i: number = count - 1; i >= 0; i--) {
 | 
			
		||||
            arr = this.sort(function (a: any, b: any): 1 | -1 {
 | 
			
		||||
                let mya: any = a;
 | 
			
		||||
                let myb: any = b;
 | 
			
		||||
                if (key) {
 | 
			
		||||
                    mya = a[key[i]];
 | 
			
		||||
                    myb = b[key[i]];
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // 加個等於數字相同不要再去排序到
 | 
			
		||||
                if (asc[i]) {
 | 
			
		||||
                    return mya >= myb ? 1 : -1;
 | 
			
		||||
                } else {
 | 
			
		||||
                    return mya <= myb ? 1 : -1;
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        return arr;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.0.8",
 | 
			
		||||
  "uuid": "02aa6cd7-21a1-4c22-bcbe-296bb938badd",
 | 
			
		||||
  "isPlugin": false,
 | 
			
		||||
  "loadPluginInWeb": true,
 | 
			
		||||
  "loadPluginInNative": true,
 | 
			
		||||
  "loadPluginInEditor": false,
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										223
									
								
								assets/Script/Engine/Utils/CCExtensions/CCExtension.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								assets/Script/Engine/Utils/CCExtensions/CCExtension.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,223 @@
 | 
			
		||||
declare namespace cc {
 | 
			
		||||
 | 
			
		||||
    export interface Node {
 | 
			
		||||
        /**
 | 
			
		||||
         * 設定世界座標
 | 
			
		||||
         * @param worldPoint 
 | 
			
		||||
         */
 | 
			
		||||
        SetWorldPosition(worldPoint: cc.Vec2): void;
 | 
			
		||||
        /**
 | 
			
		||||
         * 取得世界座標
 | 
			
		||||
         */
 | 
			
		||||
        GetWorldPosition(): cc.Vec2;
 | 
			
		||||
        /**
 | 
			
		||||
         * 設定長寬
 | 
			
		||||
         * @param size 
 | 
			
		||||
         */
 | 
			
		||||
        SetSizeDelta(size: cc.Vec2);
 | 
			
		||||
        /**
 | 
			
		||||
         * 取得長寬
 | 
			
		||||
         */
 | 
			
		||||
        GetSizeDelta(): cc.Vec2;
 | 
			
		||||
        /**
 | 
			
		||||
         * 增加一個子物件
 | 
			
		||||
         * @param childObj 
 | 
			
		||||
         */
 | 
			
		||||
        ExAddChild(childObj: cc.Prefab | cc.Node, childActive?: boolean): cc.Node;
 | 
			
		||||
 | 
			
		||||
        /**設定層級為最低層 */
 | 
			
		||||
        ExSetLowestOrder(): void;
 | 
			
		||||
 | 
			
		||||
        /**設定層級為最高層 */
 | 
			
		||||
        ExSetHighestOrder(): void;
 | 
			
		||||
 | 
			
		||||
        /**設定層級,比目標OBJ再低一層 */
 | 
			
		||||
        ExSetOrderUnderTheObj(obj: cc.Node, isNew?: boolean): number;
 | 
			
		||||
 | 
			
		||||
        /**設定層級,比目標OBJ再高一層 */
 | 
			
		||||
        ExSetOrderOverTheObj(obj: cc.Node, isNew?: boolean): number;
 | 
			
		||||
        /**位置維持在原位 */
 | 
			
		||||
        ExSetParent(parentObj: cc.Node): void;
 | 
			
		||||
        ExSetGray(showGray: boolean): void;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cc.Node.prototype.SetWorldPosition || Object.defineProperty(cc.Node.prototype, 'SetWorldPosition', {
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    value: function (cocosWorldPos: cc.Vec2) {
 | 
			
		||||
        // let cocosWorldPos = new cc.Vec2(unityWorldPos.x + 711, unityWorldPos.y + 400);
 | 
			
		||||
        this.setPosition(this.parent.convertToNodeSpaceAR(cocosWorldPos));
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
cc.Node.prototype.GetWorldPosition || Object.defineProperty(cc.Node.prototype, 'GetWorldPosition', {
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    value: function () {
 | 
			
		||||
        let cocosWorldPos = this.parent.convertToWorldSpaceAR(this.position);
 | 
			
		||||
        // let unityWorldPos = new cc.Vec2(cocosWorldPos.x - 711, cocosWorldPos.y - 400);
 | 
			
		||||
        return cocosWorldPos;
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
cc.Node.prototype.SetSizeDelta || Object.defineProperty(cc.Node.prototype, 'SetSizeDelta', {
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    value: function (size: cc.Vec2) {
 | 
			
		||||
        this.setContentSize(size.x, size.y);
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
cc.Node.prototype.GetSizeDelta || Object.defineProperty(cc.Node.prototype, 'GetSizeDelta', {
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    value: function () {
 | 
			
		||||
        let size: cc.Size = this.GetSizeDelta();
 | 
			
		||||
        return new cc.Vec2(size.width, size.width);
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
cc.Node.prototype.ExAddChild || Object.defineProperty(cc.Node.prototype, 'ExAddChild', {
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    value: function (childObj: cc.Prefab | cc.Node, childActive: boolean = true) {
 | 
			
		||||
        let gameObj = null;
 | 
			
		||||
        if (childObj instanceof cc.Prefab) {
 | 
			
		||||
            gameObj = cc.instantiate(childObj);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            gameObj = cc.instantiate(childObj);
 | 
			
		||||
        }
 | 
			
		||||
        gameObj.active = childActive ? true : childActive;
 | 
			
		||||
        gameObj.parent = this;
 | 
			
		||||
        return gameObj;
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
cc.Node.prototype.ExSetLowestOrder || Object.defineProperty(cc.Node.prototype, 'ExSetLowestOrder', {
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    value: function () {
 | 
			
		||||
        this.setSiblingIndex(0);
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
cc.Node.prototype.ExSetHighestOrder || Object.defineProperty(cc.Node.prototype, 'ExSetHighestOrder', {
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    value: function () {
 | 
			
		||||
        this.setSiblingIndex(Number.MAX_VALUE);
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
cc.Node.prototype.ExSetOrderUnderTheObj || Object.defineProperty(cc.Node.prototype, 'ExSetOrderUnderTheObj', {
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    value: function (obj: cc.Node, isNew?: boolean) {
 | 
			
		||||
 | 
			
		||||
        let newIndex: number;
 | 
			
		||||
        let objIndex = obj.getSiblingIndex();
 | 
			
		||||
 | 
			
		||||
        // 如果是新創的元件
 | 
			
		||||
        if (isNew) {
 | 
			
		||||
            newIndex = objIndex;
 | 
			
		||||
        }
 | 
			
		||||
        // 如果是已經在場景上的元件
 | 
			
		||||
        else {
 | 
			
		||||
            let myIndex = this.getSiblingIndex();
 | 
			
		||||
 | 
			
		||||
            // 如果一開始就在它下面
 | 
			
		||||
            if (myIndex < objIndex) {
 | 
			
		||||
                newIndex = objIndex - 1;
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                newIndex = objIndex;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this.setSiblingIndex(newIndex);
 | 
			
		||||
        return newIndex;
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
cc.Node.prototype.ExSetOrderOverTheObj || Object.defineProperty(cc.Node.prototype, 'ExSetOrderOverTheObj', {
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    value: function (obj: cc.Node, isNew?: boolean) {
 | 
			
		||||
        let newIndex: number;
 | 
			
		||||
        let objIndex = obj.getSiblingIndex();
 | 
			
		||||
 | 
			
		||||
        // 如果是新創的元件
 | 
			
		||||
        if (isNew) {
 | 
			
		||||
            newIndex = objIndex + 1;
 | 
			
		||||
        }
 | 
			
		||||
        // 如果是已經在場景上的元件
 | 
			
		||||
        else {
 | 
			
		||||
            let myIndex = this.getSiblingIndex();
 | 
			
		||||
 | 
			
		||||
            // 如果一開始就在它下面
 | 
			
		||||
            if (myIndex < objIndex) {
 | 
			
		||||
                newIndex = objIndex;
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                newIndex = objIndex + 1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this.setSiblingIndex(newIndex);
 | 
			
		||||
        return newIndex;
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
cc.Node.prototype.ExSetParent || Object.defineProperty(cc.Node.prototype, 'ExSetParent', {
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    value: function (parentObj: cc.Node) {
 | 
			
		||||
        let oriPos = this.GetWorldPosition();
 | 
			
		||||
        this.setParent(parentObj);
 | 
			
		||||
        this.SetWorldPosition(oriPos);
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
cc.Node.prototype.ExSetGray || Object.defineProperty(cc.Node.prototype, 'ExSetGray', {
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    value: function (showGray: boolean): void {
 | 
			
		||||
        let btn: cc.Button = this.getComponent(cc.Button);
 | 
			
		||||
        if (btn) {
 | 
			
		||||
            btn.interactable = !showGray;
 | 
			
		||||
        }
 | 
			
		||||
        let material: cc.Material = cc.Material.createWithBuiltin(showGray ? cc.Material.BUILTIN_NAME.GRAY_SPRITE.toString() : cc.Material.BUILTIN_NAME.SPRITE.toString(), 0);
 | 
			
		||||
        !showGray && material.define("USE_TEXTURE", true, 0);
 | 
			
		||||
        let spriteComs: any[] = this.getComponentsInChildren(cc.Sprite).concat(this.getComponentsInChildren(cc.Label));
 | 
			
		||||
        for (let sprite of spriteComs) {
 | 
			
		||||
            sprite.setMaterial(0, material);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 先使用createWithBuiltin,如果材質球一直Create沒被刪除,會在修改。
 | 
			
		||||
        // let material: cc.Material = cc.Material.getBuiltinMaterial(showGray ? cc.Material.BUILTIN_NAME.GRAY_SPRITE.toString() : cc.Material.BUILTIN_NAME.SPRITE.toString());
 | 
			
		||||
        // for (let sprite of spriteComs) {
 | 
			
		||||
        //     if (showGray) {
 | 
			
		||||
        //         sprite.setMaterial(0, cc.Material.getBuiltinMaterial('2d-gray-sprite'));
 | 
			
		||||
        //     }
 | 
			
		||||
        //     else {
 | 
			
		||||
        //         sprite.setMaterial(0, cc.Material.getBuiltinMaterial('2d-sprite'));
 | 
			
		||||
        //     }
 | 
			
		||||
        // }
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
// cc.Node.prototype.SetWorldPosition = function (cocosWorldPos: cc.Vec2): void {
 | 
			
		||||
//     // let cocosWorldPos = new cc.Vec2(unityWorldPos.x + 711, unityWorldPos.y + 400);
 | 
			
		||||
//     this.setPosition(this.parent.convertToNodeSpaceAR(cocosWorldPos));
 | 
			
		||||
// }
 | 
			
		||||
// cc.Node.prototype.GetWorldPosition = function (): cc.Vec2 {
 | 
			
		||||
//     let cocosWorldPos = this.parent.convertToWorldSpaceAR(this.position);
 | 
			
		||||
//     // let unityWorldPos = new cc.Vec2(cocosWorldPos.x - 711, cocosWorldPos.y - 400);
 | 
			
		||||
//     return cocosWorldPos;
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
// cc.Node.prototype.SetSizeDelta = function (size: cc.Vec2) {
 | 
			
		||||
//     this.setContentSize(size.x, size.y);
 | 
			
		||||
// }
 | 
			
		||||
// cc.Node.prototype.GetSizeDelta = function (): cc.Vec2 {
 | 
			
		||||
//     let size: cc.Size = this.GetSizeDelta();
 | 
			
		||||
//     return new cc.Vec2(size.width, size.width);
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
// cc.Node.prototype.ExAddChild = function (childObj: cc.Prefab | cc.Node): cc.Node {
 | 
			
		||||
 | 
			
		||||
//     let gameObj = null;
 | 
			
		||||
//     if (childObj instanceof cc.Prefab) {
 | 
			
		||||
//         gameObj = cc.instantiate(childObj);
 | 
			
		||||
//     }
 | 
			
		||||
//     else {
 | 
			
		||||
//         gameObj = cc.instantiate(childObj);
 | 
			
		||||
//     }
 | 
			
		||||
//     gameObj.parent = this;
 | 
			
		||||
//     return gameObj;
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
// cc.Node.prototype.ExSetLowestOrder = function (): void {
 | 
			
		||||
//     this.setSiblingIndex(0);
 | 
			
		||||
// }
 | 
			
		||||
// cc.Node.prototype.ExSetHighestOrder = function (): void {
 | 
			
		||||
//     this.setSiblingIndex(Number.MAX_VALUE);
 | 
			
		||||
// }
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.0.8",
 | 
			
		||||
  "uuid": "b373f805-9297-4af5-8ea6-0a250649b5b0",
 | 
			
		||||
  "isPlugin": false,
 | 
			
		||||
  "loadPluginInWeb": true,
 | 
			
		||||
  "loadPluginInNative": true,
 | 
			
		||||
  "loadPluginInEditor": false,
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										189
									
								
								assets/Script/Engine/Utils/CCExtensions/NumberExtension.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								assets/Script/Engine/Utils/CCExtensions/NumberExtension.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,189 @@
 | 
			
		||||
 | 
			
		||||
declare interface Number {
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 金額每三位數(千)加逗號, 並且補到小數點第2位
 | 
			
		||||
     * 輸出 41,038,560.00  
 | 
			
		||||
     * @param precision 補到小數點第幾位
 | 
			
		||||
     * @param isPadZero 是否要補零
 | 
			
		||||
     * */
 | 
			
		||||
    ExFormatNumberWithComma(precision?: number, isPadZero?: boolean): string;
 | 
			
		||||
    /**
 | 
			
		||||
     * 基本4位數(9,999-999B-T)
 | 
			
		||||
     * */
 | 
			
		||||
    ExTransferToBMK(precision?: number,offset?: number): string;
 | 
			
		||||
    /**
 | 
			
		||||
     * 數字轉字串, 頭補0
 | 
			
		||||
     * @param size 
 | 
			
		||||
     */
 | 
			
		||||
    Pad(size: number): string;
 | 
			
		||||
    /**
 | 
			
		||||
     * 四捨五入到小數點第X位 (同server計算規則)
 | 
			
		||||
     * @param precision 
 | 
			
		||||
     */
 | 
			
		||||
    ExToNumRoundDecimal(precision: number): number;
 | 
			
		||||
    /**
 | 
			
		||||
     * 無條件捨去到小數點第X位
 | 
			
		||||
     * @param precision 
 | 
			
		||||
     */
 | 
			
		||||
    ExToNumFloorDecimal(precision: number): number;
 | 
			
		||||
    /**
 | 
			
		||||
     * 無條件捨去強制保留X位小數,如:2,會在2後面補上00.即2.00 
 | 
			
		||||
     * @param precision 補到小數點第幾位
 | 
			
		||||
     * @param isPadZero 是否要補零
 | 
			
		||||
     */
 | 
			
		||||
    ExToStringFloorDecimal(precision: number, isPadZero?: boolean): string;
 | 
			
		||||
    /**
 | 
			
		||||
     * 取整數)
 | 
			
		||||
     */
 | 
			
		||||
    ExToInt():number;
 | 
			
		||||
    /**
 | 
			
		||||
     * 小數轉整數(支援科學符號)
 | 
			
		||||
     */
 | 
			
		||||
    Float2Fixed():number;
 | 
			
		||||
    /**
 | 
			
		||||
     * 數字長度(支援科學符號)
 | 
			
		||||
     */
 | 
			
		||||
    DigitLength():number;
 | 
			
		||||
 | 
			
		||||
    target: number;
 | 
			
		||||
 | 
			
		||||
   
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Number.prototype.ExFormatNumberWithComma || Object.defineProperty(Number.prototype, 'ExFormatNumberWithComma', {
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    value: function (precision: number = 2, isPadZero: boolean = true) {
 | 
			
		||||
           
 | 
			
		||||
        // let arr = String(this).split('.');
 | 
			
		||||
        let arr = this.ExToStringFloorDecimal(precision, isPadZero).split('.');
 | 
			
		||||
        let num = arr[0], result = '';
 | 
			
		||||
        while (num.length > 3) {
 | 
			
		||||
            result = ',' + num.slice(-3) + result;
 | 
			
		||||
            num = num.slice(0, num.length - 3);
 | 
			
		||||
        }
 | 
			
		||||
        if (num.length > 0) result = num + result;
 | 
			
		||||
        return arr[1] ? result + '.' + arr[1] : result;
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Number.prototype.ExTransferToBMK || Object.defineProperty(Number.prototype, 'ExTransferToBMK', {
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    value: function (precision: number=2,offset: number = 0) {
 | 
			
		||||
        /**千 */
 | 
			
		||||
        let MONEY_1K: number = 1000;
 | 
			
		||||
        /**萬 */
 | 
			
		||||
        // let MONEY_10K: number = 10000;
 | 
			
		||||
        /**十萬 */
 | 
			
		||||
        // let MONEY_100K: number = 100000;
 | 
			
		||||
        /**百萬 */
 | 
			
		||||
        let MONEY_1M: number = 1000000;
 | 
			
		||||
        /**千萬 */
 | 
			
		||||
        // let MONEY_10M: number = 10000000;
 | 
			
		||||
        /**億 */
 | 
			
		||||
        // let MONEY_100M: number = 100000000;
 | 
			
		||||
        /**十億 */
 | 
			
		||||
        let MONEY_1B: number = 1000000000;
 | 
			
		||||
        /**百億 */
 | 
			
		||||
        // let MONEY_10B: number = 10000000000;
 | 
			
		||||
        /**千億 */
 | 
			
		||||
        // let MONEY_100B: number = 100000000000;
 | 
			
		||||
        /**兆 */
 | 
			
		||||
        // let MONEY_1T: number = 1000000000000;
 | 
			
		||||
        offset = Math.pow(10, offset);
 | 
			
		||||
        // if (this >= MONEY_1T * offset) {
 | 
			
		||||
        //     //(3)1,000T
 | 
			
		||||
        //     //1T~
 | 
			
		||||
        //     return (~~(this / MONEY_1T)).ExFormatNumberWithComma(0) + "T";
 | 
			
		||||
        // }
 | 
			
		||||
        if (this >= MONEY_1B * offset) {
 | 
			
		||||
            //1,000B~900,000B
 | 
			
		||||
            //1B~900B
 | 
			
		||||
            return (this / MONEY_1B).ExFormatNumberWithComma(3, false) + "B";
 | 
			
		||||
        }
 | 
			
		||||
        else if (this >= MONEY_1M * offset) {
 | 
			
		||||
            //1,000M~900,000M
 | 
			
		||||
            //1M~900M
 | 
			
		||||
            return (this / MONEY_1M).ExFormatNumberWithComma(3, false) + "M";
 | 
			
		||||
        }
 | 
			
		||||
        else if (this >= MONEY_1K * offset) {
 | 
			
		||||
            //1,000K~900,000K
 | 
			
		||||
            //1K~90K
 | 
			
		||||
            return (this / MONEY_1K).ExFormatNumberWithComma(3, false) + "K";
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            //0~9,000,000
 | 
			
		||||
            //0~9,000
 | 
			
		||||
            return this.ExFormatNumberWithComma(precision);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
Number.prototype.Pad || Object.defineProperty(Number.prototype, 'Pad', {
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    value: function (size: number) {
 | 
			
		||||
        let s = this + "";
 | 
			
		||||
        while (s.length < size) s = "0" + s;
 | 
			
		||||
        return s;
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
Number.prototype.ExToNumRoundDecimal || Object.defineProperty(Number.prototype, 'ExToNumRoundDecimal', {
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    value: function (precision: number) {
 | 
			
		||||
        return Math.round(Math.round(this * Math.pow(10, (precision || 0) + 1)) / 10) / Math.pow(10, (precision || 0));
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
Number.prototype.ExToInt || Object.defineProperty(Number.prototype, 'ExToInt',{
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    value: function (){
 | 
			
		||||
        return ~~this;
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
Number.prototype.ExToNumFloorDecimal || Object.defineProperty(Number.prototype, 'ExToNumFloorDecimal', {
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    value: function (precision: number) { 
 | 
			
		||||
        let str = this.toPrecision(12);
 | 
			
		||||
        let dotPos = str.indexOf('.');
 | 
			
		||||
        return dotPos == -1 ? this : +`${str.substr(0, dotPos + 1 + precision)}`;
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
Number.prototype.ExToStringFloorDecimal || Object.defineProperty(Number.prototype, 'ExToStringFloorDecimal', {
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    value: function (precision: number, isPadZero: boolean = true) {
 | 
			
		||||
        // 取小數點第X位
 | 
			
		||||
        let f = this.ExToNumFloorDecimal(precision);
 | 
			
		||||
        let s = f.toString();
 | 
			
		||||
        // 補0
 | 
			
		||||
        if (isPadZero) {
 | 
			
		||||
            let rs = s.indexOf('.');
 | 
			
		||||
            if (rs < 0) {
 | 
			
		||||
                rs = s.length;
 | 
			
		||||
                s += '.';
 | 
			
		||||
            }
 | 
			
		||||
            while (s.length <= rs + precision) {
 | 
			
		||||
                s += '0';
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return s;
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
Number.prototype.Float2Fixed || Object.defineProperty(Number.prototype, 'Float2Fixed', {
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    value: function () {
 | 
			
		||||
        if (this.toString().indexOf('e') === -1) {
 | 
			
		||||
        return Number(this.toString().replace('.', ''));
 | 
			
		||||
        }
 | 
			
		||||
        const dLen = this.DigitLength();
 | 
			
		||||
        return dLen > 0 ? +parseFloat((this * Math.pow(10, dLen)).toPrecision(12)) : this;
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
Number.prototype.DigitLength || Object.defineProperty(Number.prototype, 'DigitLength', {
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    value: function () {
 | 
			
		||||
        const eSplit = this.toString().split(/[eE]/);
 | 
			
		||||
        const len = (eSplit[0].split('.')[1] || '').length - (+(eSplit[1] || 0));
 | 
			
		||||
        return len > 0 ? len : 0;
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.0.8",
 | 
			
		||||
  "uuid": "788e7381-bee6-4b74-addb-c4aa4c4ff4e3",
 | 
			
		||||
  "isPlugin": false,
 | 
			
		||||
  "loadPluginInWeb": true,
 | 
			
		||||
  "loadPluginInNative": true,
 | 
			
		||||
  "loadPluginInEditor": false,
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								assets/Script/Engine/Utils/CovrtFile.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								assets/Script/Engine/Utils/CovrtFile.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.1.2",
 | 
			
		||||
  "uuid": "919cddcd-eed2-40a7-be67-2e21365fefe4",
 | 
			
		||||
  "isBundle": false,
 | 
			
		||||
  "bundleName": "",
 | 
			
		||||
  "priority": 1,
 | 
			
		||||
  "compressionType": {},
 | 
			
		||||
  "optimizeHotUpdate": {},
 | 
			
		||||
  "inlineSpriteFrames": {},
 | 
			
		||||
  "isRemoteBundle": {},
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										109
									
								
								assets/Script/Engine/Utils/CovrtFile/ImageConver.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								assets/Script/Engine/Utils/CovrtFile/ImageConver.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,109 @@
 | 
			
		||||
export module ImageConver {
 | 
			
		||||
    export function ImageToBase64(Texture2D: cc.Texture2D): string {
 | 
			
		||||
        let image: ImageBitmap = Texture2D["_image"];
 | 
			
		||||
        let cv: HTMLCanvasElement = document.createElement("canvas");
 | 
			
		||||
        let context: CanvasRenderingContext2D = cv.getContext("2d");
 | 
			
		||||
        cv.width = image.width || 128;
 | 
			
		||||
        cv.height = image.height || 128;
 | 
			
		||||
        context.drawImage(image, 0, 0, image.width || 128, image.height || 128);
 | 
			
		||||
        let DataURL: string = cv.toDataURL();
 | 
			
		||||
        return DataURL;
 | 
			
		||||
    }
 | 
			
		||||
    export function Base64toBlob(b64Data: any, contentType: string = "image/png", sliceSize: number = 512): Blob {
 | 
			
		||||
        let byteCharacters: string = atob(b64Data.substring(b64Data.indexOf(",") + 1));
 | 
			
		||||
        const byteArrays: any[] = [];
 | 
			
		||||
        for (let offset: number = 0; offset < byteCharacters.length; offset += sliceSize) {
 | 
			
		||||
            const slice: string = byteCharacters.slice(offset, offset + sliceSize);
 | 
			
		||||
 | 
			
		||||
            const byteNumbers: any[] = new Array(slice.length);
 | 
			
		||||
            for (let i: number = 0; i < slice.length; i++) {
 | 
			
		||||
                byteNumbers[i] = slice.charCodeAt(i);
 | 
			
		||||
            }
 | 
			
		||||
            const byteArray: Uint8Array = new Uint8Array(byteNumbers);
 | 
			
		||||
            byteArrays.push(byteArray);
 | 
			
		||||
        }
 | 
			
		||||
        const blob: Blob = new Blob(byteArrays, { type: contentType });
 | 
			
		||||
        return blob;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    export function BlobToImageNode(BlobData: Blob, node: cc.Node): void {
 | 
			
		||||
        let reader: FileReader = new FileReader();
 | 
			
		||||
        reader.onloadend = function (): void {
 | 
			
		||||
            ImageConver.Base64ToImageNode(reader.result + "", node);
 | 
			
		||||
        };
 | 
			
		||||
        reader.readAsDataURL(BlobData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    export function Base64ToImageNode(base64: string, node: cc.Node): void {
 | 
			
		||||
        let image: HTMLImageElement = new Image();
 | 
			
		||||
        image.src = base64;
 | 
			
		||||
        image.onload = function (): void {
 | 
			
		||||
            let texture: cc.Texture2D = new cc.Texture2D();
 | 
			
		||||
            texture.initWithElement(image);
 | 
			
		||||
            texture.handleLoadedTexture();
 | 
			
		||||
            let spriteFrame: cc.SpriteFrame = new cc.SpriteFrame(texture);
 | 
			
		||||
            let sprite: cc.Sprite = node.addComponent(cc.Sprite);
 | 
			
		||||
            sprite.spriteFrame = spriteFrame;
 | 
			
		||||
            return;
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    export function Node2Base64(nodeCapture: cc.Node): string {
 | 
			
		||||
        let nodeCamera: cc.Node = new cc.Node();
 | 
			
		||||
        nodeCamera.parent = cc.find("Canvas");
 | 
			
		||||
        let camera: cc.Camera = nodeCamera.addComponent(cc.Camera);
 | 
			
		||||
 | 
			
		||||
        let position: cc.Vec2 = nodeCapture.getPosition();
 | 
			
		||||
        let width: number = nodeCapture.width;
 | 
			
		||||
        let height: number = nodeCapture.height;
 | 
			
		||||
 | 
			
		||||
        // 当 alignWithScreen 为 true 的时候,摄像机会自动将视窗大小调整为整个屏幕的大小。如果想要完全自由地控制摄像机,则需要将 alignWithScreen 设置为 false。(v2.2.1 新增)
 | 
			
		||||
        camera.alignWithScreen = false;
 | 
			
		||||
        // 设置摄像机的投影模式是正交(true)还是透视(false)模式
 | 
			
		||||
        camera.ortho = true;
 | 
			
		||||
        // 摄像机在正交投影模式下的视窗大小。该属性在 alignWithScreen 设置为 false 时生效。
 | 
			
		||||
        camera.orthoSize = height / 2;
 | 
			
		||||
 | 
			
		||||
        let texture: cc.RenderTexture = new cc.RenderTexture();
 | 
			
		||||
        // 如果截图内容中不包含 Mask 组件,可以不用传递第三个参数
 | 
			
		||||
        texture.initWithSize(width, height);
 | 
			
		||||
 | 
			
		||||
        // 如果设置了 targetTexture,那么摄像机渲染的内容不会输出到屏幕上,而是会渲染到 targetTexture 上。
 | 
			
		||||
        camera.targetTexture = texture;
 | 
			
		||||
 | 
			
		||||
        // 创建画布
 | 
			
		||||
        let canvas: HTMLCanvasElement = document.createElement("canvas");
 | 
			
		||||
        canvas.width = width;
 | 
			
		||||
        canvas.height = height;
 | 
			
		||||
 | 
			
		||||
        let ctx: CanvasRenderingContext2D = canvas.getContext("2d");
 | 
			
		||||
 | 
			
		||||
        nodeCapture.setPosition(cc.Vec2.ZERO);
 | 
			
		||||
        // 渲染一次摄像机,即更新一次内容到 RenderTexture 中
 | 
			
		||||
        camera.render(nodeCapture);
 | 
			
		||||
        nodeCapture.setPosition(position);
 | 
			
		||||
 | 
			
		||||
        // 从 render texture 读取像素数据,数据类型为 RGBA 格式的 Uint8Array 数组。
 | 
			
		||||
        // 默认每次调用此函数会生成一个大小为 (长 x 高 x 4) 的 Uint8Array。
 | 
			
		||||
        let data: Uint8Array = texture.readPixels();
 | 
			
		||||
        // write the render data
 | 
			
		||||
        // PNG 中 1 像素 = 32 bit(RGBA),1 byte = 8 bit,所以 1 像素 = 4 byte
 | 
			
		||||
        // 每行 width 像素,即 width * 4 字节
 | 
			
		||||
        let rowBytes: number = width * 4;
 | 
			
		||||
        for (let row: number = 0; row < height; row++) {
 | 
			
		||||
            // RenderTexture 得到的纹理是上下翻转的
 | 
			
		||||
            let srow: number = height - 1 - row;
 | 
			
		||||
            let imageData: ImageData = ctx.createImageData(width, 1);
 | 
			
		||||
            let start: number = srow * width * 4;
 | 
			
		||||
            for (let i: number = 0; i < rowBytes; i++) {
 | 
			
		||||
                imageData.data[i] = data[start + i];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ctx.putImageData(imageData, 0, row);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let dataURL: string = canvas.toDataURL("image/png");
 | 
			
		||||
 | 
			
		||||
        return dataURL;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								assets/Script/Engine/Utils/CovrtFile/ImageConver.ts.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								assets/Script/Engine/Utils/CovrtFile/ImageConver.ts.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.0.8",
 | 
			
		||||
  "uuid": "425b00d6-0d47-4978-9a9f-235733348e3d",
 | 
			
		||||
  "isPlugin": false,
 | 
			
		||||
  "loadPluginInWeb": true,
 | 
			
		||||
  "loadPluginInNative": true,
 | 
			
		||||
  "loadPluginInEditor": false,
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								assets/Script/Engine/Utils/Number.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								assets/Script/Engine/Utils/Number.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.1.2",
 | 
			
		||||
  "uuid": "d6e55fc6-00b6-496a-aae2-74d694c1223b",
 | 
			
		||||
  "isBundle": false,
 | 
			
		||||
  "bundleName": "",
 | 
			
		||||
  "priority": 1,
 | 
			
		||||
  "compressionType": {},
 | 
			
		||||
  "optimizeHotUpdate": {},
 | 
			
		||||
  "inlineSpriteFrames": {},
 | 
			
		||||
  "isRemoteBundle": {},
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										191
									
								
								assets/Script/Engine/Utils/Number/NumberEx.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								assets/Script/Engine/Utils/Number/NumberEx.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,191 @@
 | 
			
		||||
import { CoroutineV2 } from "../../CatanEngine/CoroutineV2/CoroutineV2";
 | 
			
		||||
import { RandomEx } from "./RandomEx";
 | 
			
		||||
 | 
			
		||||
export module NumberEx {
 | 
			
		||||
    /**
 | 
			
		||||
     * 數字滾動
 | 
			
		||||
     * @param startNum
 | 
			
		||||
     * @param endNum
 | 
			
		||||
     * @param callbackfn
 | 
			
		||||
     * @param chabgeRate
 | 
			
		||||
     */
 | 
			
		||||
    export function* ChangeScore(startNum: number, endNum: number, callbackfn: (num: number) => void, sec: number) {
 | 
			
		||||
        let fps = 30;
 | 
			
		||||
        let waitTime = 0.03;
 | 
			
		||||
        let changeRate = sec * fps; // -1為了讓changeRate數字能混亂點
 | 
			
		||||
        changeRate = changeRate - 1 <= 0 ? changeRate : changeRate - 1;
 | 
			
		||||
        changeRate = 1 / changeRate;
 | 
			
		||||
        let diff = endNum - startNum;
 | 
			
		||||
        let isIncrease = endNum >= startNum;
 | 
			
		||||
        let tempScore = startNum;
 | 
			
		||||
        let counter = 0;
 | 
			
		||||
        let randomRate = 0;
 | 
			
		||||
        while (true) {
 | 
			
		||||
            if (endNum != tempScore) {
 | 
			
		||||
                if (counter % 2 == 0) {
 | 
			
		||||
                    if (isIncrease) { randomRate = RandomEx.GetFloat(0, diff * changeRate).ExToNumFloorDecimal(2); }
 | 
			
		||||
                    else { randomRate = RandomEx.GetFloat(0, -diff * changeRate).ExToNumFloorDecimal(2); }
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    randomRate = -randomRate;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                tempScore += diff * changeRate + randomRate;
 | 
			
		||||
                // 遞增
 | 
			
		||||
                if (isIncrease && tempScore > endNum) {
 | 
			
		||||
                    tempScore = endNum;
 | 
			
		||||
                }
 | 
			
		||||
                // 遞減
 | 
			
		||||
                if (!isIncrease && tempScore < endNum) {
 | 
			
		||||
                    tempScore = endNum;
 | 
			
		||||
                }
 | 
			
		||||
                callbackfn(tempScore);
 | 
			
		||||
                // yield null;
 | 
			
		||||
                counter++;
 | 
			
		||||
                yield CoroutineV2.WaitTime(waitTime);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                callbackfn(endNum);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 數字跳動
 | 
			
		||||
     * @param minNum 起始數字
 | 
			
		||||
     * @param maxNum 最終數字
 | 
			
		||||
     * @param callbackfn callbackfn
 | 
			
		||||
     * @param sec 時間
 | 
			
		||||
     */
 | 
			
		||||
    export function* BeatScore(minNum: number, maxNum: number, endNum: number, callbackfn: (num: number) => void, sec: number): IterableIterator<any> {
 | 
			
		||||
        let fps: number = 13;
 | 
			
		||||
        let waitTime: number = 0.07;
 | 
			
		||||
        let changeRate: number = sec * fps; // -1為了讓changeRate數字能混亂點
 | 
			
		||||
        changeRate = changeRate - 1 <= 0 ? changeRate : changeRate - 1;
 | 
			
		||||
        changeRate = 1 / changeRate;
 | 
			
		||||
        let diff: number = maxNum - minNum;
 | 
			
		||||
        let isIncrease: boolean = maxNum >= minNum;
 | 
			
		||||
        let tempScore: number = minNum;
 | 
			
		||||
        let counter: number = 0;
 | 
			
		||||
        let randomRate: number = 0;
 | 
			
		||||
        let lastNum: number = minNum;
 | 
			
		||||
        let nowNum: number = minNum;
 | 
			
		||||
        while (true) {
 | 
			
		||||
            if (maxNum !== tempScore) {
 | 
			
		||||
                if (counter % 2 === 0) {
 | 
			
		||||
                    if (isIncrease) {
 | 
			
		||||
                        randomRate = RandomEx.GetFloat(0, diff * changeRate).ExToNumFloorDecimal(2);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        randomRate = RandomEx.GetFloat(0, -diff * changeRate).ExToNumFloorDecimal(2);
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    randomRate = -randomRate;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                tempScore += diff * changeRate + randomRate;
 | 
			
		||||
                // 遞增
 | 
			
		||||
                if (isIncrease && tempScore > maxNum) {
 | 
			
		||||
                    tempScore = maxNum;
 | 
			
		||||
                }
 | 
			
		||||
                // 遞減
 | 
			
		||||
                if (!isIncrease && tempScore < maxNum) {
 | 
			
		||||
                    tempScore = maxNum;
 | 
			
		||||
                }
 | 
			
		||||
                while(nowNum === lastNum) {
 | 
			
		||||
                    nowNum = RandomEx.GetInt(minNum, maxNum + 1);
 | 
			
		||||
                }
 | 
			
		||||
                lastNum = nowNum;
 | 
			
		||||
                callbackfn(nowNum);
 | 
			
		||||
                // yield null;
 | 
			
		||||
                counter++;
 | 
			
		||||
                yield CoroutineV2.WaitTime(waitTime);
 | 
			
		||||
            } else {
 | 
			
		||||
                callbackfn(endNum);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 检测数字是否越界,如果越界给出提示
 | 
			
		||||
     * @param {*number} num 输入数
 | 
			
		||||
     */
 | 
			
		||||
    function checkBoundary(num: number) {
 | 
			
		||||
        if (_boundaryCheckingState) {
 | 
			
		||||
            if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {
 | 
			
		||||
                console.warn(`${num} is beyond boundary when transfer to integer, the results may not be accurate`);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 精确乘法
 | 
			
		||||
     */
 | 
			
		||||
    export function times(num1: number, num2: number, ...others: number[]): number {
 | 
			
		||||
        if (others.length > 0) {
 | 
			
		||||
            return times(times(num1, num2), others[0], ...others.slice(1));
 | 
			
		||||
        }
 | 
			
		||||
        const num1Changed = num1.Float2Fixed();
 | 
			
		||||
        const num2Changed = num2.Float2Fixed();
 | 
			
		||||
        const baseNum = num1.DigitLength() + num2.DigitLength();
 | 
			
		||||
        const leftValue = num1Changed * num2Changed;
 | 
			
		||||
 | 
			
		||||
        checkBoundary(leftValue);
 | 
			
		||||
 | 
			
		||||
        return leftValue / Math.pow(10, baseNum);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 精确加法
 | 
			
		||||
     */
 | 
			
		||||
    export function plus(num1: number, num2: number, ...others: number[]): number {
 | 
			
		||||
        if (others.length > 0) {
 | 
			
		||||
            return plus(plus(num1, num2), others[0], ...others.slice(1));
 | 
			
		||||
        }
 | 
			
		||||
        const baseNum = Math.pow(10, Math.max(num1.DigitLength(), num2.DigitLength()));
 | 
			
		||||
        return (times(num1, baseNum) + times(num2, baseNum)) / baseNum;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 精确减法
 | 
			
		||||
     */
 | 
			
		||||
    export function minus(num1: number, num2: number, ...others: number[]): number {
 | 
			
		||||
        if (others.length > 0) {
 | 
			
		||||
            return minus(minus(num1, num2), others[0], ...others.slice(1));
 | 
			
		||||
        }
 | 
			
		||||
        const baseNum = Math.pow(10, Math.max(num1.DigitLength(), num2.DigitLength()));
 | 
			
		||||
        return (times(num1, baseNum) - times(num2, baseNum)) / baseNum;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 精确除法
 | 
			
		||||
     */
 | 
			
		||||
    export function divide(num1: number, num2: number, ...others: number[]): number {
 | 
			
		||||
        if (others.length > 0) {
 | 
			
		||||
            return divide(divide(num1, num2), others[0], ...others.slice(1));
 | 
			
		||||
        }
 | 
			
		||||
        const num1Changed = num1.Float2Fixed();
 | 
			
		||||
        const num2Changed = num2.Float2Fixed();
 | 
			
		||||
        checkBoundary(num1Changed);
 | 
			
		||||
        checkBoundary(num2Changed);
 | 
			
		||||
        return times((num1Changed / num2Changed), Math.pow(10, num2.DigitLength() - num1.DigitLength()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 四舍五入
 | 
			
		||||
     */
 | 
			
		||||
    export function round(num: number, ratio: number): number {
 | 
			
		||||
        const base = Math.pow(10, ratio);
 | 
			
		||||
        return divide(Math.round(times(num, base)), base);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let _boundaryCheckingState = true;
 | 
			
		||||
    /**
 | 
			
		||||
     * 是否进行边界检查,默认开启
 | 
			
		||||
     * @param flag 标记开关,true 为开启,false 为关闭,默认为 true
 | 
			
		||||
    */
 | 
			
		||||
    function enableBoundaryChecking(flag = true) {
 | 
			
		||||
        _boundaryCheckingState = flag;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								assets/Script/Engine/Utils/Number/NumberEx.ts.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								assets/Script/Engine/Utils/Number/NumberEx.ts.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.0.8",
 | 
			
		||||
  "uuid": "363f5f7f-0623-4013-8571-0bb5c1dc95e6",
 | 
			
		||||
  "isPlugin": false,
 | 
			
		||||
  "loadPluginInWeb": true,
 | 
			
		||||
  "loadPluginInNative": true,
 | 
			
		||||
  "loadPluginInEditor": false,
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										90
									
								
								assets/Script/Engine/Utils/Number/RandomEx.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								assets/Script/Engine/Utils/Number/RandomEx.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
			
		||||
 | 
			
		||||
export module RandomEx {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 取得隨機布林值
 | 
			
		||||
     */
 | 
			
		||||
    export function GetBool() {
 | 
			
		||||
        return GetInt() >= 0;
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * 取得隨機整數(回傳min ~ max - 1)
 | 
			
		||||
     * @param min
 | 
			
		||||
     * @param max
 | 
			
		||||
     */
 | 
			
		||||
    export function GetInt(min: number = Number.MIN_VALUE, max: number = Number.MAX_VALUE): number {
 | 
			
		||||
        return Math.floor(Math.random() * (max - min)) + min;
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * 取得隨機小數
 | 
			
		||||
     * @param min 
 | 
			
		||||
     * @param max 
 | 
			
		||||
     */
 | 
			
		||||
    export function GetFloat(min: number = Number.MIN_VALUE, max: number = Number.MAX_VALUE): number {
 | 
			
		||||
        return Math.random() * (max - min) + min;
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * 隨機取得複數個不重複回傳
 | 
			
		||||
     * @param num 取得數量
 | 
			
		||||
     * @param items 陣列 
 | 
			
		||||
     */
 | 
			
		||||
    export function GetMultiNoRepeat(num: number, items: any[]): any[] {
 | 
			
		||||
        let result: any[] = [];
 | 
			
		||||
        for (let i: number = 0; i < num; i++) {
 | 
			
		||||
            let ran: number = Math.floor(Math.random() * items.length);
 | 
			
		||||
            let item = items.splice(ran, 1)[0];
 | 
			
		||||
            if (result.indexOf(item) == -1) {
 | 
			
		||||
                result.push(item);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根據權重取得複數個不重複回傳
 | 
			
		||||
     * @param prize 獎項
 | 
			
		||||
     * @param weights 機率
 | 
			
		||||
     * @param count 數量
 | 
			
		||||
     */
 | 
			
		||||
    export function GetMultiNoRepeatByWeight(prize: any[], weights: number[] = null, count: number = 1): any[] {
 | 
			
		||||
        if (weights === null) {
 | 
			
		||||
            weights = [];
 | 
			
		||||
            for (let i: number = 0; i < prize.length; i++) {
 | 
			
		||||
                weights.push(1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        let target: any[] = [];
 | 
			
		||||
        for (let i: number = 0; i < count; i++) {
 | 
			
		||||
            let results: number[] = RandomEx.GetPrizeByWeight(prize, weights);
 | 
			
		||||
            prize.splice(results[0], 1);
 | 
			
		||||
            weights.splice(results[0], 1);
 | 
			
		||||
            target.push(results[1]);
 | 
			
		||||
        }
 | 
			
		||||
        return target;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根據權重隨機取值
 | 
			
		||||
     * @param prize 獎項
 | 
			
		||||
     * @param weights 機率
 | 
			
		||||
     */
 | 
			
		||||
    export function GetPrizeByWeight(prize: any[], weights: number[]): any[] {
 | 
			
		||||
        if (prize.length !== weights.length) {
 | 
			
		||||
            console.error(`GetWeight error -> prize.length:${prize.length} !== weights.length:${weights.length}`);
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        let totalWeight: number = 0;
 | 
			
		||||
        for (let i: number = 0; i < weights.length; i++) {
 | 
			
		||||
            totalWeight += weights[i];
 | 
			
		||||
        }
 | 
			
		||||
        let random: number = RandomEx.GetInt(0, totalWeight) + 1;
 | 
			
		||||
        let nowWeight: number = weights[0];
 | 
			
		||||
        for (let i: number = 0; i < weights.length; i++) {
 | 
			
		||||
            if (nowWeight >= random) {
 | 
			
		||||
                return [i, prize[i]];
 | 
			
		||||
            }
 | 
			
		||||
            nowWeight += weights[i + 1];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								assets/Script/Engine/Utils/Number/RandomEx.ts.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								assets/Script/Engine/Utils/Number/RandomEx.ts.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.0.8",
 | 
			
		||||
  "uuid": "ba4dee5b-ca5b-4435-a068-c4f5dd832bab",
 | 
			
		||||
  "isPlugin": false,
 | 
			
		||||
  "loadPluginInWeb": true,
 | 
			
		||||
  "loadPluginInNative": true,
 | 
			
		||||
  "loadPluginInEditor": false,
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								assets/Script/Engine/Utils/ScrollView.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								assets/Script/Engine/Utils/ScrollView.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.1.2",
 | 
			
		||||
  "uuid": "f6471056-03d8-4d55-b039-6b62d056547c",
 | 
			
		||||
  "isBundle": false,
 | 
			
		||||
  "bundleName": "",
 | 
			
		||||
  "priority": 1,
 | 
			
		||||
  "compressionType": {},
 | 
			
		||||
  "optimizeHotUpdate": {},
 | 
			
		||||
  "inlineSpriteFrames": {},
 | 
			
		||||
  "isRemoteBundle": {},
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										75
									
								
								assets/Script/Engine/Utils/ScrollView/ScrollBase.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								assets/Script/Engine/Utils/ScrollView/ScrollBase.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
			
		||||
import { CoroutineV2 } from "../../CatanEngine/CoroutineV2/CoroutineV2";
 | 
			
		||||
import UIPanel from "../../Component/UIPanel/UIPanel";
 | 
			
		||||
import ScrollItem from "./ScrollItem";
 | 
			
		||||
import UISuperLayout from "./UISuperLayout";
 | 
			
		||||
 | 
			
		||||
const { ccclass, property } = cc._decorator;
 | 
			
		||||
 | 
			
		||||
/** Scroll基底 */
 | 
			
		||||
@ccclass
 | 
			
		||||
export default class ScrollBase extends UIPanel {
 | 
			
		||||
	//#region 外調參數
 | 
			
		||||
 | 
			
		||||
	@property({ displayName: "List", type: UISuperLayout })
 | 
			
		||||
	public List: UISuperLayout = null;
 | 
			
		||||
 | 
			
		||||
	//#endregion
 | 
			
		||||
 | 
			
		||||
	//#region public
 | 
			
		||||
 | 
			
		||||
	/** ListData */
 | 
			
		||||
	public get ListData(): any[] { throw new Error("ListData必須被override"); }
 | 
			
		||||
 | 
			
		||||
	//#endregion
 | 
			
		||||
 | 
			
		||||
	//#region protected
 | 
			
		||||
 | 
			
		||||
	/** 列表資料 */
 | 
			
		||||
	protected _listData: any[] = [];
 | 
			
		||||
 | 
			
		||||
	//#endregion
 | 
			
		||||
 | 
			
		||||
	//#region ScrollView Function
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 創建/刷新列表
 | 
			
		||||
	 * @param isScrollTo 0 不滾動
 | 
			
		||||
	 * @param isScrollTo 1 滚动到头部
 | 
			
		||||
	 * @param isScrollTo 2 滚动到尾部
 | 
			
		||||
	 * @param isScrollTo 3 自动居中到最近Item
 | 
			
		||||
	 */
 | 
			
		||||
	public *CreateList(isScrollTo: number = 0): IterableIterator<any> {
 | 
			
		||||
		if (!this.node.active) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		this.List?.CreateItem(this.ListData.length);
 | 
			
		||||
		yield CoroutineV2.WaitTime(5 / cc.game.getFrameRate());
 | 
			
		||||
		switch (isScrollTo) {
 | 
			
		||||
			case 1: {
 | 
			
		||||
				this.List?.scrollToHeader(0);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			case 2: {
 | 
			
		||||
				this.List?.scrollToFooter(0);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			case 3: {
 | 
			
		||||
				this.List?.scrollToCenter();
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public OnRefreshEvent(node: cc.Node, index: number): void {
 | 
			
		||||
		let listData: any[] = this.ListData;
 | 
			
		||||
		let info: any = listData[index];
 | 
			
		||||
		node.getComponent(ScrollItem).ImplementSet(info, this, index);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//#endregion
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								assets/Script/Engine/Utils/ScrollView/ScrollBase.ts.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								assets/Script/Engine/Utils/ScrollView/ScrollBase.ts.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.0.8",
 | 
			
		||||
  "uuid": "3e607467-693c-46a9-ac93-ab973e52024d",
 | 
			
		||||
  "isPlugin": false,
 | 
			
		||||
  "loadPluginInWeb": true,
 | 
			
		||||
  "loadPluginInNative": true,
 | 
			
		||||
  "loadPluginInEditor": false,
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								assets/Script/Engine/Utils/ScrollView/ScrollItem.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								assets/Script/Engine/Utils/ScrollView/ScrollItem.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
 | 
			
		||||
const { ccclass, property } = cc._decorator;
 | 
			
		||||
@ccclass
 | 
			
		||||
export default class ScrollItem extends cc.Component {
 | 
			
		||||
	ImplementSet(...iniData: any[]): void {
 | 
			
		||||
		//
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								assets/Script/Engine/Utils/ScrollView/ScrollItem.ts.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								assets/Script/Engine/Utils/ScrollView/ScrollItem.ts.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.0.8",
 | 
			
		||||
  "uuid": "0a733d8c-80ae-4e0b-be1b-07dba226c237",
 | 
			
		||||
  "isPlugin": false,
 | 
			
		||||
  "loadPluginInWeb": true,
 | 
			
		||||
  "loadPluginInNative": true,
 | 
			
		||||
  "loadPluginInEditor": false,
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1445
									
								
								assets/Script/Engine/Utils/ScrollView/UISuperLayout.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1445
									
								
								assets/Script/Engine/Utils/ScrollView/UISuperLayout.ts
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.0.8",
 | 
			
		||||
  "uuid": "67f1a0e4-877e-4ad0-bc1b-e1175c620ca1",
 | 
			
		||||
  "isPlugin": false,
 | 
			
		||||
  "loadPluginInWeb": true,
 | 
			
		||||
  "loadPluginInNative": true,
 | 
			
		||||
  "loadPluginInEditor": false,
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										547
									
								
								assets/Script/Engine/Utils/ScrollView/UISuperScrollView.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										547
									
								
								assets/Script/Engine/Utils/ScrollView/UISuperScrollView.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,547 @@
 | 
			
		||||
import UISuperLayout from "./UISuperLayout";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const { ccclass, property } = cc._decorator;
 | 
			
		||||
 | 
			
		||||
const quintEaseOut: (time: number) => number = (time: number) => {
 | 
			
		||||
    time -= 1;
 | 
			
		||||
    return (time * time * time * time * time + 1);
 | 
			
		||||
};
 | 
			
		||||
const EPSILON = 1e-4;
 | 
			
		||||
const OUT_OF_BOUNDARY_BREAKING_FACTOR = 0.05;
 | 
			
		||||
var _tempVec2: cc.Vec2 = new cc.Vec2();
 | 
			
		||||
export enum ScrollViewDirection {
 | 
			
		||||
    HORIZONTAL,
 | 
			
		||||
    VERTICAL,
 | 
			
		||||
    NONE,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ccclass
 | 
			
		||||
export default class UISuperScrollview extends cc.ScrollView {
 | 
			
		||||
    private direction: ScrollViewDirection = ScrollViewDirection.NONE;
 | 
			
		||||
    private _layout: UISuperLayout;
 | 
			
		||||
    @property({
 | 
			
		||||
        tooltip: "注意!向上传递事件只会发送当前滑动相反方向,如果开启horizontal则会发送vertical事件。如果开启vertical则会发送horizontal事件。同时开启horizontal和vertical 不会发送任何事件"
 | 
			
		||||
    }) isTransmitEvent: boolean = false;
 | 
			
		||||
    @property pullRefresh: boolean = false;
 | 
			
		||||
    @property({
 | 
			
		||||
        displayName: "顶部偏移量",
 | 
			
		||||
        tooltip: "下拉时超过此偏移会发送下拉事件",
 | 
			
		||||
        visible: function () { return (this as any).pullRefresh; }
 | 
			
		||||
    }) headerOutOffset: number = 200;
 | 
			
		||||
    @property({
 | 
			
		||||
        displayName: "满足触发Header的倍数",
 | 
			
		||||
        visible: function () { return (this as any).pullRefresh; }
 | 
			
		||||
    }) headerMultiple: number = 2;
 | 
			
		||||
    @property({
 | 
			
		||||
        displayName: "底部偏移量",
 | 
			
		||||
        tooltip: "上拉时超过此偏移会发送上拉事件",
 | 
			
		||||
        visible: function () { return (this as any).pullRefresh; }
 | 
			
		||||
    }) footerOutOffset: number = 200;
 | 
			
		||||
    @property({
 | 
			
		||||
        displayName: "满足触发Footer的倍数",
 | 
			
		||||
        visible: function () { return (this as any).pullRefresh; }
 | 
			
		||||
    }) footerMultiple: number = 2;
 | 
			
		||||
    @property({
 | 
			
		||||
        type: cc.Component.EventHandler,
 | 
			
		||||
        visible: function () { return (this as any).pullRefresh; }
 | 
			
		||||
    }) headerEvents: cc.Component.EventHandler[] = [];
 | 
			
		||||
    @property({
 | 
			
		||||
        type: cc.Component.EventHandler,
 | 
			
		||||
        visible: function () { return (this as any).pullRefresh; }
 | 
			
		||||
    }) footerEvents: cc.Component.EventHandler[] = [];
 | 
			
		||||
    prevLocation: cc.Vec2 = new cc.Vec2();
 | 
			
		||||
    location: cc.Vec2 = new cc.Vec2();
 | 
			
		||||
    set autoScrolling(value: boolean) { (this as any)._autoScrolling = value; }
 | 
			
		||||
    private _touchBeganPosition = new cc.Vec2();
 | 
			
		||||
    private _touchEndPosition = new cc.Vec2();
 | 
			
		||||
    private isMoveHeader: boolean = false;
 | 
			
		||||
    private isMoveFooter: boolean = false;
 | 
			
		||||
    private isLockHeader: boolean = false;
 | 
			
		||||
    private isLockFooter: boolean = false;
 | 
			
		||||
    private headerProgress: number = 0;
 | 
			
		||||
    private footerProgress: number = 0;
 | 
			
		||||
    private isCustomScroll: boolean = false;
 | 
			
		||||
    canTouchMove: boolean = true;
 | 
			
		||||
    get view(): cc.Node { return this["_view"]; }
 | 
			
		||||
    onLoad() {
 | 
			
		||||
        if (this.layout && this.layout.autoCenter) {
 | 
			
		||||
            this.brake = 0.7;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    public onEnable() {
 | 
			
		||||
        super.onEnable();
 | 
			
		||||
        this.node.on("scroll-ended-with-threshold", this.dispatchPageTurningEvent, this);
 | 
			
		||||
    }
 | 
			
		||||
    public onDisable() {
 | 
			
		||||
        super.onDisable();
 | 
			
		||||
        this.node.off("scroll-ended-with-threshold", this.dispatchPageTurningEvent, this);
 | 
			
		||||
    }
 | 
			
		||||
    get layout() {
 | 
			
		||||
        if (!this._layout) {
 | 
			
		||||
            this._layout = this.content && this.content.getComponent(UISuperLayout);
 | 
			
		||||
        }
 | 
			
		||||
        return this._layout;
 | 
			
		||||
    }
 | 
			
		||||
    private isCallSoonFinish: boolean = false;
 | 
			
		||||
    get _curPageIdx() {
 | 
			
		||||
        return this.layout["_currPageIndex"];
 | 
			
		||||
    }
 | 
			
		||||
    getPages() {
 | 
			
		||||
        return new Array(this.layout.itemTotal);
 | 
			
		||||
    }
 | 
			
		||||
    protected _getContentTopBoundary() {
 | 
			
		||||
        if (!this.content) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        let offset = this.layout.isOfTopBoundary == 0 ? this["_topBoundary"] : this.layout.isOfTopBoundary;
 | 
			
		||||
        return offset;
 | 
			
		||||
    }
 | 
			
		||||
    protected _getContentBottomBoundary() {
 | 
			
		||||
        if (!this.content) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        let offset = this.layout.isOfButtomBoundary == 0 ? this["_bottomBoundary"] : this.layout.isOfButtomBoundary;
 | 
			
		||||
        return offset;
 | 
			
		||||
    }
 | 
			
		||||
    protected _getContentLeftBoundary() {
 | 
			
		||||
        if (!this.content) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        let offset = this.layout.isOfLeftBoundary == 0 ? this["_leftBoundary"] : this.layout.isOfLeftBoundary;
 | 
			
		||||
        return offset;
 | 
			
		||||
    }
 | 
			
		||||
    protected _getContentRightBoundary() {
 | 
			
		||||
        if (!this.content) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        let offset = this.layout.isOfRightBoundary == 0 ? this["_rightBoundary"] : this.layout.isOfRightBoundary;
 | 
			
		||||
        return offset;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected _onTouchBegan(event: cc.Event.EventTouch, captureListeners?: Node[]): void {
 | 
			
		||||
        this.isCallSoonFinish = false;
 | 
			
		||||
        this.isCustomScroll = false;
 | 
			
		||||
        this.layout["onTouchBegin"]();
 | 
			
		||||
        if (!this.canTouchMove) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        this.direction = ScrollViewDirection.NONE;
 | 
			
		||||
        if (this.layout.isPageView) {
 | 
			
		||||
            _tempVec2 = event.getLocation();
 | 
			
		||||
            // cc.Vec2.set(this._touchBeganPosition, _tempVec2.x, _tempVec2.y)
 | 
			
		||||
            this._touchBeganPosition = cc.v2(_tempVec2.x, _tempVec2.y);
 | 
			
		||||
        }
 | 
			
		||||
        super["_onTouchBegan"](event, captureListeners);
 | 
			
		||||
        if (this.isTransmitEvent) {
 | 
			
		||||
            this.transmitEvent(event, cc.Node.EventType.TOUCH_START);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    protected _onTouchMoved(event: cc.Event.EventTouch, captureListeners: any) {
 | 
			
		||||
        this.isCallSoonFinish = false;
 | 
			
		||||
        this.isCustomScroll = false;
 | 
			
		||||
 | 
			
		||||
        if (!this.canTouchMove) return;
 | 
			
		||||
        if (this.isTransmitEvent) {
 | 
			
		||||
            if (this.direction == ScrollViewDirection.NONE) {
 | 
			
		||||
                var start = event.getStartLocation();
 | 
			
		||||
                var curre = event.getLocation();
 | 
			
		||||
                var xOffset = Math.abs(start.x - curre.x);
 | 
			
		||||
                var yOffset = Math.abs(start.y - curre.y);
 | 
			
		||||
                if (xOffset > yOffset) {
 | 
			
		||||
                    // 本ScrollView滑动方向过程中达到一定偏移量是也可以向上发送事件
 | 
			
		||||
                    // if (this.vertical) {
 | 
			
		||||
                    //     if (xOffset - yOffset > 50) {
 | 
			
		||||
                    //         this.direction = UIScrollViewDirection.HORIZONTAL
 | 
			
		||||
                    //     }
 | 
			
		||||
                    // }
 | 
			
		||||
                    this.direction = ScrollViewDirection.HORIZONTAL;
 | 
			
		||||
 | 
			
		||||
                } else if (yOffset > xOffset) {
 | 
			
		||||
                    // 本ScrollView滑动方向过程中达到一定偏移量是也可以向上发送事件
 | 
			
		||||
                    // if (this.horizontal) {
 | 
			
		||||
                    //     if (yOffset - xOffset > 50) {
 | 
			
		||||
                    //         this.direction = UIScrollViewDirection.VERTICAL
 | 
			
		||||
                    //     }
 | 
			
		||||
                    // } 
 | 
			
		||||
                    this.direction = ScrollViewDirection.VERTICAL;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            var canTransmit = (this.vertical && this.direction === ScrollViewDirection.HORIZONTAL) || this.horizontal && this.direction == ScrollViewDirection.VERTICAL;
 | 
			
		||||
            if (canTransmit) {
 | 
			
		||||
                this.transmitEvent(event, cc.Node.EventType.TOUCH_MOVE);
 | 
			
		||||
                event.stopPropagation();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this.prevLocation = event.touch.getPreviousLocation();
 | 
			
		||||
        this.location = event.touch.getLocation();
 | 
			
		||||
        super["_onTouchMoved"](event, captureListeners);
 | 
			
		||||
        if (this.pullRefresh) {
 | 
			
		||||
            let outOfBoundary = this["_getHowMuchOutOfBoundary"]();
 | 
			
		||||
            let offset = this.vertical ? outOfBoundary.y : -outOfBoundary.x;
 | 
			
		||||
            if (offset > 0 && !this.isLockHeader && !this.isLockFooter) {
 | 
			
		||||
                this.headerProgress = offset < EPSILON ? 0 : offset / this.headerOutOffset;
 | 
			
		||||
                this.isMoveHeader = this.headerProgress >= this.headerMultiple;
 | 
			
		||||
                cc.Component.EventHandler.emitEvents(this.headerEvents, this, { action: false, progress: this.headerProgress, stage: this.isMoveHeader ? "wait" : "touch" });
 | 
			
		||||
                cc.Component.EventHandler.emitEvents(this.footerEvents, this, { action: false, progress: 0, stage: "release" });
 | 
			
		||||
            } else if (offset < 0 && !this.isLockHeader && !this.isLockFooter) {
 | 
			
		||||
                this.footerProgress = -offset < EPSILON ? 0 : -offset / this.footerOutOffset;
 | 
			
		||||
                this.isMoveFooter = this.footerProgress >= this.footerMultiple;
 | 
			
		||||
                cc.Component.EventHandler.emitEvents(this.footerEvents, this, { action: false, progress: this.footerProgress, stage: this.isMoveFooter ? "wait" : "touch" });
 | 
			
		||||
                cc.Component.EventHandler.emitEvents(this.headerEvents, this, { action: false, progress: 0, stage: "release" });
 | 
			
		||||
            } else if (offset == 0 && !this.isLockHeader && !this.isLockFooter) {
 | 
			
		||||
                this.clearProgress();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected _onTouchEnded(event: cc.Event.EventTouch, captureListeners: any) {
 | 
			
		||||
        this.isCallSoonFinish = false;
 | 
			
		||||
        this.isCustomScroll = false;
 | 
			
		||||
        if (!this.canTouchMove) return;
 | 
			
		||||
        if (this.layout.isPageView) {
 | 
			
		||||
            _tempVec2 = event.getLocation();
 | 
			
		||||
            // cc.Vec2.set(this._touchEndPosition, _tempVec2.x, _tempVec2.y)
 | 
			
		||||
            this._touchEndPosition = cc.v2(_tempVec2.x, _tempVec2.y);
 | 
			
		||||
        }
 | 
			
		||||
        super["_onTouchEnded"](event, captureListeners);
 | 
			
		||||
        if (this.isTransmitEvent) {
 | 
			
		||||
            this.transmitEvent(event, cc.Node.EventType.TOUCH_END);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    protected _onTouchCancelled(event: cc.Event.EventTouch, captureListeners: any) {
 | 
			
		||||
        this.isCallSoonFinish = false;
 | 
			
		||||
        this.isCustomScroll = false;
 | 
			
		||||
        if (!this.canTouchMove) return;
 | 
			
		||||
        if (this.layout.isPageView) {
 | 
			
		||||
            _tempVec2 = event.getLocation();
 | 
			
		||||
            // cc.Vec2.set(this._touchEndPosition, _tempVec2.x, _tempVec2.y)
 | 
			
		||||
            this._touchEndPosition = cc.v2(_tempVec2.x, _tempVec2.y);
 | 
			
		||||
        }
 | 
			
		||||
        if (this.isTransmitEvent) {
 | 
			
		||||
            this.transmitEvent(event, cc.Node.EventType.TOUCH_CANCEL);
 | 
			
		||||
        }
 | 
			
		||||
        super["_onTouchCancelled"](event, captureListeners);
 | 
			
		||||
    }
 | 
			
		||||
    scrollToAny(moveDelta: cc.Vec2, timeInSecond?: number, attenuated: boolean = true) {
 | 
			
		||||
        this.isCustomScroll = true;
 | 
			
		||||
        if (timeInSecond) {
 | 
			
		||||
            this._startAutoScroll(moveDelta, timeInSecond, attenuated, true);
 | 
			
		||||
        } else {
 | 
			
		||||
            this["_moveContent"](moveDelta);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    release() {
 | 
			
		||||
        this.isMoveHeader = false;
 | 
			
		||||
        this.isMoveFooter = false;
 | 
			
		||||
        if (this.isLockHeader || this.isLockFooter) {
 | 
			
		||||
            this.vertical && this.isLockHeader && (this["_topBoundary"] += this.headerOutOffset);
 | 
			
		||||
            this.vertical && this.isLockFooter && (this["_bottomBoundary"] -= this.footerOutOffset);
 | 
			
		||||
            this.horizontal && this.isLockHeader && (this["_leftBoundary"] -= this.headerOutOffset);
 | 
			
		||||
            this.horizontal && this.isLockFooter && (this["_rightBoundary"] += this.footerOutOffset);
 | 
			
		||||
            this.clearProgress();
 | 
			
		||||
            this.layout["onPositionChanged"]();
 | 
			
		||||
            this.isLockHeader = false;
 | 
			
		||||
            this.isLockFooter = false;
 | 
			
		||||
            this.startAutoScroll();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    startAutoScroll() {
 | 
			
		||||
        this["_autoScrolling"] = true;
 | 
			
		||||
        this["_outOfBoundaryAmountDirty"] = true;
 | 
			
		||||
    }
 | 
			
		||||
    protected _startAutoScroll(deltaMove: any, timeInSecond: any, attenuated: any, flag: boolean = false) {
 | 
			
		||||
        if (this.pullRefresh) {  // 如果没有刷新/加载的监听者 则不计算 
 | 
			
		||||
            if (this.isMoveHeader && !this.isLockHeader) {
 | 
			
		||||
                if (this.vertical) {
 | 
			
		||||
                    this["_topBoundary"] -= this.headerOutOffset;
 | 
			
		||||
                    deltaMove.y -= this.headerOutOffset;
 | 
			
		||||
                }
 | 
			
		||||
                if (this.horizontal) {
 | 
			
		||||
                    this["_leftBoundary"] += this.headerOutOffset;
 | 
			
		||||
                    deltaMove.x += this.headerOutOffset;
 | 
			
		||||
                }
 | 
			
		||||
                this.isLockHeader = true;
 | 
			
		||||
                cc.Component.EventHandler.emitEvents(this.headerEvents, this, { action: true, progress: this.headerProgress, stage: 'lock' });
 | 
			
		||||
            } else if (this.isMoveFooter && !this.isLockFooter) {
 | 
			
		||||
                if (this.vertical) {
 | 
			
		||||
                    this["_bottomBoundary"] += this.footerOutOffset;
 | 
			
		||||
                    deltaMove.y += this.footerOutOffset;
 | 
			
		||||
                }
 | 
			
		||||
                if (this.horizontal) {
 | 
			
		||||
                    this["_rightBoundary"] -= this.footerOutOffset;
 | 
			
		||||
                    deltaMove.x -= this.footerOutOffset;
 | 
			
		||||
                }
 | 
			
		||||
                this.isLockFooter = true;
 | 
			
		||||
                cc.Component.EventHandler.emitEvents(this.footerEvents, this, { action: true, progress: this.footerProgress, stage: 'lock' });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        super["_startAutoScroll"](deltaMove, timeInSecond, attenuated);
 | 
			
		||||
        if (!flag && this.layout.autoCenter) {
 | 
			
		||||
            const touchMoveVelocity = this["_calculateTouchMoveVelocity"]();
 | 
			
		||||
            if (!this.isQuicklyScrollable(touchMoveVelocity)) {
 | 
			
		||||
                this.soonFinish();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    protected _updateScrollBar(outOfBoundary: any) {
 | 
			
		||||
        super["_updateScrollBar"](cc.v2(outOfBoundary.x, outOfBoundary.y));
 | 
			
		||||
        if (this["_autoScrollBraking"]) return; // 自动回弹时不计算 (非手动)
 | 
			
		||||
        if (!this["_autoScrolling"]) return; // 非自动滚动时不计算 
 | 
			
		||||
        if (!this.pullRefresh) return;
 | 
			
		||||
        let offset = this.vertical ? outOfBoundary.y : -outOfBoundary.x;
 | 
			
		||||
        if (offset > 0) { // 下滑
 | 
			
		||||
            let progress = offset < EPSILON ? 0 : offset / this.headerOutOffset; //根据参数 headerOutOffset 计算当前下滑的办百分比
 | 
			
		||||
            if (this.isLockHeader) {
 | 
			
		||||
                this.headerProgress = this.headerProgress == 1 ? this.headerProgress : Math.max(progress, 1);
 | 
			
		||||
                cc.Component.EventHandler.emitEvents(this.headerEvents, this, { action: false, progress: this.headerProgress, stage: "lock" });
 | 
			
		||||
            } else {
 | 
			
		||||
                this.headerProgress = progress < this.headerProgress ? progress : this.headerProgress;
 | 
			
		||||
                cc.Component.EventHandler.emitEvents(this.headerEvents, this, { action: false, progress: this.headerProgress, stage: "release" });
 | 
			
		||||
            }
 | 
			
		||||
        } else if (offset < 0) {
 | 
			
		||||
            let progress = -offset < EPSILON ? 0 : -offset / this.footerOutOffset;  //根据参数 footerOutOffset 计算当前下滑的办百分比
 | 
			
		||||
            if (this.isLockFooter) {
 | 
			
		||||
                this.footerProgress = this.footerProgress == 1 ? this.footerProgress : Math.max(progress, 1);
 | 
			
		||||
                cc.Component.EventHandler.emitEvents(this.footerEvents, this, { action: false, progress: this.footerProgress, stage: "lock" });
 | 
			
		||||
            } else {
 | 
			
		||||
                this.footerProgress = progress < this.footerProgress ? progress : this.footerProgress;
 | 
			
		||||
                cc.Component.EventHandler.emitEvents(this.footerEvents, this, { action: false, progress: this.footerProgress, stage: "release" });
 | 
			
		||||
            }
 | 
			
		||||
        } else if (offset == 0) {
 | 
			
		||||
            // 正常滑动时 如果没有锁定头和尾时 释放所有进度
 | 
			
		||||
            if (!this.isLockHeader && !this.isLockFooter) {
 | 
			
		||||
                this.clearProgress();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    private clearProgress() {
 | 
			
		||||
        cc.Component.EventHandler.emitEvents(this.headerEvents, this, { action: false, progress: 0, stage: "release" });
 | 
			
		||||
        cc.Component.EventHandler.emitEvents(this.footerEvents, this, { action: false, progress: 0, stage: "release" });
 | 
			
		||||
    }
 | 
			
		||||
    private dispatchPageTurningEvent() {
 | 
			
		||||
        if (this.layout["_lastPageIndex"] === this.layout["_currPageIndex"]) return;
 | 
			
		||||
        this.layout["_lastPageIndex"] = this.layout["_currPageIndex"];
 | 
			
		||||
        cc.Component.EventHandler.emitEvents(this.layout.pageEvents, this, "page-turning");
 | 
			
		||||
        this.node.emit("page-turning", this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected _handleReleaseLogic(touch: any) {
 | 
			
		||||
        if (this.layout.isPageView) {
 | 
			
		||||
            this._autoScrollToPage();
 | 
			
		||||
            if (this["_scrolling"]) {
 | 
			
		||||
                this["_scrolling"] = false;
 | 
			
		||||
                if (!this["_autoScrolling"]) {
 | 
			
		||||
                    this["_dispatchEvent"](cc.ScrollView.EventType.SCROLL_ENDED);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            super["_handleReleaseLogic"](touch);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    protected _autoScrollToPage() {
 | 
			
		||||
        const bounceBackStarted = this["_startBounceBackIfNeeded"]();
 | 
			
		||||
        if (bounceBackStarted) {
 | 
			
		||||
            const bounceBackAmount = this["_getHowMuchOutOfBoundary"]();
 | 
			
		||||
            this["_clampDelta"](bounceBackAmount);
 | 
			
		||||
            if (bounceBackAmount.x > 0 || bounceBackAmount.y < 0) {
 | 
			
		||||
                if (this.layout.horizontal) {
 | 
			
		||||
                    if (this.layout.horizontalAxisDirection == UISuperLayout.HorizontalAxisDirection.LEFT_TO_RIGHT) {
 | 
			
		||||
                        this.layout["_currPageIndex"] = this.layout.itemTotal === 0 ? 0 : this.layout.itemTotal - 1;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        this.layout["_currPageIndex"] = 0;
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (this.layout.verticalAxisDirection == UISuperLayout.VerticalAxisDirection.TOP_TO_BOTTOM) {
 | 
			
		||||
                        this.layout["_currPageIndex"] = this.layout.itemTotal === 0 ? 0 : this.layout.itemTotal - 1;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        this.layout["_currPageIndex"] = 0;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (bounceBackAmount.x < 0 || bounceBackAmount.y > 0) {
 | 
			
		||||
                if (this.layout.horizontal) {
 | 
			
		||||
                    if (this.layout.horizontalAxisDirection == UISuperLayout.HorizontalAxisDirection.LEFT_TO_RIGHT) {
 | 
			
		||||
                        this.layout["_currPageIndex"] = 0;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        this.layout["_currPageIndex"] = this.layout.itemTotal === 0 ? 0 : this.layout.itemTotal - 1;
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (this.layout.verticalAxisDirection == UISuperLayout.VerticalAxisDirection.TOP_TO_BOTTOM) {
 | 
			
		||||
                        this.layout["_currPageIndex"] = 0;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        this.layout["_currPageIndex"] = this.layout.itemTotal === 0 ? 0 : this.layout.itemTotal - 1;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (this.layout.indicator) {
 | 
			
		||||
                this.layout.indicator["_changedState"]();
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            const moveOffset = new cc.Vec2();
 | 
			
		||||
            // cc.Vec2.subtract(moveOffset, this._touchBeganPosition, this._touchEndPosition)
 | 
			
		||||
            moveOffset.x = this._touchBeganPosition.x - this._touchEndPosition.x;
 | 
			
		||||
            moveOffset.y = this._touchBeganPosition.y - this._touchEndPosition.y;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            const index = this.layout["_currPageIndex"];
 | 
			
		||||
            var nextIndex = index + this.getDragDirection(moveOffset);
 | 
			
		||||
            var timeInSecond = this.layout.pageTurningSpeed * Math.abs(index - nextIndex);
 | 
			
		||||
            if (this.layout.footerLoop) {
 | 
			
		||||
                if (nextIndex >= this.layout.itemTotal) {
 | 
			
		||||
                    nextIndex = 0;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (this.layout.headerLoop) {
 | 
			
		||||
                if (nextIndex < 0) {
 | 
			
		||||
                    nextIndex = this.layout.itemTotal - 1;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            const count = this.layout.itemTotal;
 | 
			
		||||
            if (nextIndex < count) {
 | 
			
		||||
                if (this.isScrollable(moveOffset, index, nextIndex)) {
 | 
			
		||||
                    this.scrollToPage(nextIndex, timeInSecond);
 | 
			
		||||
                    return;
 | 
			
		||||
                } else {
 | 
			
		||||
                    const touchMoveVelocity = this["_calculateTouchMoveVelocity"]();
 | 
			
		||||
                    if (this.isQuicklyScrollable(touchMoveVelocity)) {
 | 
			
		||||
                        this.scrollToPage(nextIndex, timeInSecond);
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            this.scrollToPage(index, timeInSecond);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    savePageIndex(idx: number) {
 | 
			
		||||
        if (idx < 0 || idx >= this.layout.itemTotal) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        this.layout["_currPageIndex"] = idx;
 | 
			
		||||
        if (this.layout.indicator) {
 | 
			
		||||
            this.layout.indicator["_changedState"]();
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    protected scrollToPage(idx: number, timeInSecond = 0.3) {
 | 
			
		||||
        if (idx < 0 || idx >= this.layout.itemTotal) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (this.savePageIndex(idx)) {
 | 
			
		||||
            this.layout.scrollToIndex(idx, timeInSecond);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 快速滑动
 | 
			
		||||
    protected isQuicklyScrollable(touchMoveVelocity: cc.Vec3) {
 | 
			
		||||
        if (this.horizontal) {
 | 
			
		||||
            if (Math.abs(touchMoveVelocity.x) > this.layout.autoPageTurningThreshold) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (this.vertical) {
 | 
			
		||||
            if (Math.abs(touchMoveVelocity.y) > this.layout.autoPageTurningThreshold) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    protected getDragDirection(moveOffset: cc.Vec2) {
 | 
			
		||||
        if (this.horizontal) {
 | 
			
		||||
            if (moveOffset.x === 0) {
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.layout.horizontalAxisDirection == UISuperLayout.HorizontalAxisDirection.LEFT_TO_RIGHT) {
 | 
			
		||||
                return (moveOffset.x > 0 ? this.layout.groupItemTotal : -this.layout.groupItemTotal);
 | 
			
		||||
            } else {
 | 
			
		||||
                return (moveOffset.x < 0 ? this.layout.groupItemTotal : -this.layout.groupItemTotal);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // 由于滚动 Y 轴的原点在在右上角所以应该是小于 0
 | 
			
		||||
            if (moveOffset.y === 0) {
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.layout.verticalAxisDirection == UISuperLayout.VerticalAxisDirection.TOP_TO_BOTTOM) {
 | 
			
		||||
                return (moveOffset.y < 0 ? this.layout.groupItemTotal : -this.layout.groupItemTotal);
 | 
			
		||||
            } else {
 | 
			
		||||
                return (moveOffset.y > 0 ? this.layout.groupItemTotal : -this.layout.groupItemTotal);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 是否超过自动滚动临界值
 | 
			
		||||
    protected isScrollable(offset: cc.Vec2, index: number, nextIndex: number) {
 | 
			
		||||
        const viewTrans = this.view;
 | 
			
		||||
        if (!viewTrans) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (this.horizontal) {
 | 
			
		||||
            return Math.abs(offset.x) >= viewTrans.width * this.layout.scrollThreshold;
 | 
			
		||||
        } else if (this.vertical) {
 | 
			
		||||
            return Math.abs(offset.y) >= viewTrans.height * this.layout.scrollThreshold;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    protected transmitEvent(event: any, eventType: string) {
 | 
			
		||||
        var e = new cc.Event.EventTouch(event.getTouches(), event.bubbles);
 | 
			
		||||
        e.type = eventType;
 | 
			
		||||
        e.touch = event.touch;
 | 
			
		||||
        let target: any = event.target;
 | 
			
		||||
        target.parent.dispatchEvent(e);
 | 
			
		||||
    }
 | 
			
		||||
    private soonFinish() {
 | 
			
		||||
        this.isCallSoonFinish = true;
 | 
			
		||||
        this.layout["soonFinish"]();
 | 
			
		||||
    }
 | 
			
		||||
    protected _processAutoScrolling(dt: number) {
 | 
			
		||||
        let isAutoScrollBrake = this["_isNecessaryAutoScrollBrake"]();
 | 
			
		||||
        let brakingFactor = isAutoScrollBrake ? OUT_OF_BOUNDARY_BREAKING_FACTOR : 1;
 | 
			
		||||
        this["_autoScrollAccumulatedTime"] += dt * (1 / brakingFactor);
 | 
			
		||||
 | 
			
		||||
        let percentage = Math.min(1, this["_autoScrollAccumulatedTime"] / this["_autoScrollTotalTime"]);
 | 
			
		||||
        if (this["_autoScrollAttenuate"]) {
 | 
			
		||||
            percentage = quintEaseOut(percentage);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let newPosition = this["_autoScrollStartPosition"].add(this["_autoScrollTargetDelta"].mul(percentage));
 | 
			
		||||
        let reachedEnd = Math.abs(percentage - 1) <= EPSILON;
 | 
			
		||||
 | 
			
		||||
        let fireEvent = Math.abs(percentage - 1) <= this["getScrollEndedEventTiming"]();
 | 
			
		||||
        if (fireEvent && !this["_isScrollEndedWithThresholdEventFired"]) {
 | 
			
		||||
            this["_dispatchEvent"]('scroll-ended-with-threshold');
 | 
			
		||||
            this["_isScrollEndedWithThresholdEventFired"] = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.elastic) {
 | 
			
		||||
            let brakeOffsetPosition = newPosition.sub(this["_autoScrollBrakingStartPosition"]);
 | 
			
		||||
            if (isAutoScrollBrake) {
 | 
			
		||||
                brakeOffsetPosition = brakeOffsetPosition.mul(brakingFactor);
 | 
			
		||||
            }
 | 
			
		||||
            newPosition = this["_autoScrollBrakingStartPosition"].add(brakeOffsetPosition);
 | 
			
		||||
        } else {
 | 
			
		||||
            let moveDelta = newPosition.sub(this.getContentPosition());
 | 
			
		||||
            let outOfBoundary = this["_getHowMuchOutOfBoundary"](moveDelta);
 | 
			
		||||
            if (!outOfBoundary.fuzzyEquals(cc.v2(0, 0), EPSILON)) {
 | 
			
		||||
                newPosition = newPosition.add(outOfBoundary);
 | 
			
		||||
                reachedEnd = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (reachedEnd) {
 | 
			
		||||
            this["_autoScrolling"] = false;
 | 
			
		||||
        }
 | 
			
		||||
        if (this.layout.autoCenter && !this.isCallSoonFinish && !this.isCustomScroll) {
 | 
			
		||||
            if (this["_autoScrollTotalTime"] < 2 || percentage >= 0.8) {
 | 
			
		||||
                this.soonFinish();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        let deltaMove = newPosition.sub(this.getContentPosition());
 | 
			
		||||
        this["_moveContent"](this["_clampDelta"](deltaMove), reachedEnd);
 | 
			
		||||
        this["_dispatchEvent"]('scrolling');
 | 
			
		||||
        if (!this["_autoScrolling"]) {
 | 
			
		||||
            this["_isBouncing"] = false;
 | 
			
		||||
            this["_scrolling"] = false;
 | 
			
		||||
            this["_dispatchEvent"]('scroll-ended');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.0.8",
 | 
			
		||||
  "uuid": "dfb04646-c016-4594-b7b3-8d83fa7a925a",
 | 
			
		||||
  "isPlugin": false,
 | 
			
		||||
  "loadPluginInWeb": true,
 | 
			
		||||
  "loadPluginInNative": true,
 | 
			
		||||
  "loadPluginInEditor": false,
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user