import { UITransform, Vec2, Vec3,Node,screen, _decorator, find, director, Color, UIOpacity, tween, Label } from "cc"; const { ccclass, property } = _decorator; @ccclass("utils") export class utils { /** * 验证字符串是否是空的 * @s 字符串 */ public static isNull(s: string) { if(s == "" || s == null || s == undefined){ return true; } if (typeof s === 'string'){ let re = new RegExp("^[ ]+$"); return re.test(s); }else{ return false; } } /** * 合并多个字典 * @param args */ public static merge(...args) { let mergeFn = (arg1: T, arg2: U) : (T & U) =>{ let res = {} as (T & U); res = Object.assign(arg1, arg2); return res; }; let nDict = {}; args.forEach(obj => { nDict = mergeFn(nDict,obj); }); return nDict; } /** * 深度拷贝 * @param {any} sObj 拷贝的对象 * @returns */ public static clone(sObj: any) { if (sObj === null || typeof sObj !== "object") { return sObj; } let s: { [key: string]: any } = {}; if (sObj.constructor === Array) { s = []; } for (let i in sObj) { if (sObj.hasOwnProperty(i)) { s[i] = this.clone(sObj[i]); } } return s; } /** * 将object转化为数组 * @param { any} srcObj * @returns */ public static objectToArray(srcObj: { [key: string]: any }) { let resultArr: any[] = []; // to array for (let key in srcObj) { if (!srcObj.hasOwnProperty(key)) { continue; } resultArr.push(srcObj[key]); } return resultArr; } /** * !#zh 将数组转化为object。 */ /** * 将数组转化为object。 * @param { any} srcObj * @param { string} objectKey * @returns */ public static arrayToObject(srcObj: any, objectKey: string) { let resultObj: { [key: string]: any } = {}; // to object for (var key in srcObj) { if (!srcObj.hasOwnProperty(key) || !srcObj[key][objectKey]) { continue; } resultObj[srcObj[key][objectKey]] = srcObj[key]; } return resultObj; } /** * 根据权重,计算随机内容 * @param {arrany} weightArr * @param {number} totalWeight 权重 * @returns */ public static getWeightRandIndex(weightArr: [], totalWeight: number) { let randWeight: number = Math.floor(Math.random() * totalWeight); let sum: number = 0; for (var weightIndex: number = 0; weightIndex < weightArr.length; weightIndex++) { sum += weightArr[weightIndex]; if (randWeight < sum) { break; } } return weightIndex; } /** * 从n个数中获取m个随机数 * @param {Number} n 总数 * @param {Number} m 获取数 * @returns {Array} array 获取数列 */ public static getRandomNFromM(n: number, m: number) { let array: any[] = []; let intRd: number = 0; let count: number = 0; while (count < m) { if (count >= n + 1) { break; } intRd = this.getRandomInt(0, n); var flag = 0; for (var i = 0; i < count; i++) { if (array[i] === intRd) { flag = 1; break; } } if (flag === 0) { array[count] = intRd; count++; } } return array; } /** * 获取随机整数 * @param {Number} min 最小值 * @param {Number} max 最大值 * @returns */ public static getRandomInt(min: number, max: number) { let r: number = Math.random(); let rr: number = r * (max - min + 1) + min; return Math.floor(rr); } /** * 获取字符串长度 * @param {string} render * @returns */ public static getStringLength(render: string) { let strArr: string = render; let len: number = 0; for (let i: number = 0, n = strArr.length; i < n; i++) { let val: number = strArr.charCodeAt(i); if (val <= 255) { len = len + 1; } else { len = len + 2; } } return Math.ceil(len / 2); } /** * 要从一个数组模型中随机取出 n 个元素 * @param arr * @param n * @returns 返回一个新的数组 */ public static getRandomElements(arr: T[], n: number): T[] { if (n <= 0) return []; // 如果 n 小于等于 0,返回空数组 //复制数组以避免修改原数组 const copy = [...arr]; //Fisher-Yates 洗牌算法 for (let i = copy.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [copy[i], copy[j]] = [copy[j], copy[i]]; } //如果 n 超过数组长度,返回乱序后的整个数组 if (n >= copy.length) { return copy; } //返回前 n 个元素 return copy.slice(0, n); } /** * 判断传入的参数是否为空的Object。数组或undefined会返回false * @param obj */ public static isEmptyObject(obj: any) { let result: boolean = true; if (obj && obj.constructor === Object) { for (var key in obj) { if (obj.hasOwnProperty(key)) { result = false; break; } } } else { result = false; } return result; } /** * 判断是否是新的一天 * @param {Object|Number} dateValue 时间对象 todo MessageCenter 与 pve 相关的时间存储建议改为 Date 类型 * @returns {boolean} */ public static isNewDay(dateValue: any) { // todo:是否需要判断时区? var oldDate: any = new Date(dateValue); var curDate: any = new Date(); var oldYear = oldDate.getYear(); var oldMonth = oldDate.getMonth(); var oldDay = oldDate.getDate(); var curYear = curDate.getYear(); var curMonth = curDate.getMonth(); var curDay = curDate.getDate(); if (curYear > oldYear) { return true; } else { if (curMonth > oldMonth) { return true; } else { if (curDay > oldDay) { return true; } } } return false; } /** * 获取对象属性数量 * @param {object}o 对象 * @returns */ public static getPropertyCount(o: Object) { var n, count = 0; for (n in o) { if (o.hasOwnProperty(n)) { count++; } } return count; } /** * 返回一个差异化数组(将array中diff里的值去掉) * @param array * @param diff */ public static difference(array: [], diff: any) { let result: any[] = []; if (array.constructor !== Array || diff.constructor !== Array) { return result; } let length = array.length; for (let i: number = 0; i < length; i++) { if (diff.indexOf(array[i]) === -1) { result.push(array[i]); } } return result; } public static _stringToArray(string: string) { // 用于判断emoji的正则们 var rsAstralRange = '\\ud800-\\udfff'; var rsZWJ = '\\u200d'; var rsVarRange = '\\ufe0e\\ufe0f'; var rsComboMarksRange = '\\u0300-\\u036f'; var reComboHalfMarksRange = '\\ufe20-\\ufe2f'; var rsComboSymbolsRange = '\\u20d0-\\u20ff'; var rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange; var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']'); var rsFitz = '\\ud83c[\\udffb-\\udfff]'; var rsOptVar = '[' + rsVarRange + ']?'; var rsCombo = '[' + rsComboRange + ']'; var rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')'; var reOptMod = rsModifier + '?'; var rsAstral = '[' + rsAstralRange + ']'; var rsNonAstral = '[^' + rsAstralRange + ']'; var rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}'; var rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]'; var rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*'; var rsSeq = rsOptVar + reOptMod + rsOptJoin; var rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')'; var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g'); var hasUnicode = function (val: any) { return reHasUnicode.test(val); }; var unicodeToArray = function (val: any) { return val.match(reUnicode) || []; }; var asciiToArray = function (val: any) { return val.split(''); }; return hasUnicode(string) ? unicodeToArray(string) : asciiToArray(string); } // 模拟传msg的uuid public static simulationUUID() { function s4() { return Math.floor((1 + Math.random()) * 0x10000) .toString(16) .substring(1); } return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); } public static trim(str: string) { return str.replace(/(^\s*)|(\s*$)/g, ""); } /** * 判断当前时间是否在有效时间内 * @param {String|Number} start 起始时间。带有时区信息 * @param {String|Number} end 结束时间。带有时区信息 */ public static isNowValid(start: any, end: any) { var startTime = new Date(start); var endTime = new Date(end); var result = false; if (startTime.getDate() + '' !== 'NaN' && endTime.getDate() + '' !== 'NaN') { var curDate = new Date(); result = curDate < endTime && curDate > startTime; } return result; } /** * 返回相隔天数 * @param start * @param end * @returns */ public static getDeltaDays(start: any, end: any) { start = new Date(start); end = new Date(end); let startYear: number = start.getFullYear(); let startMonth: number = start.getMonth() + 1; let startDate: number = start.getDate(); let endYear: number = end.getFullYear(); let endMonth: number = end.getMonth() + 1; let endDate: number = end.getDate(); start = new Date(startYear + '/' + startMonth + '/' + startDate + ' GMT+0800').getTime(); end = new Date(endYear + '/' + endMonth + '/' + endDate + ' GMT+0800').getTime(); let deltaTime = end - start; return Math.floor(deltaTime / (24 * 60 * 60 * 1000)); } /** * 获取数组最小值 * @param array 数组 * @returns */ public static getMin(array: number[]) { let result: number = null!; if (array.constructor === Array) { let length = array.length; for (let i = 0; i < length; i++) { if (i === 0) { result = Number(array[0]); } else { result = result > Number(array[i]) ? Number(array[i]) : result; } } } return result; } /** * 格式化两位小数点 * @param time * @returns */ public static formatTwoDigits(time: number) { //@ts-ignore return (Array(2).join(0) + time).slice(-2); } /** * 根据格式返回时间 * @param date 时间 * @param fmt 格式 * @returns */ public static formatDate(date: Date, fmt: string) { let o: any = { "M+": date.getMonth() + 1, //月份 "d+": date.getDate(), //日 "h+": date.getHours(), //小时 "m+": date.getMinutes(), //分 "s+": date.getSeconds(), //秒 "q+": Math.floor((date.getMonth() + 3) / 3), //季度 "S": date.getMilliseconds() //毫秒 }; if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length)); for (let k in o) if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); return fmt; } /** * 获取格式化后的日期(不含小时分秒) */ public static getDay() { let date: Date = new Date(); return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate(); } /** * 格式化名字,XXX... * @param {string} name 需要格式化的字符串 * @param {number}limit * @returns {string} 返回格式化后的字符串XXX... */ public static formatName(name: string, limit: number) { limit = limit || 6; var nameArray = this._stringToArray(name); var str = ''; var length = nameArray.length; if (length > limit) { for (var i = 0; i < limit; i++) { str += nameArray[i]; } str += '...'; } else { str = name; } return str; } /** * 格式化钱数,超过10000 转换位 10K 10000K 转换为 10M * @param {number}money 需要被格式化的数值 * @returns {string}返回 被格式化的数值 */ public static formatMoney(money: number) { let arrUnit: string[] = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 'B', 'N', 'D']; let strValue: string = ''; for (let idx: number = 0; idx < arrUnit.length; idx++) { if (money >= 10000) { money /= 1000; } else { strValue = Math.floor(money) + arrUnit[idx]; break; } } if (strValue === '') { strValue = Math.floor(money) + 'U'; //超过最大值就加个U } return strValue; } /** * 格式化数值 * @param {number}value 需要被格式化的数值 * @returns {string}返回 被格式化的数值 */ public static formatValue(value: number) { let arrUnit: string[] = []; let strValue: string = ''; for (let i = 0; i < 26; i++) { arrUnit.push(String.fromCharCode(97 + i)); } for (let idx: number = 0; idx < arrUnit.length; idx++) { if (value >= 10000) { value /= 1000; } else { strValue = Math.floor(value) + arrUnit[idx]; break; } } return strValue; } /** * 根据剩余秒数格式化剩余时间 返回 HH:MM:SS * @param {Number} leftSec */ public static formatTimeForSecond(leftSec: number, withoutSeconds: boolean = false) { let timeStr: string = ''; let sec: number = leftSec % 60; let leftMin: number = Math.floor(leftSec / 60); leftMin = leftMin < 0 ? 0 : leftMin; let hour: number = Math.floor(leftMin / 60); let min: number = leftMin % 60; if (hour > 0) { timeStr += hour > 9 ? hour.toString() : '0' + hour; timeStr += ':'; } else { timeStr += '00:'; } timeStr += min > 9 ? min.toString() : '0' + min; if (!withoutSeconds) { timeStr += ':'; timeStr += sec > 9 ? sec.toString() : '0' + sec; } return timeStr; } /** * 根据剩余毫秒数格式化剩余时间 返回 HH:MM:SS * * @param {Number} ms */ public static formatTimeForMillisecond(ms: number): Object { let second: number = Math.floor(ms / 1000 % 60); let minute: number = Math.floor(ms / 1000 / 60 % 60); let hour: number = Math.floor(ms / 1000 / 60 / 60); return { 'hour': hour, 'minute': minute, 'second': second }; } /** * 格式化时间戳字符串 * @param timestamp 1740006560000 * @returns 输出2025-02-20 05:09:20 */ public static formatTimestamp(timestamp: number): string { const date = new Date(timestamp); //获取年月日时分秒 const year = date.getFullYear(); //月份从 0 开始,需要 +1 const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const hours = String(date.getHours()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0'); const seconds = String(date.getSeconds()).padStart(2, '0'); //拼接成 YYYY-MM-DD HH:MM:SS 格式 return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; } /** * 随机乱序数组 * @param array * @returns */ public static randomArray(array) { // 使用 Fisher-Yates Shuffle 算法 for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); // 交换元素 [array[i], array[j]] = [array[j], array[i]]; } return array; } /** * 获得开始和结束两者之间相隔分钟数 * * @static * @param {number} start * @param {number} end * @memberof utils */ public static getOffsetMimutes(start: number, end: number) { let offSetTime: number = end - start; let minute: number = Math.floor((offSetTime % (1000 * 60 * 60)) / (1000 * 60)); return minute; } /** * 获取随机小数 * @param {Number} min 最小值 * @param {Number} max 最大值 * @returns */ public static getRandomFloat(min: number, max: number) { return Math.random() * (max - min) + min; } /** * 返回指定小数位的数值 * @param {number} num * @param {number} idx */ public static formatNumToFixed(num: number, idx: number = 0) { return Number(num.toFixed(idx)); } /** * 用于数值到达另外一个目标数值之间进行平滑过渡运动效果 * @param {number} targetValue 目标数值 * @param {number} curValue 当前数值 * @param {number} ratio 过渡比率 * @returns */ public static lerp(targetValue: number, curValue: number, ratio: number = 0.25) { let v: number = curValue; if (targetValue > curValue) { v = curValue + (targetValue - curValue) * ratio; } else if (targetValue < curValue) { v = curValue - (curValue - targetValue) * ratio; } return v; } /** * 数据解密 * @param {String} str */ public static decrypt(b64Data: string) { if(b64Data == null || b64Data == undefined){ return ""; } let n: number = 6; if (b64Data.length % 2 === 0) { n = 7; } let decodeData = ''; for (var idx = 0; idx < b64Data.length - n; idx += 2) { decodeData += b64Data[idx + 1]; decodeData += b64Data[idx]; } decodeData += b64Data.slice(b64Data.length - n + 1); decodeData = this._base64Decode(decodeData); return decodeData; } /** * 数据加密 * @param {String} str */ public static encrypt(str: string) { if(str == null || str == undefined){ return ""; } let b64Data = this._base64encode(str); let n: number = 6; if (b64Data.length % 2 === 0) { n = 7; } let encodeData: string = ''; for (let idx = 0; idx < (b64Data.length - n + 1) / 2; idx++) { encodeData += b64Data[2 * idx + 1]; encodeData += b64Data[2 * idx]; } encodeData += b64Data.slice(b64Data.length - n + 1); return encodeData; } //public method for encoding /** * base64加密 * @param {string}input * @returns */ private static _base64encode(input: string) { let keyStr: string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; let output: string = "", chr1: number, chr2: number, chr3: number, enc1: number, enc2: number, enc3: number, enc4: number, i: number = 0; input = this._utf8Encode(input); while (i < input.length) { chr1 = input.charCodeAt(i++); chr2 = input.charCodeAt(i++); chr3 = input.charCodeAt(i++); enc1 = chr1 >> 2; enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4); } return output; } /** * utf-8 加密 * @param string * @returns */ private static _utf8Encode(string: string) { string = string.replace(/\r\n/g, "\n"); let utftext: string = ""; for (let n: number = 0; n < string.length; n++) { let c: number = string.charCodeAt(n); if (c < 128) { utftext += String.fromCharCode(c); } else if ((c > 127) && (c < 2048)) { utftext += String.fromCharCode((c >> 6) | 192); utftext += String.fromCharCode((c & 63) | 128); } else { utftext += String.fromCharCode((c >> 12) | 224); utftext += String.fromCharCode(((c >> 6) & 63) | 128); utftext += String.fromCharCode((c & 63) | 128); } } return utftext; } /** * utf-8解密 * @param utftext * @returns */ private static _utf8Decode(utftext: string) { let string = ""; let i: number = 0; let c: number = 0; let c1: number = 0; let c2: number = 0; let c3: number = 0; while (i < utftext.length) { c = utftext.charCodeAt(i); if (c < 128) { string += String.fromCharCode(c); i++; } else if ((c > 191) && (c < 224)) { c2 = utftext.charCodeAt(i + 1); string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); i += 2; } else { c2 = utftext.charCodeAt(i + 1); c3 = utftext.charCodeAt(i + 2); string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); i += 3; } } return string; } /** * base64解密 * @param {string}input 解密字符串 * @returns */ private static _base64Decode(input: string) { let keyStr: string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; let output: string = ""; let chr1: number; let chr2: number; let chr3: number; let enc1: number; let enc2: number; let enc3: number; let enc4: number; let i: number = 0; input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); while (i < input.length) { enc1 = keyStr.indexOf(input.charAt(i++)); enc2 = keyStr.indexOf(input.charAt(i++)); enc3 = keyStr.indexOf(input.charAt(i++)); enc4 = keyStr.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 != 64) { output = output + String.fromCharCode(chr2); } if (enc4 != 64) { output = output + String.fromCharCode(chr3); } } output = this._utf8Decode(output); return output; } /** * 开始展示文字 * @param lable 文本 * @param words 播放的文字 * @param cb 播放完成回调 * @param cbbS 回调延迟 * @param delay 延迟逐字播放 * @param s 一个字的播放速度/秒 */ public static verbatim(lable: Label,words: string, cb?: Function,cbbS:number = 0,delay: number = 0,s: number = 0.1,){ if (!words.hasOwnProperty('length')) { return; } lable.node.active = true; lable.unscheduleAllCallbacks(); let f = function(){ let arr = words.replace(/ /g,"").split(''); var step = 0; var allWords: string = ""; let fun: Function = function () { allWords += arr[step]; lable.string = allWords; if (++step >= arr.length) { lable.unschedule(fun); let cbFun = ()=>{cb?.()}; cbbS > 0 ? (lable.scheduleOnce(cbFun, cbbS)) : cbFun(); } }.bind(this); lable.schedule(fun,s,Number.MAX_SAFE_INTEGER); }; delay > 0 ? (lable.scheduleOnce(f.bind(this),delay)) : f(); } /** * 页面渐隐渐显动画 * @param n 节点 * @param isApper 是否是出现 * @param restore 是否恢复255显示的状态 * @param cb 执行完回调 */ public static pageAnim(n: Node,isApper: boolean = true,cb?:Function){ let uiop:UIOpacity = n.getComponent(UIOpacity); if(!n || !uiop){ cb?.(); }else{ uiop.opacity = isApper ? 25 : 255; let toOpacity: number = isApper ? 255 : 25; tween(uiop) .to(0.4,{opacity:toOpacity}) .call(function(){ cb?.(); }.bind(this)) .start(); } } /** * 将数组(array)拆分成多个 size 长度的区块,并将这些区块组成一个新数组 * @param {Array}array * @param {number}size * @returns */ public static chunk(array: any[], size: number) { var length = array === null ? 0 : array.length; if (!length || size < 1) { return []; } let result = []; while (array.length > size) { result.push(array.slice(0, size)); array = array.slice(size); } result.push(array); return result; } /** * 查询一个节点下的子节点 */ public static findName(root: Node, name: string) : Node{ let child: Node; if (name.indexOf("/") != -1) { child = find(name, root); } else { if (!root) { root = director.getScene(); } if (root.name === name) { child = root; } else { child = this.findChild(name, root); } } if (child) { return child; } else { console.log("没有找到指定的Node, node name ==", name); return null; } } private static findChild(name: string, parent: Node): Node { let child: Node = parent.getChildByName(name); if (child) { return child; } else { let children: Node[] = parent.children; for (let i = 0; i < children.length; i++) { child = this.findChild(name, children[i]); if (child) { return child; } } } return null; } /** * 处理万为单位 * @param num 数值 * @param point 保留小数点 * @param s 是否去掉无用的0 * @returns */ public static numUnit(num: number,point: number = 0,f: boolean = true): string { let n: number = num; let unit: number = 10000; if (n > unit) { if(point == 0){ n = Math.ceil(n / unit); return n.toString() + "万"; }else{ let s: string = (n / unit).toFixed(point); if(f){ return this.removeZeros(s) + "万"; }else{ return s + "万"; } } }else{ return Math.ceil(num).toString(); } } /** * 格式化数字: * * 如果小数部分全是 0(如 38.0 或 38.00),去掉小数部分,返回整数。 * 如果是小数(如 38.1 或 38.01),保留两位小数。 * @param num 输入的数字 * @returns 格式化后的数字 */ public static formatNumber(num: number): number | string { // 如果 num 是 undefined 或 null,返回 0 或空字符串 if (num == undefined || num == null) return 0; //将数字转换为字符串 const numStr = num.toString(); //判断小数部分是否全是 0 如果小数部分全是 0,去掉小数部分并返回整数 if (numStr.includes('.') && /\.0+$/.test(numStr)) { return parseInt(numStr, 10); } else { //否则保留两位小数 const fixedNum = num.toFixed(2); //如果小数部分全是 0,去掉小数部分 if(fixedNum.endsWith(".00")) { return parseInt(fixedNum, 10); } return parseFloat(fixedNum); } } /** * 去掉小数点后无用的0 * @param numberString 字符串呢 * @returns */ public static removeZeros(numberString: string): string { const trimmedString = numberString.trim(); // 去除首尾空格 const decimalIndex = trimmedString.indexOf('.'); if (decimalIndex !== -1) { let endIndex = trimmedString.length - 1; while (trimmedString[endIndex] === '0') { endIndex--; } if (trimmedString[endIndex] === '.') { endIndex--; // 如果小数点后面全是零,也去掉小数点 } return trimmedString.slice(0, endIndex + 1); } return trimmedString; } /** * 数组移除某一个元素 */ public static remove(arr,p){ let index = arr.indexOf(p) if (index > -1) { arr.splice(index, 1) } } /** * 16进制的颜色 * @param hexColor * @returns */ public static hexColor(hexColor) { const hex = hexColor.replace(/^#?/, "0x"); const c = parseInt(hex); const r = c >> 16; const g = (65280 & c) >> 8; const b = 255 & c; return new Color(r, g, b, 255); }; /** * 计算两点间的距离 */ public static pDistance(localPos: Vec3,tarPos: Vec3): number { let dx = localPos.x - tarPos.x; let dy = localPos.y - tarPos.y; let dis = Math.sqrt(dx * dx + dy * dy); return dis; } /** * 计算两点之间的绝对距离 */ public static pAbsDistance(a: Vec3,b: Vec3): number { let p: number = Math.abs(a.x - b.x); let k: number = Math.abs(a.y - b.y); return p + k; } /** * 角度转向量 * @param angle * @returns */ public static angle_to_vector (angle: number): Vec2 { // tan = sin / cos 将传入的角度转为弧度 let radian = this.angle_to_radian(angle); // 算出cos,sin和tan let cos = Math.cos(radian);// 邻边 / 斜边 let sin = Math.sin(radian);// 对边 / 斜边 let tan = sin / cos;// 对边 / 邻边 //结合在一起并归一化 let vec = new Vec2(cos, sin).normalize(); //返回向量 return(vec); } /** * 向量转角度 * @param vector * @returns */ public static vector_to_angle (vector: Vec2): number { //将传入的向量归一化 let dir = vector.normalize(); //计算出目标角度的弧度 let radian = dir.signAngle(new Vec2(1, 0)); //把弧度计算成角度 let angle = -this.radian_to_angle(radian); //返回角度 return(angle); } /** * 角度转弧度 * @param angle * @returns */ public static angle_to_radian (angle: number): number { //角度转弧度公式 π / 180 * 角度 计算出弧度 let radian = Math.PI / 180 * angle; //返回弧度 return(radian); } /** * 弧度转角度 * @param radian * @returns */ public static radian_to_angle (radian: number): number { //弧度转角度公式 180 / π * 弧度 计算出角度 let angle = 180 / Math.PI * radian; //返回角度 return(angle); } /** * 计算弧度 * @param start * @param end */ public static getAngle(start: Vec3, end: Vec3) { //两点的x、y值 var x = end.x - start.x; var y = end.y - start.y; var hypotenuse = Math.sqrt(x * x + y * y); //斜边长度 var cos = x / hypotenuse; var radian = Math.acos(cos); //求出弧度 var angle = 180 / (Math.PI / radian); //用弧度算出角度 if (y < 0) { angle = 0 - angle; }else if (y == 0 && x < 0) { angle = 180; } return angle; } /** * 扣血转化成字符串 * @param number * @returns */ public static numberToString(number:number) { let str = ''; let data = [ { minnum: 1000000000,maxnum:9999999999, toStr: 'o' }, { minnum: 100000000,maxnum:999999999, toStr: 'p' }, { minnum: 10000000,maxnum:99999999, toStr: 't' }, { minnum: 1000000,maxnum:9999999, toStr: 'g' }, { minnum: 100000,maxnum:999999, toStr: 'm' }, { minnum: 10000,maxnum:99999, toStr: 'b' }, { minnum: 1000,maxnum:9999, toStr: 'K' } ]; for (let i: number = 0; i < data.length; i++) { if (number >= data[i].minnum && number <= data[i].maxnum ) { number /= data[i].minnum; let m: string = number.toFixed(1); str = m + data[i].toStr; return str; } } return number.toFixed(0); } /** * 将某个节点下的坐标转移到另外一个节点 * @param fromNode 坐标所在的节点 * @param toNode 目标节点 * @returns 转换后的坐标值 */ public static convertPosition(fromNode: Node, toNode: Node): Vec3 { let pos: Vec3 = fromNode.position.clone(); // 将 pos 转为世界坐标系下的坐标 const worldPos: Vec3 = fromNode.parent.getComponent(UITransform).convertToWorldSpaceAR(pos); // 将世界坐标系下的坐标转为目标节点的局部坐标系下的坐标 const localPos: Vec3 = toNode.getComponent(UITransform).convertToNodeSpaceAR(worldPos); return localPos; } /** * 以敌人的中心点 攻击范围为半径 产生随机坐标 * @param center 中心点 * @param radius 半径 * @returns */ public static randomPointGenerator(center: Vec3,radius: number){ //随机角度 let angle = Math.random() * Math.PI * 2; //随机距离 let distance = Math.sqrt(Math.random()) * radius; //根据极坐标转换成笛卡尔坐标 const x = center.x + distance * Math.cos(angle); const y = center.y + distance * Math.sin(angle); return new Vec3(x,y,1); } /** * 将某个节点上的坐标转移到另外一个节点 * @param fromNode 坐标所在的节点 * @param toNode 目标节点 * @returns 转换后的坐标值 */ public static convertPositionPos(fromNode: Node,pos: Vec3, toNode: Node): Vec3 { let nPos: Vec3 = pos.clone(); // 将 pos 转为世界坐标系下的坐标 const worldPos: Vec3 = fromNode.parent.getComponent(UITransform).convertToWorldSpaceAR(nPos); // 将世界坐标系下的坐标转为目标节点的局部坐标系下的坐标 const localPos: Vec3 = toNode.getComponent(UITransform).convertToNodeSpaceAR(worldPos); return localPos; } /** * 根据A,B两个坐标点 和抛物线的弧度 来计算中心点坐标 */ public static calculateParabolaCenter(start: Vec3, end: Vec3){ // 计算两点之间的水平距离 const deltaX = end.x - start.x; // 将控制点的 x 坐标设置为两点的中点 const controlX = (start.x + end.x) / 2; // 计算抛物线的最高点,使其位于两点之间的中间位置 可以根据需要调整最高点的位置 const highestY = Math.max(start.y, end.y) + Math.abs(deltaX) / 4; // 计算控制点的 y 坐标 const controlY = highestY; //返回抛物线的中心坐标点 return new Vec3(controlX, controlY); } /** * 生成一个随机颜色值的函数 */ public static getRandomColor() { let r = Math.floor(Math.random() * 256); let g = Math.floor(Math.random() * 256); let b = Math.floor(Math.random() * 256); return new Color(r, g, b); } /** * 获取是否为16:9的常规屏幕尺寸 */ public static isNormalScreen(): boolean { return screen.windowSize.height /screen.windowSize.width < 1335 / 750; } }