123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- import { _decorator, Asset, Component, Constructor, director, Node, ParticleSystem, Prefab, SceneAsset, setPropertyEnumType, SkeletalAnimation, Sprite, SpriteFrame, tween, UIOpacity, UITransform, v2, v3, Vec3 } from 'cc';
- import { Utils } from './Utils';
- import { Constants } from '../data/Constants';
- import { bundleMgr } from '../core/manager/BundleManager';
- import { Logger } from '../extend/Logger';
- import { PoolManager } from '../core/manager/PoolManager';
- import { uiMgr } from '../core/manager/UIManager';
- const { ccclass, property } = _decorator;
- /**
- * 游戏道具类型枚举
- * @description 道具类型标识
- */
- export enum ITEM_TYPE {
- Diamond = "diamond",//钻石
- Coin = "coin",//金币
- Boomerang = "boomerang",//飞镖
- }
- /**
- * 游戏特效类型枚举
- * @description 所有特效资源分类标识
- */
- export enum EffectType {
- Bleeding = "effects/Prefabs/blood", //流血效果(子弹命中敌人)
- BarrelExplosion = "effects/Prefabs/OilBoom", //油桶爆炸(基础爆炸)
- RockDebris = "effects/Prefabs/StoneBoom",// 沙堆/石礅碎裂烟雾
- VehicleSmoke = "effects/Prefabs/HeavySmoke", // 坦克/汽车受损烟雾(浓烟)
- VehicleExplosion = "effects/Prefabs/TankBoom" // 坦克/汽车爆炸(强烈爆炸)
- }
- @ccclass('ResUtil')
- export class ResUtil {
- /**
- * 设置精灵贴图 只能prefabs包 texture目录下
- * @param path 资源路径
- * 只能是在prefabs这个bundle目录下、然后如果使用
- * @param target 精灵或者节点
- * @param cb 回调函数
- */
- public static setSpriteFrame(name: string, target: Sprite | Node) {
- if(Utils.isNull(name))return;
- const path:string = `texture/${name}/spriteFrame`;
- bundleMgr.loadAsset(Constants.bundleName.prefabs,path,SpriteFrame, (err: Error, clip: SpriteFrame) => {
- if(!err) {
- let sprite: Sprite = target instanceof Node ? target.getComponent(Sprite) : target;
- if (!sprite) {
- console.error('Target node does not have a Sprite component!');
- }else{
- sprite.spriteFrame = clip;
- }
- }
- });
- }
- /**
- * 设置精灵贴图 指定自定义的包
- * @param bundle 包名如果传空 默认prefabs包名
- * @param path 路径 如果在prefabs包下,就可以直接传递传图片名称或者path路径(hit_m or texture/hit_m/spriteFrame)
- * 如果不在prefabs包下就只能传递完成的路径(texture/hit_m/spriteFrame)
- * @param type 类型
- * @param target 图片或者节点
- * @param onComplete 设置完成的回调
- */
- public static setSpriteFrameAsset(
- bundle: string,
- path: string,
- type: Constructor<Asset>,
- target: Sprite | Node,
- onComplete?: (err: Error | null, asset?: any) => void){
- if(Utils.isNull(bundle)){
- bundle = Constants.bundleName.prefabs;
- if(!path.startsWith("texture/")){
- path = `texture/${path}/spriteFrame`;
- }
- }
- if(Utils.isNull(path))return;
- bundleMgr.loadAsset(bundle,path,type, (err: Error, clip: SpriteFrame) => {
- if(!err) {
- let sprite: Sprite = target instanceof Node ? target.getComponent(Sprite) : target;
- if (!sprite) {
- console.error('Target node does not have a Sprite component!');
- }else{
- sprite.spriteFrame = clip;
- }
- }
- onComplete?.(err,clip);
- });
- }
- /**
- * 预加载场景资源(支持进度回调)
- * @param {string} sceneName - 场景名称(如 "main" 或者"level1")
- * @param {Function} [onComplete] - 完成回调
- * @param {Function} [onProgress] - 加载进度回调
- * @param {Error | null} onProgress.err - 错误信息(成功时为 null)
- * @param {number} onProgress.progress - 加载进度(0~1)
- */
- public static preloadScene(
- sceneName: string,
- onComplete?: () => void,
- onProgress?: (err: Error | null, progress: number) => void
- ): void {
- if (Utils.isNull(sceneName)) {
- Logger.warn("[SceneLoader] Scene name cannot be null or empty!");
- return;
- }//开始预加载场景
- director.preloadScene(
- sceneName,
- (loadedCount: number, totalCount: number) => {//进度更新回调
- const progress = totalCount > 0 ? loadedCount / totalCount : 0;
- onProgress?.(null, progress); // 通知进度(未完成)
- },//加载完成回调
- (error: Error | null, sceneAsset?: SceneAsset) => {
- if (error) {
- Logger.error(`[SceneLoader] Failed to preload scene "${sceneName}":`, error);
- onProgress?.(error,0); // 通知失败
- return;
- }
- onComplete?.();
- //预加载成功
- Logger.log(`[SceneLoader] Scene "${sceneName}" preloaded successfully`);
- onProgress?.(null, 1);
- }
- );
- }
- /**
- * 加载敌人的预制体
- * @param name 敌人的名字
- * @param parent 父节点
- * @returns 返回一个敌人
- */
- public static loadEnemyRes(name: string, parent?: Node): Promise<Node | null> {
- return new Promise(async (resolve, reject) => {
- try {
- if(Utils.isNull(name)) {
- resolve(null);
- return;
- }
- //加载资源
- const path = `enemy/${name}`;
- const res = await bundleMgr.loadAsset(Constants.bundleName.prefabs, path, Prefab) as Prefab;
- //从对象池获取实例
- const enemy = PoolManager.getNode(res) as Node;
- //设置父节点
- if (parent) {
- enemy.parent = parent;
- }
- //返回结果
- resolve(enemy);
- } catch (error) {
- Logger.error(`[loadEnemyRes] 加载敌人资源失败: ${name}`, error);
- reject(new Error(`加载敌人资源失败: ${name}`));
- }
- });
- }
- /**
- * 加载枪的预制体
- * @param name 枪的名称
- * @param parent 父节点
- * @returns 返回一把枪
- */
- public static async loadGunRes(path: string,parent?: Node){
- return new Promise(async (resolve, reject) => {
- try {
- if(Utils.isNull(path)) {
- resolve(null);
- return;
- }
- const res: Prefab = await bundleMgr.loadAsset(Constants.bundleName.prefabs, path, Prefab) as Prefab;
- const gun: Node = PoolManager.getNode(res) as Node;
- if (parent) {
- gun.parent = parent;
- }
- resolve(gun);
- } catch (error) {
- Logger.error(`[loadGunRes] 加载枪资源失败: ${name}`, error);
- reject(error);
- }
- });
- }
- /**
- * 只加载prefabs资源ui和页面 预制体
- * @param name 名字 只能是预知体
- * @param parent 父节点
- * @returns 返回一个节点
- */
- public static loadRes(path: string, parent?: Node): Promise<Node | null> {
- return new Promise(async (resolve, reject) => {
- try {
- if (Utils.isNull(path)) {
- resolve(null);
- return;
- }
- //加载资源 从对象池获取实例
- const res = await bundleMgr.loadAsset(Constants.bundleName.prefabs, path, Prefab) as Prefab;
- const n = PoolManager.getNode(res) as Node;
- //设置父节点
- if (parent) {
- n.parent = parent;
- }
- //返回结果
- resolve(n);
- } catch (error) {
- console.error(`加载资源失败: ${path}`, error);
- reject(new Error(`加载资源失败:: ${path}`));
- }
- });
- }
- /**
- * 购买金币和钻石的动画
- * @param type
- * @param starNode
- * @param targetNode
- * @param count
- * @param radius
- * @param callback
- */
- public static async flyAnim(type: ITEM_TYPE, starNode: Node, targetNode: Node, count: number, radius: number, callback: Function) {
- let getPoint = (r, ox, oy, count) => {
- var point = []; //结果
- var radians = (Math.PI / 180) * Math.round(360 / count), //弧度
- i = 0;
- for (; i < count; i++) {
- var x = ox + r * Math.sin(radians * i),
- y = oy + r * Math.cos(radians * i);
- point.unshift(v2(x, y)); //为保持数据顺时针
- }
- return point;
- }
- let start = starNode.worldPosition;
- var array = getPoint(radius, start.x, start.y, count);
- var nodeArray = new Array();
- for (var i = 0; i < array.length; i++) {
- let ret: any = await bundleMgr.loadAsset(Constants.bundleName.prefabs, `items/${type}`, Prefab);
- var gold = PoolManager.getNode(ret);
- gold.setWorldScale(v3(0.7, 0.7, 0.7));
- gold.parent = uiMgr.getCurentSceneRoot();
- var randPos = v3(array[i].x + Utils.getRandomInt(0, 50), array[i].y + Utils.getRandomInt(0, 50), 1);
- gold.setWorldPosition(start);
- nodeArray.push({ gold, randPos });
- }
- var notPlay = false;
- let dstPos = targetNode.worldPosition.clone();
- for (let i = 0; i < nodeArray.length; i++) {
- let pos = nodeArray[i].randPos;
- let node = nodeArray[i].gold;
- nodeArray[i].gold.id = i;
- tween(node)
- .to(0.2, { worldPosition: pos })
- .delay(i * 0.03)
- .to(0.7, { worldPosition: v3(dstPos.x, dstPos.y, 1) })
- .call(() => {
- if(!notPlay) {
- notPlay = true;
- tween(targetNode)
- .to(0.1, { scale: v3(2, 2, 2) })
- .to(0.1, { scale: v3(1, 1, 1) })
- .call(() => {
- notPlay = false;
- }).start()
- }
- let idx: number = node["_id"];
- callback(idx == nodeArray.length - 1);
- PoolManager.putNode(node);
- })
- .start()
- }
- }
- /**
- * 加载特效
- * @param type 路径
- * @param worldPos 位置
- * @param parent 父节点
- * @param callback 回调函数
- */
- public static async loadEffect(type: EffectType,worldPos:Vec3,removeTime: number = 0, parent?: Node, callback?: Function){
- let res: Node = await ResUtil.loadRes(type,parent);
- if(worldPos){
- res.worldPosition = worldPos;
- }
- if(parent){
- res.getComponentsInChildren(ParticleSystem)
- .forEach(e => {
- e.play();
- });
- }
- if(removeTime > 0){
- res.getComponent(UIOpacity).scheduleOnce(function(){
- this.getComponentsInChildren(ParticleSystem)
- .forEach(e => {
- e.stop();
- });
- PoolManager.putNode(this);
- }.bind(res),removeTime,);
- }
- }
- /**
- * 播放骨骼动画
- * @param skeletal 骨骼动画
- * @param name 动画名称
- * @param time 播放时间 播放的时长会控制播放的速度快慢
- * @param cb 播放完成回调
- */
- public static playSkeletalAnim(skeletal: SkeletalAnimation,name: string,time: number = 0, cb: Function = null){
- if(!skeletal)return;
- //查找目标动画剪辑
- const clip = skeletal.clips.find(c => c.name == name);
- if (!clip) {
- Logger.error(`[ResUtil.playSkeletalAnim] 未找到名为 "${name}" 的动画剪辑`);
- return;
- }
- //核心逻辑:通过调整播放速度,使动画总时长等于传入的time
- //公式推导:原始时长 = 播放速度 × 实际播放时间 → 播放速度 = 原始时长 / 目标时长
- if(time > 0) {
- clip.speed = clip.duration / time;
- }else{//恢复默认速度(避免之前的修改影响后续播放)
- clip.speed = 1;
- }
- skeletal.play(name);
- if(cb){
- skeletal.scheduleOnce(cb,time);
- }
- }
- }
|