feat: add builder configuration file
feat: add cocos-service configuration file feat: add device configuration file feat: add engine configuration file feat: add information configuration file feat: add program configuration file feat: add project configuration file feat: add TypeScript configuration file
This commit is contained in:
427
assets/script/common/AStar.ts
Normal file
427
assets/script/common/AStar.ts
Normal file
@@ -0,0 +1,427 @@
|
||||
type index = {
|
||||
x: number,
|
||||
y: number
|
||||
}
|
||||
|
||||
/**
|
||||
* AStar类用于实现A*寻路算法。
|
||||
*/
|
||||
export class AStar {
|
||||
private map: any[][] = []; // 地图数组
|
||||
private weightMap: number[][] = null; // 权重地图数组
|
||||
private openlist: index[] = []; // 开放列表,存储待评估的节点
|
||||
private closelist: index[] = []; // 封闭列表,存储已评估的节点
|
||||
private parentMap: Map<index, index>; // 父节点映射,用于回溯路径
|
||||
private gcostMap: Map<index, number>; // g成本映射,记录从起点到当前节点的成本
|
||||
private hcostMap: Map<index, number>; // h成本映射,记录从当前节点到终点的估算成本
|
||||
private startValue: number = 0;
|
||||
private endValue: number = 0;
|
||||
private pathValue: number = 0;
|
||||
private isEightDirections: boolean = false;
|
||||
private neighborMap: Map<index, index[]>;
|
||||
|
||||
constructor() {
|
||||
this.openlist = [];
|
||||
this.closelist = [];
|
||||
this.parentMap = new Map();
|
||||
this.gcostMap = new Map();
|
||||
this.hcostMap = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置地图。
|
||||
* @param map 二维数组表示的地图
|
||||
*/
|
||||
setMap(map: number[][]) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置权重地图。
|
||||
* @param weightMap 二维数组表示的权重地图
|
||||
*/
|
||||
setWeightMap(weightMap: number[][]) {
|
||||
this.weightMap = weightMap;
|
||||
}
|
||||
|
||||
setValue(startValue: number, endValue: number, pathValue: number) {
|
||||
this.startValue = startValue;
|
||||
this.endValue = endValue;
|
||||
this.pathValue = pathValue;
|
||||
}
|
||||
|
||||
setEightDirections(isEightDirections: boolean) {
|
||||
this.isEightDirections = isEightDirections;
|
||||
}
|
||||
|
||||
setNeighborMap(neighborMap: Map<index, index[]>) {
|
||||
this.neighborMap = neighborMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在给定范围内根据指定类型搜索从起点到终点的路径。
|
||||
* @param start 起点索引
|
||||
* @param end 终点索引
|
||||
* @param type 搜索类型(可能影响启发式函数的选择)
|
||||
* @returns 返回找到的路径数组,如果无路径则返回空数组
|
||||
*/
|
||||
search(start: index, end: index, type: number) {
|
||||
this.init(); // 初始化搜索环境
|
||||
|
||||
if (this.map[start.y][start.x] !== this.startValue ||
|
||||
this.map[end.y][end.x] !== this.endValue) {
|
||||
console.log("Invalid start or end point");
|
||||
return [];
|
||||
}
|
||||
|
||||
// 初始化起点信息,并将其加入开放列表
|
||||
this.openlist.push(start);
|
||||
this.gcostMap.set(start, 0); // 起点到起点的距离为0
|
||||
this.hcostMap.set(start, this.getHeuristicCost(start, end, type)); // 起点到终点的启发式成本
|
||||
this.parentMap.set(start, null); // 起点的父节点设置为null
|
||||
|
||||
// 循环直到开放列表为空,即所有可能的路径都被考虑
|
||||
while (this.openlist.length > 0) {
|
||||
const current = this.getMinCostNode(this.openlist); // 从开放列表中选择成本最小的节点
|
||||
|
||||
if (!current) {
|
||||
this.openlist.splice(this.openlist.indexOf(current), 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 判断是否到达终点
|
||||
if (this.isArrived(current, end)) {
|
||||
return this.reconstructPath(end); // 如果到达终点,返回路径重构的结果
|
||||
} else {
|
||||
// 从开放列表移除当前节点,将其加入封闭列表
|
||||
this.openlist.splice(this.openlist.indexOf(current), 1);
|
||||
this.closelist.push(current);
|
||||
|
||||
// 遍历当前节点的所有邻居节点
|
||||
const neighbors = this.getNeighbors(current);
|
||||
for (const neighbor of neighbors) {
|
||||
const hCost = this.getHeuristicCost(neighbor, end, type); // 计算邻居节点到终点的启发式成本
|
||||
const gCost = this.gcostMap.get(current) + this.getGCost(current, neighbor, type); // 计算当前节点到邻居节点的实际成本
|
||||
const totalCost = gCost + hCost; // 计算总成本
|
||||
|
||||
// 根据邻居节点的状态进行处理
|
||||
if (!this.isInClosedList(neighbor)) {
|
||||
if (!this.isInOpenList(neighbor)) {
|
||||
this.openlist.push(neighbor); // 如果邻居未在开放列表中,将其加入
|
||||
this.updateCost(neighbor, gCost, hCost); // 更新邻居节点的成本信息
|
||||
this.updateParent(neighbor, current); // 更新邻居节点的父节点信息
|
||||
} else {
|
||||
// 如果邻居已在开放列表中,但通过当前节点发现更优路径
|
||||
if (totalCost < this.getCost(neighbor)) {
|
||||
this.updateCost(neighbor, gCost, hCost); // 更新成本信息
|
||||
this.updateParent(neighbor, current); // 更新父节点信息
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log("No path found.");
|
||||
return []; // 如果无法找到路径,返回空数组
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化算法状态。
|
||||
*/
|
||||
private init() {
|
||||
// 重置所有列表和映射
|
||||
this.openlist = [];
|
||||
this.closelist = [];
|
||||
this.parentMap = new Map();
|
||||
this.gcostMap = new Map();
|
||||
this.hcostMap = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前节点的邻居。
|
||||
* @param current 当前节点坐标
|
||||
* @returns 邻居节点数组
|
||||
*/
|
||||
private getNeighbors(current: index) {
|
||||
if (this.neighborMap.size === 0) {
|
||||
const neighbors: index[] = [];
|
||||
|
||||
// 尝试获取四个方向的邻居节点
|
||||
const upindex = { x: current.x, y: current.y + 1 };
|
||||
const downindex = { x: current.x, y: current.y - 1 };
|
||||
const leftindex = { x: current.x - 1, y: current.y };
|
||||
const rightindex = { x: current.x + 1, y: current.y };
|
||||
|
||||
const upleftindex = { x: current.x - 1, y: current.y + 1 };
|
||||
const uprightindex = { x: current.x + 1, y: current.y + 1 };
|
||||
const downleftindex = { x: current.x - 1, y: current.y - 1 };
|
||||
const downrightindex = { x: current.x + 1, y: current.y - 1 };
|
||||
|
||||
// 如果合法则加入邻居列表
|
||||
if (this.isValidIndex(upindex) && (this.map[upindex.y][upindex.x] == this.pathValue ||
|
||||
this.map[upindex.y][upindex.x] == this.endValue)) {
|
||||
neighbors.push(upindex);
|
||||
}
|
||||
if (this.isValidIndex(downindex) && (this.map[downindex.y][downindex.x] == this.pathValue ||
|
||||
this.map[downindex.y][downindex.x] == this.endValue)) {
|
||||
neighbors.push(downindex);
|
||||
}
|
||||
if (this.isValidIndex(leftindex) && (this.map[leftindex.y][leftindex.x] == this.pathValue ||
|
||||
this.map[leftindex.y][leftindex.x] == this.endValue)) {
|
||||
neighbors.push(leftindex);
|
||||
}
|
||||
if (this.isValidIndex(rightindex) && (this.map[rightindex.y][rightindex.x] == this.pathValue ||
|
||||
this.map[rightindex.y][rightindex.x] == this.endValue)) {
|
||||
neighbors.push(rightindex);
|
||||
}
|
||||
|
||||
if (this.isEightDirections) {
|
||||
if (this.isValidIndex(upleftindex) && (this.map[upleftindex.y][upleftindex.x] == this.pathValue ||
|
||||
this.map[upleftindex.y][upleftindex.x] == this.endValue)) {
|
||||
neighbors.push(upleftindex);
|
||||
}
|
||||
if (this.isValidIndex(downleftindex) && (this.map[downleftindex.y][downleftindex.x] == this.pathValue ||
|
||||
this.map[downleftindex.y][downleftindex.x] == this.endValue)) {
|
||||
neighbors.push(downleftindex);
|
||||
}
|
||||
if (this.isValidIndex(downrightindex) && (this.map[downrightindex.y][downrightindex.x] == this.pathValue ||
|
||||
this.map[downrightindex.y][downrightindex.x] == this.endValue)) {
|
||||
neighbors.push(downrightindex);
|
||||
}
|
||||
if (this.isValidIndex(uprightindex) && (this.map[uprightindex.y][uprightindex.x] == this.pathValue ||
|
||||
this.map[uprightindex.y][uprightindex.x] == this.endValue)) {
|
||||
neighbors.push(uprightindex);
|
||||
}
|
||||
}
|
||||
|
||||
return neighbors;
|
||||
} else {
|
||||
const _neighbors = this.neighborMap.get(current);
|
||||
const neighbors: index[] = [];
|
||||
for (const neighbor of _neighbors) {
|
||||
if (this.map[neighbor.y][neighbor.x] == this.pathValue ||
|
||||
this.map[neighbor.y][neighbor.x] == this.endValue) {
|
||||
neighbors.push(neighbor);
|
||||
}
|
||||
}
|
||||
return neighbors;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算从当前节点到邻居节点的实际成本
|
||||
* @param current 当前节点
|
||||
* @param neighbor 邻居节点
|
||||
* @param type 距离计算类型
|
||||
* @returns 返回从当前节点到邻居节点的成本
|
||||
*/
|
||||
private getGCost(current: index, neighbor: index, type: number = 0) {
|
||||
let baseCost = 0;
|
||||
|
||||
// 计算基础距离成本
|
||||
switch (type) {
|
||||
case 0: {
|
||||
// 计算曼哈顿距离
|
||||
baseCost = Math.abs(current.x - neighbor.x) + Math.abs(current.y - neighbor.y);
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
// 计算欧几里得距离
|
||||
baseCost = Math.sqrt(Math.pow(current.x - neighbor.x, 2) + Math.pow(current.y - neighbor.y, 2));
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// 计算切比雪夫距离
|
||||
const x = Math.abs(current.x - neighbor.x);
|
||||
const y = Math.abs(current.y - neighbor.y);
|
||||
const minxy = Math.min(x, y);
|
||||
baseCost = x + y + (Math.sqrt(2) - 2) * minxy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果存在权重地图,应用权重
|
||||
if (this.weightMap && this.isValidIndex(neighbor)) {
|
||||
const weight = this.weightMap[neighbor.y][neighbor.x];
|
||||
return baseCost * weight;
|
||||
}
|
||||
|
||||
return baseCost;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据给定的起始点和目标点,以及类型参数,计算启发式成本。
|
||||
* @param start 起始点的索引,包含 x 和 y 坐标。
|
||||
* @param end 目标点的索引,包含 x 和 y 坐标。
|
||||
* @param type 计算成本的类型,0 表示曼哈顿距离,1 表示欧几里得距离,2 表示切比雪夫距离,默认为 0。
|
||||
* @returns 返回根据指定类型计算出的启发式成本。
|
||||
*/
|
||||
private getHeuristicCost(start: index, end: index, type: number = 0) {
|
||||
switch (type) {
|
||||
case 0: {
|
||||
// 计算曼哈顿距离
|
||||
return Math.abs(start.x - end.x) + Math.abs(start.y - end.y);
|
||||
}
|
||||
case 1: {
|
||||
// 计算欧几里得距离
|
||||
return Math.sqrt(Math.pow(start.x - end.x, 2) + Math.pow(start.y - end.y, 2));
|
||||
}
|
||||
case 2: {
|
||||
// 计算切比雪夫距离
|
||||
const x = Math.abs(start.x - end.x);
|
||||
const y = Math.abs(start.y - end.y);
|
||||
const minxy = Math.min(x, y);
|
||||
return x + y + (Math.sqrt(2) - 2) * minxy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查节点是否在封闭列表中。
|
||||
* @param node 节点坐标
|
||||
* @returns {boolean} 如果节点在封闭列表中返回true,否则返回false
|
||||
*/
|
||||
private isInClosedList(node: index): boolean {
|
||||
// 遍历封闭列表查找节点
|
||||
let _index = -1;
|
||||
for (let i = 0; i < this.closelist.length; i++) {
|
||||
if (this.closelist[i].x == node.x && this.closelist[i].y == node.y) {
|
||||
_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return _index != -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查节点是否在开放列表中。
|
||||
* @param node 节点坐标
|
||||
* @returns {boolean} 如果节点在开放列表中返回true,否则返回false
|
||||
*/
|
||||
private isInOpenList(node: index): boolean {
|
||||
// 遍历开放列表查找节点
|
||||
let _index = -1;
|
||||
for (let i = 0; i < this.openlist.length; i++) {
|
||||
if (this.openlist[i].x == node.x && this.openlist[i].y == node.y) {
|
||||
_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return _index != -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取节点的成本。
|
||||
* @param node 节点坐标
|
||||
* @returns 节点的总成本
|
||||
*/
|
||||
private getCost(node: index): number {
|
||||
let gcost = 0;
|
||||
let hcost = 0;
|
||||
this.gcostMap.forEach((value, key) => {
|
||||
if (key.x == node.x && key.y == node.y) {
|
||||
gcost = value;
|
||||
}
|
||||
});
|
||||
this.hcostMap.forEach((value, key) => {
|
||||
if (key.x == node.x && key.y == node.y) {
|
||||
hcost = value;
|
||||
}
|
||||
});
|
||||
return gcost + hcost;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取开放列表中成本最小的节点。
|
||||
* @param list 开放列表
|
||||
* @returns 成本最小的节点
|
||||
*/
|
||||
private getMinCostNode(list: index[]): index {
|
||||
let _minindex = -1;
|
||||
let _mincost = Number.MAX_SAFE_INTEGER;
|
||||
// 寻找成本最小的节点
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const cost = this.getCost(list[i]);
|
||||
if (cost < _mincost) {
|
||||
_minindex = i;
|
||||
_mincost = cost;
|
||||
}
|
||||
}
|
||||
return list[_minindex];
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新节点的成本和父节点信息。
|
||||
* @param node 节点坐标
|
||||
* @param gcost 从起点到当前节点的实际成本
|
||||
* @param hcost 从当前节点到终点的估算成本
|
||||
*/
|
||||
private updateCost(node: index, gcost: number, hcost: number) {
|
||||
this.gcostMap.set(node, gcost);
|
||||
this.hcostMap.set(node, hcost);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新节点的父节点。
|
||||
* @param node 节点坐标
|
||||
* @param parent 父节点坐标
|
||||
*/
|
||||
private updateParent(node: index, parent: index) {
|
||||
const existingparent = this.parentMap.get(node);
|
||||
// 仅当新的父节点成本更低时更新
|
||||
if (!existingparent || this.getCost(parent) < this.getCost(existingparent)) {
|
||||
this.parentMap.set(node, parent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据父节点映射重构路径。
|
||||
* @param endPoint 终点坐标
|
||||
* @returns 从起点到终点的路径
|
||||
*/
|
||||
private reconstructPath(endPoint: index) {
|
||||
const path = [endPoint];
|
||||
let currentnode = endPoint;
|
||||
|
||||
// 逆向回溯路径
|
||||
while (currentnode) {
|
||||
let parent = null;
|
||||
this.parentMap.forEach((value, key) => {
|
||||
if (currentnode.x == key.x && currentnode.y == key.y) {
|
||||
parent = value;
|
||||
}
|
||||
})
|
||||
if (parent) {
|
||||
path.unshift(parent);
|
||||
currentnode = parent;
|
||||
} else {
|
||||
currentnode = null;
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查给定的索引是否为有效索引。
|
||||
* @param index 一个包含x和y属性的对象,代表要检查的索引位置。
|
||||
* @returns 返回一个布尔值,如果索引位置有效,则为true;否则为false。
|
||||
*/
|
||||
private isValidIndex(index: index): boolean {
|
||||
return index.x >= 0 && index.x < this.map[0].length && index.y >= 0 && index.y < this.map.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前节点是否到达了目标节点。
|
||||
* @param node 一个包含x和y属性的对象,代表当前节点的位置。
|
||||
* @param end 一个包含x和y属性的对象,代表目标节点的位置。
|
||||
* @returns 返回一个布尔值,如果当前节点已到达目标节点,则为true;否则为false。
|
||||
*/
|
||||
private isArrived(node: index, end: index) {
|
||||
// 比较当前节点和目标节点的坐标,判断是否已经到达目标位置
|
||||
return node.x == end.x && node.y == end.y;
|
||||
}
|
||||
}
|
||||
|
||||
9
assets/script/common/AStar.ts.meta
Normal file
9
assets/script/common/AStar.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "0da25f34-2c17-4ebe-a9d6-1a4c2c03a99b",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
185
assets/script/common/AudioTools.ts
Normal file
185
assets/script/common/AudioTools.ts
Normal file
@@ -0,0 +1,185 @@
|
||||
import { AudioClip, Node, AudioSource, tween } from "cc";
|
||||
import { ResModel } from "../model/ResModel";
|
||||
import { LoadTools } from "./LoadTools";
|
||||
|
||||
/** 声音相关工具类 */
|
||||
export class AudioTools {
|
||||
/** 单例模式 */
|
||||
private static _instance: AudioTools = new AudioTools();
|
||||
private constructor() { }
|
||||
public static get _ins() {
|
||||
return this._instance;
|
||||
}
|
||||
private audioDic: Map<string, AudioSource> = new Map();
|
||||
private audioVolumeDic: Map<string, number> = new Map();
|
||||
private audioPermanentDic: Map<AudioSource, boolean> = new Map();
|
||||
bgAudio: AudioSource; //背景音乐
|
||||
isPlayAudio: boolean = true; //是否播放音效
|
||||
isPlayBG: boolean = true; //是否播放音乐
|
||||
|
||||
/** 播放背景音乐 */
|
||||
playBG(audioUrl: string, value = 0.5) {
|
||||
if (!this.isPlayBG) { return; }
|
||||
if (ResModel._ins.AudioClipDic.get(audioUrl) != null) {
|
||||
this.bgAudio = this.newAudioSource(ResModel._ins.AudioClipDic.get(audioUrl), value, true);
|
||||
this.audioDic.set(audioUrl, this.bgAudio);
|
||||
this.audioVolumeDic.set(audioUrl, value);
|
||||
this.audioPermanentDic.set(this.bgAudio, true);
|
||||
return this.bgAudio;
|
||||
} else {
|
||||
LoadTools._ins.loadResAny('music/' + audioUrl + '.mp3', AudioClip, (audio: AudioClip) => {
|
||||
this.bgAudio = this.newAudioSource(audio, value, true);
|
||||
this.audioDic.set(audioUrl, this.bgAudio);
|
||||
this.audioVolumeDic.set(audioUrl, value);
|
||||
this.audioPermanentDic.set(this.bgAudio, true);
|
||||
return this.bgAudio;
|
||||
});
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 播放游戏音效
|
||||
* @param audioUrl 音效文件的res/music/的路径 或者 resArr拖动的名字
|
||||
* @param value 音量大小
|
||||
* @param isLoop 是否循环播放
|
||||
* @param isPermanent 是否永久播放
|
||||
*/
|
||||
playAudio(audioUrl: string, value = 0.5, isLoop = false, isPermanent = false) {
|
||||
if (!this.isPlayAudio) { return; }
|
||||
if (ResModel._ins.AudioClipDic.get(audioUrl) != null) {
|
||||
let audioE = this.newAudioSource(ResModel._ins.AudioClipDic.get(audioUrl), value, isLoop);
|
||||
this.audioDic.set(audioUrl, audioE);
|
||||
this.audioVolumeDic.set(audioUrl, value);
|
||||
this.audioPermanentDic.set(audioE, isPermanent);
|
||||
return audioE;
|
||||
} else {
|
||||
LoadTools._ins.loadResAny('music/' + audioUrl + '.mp3', AudioClip, (audio: AudioClip) => {
|
||||
let audioE = this.newAudioSource(audio, value, isLoop);
|
||||
this.audioDic.set(audioUrl, audioE);
|
||||
this.audioVolumeDic.set(audioUrl, value);
|
||||
this.audioPermanentDic.set(audioE, isPermanent);
|
||||
return audioE;
|
||||
});
|
||||
}
|
||||
};
|
||||
/** 停止播放某个音效 */
|
||||
stopAudio(audioName: string) {
|
||||
let audioE = this.audioDic.get(audioName);
|
||||
if (audioE != null) {
|
||||
audioE.stop();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 新建一个audioSource 来播放音效
|
||||
* @param audioUrl 音效文件的res/music/的路径 或者 resArr拖动的名字
|
||||
* @param value 音量大小
|
||||
* @param isLoop 是否循环播放
|
||||
*/
|
||||
playAudioSource(audioUrl: string, value = 0.5, isLoop = false) {
|
||||
if (!this.isPlayAudio) { return; }
|
||||
if (ResModel._ins.AudioClipDic.get(audioUrl) != null) {
|
||||
return this.newAudioSource(ResModel._ins.AudioClipDic.get(audioUrl), value, isLoop);
|
||||
} else {
|
||||
LoadTools._ins.loadResAny(audioUrl, AudioClip, (audioE: AudioClip) => {
|
||||
this.newAudioSource(audioE, value, isLoop);
|
||||
});
|
||||
}
|
||||
}
|
||||
/** 新建一个 audioSource 播放音效 */
|
||||
newAudioSource(audioClip: AudioClip, value = 0.5, isLoop = false) {
|
||||
if (!this.isPlayAudio) { return; }
|
||||
let node = new Node();
|
||||
let audioE = node.addComponent(AudioSource);
|
||||
audioE.clip = audioClip;
|
||||
audioE.loop = isLoop;
|
||||
audioE.volume = value;
|
||||
|
||||
audioE.play();
|
||||
if (isLoop == false) {
|
||||
tween(node).delay(audioE.duration + 0.1)
|
||||
.removeSelf().union().start();
|
||||
}
|
||||
return audioE;
|
||||
};
|
||||
|
||||
/** 停止所有音效 */
|
||||
stopAllAudio() {
|
||||
this.isPlayAudio = false;
|
||||
this.isPlayBG = false;
|
||||
for (let audioE of this.audioDic.values()) {
|
||||
audioE.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/** 恢复所有音效 */
|
||||
resumeAllAudio() {
|
||||
for (let audioE of this.audioDic.values()) {
|
||||
if (this.audioPermanentDic.get(audioE) && !audioE.playing) {
|
||||
audioE.play();
|
||||
}
|
||||
audioE.volume = this.audioVolumeDic.get(audioE.clip.name);
|
||||
}
|
||||
this.isPlayAudio = true;
|
||||
this.isPlayBG = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复指定音效
|
||||
* @param audioName 音效文件的res/music/的路径 或者 resArr拖动的名字
|
||||
*/
|
||||
resumeAudioByName(audioName: string) {
|
||||
let audioE = this.audioDic.get(audioName);
|
||||
if (audioE != null) {
|
||||
if (!audioE.playing) {
|
||||
audioE.play();
|
||||
}
|
||||
audioE.volume = this.audioVolumeDic.get(audioE.clip.name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置指定音效的音量
|
||||
* @param audioName 音效文件的res/music/的路径 或者 resArr拖动的名字
|
||||
* @param value 音量大小
|
||||
*/
|
||||
setAudioVolume(audioName: string, value: number) {
|
||||
let audioE = this.audioDic.get(audioName);
|
||||
if (audioE != null) {
|
||||
audioE.volume = value;
|
||||
this.audioVolumeDic.set(audioName, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置指定音效的循环播放
|
||||
* @param audioName 音效文件的res/music/的路径 或者 resArr拖动的名字
|
||||
* @param isLoop 是否循环播放
|
||||
*/
|
||||
setAudioLoop(audioName: string, isLoop: boolean) {
|
||||
let audioE = this.audioDic.get(audioName);
|
||||
if (audioE != null) {
|
||||
audioE.loop = isLoop;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置指定音效的静音
|
||||
* @param audioName 音效文件的res/music/的路径 或者 resArr拖动的名字
|
||||
*/
|
||||
muteAudio(audioName: string) {
|
||||
let audioE = this.audioDic.get(audioName);
|
||||
if (audioE != null) {
|
||||
audioE.volume = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置所有音效的静音
|
||||
*/
|
||||
muteAllAudio() {
|
||||
for (let audioE of this.audioDic.values()) {
|
||||
audioE.volume = 0.0;
|
||||
}
|
||||
this.isPlayAudio = false;
|
||||
this.isPlayBG = false;
|
||||
}
|
||||
}
|
||||
9
assets/script/common/AudioTools.ts.meta
Normal file
9
assets/script/common/AudioTools.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "b2bc325c-94d6-4223-8d5a-918cbdadfd93",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
98
assets/script/common/BFS.ts
Normal file
98
assets/script/common/BFS.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
class Queue<T> {
|
||||
private items: T[];
|
||||
|
||||
constructor() {
|
||||
this.items = [];
|
||||
}
|
||||
|
||||
enqueue(item: T): void {
|
||||
this.items.push(item);
|
||||
}
|
||||
|
||||
dequeue(): T | undefined {
|
||||
return this.items.shift();
|
||||
}
|
||||
|
||||
isEmpty(): boolean {
|
||||
return this.items.length === 0;
|
||||
}
|
||||
}
|
||||
|
||||
type index = {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export class BFSPathfinding {
|
||||
private map: number[][];
|
||||
private visited: boolean[][];
|
||||
private rows: number;
|
||||
private cols: number;
|
||||
private directions: index[];
|
||||
|
||||
constructor() {
|
||||
this.map = [];
|
||||
this.rows = 0;
|
||||
this.cols = 0;
|
||||
this.visited = [];
|
||||
this.directions = [];
|
||||
}
|
||||
|
||||
setMap(map: number[][]) {
|
||||
this.map = map;
|
||||
this.rows = this.map.length;
|
||||
this.cols = this.map[0].length;
|
||||
this.visited = Array.from({ length: this.rows }, () => Array(this.cols).fill(false));
|
||||
this.directions = [{ x: 0, y: -1 }, { x: 0, y: 1 }, { x: -1, y: 0 }, { x: 1, y: 0 }];
|
||||
}
|
||||
|
||||
findPath(start: index, end: index): index[] {
|
||||
if (this.map[start.y][start.x] !== 0 ||
|
||||
this.map[end.y][end.x] !== 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const queue = new Queue<index>();
|
||||
const prev: index[][] = Array.from({ length: this.rows }, () => Array(this.cols).fill(null));
|
||||
|
||||
queue.enqueue(start);
|
||||
this.visited[start.y][start.x] = true;
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
const current = queue.dequeue()!;
|
||||
if (current.x === end.x && current.y === end.y) {
|
||||
return this.constructPath(prev, start, end);
|
||||
}
|
||||
|
||||
for (const direction of this.directions) {
|
||||
const nextX = current.x + direction.x;
|
||||
const nextY = current.y + direction.y;
|
||||
|
||||
if (this.isValid(nextX, nextY) && this.map[nextY][nextX] === 0 && !this.visited[nextY][nextX]) {
|
||||
queue.enqueue({ x: nextX, y: nextY });
|
||||
this.visited[nextY][nextX] = true;
|
||||
prev[nextY][nextX] = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private isValid(x: number, y: number): boolean {
|
||||
return x >= 0 && x < this.cols && y >= 0 && y < this.rows;
|
||||
}
|
||||
|
||||
private constructPath(prev: index[][], start: index, end: index): index[] {
|
||||
const path: index[] = [];
|
||||
let current = end;
|
||||
|
||||
while (current !== start) {
|
||||
path.unshift(current);
|
||||
current = prev[current.y][current.x]!;
|
||||
}
|
||||
path.unshift(start);
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
9
assets/script/common/BFS.ts.meta
Normal file
9
assets/script/common/BFS.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "8ac0c6a2-e192-4b14-8cbe-97c0f60d1c1e",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
281
assets/script/common/LoadTools.ts
Normal file
281
assets/script/common/LoadTools.ts
Normal file
@@ -0,0 +1,281 @@
|
||||
import { director, Node, instantiate, Prefab, Vec3, v3, SpriteFrame, Sprite, AssetManager, resources, assetManager, AudioClip, Material, Texture2D, Label } from "cc";
|
||||
import { ResModel } from "../model/ResModel";
|
||||
import { weiSan } from "./weiSanTools";
|
||||
import { EventManager, EventData } from "../manager/EventManager";
|
||||
import { PoolManager } from "../manager/PoolManager";
|
||||
|
||||
/** 加载资源相关工具类 */
|
||||
export class LoadTools {
|
||||
/** 单例模式 */
|
||||
private static _instance: LoadTools = new LoadTools();
|
||||
private constructor() { }
|
||||
public static get _ins() {
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载游戏场景
|
||||
* @param sceneName 加载场景的名字
|
||||
* @param callFunc 加载回调
|
||||
*/
|
||||
loadScene(sceneName: string, callFunc: any, isClear = true) {
|
||||
if (isClear) {
|
||||
ResModel._ins.clearResDic();
|
||||
}
|
||||
director.preloadScene(sceneName, () => {
|
||||
director.loadScene(sceneName, callFunc);
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 加载resource 下的预制体 资源
|
||||
* @param url resource 下的资源路径
|
||||
* @param callBack 加载完成回调
|
||||
*/
|
||||
loadResPrefab(url: string, callBack?: any, parent?: Node, Pos?: Vec3) {
|
||||
this.loadResAny(url, Prefab, (prefab: Prefab) => {
|
||||
let clone = instantiate(prefab);
|
||||
if (parent) { parent.addChild(clone) };
|
||||
if (Pos) { clone.position = v3(Pos.x, Pos.y, 0) };
|
||||
if (callBack != null) {
|
||||
callBack(clone);
|
||||
}
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 加载resource 下的图片资源并渲染到节点上
|
||||
* @param url resource 下的资源路径
|
||||
* @param callBack 加载完成回调
|
||||
*/
|
||||
loadResSpriteFrame(url: string, sprite: Node, parent?: Node, Pos?: Vec3, callBack?: any) {
|
||||
resources.load(url + "/spriteFrame", SpriteFrame, function (error: any, SpriteFrame: SpriteFrame) {
|
||||
if (error) {
|
||||
weiSan.error(error);
|
||||
} else {
|
||||
sprite.getComponent(Sprite).spriteFrame = SpriteFrame;
|
||||
if (parent) { parent.addChild(sprite) };
|
||||
if (Pos) { sprite.position = v3(Pos.x, Pos.y, 0) };
|
||||
if (callBack != null) {
|
||||
callBack(sprite);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 加载resource 下的游戏资源
|
||||
* @param url resource 下的资源路径
|
||||
* @param resType 加载资源的类型
|
||||
* @param callBack 加载完成回调
|
||||
*/
|
||||
loadResAny(url: string, resType: any, callBack?: any) {
|
||||
resources.load(url, resType, function (error: any, res: any) {
|
||||
if (error) {
|
||||
weiSan.error(error);
|
||||
} else {
|
||||
if (callBack != null) {
|
||||
callBack(res);
|
||||
}
|
||||
if (resType == Prefab) {
|
||||
ResModel._ins.PrefabDic.set(url, res);
|
||||
} else if (resType == SpriteFrame) {
|
||||
if (!ResModel._ins.SpriteFrameDic.has(url)) {
|
||||
ResModel._ins.SpriteFrameDic.set(url, res);
|
||||
}
|
||||
} else if (resType == AudioClip) {
|
||||
if (!ResModel._ins.AudioClipDic.has(url)) {
|
||||
ResModel._ins.AudioClipDic.set(url, res);
|
||||
}
|
||||
} else if (resType == Material) {
|
||||
if (!ResModel._ins.MaterialDic.has(url)) {
|
||||
ResModel._ins.MaterialDic.set(url, res);
|
||||
}
|
||||
} else if (resType == Texture2D) {
|
||||
if (!ResModel._ins.TextureDic.has(url)) {
|
||||
ResModel._ins.TextureDic.set(url, res);
|
||||
}
|
||||
} else if (resType === Prefab) {
|
||||
if (!ResModel._ins.PrefabDic.has(url)) {
|
||||
ResModel._ins.PrefabDic.set(url, res);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
/** 加载bundle 场景 */
|
||||
loadBundleScene(bundleName: string, sceneName: string, onFinishBack?: () => void, isInScene: boolean = true) {
|
||||
assetManager.loadBundle(
|
||||
bundleName,
|
||||
(err: any, bundle: AssetManager.Bundle) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
}
|
||||
else {
|
||||
if (!isInScene) { return; }
|
||||
bundle.loadScene(sceneName, (err, scene) => {
|
||||
if (onFinishBack) {
|
||||
onFinishBack();
|
||||
}
|
||||
director.runScene(scene);
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 分帧加载预制体节点
|
||||
* @param prefabName 要加载的预制体名称
|
||||
* @param perFrameNum 每帧加载的数量
|
||||
* @param totalNum 总共需要加载的数量
|
||||
* @param usePool 是否使用对象池,默认为true
|
||||
* @param callBack 对每个创建的节点进行操作的回调函数
|
||||
* @returns Promise<Node[]> 返回所有创建的节点数组
|
||||
*/
|
||||
loadPrefabByFrame(prefabName: string, perFrameNum: number, totalNum: number, usePool: boolean = true, callBack?: (node: Node, index: number) => void): Promise<Node[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 参数验证
|
||||
if (!prefabName || prefabName.trim() === '') {
|
||||
const error = 'prefabName cannot be empty';
|
||||
weiSan.error(error);
|
||||
reject(new Error(error));
|
||||
return;
|
||||
}
|
||||
|
||||
if (perFrameNum <= 0) {
|
||||
const error = 'perFrameNum must be greater than 0';
|
||||
weiSan.error(error);
|
||||
reject(new Error(error));
|
||||
return;
|
||||
}
|
||||
|
||||
if (totalNum <= 0) {
|
||||
const error = 'totalNum must be greater than 0';
|
||||
weiSan.error(error);
|
||||
reject(new Error(error));
|
||||
return;
|
||||
}
|
||||
|
||||
// 触发加载开始事件
|
||||
EventManager.dispatchEvent(EventData.PREFAB_LOAD_START, {
|
||||
prefabName: prefabName,
|
||||
totalNum: totalNum,
|
||||
usePool: usePool
|
||||
});
|
||||
|
||||
// 创建节点的函数
|
||||
const createNodeFunction = (prefab?: Prefab): Node | null => {
|
||||
if (usePool) {
|
||||
// 使用对象池创建节点
|
||||
return PoolManager._ins.getNode(prefabName);
|
||||
} else {
|
||||
// 使用传统方式创建节点
|
||||
if (!prefab) {
|
||||
weiSan.error(`Prefab is required when not using pool: ${prefabName}`);
|
||||
return null;
|
||||
}
|
||||
return instantiate(prefab);
|
||||
}
|
||||
};
|
||||
|
||||
if (usePool) {
|
||||
// 使用对象池时,直接开始分帧创建
|
||||
this.startFrameBasedCreation(prefabName, perFrameNum, totalNum, usePool, callBack, createNodeFunction, resolve, reject);
|
||||
} else {
|
||||
// 不使用对象池时,先加载预制体资源
|
||||
this.loadResAny(prefabName, Prefab, (prefab: Prefab) => {
|
||||
if (!prefab) {
|
||||
const error = `Failed to load prefab: ${prefabName}`;
|
||||
weiSan.error(error);
|
||||
reject(new Error(error));
|
||||
return;
|
||||
}
|
||||
this.startFrameBasedCreation(prefabName, perFrameNum, totalNum, usePool, callBack, createNodeFunction, resolve, reject, prefab);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始分帧创建节点的核心逻辑
|
||||
*/
|
||||
private startFrameBasedCreation(
|
||||
prefabName: string,
|
||||
perFrameNum: number,
|
||||
totalNum: number,
|
||||
usePool: boolean,
|
||||
callBack: ((node: Node, index: number) => void) | undefined,
|
||||
createNodeFunction: (prefab?: Prefab) => Node | null,
|
||||
resolve: (value: Node[]) => void,
|
||||
reject: (reason?: any) => void,
|
||||
prefab?: Prefab
|
||||
) {
|
||||
// 开始分帧创建节点
|
||||
const createdNodes: Node[] = [];
|
||||
let currentCount = 0;
|
||||
|
||||
const createNodesPerFrame = () => {
|
||||
const remainingCount = totalNum - currentCount;
|
||||
const currentFrameCount = Math.min(perFrameNum, remainingCount);
|
||||
|
||||
// 创建当前帧的节点
|
||||
for (let i = 0; i < currentFrameCount; i++) {
|
||||
try {
|
||||
const node = createNodeFunction(prefab);
|
||||
if (!node) {
|
||||
const error = `Failed to create node: ${prefabName}`;
|
||||
weiSan.error(error);
|
||||
reject(new Error(error));
|
||||
return;
|
||||
}
|
||||
|
||||
createdNodes.push(node);
|
||||
|
||||
// 执行回调函数
|
||||
if (callBack) {
|
||||
try {
|
||||
callBack(node, currentCount);
|
||||
} catch (callbackError) {
|
||||
weiSan.warn(`Callback error for node ${currentCount}: ${callbackError}`);
|
||||
}
|
||||
}
|
||||
|
||||
currentCount++;
|
||||
} catch (error) {
|
||||
weiSan.error(`Error creating node instance: ${error}`);
|
||||
reject(new Error(`Error creating node instance: ${error}`));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算进度并触发进度事件
|
||||
const progress = (currentCount / totalNum) * 100;
|
||||
EventManager.dispatchEvent(EventData.PREFAB_LOAD_PROGRESS, {
|
||||
prefabName: prefabName,
|
||||
currentNum: currentCount,
|
||||
totalNum: totalNum,
|
||||
progress: progress
|
||||
});
|
||||
|
||||
// 检查是否完成
|
||||
if (currentCount >= totalNum) {
|
||||
// 触发完成事件
|
||||
EventManager.dispatchEvent(EventData.PREFAB_LOAD_COMPLETE, {
|
||||
prefabName: prefabName,
|
||||
totalNum: totalNum,
|
||||
nodes: createdNodes
|
||||
});
|
||||
|
||||
weiSan.log(`Successfully loaded ${totalNum} instances of prefab: ${prefabName}`);
|
||||
resolve(createdNodes);
|
||||
} else {
|
||||
// 继续下一帧,使用 setTimeout 来实现分帧
|
||||
setTimeout(() => {
|
||||
createNodesPerFrame();
|
||||
}, 0);
|
||||
}
|
||||
};
|
||||
|
||||
// 开始第一帧的创建
|
||||
createNodesPerFrame();
|
||||
}
|
||||
}
|
||||
9
assets/script/common/LoadTools.ts.meta
Normal file
9
assets/script/common/LoadTools.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "6a25c810-7df7-4956-9123-e14f337f08f5",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
706
assets/script/common/Tools.ts
Normal file
706
assets/script/common/Tools.ts
Normal file
@@ -0,0 +1,706 @@
|
||||
import { SpriteFrame, Vec3, Node, Vec2, Button, v2, sys, instantiate, game, Game, v3, Sprite, misc, UITransform, Layers, Camera, Canvas, director, Director, ImageAsset, renderer, RenderTexture, Size, Texture2D, view, sp } from "cc";
|
||||
import { PlatformManager } from "../manager/PlatformManager";
|
||||
import { ResModel } from "../model/ResModel";
|
||||
import { LoadTools } from "./LoadTools";
|
||||
import { AStar } from "./AStar";
|
||||
import { BFSPathfinding } from "./BFS";
|
||||
import { SplitRenderHelper } from "../SplitRender/SplitRenderHelper";
|
||||
|
||||
export type index = {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
game._calculateDT = function _calculateDT(useFixedDeltaTime: boolean): number {
|
||||
this._useFixedDeltaTime = useFixedDeltaTime;
|
||||
|
||||
if (useFixedDeltaTime) {
|
||||
this._startTime = performance.now();
|
||||
return this.frameTime / 1000;
|
||||
}
|
||||
|
||||
const now = performance.now();
|
||||
this._deltaTime = now > this._startTime ? (now - this._startTime) / 1000 : 0;
|
||||
if (this._deltaTime > Game.DEBUG_DT_THRESHOLD) {
|
||||
this._deltaTime = this.frameTime / 1000;
|
||||
}
|
||||
this._startTime = now;
|
||||
return this._deltaTime * Tools.getTimeScale();
|
||||
}
|
||||
|
||||
/** 游戏常用工具类 */
|
||||
export class Tools {
|
||||
private static timeScale = 1;
|
||||
|
||||
private static aStar: AStar = new AStar();
|
||||
|
||||
private static bfs: BFSPathfinding = new BFSPathfinding();
|
||||
|
||||
/**
|
||||
* 使用A*算法进行搜索。
|
||||
* @param map 二维数字数组,代表地图或其他需要搜索的结构。
|
||||
* @param start 搜索的起始点,其格式为 index 类型。
|
||||
* @param end 搜索的终点,其格式为 index 类型。
|
||||
* @param type 搜索类型,用数字表示,默认为 0。不同的类型可能影响搜索算法的行为。
|
||||
* @returns 返回搜索结果,通常是一个路径数组或者搜索到的目标。
|
||||
*/
|
||||
public static aStarSearch(map: number[][], start: index, end: index, type: number = 0) {
|
||||
this.aStar.setMap(map);
|
||||
return this.aStar.search(start, end, type);
|
||||
}
|
||||
|
||||
public static setaStarValue(startValue: number, endValue: number, pathValue: number) {
|
||||
this.aStar.setValue(startValue, endValue, pathValue);
|
||||
}
|
||||
|
||||
public static setaStarEightDirections(isEightDirections: boolean) {
|
||||
this.aStar.setEightDirections(isEightDirections);
|
||||
}
|
||||
|
||||
public static setaStarWeightMap(weightMap: number[][]) {
|
||||
this.aStar.setWeightMap(weightMap);
|
||||
}
|
||||
|
||||
public static setaStarNeighborMap(neighborMap: Map<index, index[]>) {
|
||||
this.aStar.setNeighborMap(neighborMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用广度优先搜索算法在地图上寻找从起点到终点的路径。
|
||||
* @param map 二维数字数组表示的地图。
|
||||
* @param start 起点的索引。
|
||||
* @param end 终点的索引。
|
||||
* @returns 返回一个路径数组,如果不存在路径则返回空数组。
|
||||
*/
|
||||
public static bfsSearch(map: number[][], start: index, end: index) {
|
||||
this.bfs.setMap(map); // 设置广度优先搜索对象的地图
|
||||
return this.bfs.findPath(start, end); // 执行搜索并返回路径
|
||||
}
|
||||
|
||||
/**
|
||||
* 存储本地数据
|
||||
* @param {*} isObject 是否是一个对象或者数组
|
||||
*/
|
||||
public static setStorage(key: string, value: any, isObject = false) {
|
||||
PlatformManager.setStorage(key, value, isObject);
|
||||
}
|
||||
/**
|
||||
* 获取存储数据
|
||||
* @param {*} isObject 是否是一个对象或者数组
|
||||
*/
|
||||
public static getStorage(key: string, isObject = false) {
|
||||
return PlatformManager.getStorage(key, isObject);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取时间缩放
|
||||
* @returns 时间缩放
|
||||
*/
|
||||
public static getTimeScale() {
|
||||
return Tools.timeScale;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置时间缩放
|
||||
* @param scale 时间缩放
|
||||
*/
|
||||
public static setTimeScale(scale: number) {
|
||||
Tools.timeScale = scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取截图
|
||||
* @param node 截图的节点
|
||||
* @param parent 截图的父节点
|
||||
* @param pos 截图的位置
|
||||
* @param canvas 默认摄像机渲染的canvas
|
||||
* @param normalCamera 默认摄像机
|
||||
*/
|
||||
public static getScreenShot(node: Node, parent: Node, pos: Vec3, canvas: Canvas, normalCamera: Camera) {
|
||||
const newCamera = new Node("PhotoCamera");
|
||||
|
||||
newCamera.setParent(canvas.node);
|
||||
newCamera.setPosition(0, 0, 1000);
|
||||
newCamera.addComponent(Camera);
|
||||
newCamera.getComponent(Camera).visibility = normalCamera.visibility;
|
||||
newCamera.getComponent(Camera).clearFlags = normalCamera.clearFlags;
|
||||
newCamera.getComponent(Camera).near = normalCamera.near;
|
||||
newCamera.getComponent(Camera).far = normalCamera.far;
|
||||
newCamera.getComponent(Camera).projection = renderer.scene.CameraProjection.ORTHO;
|
||||
newCamera.getComponent(Camera).orthoHeight = normalCamera.orthoHeight;
|
||||
|
||||
const newRenderTexture = new RenderTexture();
|
||||
newRenderTexture.reset({
|
||||
width: view.getVisibleSize().width,
|
||||
height: view.getVisibleSize().height
|
||||
});
|
||||
newCamera.getComponent(Camera).targetTexture = newRenderTexture;
|
||||
|
||||
director.once(Director.EVENT_AFTER_DRAW, () => {
|
||||
const newSpriteFrame = new SpriteFrame();
|
||||
|
||||
let data = newRenderTexture.readPixels(Math.round(node.getWorldPosition().x - node.getComponent(UITransform).width / 2 * node.scale.x),
|
||||
Math.round(node.getWorldPosition().y - node.getComponent(UITransform).height / 2 * node.scale.y),
|
||||
node.getComponent(UITransform).width * node.scale.x, node.getComponent(UITransform).height * node.scale.y)
|
||||
|
||||
let image = new ImageAsset();
|
||||
image.reset({
|
||||
_data: data,
|
||||
_compressed: false,
|
||||
width: node.getComponent(UITransform).width * node.scale.x,
|
||||
height: node.getComponent(UITransform).height * node.scale.y,
|
||||
format: Texture2D.PixelFormat.RGBA8888
|
||||
})
|
||||
|
||||
let newTexture = new Texture2D();
|
||||
newTexture.image = image;
|
||||
newSpriteFrame.texture = newTexture;
|
||||
newSpriteFrame.packable = false;
|
||||
|
||||
if (sys.isNative && (sys.os === sys.OS.IOS || sys.os === sys.OS.OSX)) {
|
||||
newSpriteFrame.flipUVY = false;
|
||||
} else {
|
||||
newSpriteFrame.flipUVY = true;
|
||||
}
|
||||
|
||||
let newSprite = new Node("PhotoSprite");
|
||||
newSprite.addComponent(Sprite);
|
||||
newSprite.setParent(parent);
|
||||
newSprite.setPosition(pos);
|
||||
newSprite.layer = Layers.Enum.UI_2D;
|
||||
newSprite.getComponent(UITransform)
|
||||
.setContentSize(new Size(node.getComponent(UITransform).width,
|
||||
node.getComponent(UITransform).height));
|
||||
newSprite.getComponent(Sprite).spriteFrame = newSpriteFrame;
|
||||
|
||||
newCamera.getComponent(Camera).targetTexture = null;
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存文件。
|
||||
* @param textToWrite 要保存的文件内容
|
||||
* @param fileNameToSaveAs 要保存的文件名
|
||||
* @param fileType 要保存的文件格式,默认为Json
|
||||
*/
|
||||
public static saveForBrowser(textToWrite: any, fileNameToSaveAs: string, fileType: string = 'application/json') {
|
||||
if (sys.isBrowser) {
|
||||
console.log("浏览器");
|
||||
let textFileAsBlob = new Blob([textToWrite], { type: fileType });
|
||||
let downloadLink = document.createElement("a");
|
||||
downloadLink.download = fileNameToSaveAs;
|
||||
downloadLink.innerHTML = "Download File";
|
||||
downloadLink.href = window.webkitURL.createObjectURL(textFileAsBlob);
|
||||
|
||||
downloadLink.click();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取spine指定动画时间,如果没有该动画 则返回0
|
||||
* @param spine 骨骼动画
|
||||
* @param aniName 动画名称
|
||||
* @returns 动画时间
|
||||
*/
|
||||
public static getSpineAnimationTime(spine: sp.Skeleton, aniName: string) {
|
||||
const state = spine.getState();
|
||||
if (state == undefined) throw `[ERROR SPINE ANIMATION] 无法获取获取动画状态`;
|
||||
const { animations } = state.data.skeletonData;
|
||||
let result = 0;
|
||||
for (const key in animations) {
|
||||
if (Object.prototype.hasOwnProperty.call(animations, key)) {
|
||||
const element = animations[key];
|
||||
if (element.name == aniName) {
|
||||
result = element.duration;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//-----------------------------节点预制体相关-------------------------------
|
||||
/**
|
||||
* 新建一个预制体在场景里
|
||||
* @param preName 预制体名字或url
|
||||
* @param callFunc 加载预制体 完成后回调
|
||||
* @param parent 存放预制体的父节点
|
||||
* @param Pos 预制体的坐标
|
||||
* @param zIndex 预制体的层级
|
||||
*/
|
||||
public static newPrefab(preName: string, parent?: Node, Pos?: Vec3, callFunc?: (age?: Node) => void): Node {
|
||||
return NodeTools._ins.newPrefab(preName, callFunc, parent, Pos);
|
||||
}
|
||||
/**
|
||||
* 新建一个图片在场景里
|
||||
* @param sprName 图片名字或url
|
||||
* @param callFunc 加载预制体 完成后回调
|
||||
* @param parent 存放预制体的父节点
|
||||
* @param Pos 预制体的坐标
|
||||
* @param zIndex 预制体的层级
|
||||
*/
|
||||
public static newSprite(sprName: string, parent?: Node, Pos?: Vec3, callFunc?: (age?: Node) => void): Node {
|
||||
return NodeTools._ins.newSprite(sprName, callFunc, parent, Pos);
|
||||
}
|
||||
/**
|
||||
* 设置一个节点的SpriteFrame
|
||||
* @param nodeT 节点的Node
|
||||
* @param sprUrl 图片的url或者存放到resArr的名字
|
||||
*/
|
||||
public static setSpriteFrame(nodeT: Node, sprUrl: string) {
|
||||
NodeTools._ins.setSpriteFrame(nodeT, sprUrl);
|
||||
}
|
||||
/** 设置一个节点的 groupIndex 包含子物体 */
|
||||
public static setNodeLayerIndex(nodeT: Node, layer: Layers.Enum) {
|
||||
NodeTools._ins.setNodeLayerIndex(nodeT, layer);
|
||||
}
|
||||
/**
|
||||
* 设置一个Button的按下和松开的SpriteFrame
|
||||
* @param norUrl 默认状态的名字或者路径
|
||||
* @param preUrl 按下状态的名字或者路径
|
||||
*/
|
||||
public static setBtnClickSpr(Btn: Button, norUrl: string, preUrl: string) {
|
||||
NodeTools._ins.setBtnClickSpr(Btn, norUrl, preUrl);
|
||||
};
|
||||
/** 切换父物体 不改变显示位置*/
|
||||
public static setNodeParent(node: Node, parent: Node) {
|
||||
NodeTools._ins.setNodeParent(node, parent);
|
||||
};
|
||||
//----------------------------------数学数组相关----------------------------------
|
||||
|
||||
/**
|
||||
* 将多维数组展平
|
||||
* @param arr 需要展平的多维数组
|
||||
* @returns 展平后的数组
|
||||
*/
|
||||
public static flatArray(arr: any[]) {
|
||||
return arr.reduce((acc, curr) => {
|
||||
return acc.concat(Array.isArray(curr) ? Tools.flatArray(curr) : curr);
|
||||
}, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数组中随机获取元素
|
||||
* @param arr 需要随机获取元素的数组
|
||||
* @param num 需要获取的元素数量
|
||||
* @param sortFunc 排序函数,可选
|
||||
* @returns 返回随机获取的元素数组
|
||||
*/
|
||||
public static randomGetElementFromArray(arr: any[], num: number, sortFunc: (a: any, b: any) => number = null) {
|
||||
const tempArr = arr.slice();
|
||||
const resultArr = [];
|
||||
while (resultArr.length < num) {
|
||||
const randomIndex = Tools.random(0, tempArr.length - 1);
|
||||
resultArr.push(tempArr[randomIndex]);
|
||||
tempArr.splice(randomIndex, 1);
|
||||
}
|
||||
if (sortFunc) {
|
||||
resultArr.sort(sortFunc);
|
||||
}
|
||||
return resultArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取随机数
|
||||
* @param isInteger 是否随机整数 默认整数
|
||||
*/
|
||||
public static random(x1: number, x2: number, isInteger = true): number {
|
||||
return MathTools._ins.random(x1, x2, isInteger);
|
||||
}
|
||||
/**
|
||||
* 根据概率数组 随机概率 返回数组的index
|
||||
* @param chooseArr 需要随机概率的数组 例如[0.05,0.1,0.2,0.3]
|
||||
*/
|
||||
public static chooseRandom(chooseArr: Array<number>) {
|
||||
return MathTools._ins.chooseRandom(chooseArr);
|
||||
}
|
||||
/** 传入一个弧度 返回一个Y轴折射后的弧度 */
|
||||
public static refractionY(rad: number) {
|
||||
return MathTools._ins.refractionY(rad);
|
||||
};
|
||||
/** 传入一个弧度 返回一个轴折射后的弧度 */
|
||||
public static refractionX(rad: number) {
|
||||
return MathTools._ins.refractionX(rad);
|
||||
};
|
||||
/** 重新打乱一个数组的顺序 洗牌 */
|
||||
public static againSortArr(Arr: Array<any>) {
|
||||
MathTools._ins.againSortArr(Arr);
|
||||
};
|
||||
/**
|
||||
* 将一个数组 按照里面的对象排序
|
||||
* @param tempArr 传入的数组
|
||||
* @param sortName 对象属性名字
|
||||
* @param isReverse 是否倒序
|
||||
*/
|
||||
public static sortArrForObject(tempArr: Array<any>, sortName: string, isReverse = false) {
|
||||
MathTools._ins.sortArrForObject(tempArr, sortName, isReverse);
|
||||
};
|
||||
/**
|
||||
* 取一定范围内不重复的数字
|
||||
* @param minNum 最小取值范围
|
||||
* @param maxNum 最大取值范围
|
||||
* @param getNum 取几个数字
|
||||
*/
|
||||
public static getDiffNumRandom(minNum: number, maxNum: number, getNum: number) {
|
||||
return MathTools._ins.getDiffNumRandom(minNum, maxNum, getNum);
|
||||
};
|
||||
//--------------------------------向量坐标计算相关------------------------------------
|
||||
/**
|
||||
* 根据两个点 求角度
|
||||
* @param pos1 起始点坐标
|
||||
* @param pos2 结束点坐标
|
||||
* @param isVertical 是否以竖直方向为0度开始
|
||||
*/
|
||||
public static getAngleForPos(pos1: Vec2, pos2: Vec2, isVertical = false): number {
|
||||
return VecTools._ins.getAngleForPos(pos1, pos2, isVertical);
|
||||
};
|
||||
/** 获取两个坐标之间的距离 */
|
||||
public static getDistance(pos1: Vec3 | Vec2, pos2: Vec3 | Vec2): number {
|
||||
if (pos1 instanceof Vec2 && pos2 instanceof Vec2) {
|
||||
return Vec2.distance(pos1, pos2);
|
||||
}
|
||||
if (pos1 instanceof Vec3 && pos2 instanceof Vec3) {
|
||||
return Vec3.distance(pos1, pos2);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 根据一个角度和长度 计算相对应的坐标
|
||||
* @param angle 角度
|
||||
* @param len 该角度上的长度
|
||||
* @param startPos 初始的坐标
|
||||
*/
|
||||
public static getPosForAngleLen(angle: number, len: number, startPos: Vec2 = v2(0, 0)) {
|
||||
return VecTools._ins.getPosForAngleLen(angle, len, startPos);
|
||||
}
|
||||
/**
|
||||
* 获取节点在另一个节点下的坐标
|
||||
* @param obj 节点
|
||||
* @param mainObj 相对于的另一个节点
|
||||
*/
|
||||
public static getToNodePosForNode(obj: Node, mainObj: Node): Vec2 {
|
||||
return VecTools._ins.getToNodePosForNode(obj, mainObj);
|
||||
};
|
||||
/** 获取节点的世界坐标 */
|
||||
public static getToWorldPosAR(obj: Node) {
|
||||
return VecTools._ins.getToWorldPosAR(obj);
|
||||
}
|
||||
/**
|
||||
* 通过世界坐标 获取相对节点的坐标
|
||||
* @param worldPos 世界坐标
|
||||
* @param obj 相对节点下的
|
||||
*/
|
||||
public static getToNodePosForWorld(worldPos: Vec3, obj: Node) {
|
||||
return VecTools._ins.getToNodePosForWorld(worldPos, obj);
|
||||
}
|
||||
/** 二维向量转三维向量 */
|
||||
public static getVec3ForVec2(vec2: Vec2) {
|
||||
return VecTools._ins.getVec3ForVec2(vec2);
|
||||
}
|
||||
/** 三维向量转二维向量 */
|
||||
public static getVec2ForVec3(vec3: Vec3) {
|
||||
return VecTools._ins.getVec2ForVec3(vec3);
|
||||
}
|
||||
/** 获取两向量方向 */
|
||||
public static getDirection(startPos: Vec3 | Vec2, endPos: Vec3 | Vec2) {
|
||||
return VecTools._ins.getDirection(startPos, endPos);
|
||||
}
|
||||
/** 获取多边形面积 */
|
||||
public static getPolygonArea(polygon: Vec2[]) {
|
||||
return VecTools._ins.getPolygonArea(polygon);
|
||||
}
|
||||
//--------------------------------数组操作相关------------------------------------
|
||||
/** 根据value值 从数组里面移除 */
|
||||
public static removeArrForValue(tempArr: Array<any>, value: any) {
|
||||
return tempArr.splice(tempArr.indexOf(value), 1);
|
||||
}
|
||||
/** 从数组里面添加一个该数组里没有的元素 */
|
||||
public static addArrNoValue(tempArr: Array<any>, value: any) {
|
||||
if (tempArr.indexOf(value) < 0) {
|
||||
tempArr.push(value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/** 从数组指定位置 插入某个元素 */
|
||||
public static addArrIndex(tempArr: Array<any>, index: number, value: any) {
|
||||
return tempArr.splice(index, 0, value);
|
||||
}
|
||||
//--------------------------------其他------------------------------------
|
||||
/**
|
||||
* 字符串指定位置插入新字符
|
||||
* @param source 需要操作的字符串
|
||||
* @param start 从那个位置开始插入
|
||||
* @param newStr 插入的新的字符
|
||||
*/
|
||||
public static insertStrForIndex(source: string, start: number, newStr: string): string {
|
||||
return source.slice(0, start) + newStr + source.slice(start);
|
||||
};
|
||||
/**
|
||||
* 数字整数前边补零 并返回字符串
|
||||
* @param num 传入的数字
|
||||
* @param length 前边补几个零
|
||||
*/
|
||||
public static prefixInteger(num: number, length = 2): string {
|
||||
return (Array(length).join('0') + num).slice(-length);
|
||||
};
|
||||
/** 获取系统语言 */
|
||||
public static getLanguageType(): string {
|
||||
var langNumType = "EN"; //默认英语
|
||||
if (sys.language == sys.Language.CHINESE) {
|
||||
if (sys.languageCode.toLowerCase().indexOf("zh-cn") != -1 ||
|
||||
sys.languageCode.toLowerCase().indexOf("zh_cn") != -1 ||
|
||||
sys.languageCode.toLowerCase().indexOf("zh-hans-cn") != -1) {
|
||||
langNumType = "CN"; //简体
|
||||
} else {
|
||||
langNumType = "CHT"; //繁体
|
||||
}
|
||||
}
|
||||
// else if ( sys.language == sys.Language.KOREAN ) {
|
||||
// langNumType = "KOR"; //韩语
|
||||
// }
|
||||
// else if (sys.language == sys.Language.JAPANESE) {
|
||||
// langNumType = "JP"; //日语
|
||||
// }
|
||||
// else if ( window.navigator.language == "th-TH" ) {
|
||||
// langNumType = "TH"; //泰语
|
||||
// }
|
||||
return langNumType;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** 节点相关 工具类 */
|
||||
class NodeTools {
|
||||
/** 单例模式 */
|
||||
private static _instance: NodeTools = new NodeTools();
|
||||
private constructor() { }
|
||||
public static get _ins() {
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
/** 新建一个预制体在场景里 */
|
||||
public newPrefab(preName: string, callFunc?: (age?: Node) => void, parent?: Node, Pos?: Vec3): Node {
|
||||
let prefab = ResModel._ins.PrefabDic.get(preName);
|
||||
let clone: Node = null;
|
||||
if (prefab != null) {
|
||||
clone = instantiate(prefab);
|
||||
if (parent) { parent.addChild(clone) };
|
||||
if (Pos) { clone.position = v3(Pos.x, Pos.y, Pos.z) };
|
||||
if (callFunc != null) {
|
||||
callFunc(clone);
|
||||
}
|
||||
} else {
|
||||
LoadTools._ins.loadResPrefab(preName, callFunc, parent, Pos);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
/** 新建一个图片在场景里 */
|
||||
public newSprite(sprName: string, callFunc?: (age?: Node) => void, parent?: Node, Pos?: Vec3) {
|
||||
let sprite = new Node();
|
||||
sprite.name = sprName;
|
||||
sprite.layer = Layers.Enum.UI_2D;
|
||||
if (ResModel._ins.SpriteFrameDic.get(sprName) != null) {
|
||||
sprite.addComponent(Sprite).spriteFrame = ResModel._ins.SpriteFrameDic.get(sprName);
|
||||
if (parent) { parent.addChild(sprite) };
|
||||
if (Pos) { sprite.position = v3(Pos.x, Pos.y, Pos.z) };
|
||||
if (callFunc != null) {
|
||||
callFunc(sprite);
|
||||
}
|
||||
} else {
|
||||
sprite.addComponent(Sprite);
|
||||
LoadTools._ins.loadResSpriteFrame(sprName, sprite, parent, Pos, callFunc);
|
||||
}
|
||||
return sprite;
|
||||
}
|
||||
/** 设置一个节点的SpriteFrame */
|
||||
public setSpriteFrame(nodeT: Node, sprUrl: string) {
|
||||
if (nodeT.getComponent(Sprite) == null) {
|
||||
nodeT.addComponent(Sprite);
|
||||
}
|
||||
if (ResModel._ins.SpriteFrameDic.get(sprUrl)) {
|
||||
nodeT.getComponent(Sprite).spriteFrame = ResModel._ins.SpriteFrameDic.get(sprUrl)
|
||||
} else {
|
||||
LoadTools._ins.loadResAny(sprUrl, SpriteFrame, (spriteFrame: SpriteFrame) => {
|
||||
nodeT.getComponent(Sprite).spriteFrame = spriteFrame;
|
||||
})
|
||||
}
|
||||
}
|
||||
/** 设置一个节点的 所属层 包含子物体 */
|
||||
public setNodeLayerIndex(nodeT: Node, layer: Layers.Enum) {
|
||||
nodeT.layer = layer;
|
||||
for (let i = 0; i < nodeT.children.length; i++) {
|
||||
this.setNodeLayerIndex(nodeT.children[i], layer);
|
||||
}
|
||||
}
|
||||
/** 设置一个Button的按下和松开的SpriteFrame */
|
||||
public setBtnClickSpr(Btn: Button, norUrl: string, preUrl: string) {
|
||||
if (ResModel._ins.SpriteFrameDic.get(norUrl)) {
|
||||
Btn.getComponent(Button).normalSprite = ResModel._ins.SpriteFrameDic.get(norUrl)
|
||||
Btn.getComponent(Button).hoverSprite = ResModel._ins.SpriteFrameDic.get(norUrl)
|
||||
Btn.getComponent(Button).pressedSprite = ResModel._ins.SpriteFrameDic.get(preUrl)
|
||||
} else {
|
||||
LoadTools._ins.loadResAny(norUrl, SpriteFrame, (spr: SpriteFrame) => {
|
||||
Btn.getComponent(Button).normalSprite = spr;
|
||||
Btn.getComponent(Button).hoverSprite = spr;
|
||||
});
|
||||
LoadTools._ins.loadResAny(preUrl, SpriteFrame, (spr: SpriteFrame) => {
|
||||
Btn.getComponent(Button).pressedSprite = spr;
|
||||
});
|
||||
}
|
||||
};
|
||||
/** 切换父物体 不改变坐标 */
|
||||
public setNodeParent(node: Node, parent: Node) {
|
||||
let Pos = VecTools._ins.getToNodePosForNode(node, parent);
|
||||
node.parent = parent;
|
||||
node.position = v3(Pos.x, Pos.y);
|
||||
};
|
||||
}
|
||||
|
||||
/** 数学数组计算相关 工具类 */
|
||||
class MathTools {
|
||||
/** 单例模式 */
|
||||
private static _instance: MathTools = new MathTools();
|
||||
private constructor() { }
|
||||
public static get _ins() {
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
/** 获取随机数 */
|
||||
public random(x1: number, x2: number, isInteger = true): number {
|
||||
if (isInteger) {
|
||||
return x1 + Math.floor(Math.random() * (x2 - x1 + 1));
|
||||
}
|
||||
return Math.random() * (x2 - x1) + x1;
|
||||
}
|
||||
/** 根据概率数组 随机概率 返回数组的index */
|
||||
public chooseRandom(chooseArr: Array<number>) {
|
||||
let total = 0; //概率总值
|
||||
//首先计算出概率的总值,用来计算随机范围
|
||||
for (let i = 0; i < chooseArr.length; i++) {
|
||||
total += chooseArr[i];
|
||||
}
|
||||
let randNum = this.random(0, total, false)
|
||||
for (let i = 0; i < chooseArr.length; i++) {
|
||||
if (randNum < chooseArr[i] && chooseArr[i] > 0) {
|
||||
return i;
|
||||
} else {
|
||||
randNum -= chooseArr[i];
|
||||
}
|
||||
}
|
||||
return chooseArr.length - 1;
|
||||
}
|
||||
/** 弧度折射Y轴 */
|
||||
public refractionY(rad: number) {
|
||||
return Math.atan2(Math.sin(rad), -Math.cos(rad));
|
||||
};
|
||||
/** 弧度折射X轴 */
|
||||
public refractionX(rad: number) {
|
||||
return Math.atan2(-Math.sin(rad), Math.cos(rad));
|
||||
};
|
||||
/** 重新洗牌 一个数组 */
|
||||
public againSortArr(Arr: Array<any>) {
|
||||
for (let i = 0; i < Arr.length; i++) {
|
||||
let tempR = Tools.random(0, Arr.length - 1);
|
||||
if (tempR != i) {
|
||||
let temp = Arr[i];
|
||||
Arr[i] = Arr[tempR];
|
||||
Arr[tempR] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
/** 数组 对象排序 对象属性 是否倒序 */
|
||||
public sortArrForObject(tempArr: Array<any>, sortName: string, isReverse = false) {
|
||||
if (!isReverse) {
|
||||
tempArr.sort((a, b) => {
|
||||
return a[sortName] - b[sortName];
|
||||
});
|
||||
} else {
|
||||
tempArr.sort((a, b) => {
|
||||
return b[sortName] - a[sortName];
|
||||
});
|
||||
}
|
||||
};
|
||||
/** 取一定范围内不重复的数字 */
|
||||
public getDiffNumRandom(minNum: number, maxNum: number, getNum: number) {
|
||||
var arr = [];
|
||||
for (let i = minNum; i <= maxNum; i++) {
|
||||
arr.push(i);
|
||||
}
|
||||
const tempLen = arr.length - getNum;
|
||||
for (let i = 0; i < tempLen; i++) {
|
||||
let tempI = Tools.random(0, arr.length - 1);
|
||||
arr.splice(tempI, 1);
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
}
|
||||
|
||||
/** 向量坐标转换相关工具类 */
|
||||
class VecTools {
|
||||
/** 单例模式 */
|
||||
private static _instance: VecTools = new VecTools();
|
||||
private constructor() { }
|
||||
public static get _ins() {
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
/** 根据两个点 求角度 */
|
||||
public getAngleForPos(pos1: Vec2, pos2: Vec2, isVertical = false): number {
|
||||
let rad = 0;
|
||||
if (isVertical) {
|
||||
rad = -Math.atan2(pos2.x - pos1.x, pos2.y - pos1.y);
|
||||
} else {
|
||||
rad = Math.atan2(pos2.y - pos1.y, pos2.x - pos1.x);
|
||||
}
|
||||
return misc.radiansToDegrees(rad);
|
||||
}
|
||||
/** 根据一个角度和长度 计算相对应的坐标 */
|
||||
public getPosForAngleLen(angle: number, len: number, startPos: Vec2 = v2(0, 0)) {
|
||||
let rad = misc.degreesToRadians(angle);
|
||||
return v2(startPos.x + Math.cos(rad) * len, startPos.y + Math.sin(rad) * len);
|
||||
}
|
||||
/** 获取节点在另一个节点下的坐标 */
|
||||
public getToNodePosForNode(obj: Node, mainObj: Node): Vec2 {
|
||||
let worldPos = obj.parent.getComponent(UITransform).convertToWorldSpaceAR(obj.position);
|
||||
let nodePos = mainObj.getComponent(UITransform).convertToNodeSpaceAR(worldPos)
|
||||
return v2(nodePos.x, nodePos.y);
|
||||
};
|
||||
/** 获取节点的世界坐标 */
|
||||
public getToWorldPosAR(obj: Node) {
|
||||
return obj.parent.getComponent(UITransform).convertToWorldSpaceAR(obj.position);
|
||||
}
|
||||
/** 通过世界坐标 获取相对节点的坐标 */
|
||||
public getToNodePosForWorld(worldPos: Vec3, obj: Node) {
|
||||
return obj.getComponent(UITransform).convertToNodeSpaceAR(worldPos);
|
||||
}
|
||||
|
||||
/** 二维向量转三维向量 */
|
||||
public getVec3ForVec2(vec2: Vec2) {
|
||||
return v3(vec2.x, vec2.y, 0);
|
||||
}
|
||||
|
||||
/** 三维向量转二维向量 */
|
||||
public getVec2ForVec3(vec3: Vec3) {
|
||||
return v2(vec3.x, vec3.y);
|
||||
}
|
||||
|
||||
/** 获取两向量方向和距离
|
||||
* @param startPos 起始位置
|
||||
* @param endPos 结束位置
|
||||
*/
|
||||
public getDirection(startPos: Vec3 | Vec2, endPos: Vec3 | Vec2) {
|
||||
if (startPos instanceof Vec2 && endPos instanceof Vec2) {
|
||||
const deltaPos = endPos.clone().subtract(startPos);
|
||||
const direction = deltaPos.normalize();
|
||||
return direction;
|
||||
} else if (startPos instanceof Vec3 && endPos instanceof Vec3) {
|
||||
const deltaPos = endPos.clone().subtract(startPos);
|
||||
const direction = deltaPos.normalize();
|
||||
return direction;
|
||||
}
|
||||
}
|
||||
|
||||
public getPolygonArea(polygon: Vec2[]) {
|
||||
return SplitRenderHelper.calculatePolygonArea(polygon);
|
||||
}
|
||||
}
|
||||
9
assets/script/common/Tools.ts.meta
Normal file
9
assets/script/common/Tools.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "f3cbf87f-aa09-459e-8527-cd6f1be98ffb",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
31
assets/script/common/weiSanTools.ts
Normal file
31
assets/script/common/weiSanTools.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
export class weiSan {
|
||||
/** 是否开启 Log 信息 */
|
||||
private static isLog = true;
|
||||
private static logName:string = "微伞游戏Log:"
|
||||
private static logInfo:string = "本游戏包含的所有内容(包括但不限于:代码、图片、视像及声音内容、名称)的所有权归北京米兜科技有限公司所有。任何单位或个人将本游戏提供的内容与服务用于商业、盈利、广告性等目的时,需征得北京米兜科技有限公司相关权利人的书面许可;将本网站提供的内容与服务用于非商业用途时,应遵守著作权法以及其他相关法律的规定,不得侵犯游戏所有者及相关权利人的权益。"
|
||||
|
||||
/** 普通log信息 */
|
||||
public static log(...data: any[]): void {
|
||||
if (!this.isLog) { return; }
|
||||
console.log(this.logName,...data);
|
||||
}
|
||||
/** 追踪函数调用的log */
|
||||
public static logTrace(...data: any[]){
|
||||
if (!this.isLog) { return; }
|
||||
console.trace(this.logName,...data);
|
||||
}
|
||||
/** 打印错误log信息 */
|
||||
public static error(...data: any[]): void {
|
||||
if (!this.isLog) { return; }
|
||||
console.error(this.logName,...data);
|
||||
}
|
||||
/** 打印警告log信息 */
|
||||
public static warn(...data: any[]): void {
|
||||
if (!this.isLog) { return; }
|
||||
console.warn(this.logName,...data);
|
||||
}
|
||||
|
||||
public static initLog(){
|
||||
console.log(this.logInfo);
|
||||
}
|
||||
}
|
||||
9
assets/script/common/weiSanTools.ts.meta
Normal file
9
assets/script/common/weiSanTools.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "289a2b50-88c3-4705-9e74-63d767daaefb",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
Reference in New Issue
Block a user