[add] Engine

This commit is contained in:
2022-08-26 16:48:17 +08:00
parent f67e566f2a
commit d9c19f096c
197 changed files with 10626 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "b43bf5ea-67c5-4fc8-9893-1f406a52508f",
"importer": "folder",
"isBundle": false,
"bundleName": "",
"priority": 1,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

View 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);
}
}

View File

@@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "c5872cc0-91a4-49cb-a055-e037accd801d",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "d042d487-d962-4d90-920e-70ab9b8b383c",
"importer": "folder",
"isBundle": false,
"bundleName": "",
"priority": 1,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

View 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;
}
}

View File

@@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "f3ba292a-ecad-4485-ab60-1cd3ee94979a",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View 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;
}
}

View File

@@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "ce946cad-16db-4383-a734-43bb8f14089e",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "4e2d4321-bbfb-46d8-87bc-15a7c99c000d",
"importer": "folder",
"isBundle": false,
"bundleName": "",
"priority": 1,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

View 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));
}
}

View File

@@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "b47d81c4-01a1-45cd-94f8-19daf96f17a8",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "dcb480f5-98b4-4a48-9d82-e3e1fe837e8d",
"importer": "folder",
"isBundle": false,
"bundleName": "",
"priority": 1,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

View File

@@ -0,0 +1,127 @@
declare interface Array<T> {
/**
* 移除一個值並且回傳
* @param index
*/
ExRemoveAt(index: number): T;
/**
* 移除全部值(注意. 參考的也會被清空)
* @example
*
* let bar: number[] = [1, 2, 3];
* let bar2: number[] = bar;
* bar.Clear();
* console.log(bar, bar2);
*
* // {
* // "bar": [],
* // "bar2": []
* // }
*/
Clear(): void;
/**
* 物件陣列排序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<cc.Component.EventHandler>forHoldButton使用
* Add a non persistent listener to the UnityEvent.
* @param call Callback function.
*/
AddListener(call: Function): void;
}
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.Clear || Object.defineProperty(Array.prototype, "Clear", {
enumerable: false,
value: function (): void {
this.length = 0;
// let foo: number[] = [1, 2, 3];
// let bar: number[] = [1, 2, 3];
// let foo2: number[] = foo;
// let bar2: number[] = bar;
// foo = [];
// bar.length = 0;
// console.log(foo, bar, foo2, bar2);
// {
// "foo": [],
// "bar": [],
// "foo2": [
// 1,
// 2,
// 3
// ],
// "bar2": []
// }
}
});
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;
}
});
Array.prototype.AddListener || Object.defineProperty(Array.prototype, "AddListener", {
enumerable: false,
value: function (call: Function): void {
let EventHandler: cc.Component.EventHandler = new cc.Component.EventHandler();
EventHandler.target = <any>"Callback";
EventHandler.component = "Callback";
EventHandler.handler = <any>call;
this.push(EventHandler);
}
});

View File

@@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "02aa6cd7-21a1-4c22-bcbe-296bb938badd",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,429 @@
declare namespace cc {
export interface Node {
/**
* 設定世界座標
* @param worldPoint
*/
SetWorldPosition(worldPoint: cc.Vec2 | cc.Vec3): void;
/**
* 取得世界座標
*/
GetWorldPosition(): cc.Vec2;
/**
* 取得目標在自己區域的相對座標
* @param target
*/
GetTargetInMyLocalPosition(target: cc.Node): cc.Vec2;
/**
* 取得目標距離
* @param target
*/
GetTargetDistance(target: cc.Node): number;
/**
* 設定長寬
* @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;
/** 通過觀察目標來設置 rotation */
ExLookAt(targetPos: cc.Vec3): void;
/**
* !#zh
* 当前节点的自身激活状态。<br/>
* 值得注意的是,一个节点的父节点如果不被激活,那么即使它自身设为激活,它仍然无法激活。<br/>
* 如果你想检查节点在场景中实际的激活状态可以使用 {{#crossLink "Node/activeInHierarchy:property"}}{{/crossLink}}。
* @param value 当前节点的自身激活状态
*/
SetActive(value: boolean): void;
}
// export interface ActionInterval {
// step(dt: number): void;
// }
export interface Tween {
/**
* 獲取持續時間
* @example let duration = tween.duration();
*/
duration(): number;
/**
* 獲取已經進行的時間
* @example let elapsed = tween.elapsed();
*/
elapsed(): number;
/**
* 跳轉到指定時間
* @param time 時間(秒)
* @example tween.goto(2);
*/
goto(time: number): void;
}
}
cc.Node.prototype.SetWorldPosition || Object.defineProperty(cc.Node.prototype, 'SetWorldPosition', {
enumerable: false,
value: function (cocosWorldPos: cc.Vec2 | cc.Vec3) {
// 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.GetTargetInMyLocalPosition || Object.defineProperty(cc.Node.prototype, 'GetTargetInMyLocalPosition', {
enumerable: false,
value: function (target: cc.Node): cc.Vec2 {
let selfPos: cc.Vec2 = this.GetWorldPosition();
let targetPos: cc.Vec2 = target.GetWorldPosition();
let diff: cc.Vec2 = targetPos.sub(selfPos);
let newPos: cc.Vec2 = this.getPosition().add(diff);
return newPos;
}
});
cc.Node.prototype.GetTargetDistance || Object.defineProperty(cc.Node.prototype, 'GetTargetDistance', {
enumerable: false,
value: function (target: cc.Node): number {
let vector: cc.Vec2 = target.GetWorldPosition().sub(this.GetWorldPosition());
let distance: number = vector.mag();
return distance;
}
});
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.ExLookAt || Object.defineProperty(cc.Node.prototype, "ExLookAt", {
enumerable: false,
value: function (targetPos: cc.Vec3): void {
let TargetX: number = targetPos.x;
let TargetY: number = targetPos.y;
let SelfX: number = this.x;
let SelfY: number = this.y;
let r1: number = Math.atan2(TargetX - SelfX, TargetY - SelfY);
let angle: number = (180 * r1 / Math.PI);
this.angle = -angle;
},
});
cc.Node.prototype.SetActive || Object.defineProperty(cc.Node.prototype, "SetActive", {
enumerable: false,
value: function (value: boolean): void {
this.active = value;
},
});
// 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);
// }
// cc.ActionInterval.prototype.step || Object.defineProperty(cc.ActionInterval.prototype, "step", {
// enumerable: false,
// value: function (dt: number): void {
// if (this.paused) {
// return;
// }
// if (this._firstTick && !this._goto) {
// this._firstTick = false;
// this._elapsed = 0;
// } else {
// this._elapsed += dt;
// }
// let t: number = this._elapsed / (this._duration > 0.0000001192092896 ? this._duration : 0.0000001192092896);
// t = (1 > t ? t : 1);
// this.update(t > 0 ? t : 0);
// // Compatible with repeat class, Discard after can be deleted (this._repeatMethod)
// if (this._repeatMethod && this._timesForRepeat > 1 && this.isDone()) {
// if (!this._repeatForever) {
// this._timesForRepeat--;
// }
// this.startWithTarget(this.target);
// this.step(this._elapsed - this._duration);
// }
// }
// });
// /**
// * 暂停
// * @example tween.pause();
// */
// cc.Tween.prototype.pause = function () {
// this._finalAction.paused = true;
// };
// /**
// * 恢复
// * @example tween.resume();
// */
// cc.Tween.prototype.resume = function () {
// this._finalAction.paused = false;
// };
// /**
// * 倍速播放
// * @param speed 倍速
// * @example tween.speed(2);
// */
// cc.Tween.prototype.speed = function (speed) {
// this._finalAction._speedMethod = true;
// this._finalAction._speed = speed;
// }
cc.Tween.prototype.duration || Object.defineProperty(cc.Tween.prototype, "duration", {
enumerable: false,
value: function (): number {
// let duration: number = this._finalAction._duration;
let duration: number = 0;
for (let i: number = 0; i < this["_actions"].length - 1; i++) {
const action: any = this["_actions"][i];
duration += action._duration;
}
// duration += ((cc.game.getFrameRate() / 3) / cc.game.getFrameRate());
duration *= 1.3;
return duration;
}
});
cc.Tween.prototype.elapsed || Object.defineProperty(cc.Tween.prototype, "elapsed", {
enumerable: false,
value: function (): number {
return this._finalAction._elapsed;
}
});
cc.Tween.prototype.goto || Object.defineProperty(cc.Tween.prototype, "goto", {
enumerable: false,
/**
* @param time 時間(秒)
*/
value: function (time: number): void {
this._finalAction._goto = true;
this._finalAction._elapsed = time;
}
});
// cc.ActionManager.prototype.pauseByTag = function (tag, pause) {
// if (tag === cc.Action.TAG_INVALID) {
// cc.logID(1002);
// }
// let hashTargets = this._hashTargets;
// for (let name in hashTargets) {
// let element = hashTargets[name];
// for (let i = 0, l = element.actions.length; i < l; ++i) {
// let action = element.actions[i];
// if (action && action.getTag() === tag) {
// action.paused = pause;
// break;
// }
// }
// }
// }
// /**
// * 根据标签暂停动作
// * @param tag tween的标签
// * @example cc.Tween.pauseByTag(100);
// */
// cc.Tween.pauseByTag = function (tag) {
// cc.director.getActionManager().pauseByTag(tag, true);
// }
// /**
// * 根据标签恢复动作
// * @param tag tween的标签
// * @example cc.Tween.resumeByTag(100);
// */
// cc.Tween.resumeByTag = function (tag) {
// cc.director.getActionManager().pauseByTag(tag, false);
// }

View File

@@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "b373f805-9297-4af5-8ea6-0a250649b5b0",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View 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;
}
})

View File

@@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "788e7381-bee6-4b74-addb-c4aa4c4ff4e3",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "919cddcd-eed2-40a7-be67-2e21365fefe4",
"importer": "folder",
"isBundle": false,
"bundleName": "",
"priority": 1,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

View 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 bitRGBA1 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;
}
}

View File

@@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "425b00d6-0d47-4978-9a9f-235733348e3d",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "d6e55fc6-00b6-496a-aae2-74d694c1223b",
"importer": "folder",
"isBundle": false,
"bundleName": "",
"priority": 1,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

View File

@@ -0,0 +1,192 @@
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.ExToInt());
// 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 = false;
/**
* 是否进行边界检查
* @param flag 标记开关true 为开启false 为关闭
*/
function enableBoundaryChecking(flag = true) {
_boundaryCheckingState = flag;
}
}

View File

@@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "363f5f7f-0623-4013-8571-0bb5c1dc95e6",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View 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];
}
}
}

View File

@@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "ba4dee5b-ca5b-4435-a068-c4f5dd832bab",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "f6471056-03d8-4d55-b039-6b62d056547c",
"importer": "folder",
"isBundle": false,
"bundleName": "",
"priority": 1,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

View 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
}

View File

@@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "3e607467-693c-46a9-ac93-ab973e52024d",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,8 @@
const { ccclass, property } = cc._decorator;
@ccclass
export default class ScrollItem extends cc.Component {
ImplementSet(...iniData: any[]): void {
//
}
}

View File

@@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "0a733d8c-80ae-4e0b-be1b-07dba226c237",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "67f1a0e4-877e-4ad0-bc1b-e1175c620ca1",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View 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;
}
public _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);
}
}
public _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();
}
}
}
public _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);
}
}
public _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');
}
}
}

View File

@@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "dfb04646-c016-4594-b7b3-8d83fa7a925a",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}