大侠
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

646 lines
24 KiB

import { _decorator, assetManager, builtinResMgr, Button, Component, error, EventHandler, EventTouch, ImageAsset, instantiate, isValid, IVec2Like, Label, Material, Node, RichText, screen, sp, Sprite, SpriteAtlas, SpriteFrame, Texture2D, Tween, tween, UIOpacity, UITransform, v3, Vec2, Vec3, view, warn, Widget } from "cc";
import StringUtils from "../ExUtils/StringUtils";
import ResLoad from "../ResUrl/ResLoad";
import Language from "../Language";
import { ESounds } from "../MesEvent/ESounds";
import { RichTextTypeData, RichText_Color, RichText_Size, RichText_Outline, RichText_b, RichText_i, RichText_u, IData, SoundType } from "../const/GameInterface";
import { Global } from "../Global";
import InstanceT from "../ExUtils/InstanceT";
import GDUtils from "./GDUtils";
import { EResUrls } from "../ResUrl/EResUrls";
@InstanceT.DecorateInstance()
class CNodeUtils {
/** 单例 */
public static readonly instance: CNodeUtils;
//#region 节点查找
getNodeEx(target: Node | Component, name: string, active: boolean) {
let tNode: Node = target instanceof Component ? target.node : target;
let arr: Node[] = [];
tNode.children.forEach(n => {
if (n.name == name && (!active || n.activeInHierarchy)) arr.push(n);
arr = arr.concat(this.getNodeEx(n, name, active));
})
return arr;
}
/**根据节点路径 寻找节点 */
getNode(target: Node | Component, ...url: any[]) {
let tNode: Node = target instanceof Component ? target.node : target;
for (let i in url) {
tNode = tNode.getChildByName(`${url[i]}`);
if (!tNode) {
ConsoleEx.Error(`查找错误,${url[i]}`)
return null;
}
}
return tNode;
}
//#endregion
//#region 节点组件查询
/**获取组件 不存在那么就添加 */
getOrAddComponent<T extends Component>(node: Node | Component, cla): any {
let temp = node.getComponent(cla);
if (!temp) {
temp = node.addComponent(cla);
}
return temp as T;
}
//#endregion
//#region 节点组件属性设置
/**设置节点透明度 */
public setOpacity(node: Node | Component, Opacity: number) {
const tClassPoxy: UIOpacity = this.getOrAddComponent(node, UIOpacity);
tClassPoxy.opacity = Opacity;
}
/**设置节点缩放 */
setScale(target: Node | Component, scale: number) {
const tNode: Node = target instanceof Component ? target.node : target;
tNode.setScale(scale, scale, scale);
}
/**设置节点显示 */
setActive(target: Node | Component, active: boolean, ADFlag: number = 0) {
const tNode: Node = target instanceof Component ? target.node : target;
if (tNode.active != active) {
tNode.active = active;
//ADFlag && active && TDAnalyticsMgr.ins.TrySendtrack(GEData.GEeventName.ad_show, { ad_id: ADFlag });
}
}
/**设置按钮是否禁用 */
setBtnInteractable(btn: Button, _interactable: boolean) {
btn.interactable = _interactable;
const tempSprite = btn.getComponent(Sprite)
if (tempSprite) {
tempSprite.grayscale = !_interactable;
}
}
/**缓动图片范围 */
TweenSpriteRange(node: Node | Sprite, fillRange: number, time: number = 0.2) {
const tSprite: Sprite = node instanceof Node ? node.getComponent(Sprite) : node;
if (time > 0) {
tween(tSprite).to(time, { fillRange: fillRange }).start();
} else {
tSprite.fillRange = fillRange;
}
}
/**设置数组节点 全部是否置灰 */
public setAllGray(node: Node[], isGray: boolean, exclude: Node[] = []) {
node.forEach(child => {
this.setChiladAllGray(child, isGray, exclude);
})
}
/**设置节点以及子节点 全部是否置灰 */
public setChiladAllGray(node: Node | Sprite, isGray: boolean, exclude: Node[] = []) {
const tNode: Node = node instanceof Sprite ? node.node : node;
const children = tNode.children;
children.forEach(child => {
if (!exclude.includes(child)) {
this.setChiladAllGray(child, isGray, exclude);
}
})
if (!exclude.includes(tNode)) {
this.setGray(tNode, isGray);
}
}
/**设置节点是否置灰 */
public setGray(node: Node | Sprite, isGray: boolean) {
const tSprite: Sprite = node instanceof Node ? node.getComponent(Sprite) : node;
if (tSprite) {
tSprite.grayscale = isGray;
} else {
const label = node.getComponent(Label);
if (label) {
if (!label["Org"]) { label["Org"] = label.material; }
if (isGray) {
label.material = builtinResMgr.get<Material>('ui-sprite-gray-material')
} else {
label.material = label["Org"];
}
}
}
}
/**
* 更新label文字
* **/
public switchText(node: Node | Label, str: any, forceUpdate = false, isNum = false) {
if (!isValid(node)) {
warn('要更新的节点为空,str=' + str);
return;
}
const label: Label = node instanceof Node ? node.getComponent(Label) : node;
if (!label) { return; }
label.node.attr({ original: str });
label.string = isNum ? str : Language.getValue(str);
// if (label.useSystemFont) {
// label.font = AppLanguage.getFont()
// }
forceUpdate && label.updateRenderData();
}
/**
* 更新RichText文字
* **/
public switchRichText(node: Node | RichText, str: any, forceUpdate = false, isNum = false) {
if (!isValid(node)) {
warn('要更新的节点为空,str=' + str);
return;
}
const label: RichText = node instanceof Node ? node.getComponent(RichText) : node;
if (!label) { return; }
label.node.attr({ original: str });
label.string = isNum ? str : Language.getValue(str);
if (label.useSystemFont) {
//@ts-ignore
//label.font = AppLanguage.getFont()
}
forceUpdate && label["_updateRichTextStatus"]();
}
/**
* 更新label文字,文字颜色与品质相关
* **/
public switchTextByQuality(node: Node | Label, str: any, quality: number, forceUpdate = false, isNum = false) {
if (!isValid(node)) {
warn('要更新的节点为空,str=' + str);
return;
}
const label: Label = node instanceof Node ? node.getComponent(Label) : node;
if (!label) { return; }
label.color = label.color.fromHEX(GDUtils.GetQualityColorHEX(quality));
label.node.attr({ original: str });
label.string = isNum ? str : Language.getValue(str);
forceUpdate && label.updateRenderData();
}
// /**
// * 更新label文字,文字颜色与品质相关
// * **/
// public switchRichTextByQuality(node: Node | RichText, str: any, quality: number, forceUpdate = false, isNum = false) {
// if (!isValid(node)) {
// warn('要更新的节点为空,str=' + str);
// return;
// }
// const label: RichText = node instanceof Node ? node.getComponent(RichText) : node;
// if (!label) { return; }
// label.node.attr({ original: str });
// label.string = isNum ? str : Language.getValue(str);
// if (label.useSystemFont) {
// //@ts-ignore
// //label.font = AppLanguage.getFont()
// }
// forceUpdate && label["_updateRichTextStatus"]();
// }
/**数字文本缓动 */
TweenNumText(target: Node | Label, toNum: number, isConvert: boolean = true) {
if (!isValid(target)) {
warn('要更新的节点为空');
return;
}
const tNode: Node = target instanceof Node ? target : target.node;
Tween.stopAllByTarget(tNode);
if (tNode["CurNum"] != 0 && !tNode["CurNum"]) {
tNode["TargetNum"] = toNum;
tNode["CurNum"] = toNum;
}
tNode["CurNum"] = tNode["TargetNum"];
tNode["TargetNum"] = toNum;
let offsetNum = tNode["TargetNum"] - tNode["CurNum"];
if (offsetNum != 0) {
tween(tNode).update(0.3, (node: Node, ratio: number) => {
let tNum = tNode["CurNum"] + offsetNum * ratio;
this.switchText(node, isConvert ? StringUtils.HurtConvert(Math.floor(tNum)) : Math.floor(tNum));
}).start();
} else {
this.switchText(target, isConvert ? StringUtils.HurtConvert(Math.floor(tNode["CurNum"])) : Math.floor(tNode["CurNum"]));
}
}
addBtnLister(node: Node, callback: Function, target?: any, audio: SoundType = ESounds.So_click) {
node.off('click');
node.on('click', () => {
if (audio == ESounds.So_click) {
Global.Audio.playEffect(ESounds.So_click);
} else {
Global.Audio.playEffect(audio);
}
callback.call(target, node)
})
}
addEventHandler<T extends EventHandler>(node: Node, calssname = '', callFunc = '', customData = '') {
let event_handler = new Component.EventHandler();
event_handler.target = node;
event_handler.component = calssname;
event_handler.handler = callFunc;
event_handler.customEventData = customData;
return event_handler;
}
/**
* 添加button事件
* @param node
* @param callback
* @param target
* @param isAudio
*/
addBtnComponent(node: Node, callback: Function, target?: any, isAudio: SoundType = ESounds.So_click) {
if (!node.getComponent(Button)) {
let btn: Button = node.addComponent(Button);
btn.transition = Button.Transition.SCALE;
btn.duration = 0.1;
btn.target = node;
btn.zoomScale = 0.93;
}
this.addBtnLister(node, callback, target, isAudio);
}
addNoScaleBtnComponent(node: Node, callback: Function, target?: any, isAudio: SoundType = ESounds.So_click) {
if (!node.getComponent(Button)) {
let btn: Button = node.addComponent(Button);
btn.transition = Button.Transition.NONE;
btn.duration = 0.1;
btn.target = node;
btn.zoomScale = 0.93;
}
this.addBtnLister(node, callback, target, isAudio);
}
ClearBtnCommontEffect(node: Node) {
node.off('click');
}
/**添加按钮长按 */
addBtnLongpress(node: Node | Button, downCallBack: Function, upCallback: Function) {
let tNode = null;
if (node instanceof Node) {
tNode = node;
} else {
tNode = node.node;
}
tNode.off(Node.EventType.TOUCH_START);
tNode.off(Node.EventType.TOUCH_MOVE);
tNode.off(Node.EventType.TOUCH_END);
tNode.off(Node.EventType.TOUCH_CANCEL);
let isDown = false;
tNode.on(Node.EventType.TOUCH_START, (event: EventTouch) => {
isDown = true;
downCallBack && downCallBack(event);
}, this)
tNode.on(Node.EventType.TOUCH_END, (event: EventTouch) => {
isDown = false;
upCallback && upCallback(event);
}, this)
tNode.on(Node.EventType.TOUCH_CANCEL, (event: EventTouch) => {
isDown = false;
upCallback && upCallback(event);
}, this)
}
/**
*
* @param node
* @param callback
* @param target
*/
addMoveComponent(node: Node, callback: Function, target?: any, TiggerFuntion?: Function) {
node.off(Node.EventType.TOUCH_START);
node.off(Node.EventType.TOUCH_MOVE);
node.off(Node.EventType.TOUCH_END);
let isMove = false;
node.on(Node.EventType.TOUCH_START, (event: EventTouch) => {
isMove = false;
TiggerFuntion && TiggerFuntion(true);
}, this)
node.on(Node.EventType.TOUCH_MOVE, (event) => {
let touch = event.touch;
let deltaMove = touch.getLocation().sub(touch.getStartLocation());
if (deltaMove.mag() > 10) {
isMove = true;
}
}, this)
node.on(Node.EventType.TOUCH_END, (event) => {
TiggerFuntion && TiggerFuntion(false);
!isMove && callback.call(target, node, event);
}, this)
}
/**富文本处理 列表*/
ContainRichtTextByList(value: string, list: RichTextTypeData[]): string {
let tVa: string = value;
for (let i = 0; i < list.length; i++) {
const tData = list[i];
tVa = this.ContainRichtText(tVa, tData);
}
return tVa;
}
/**富文本处理 */
ContainRichtText(value: string, data: RichTextTypeData): string {
if (data instanceof RichText_Color) {
return `<color=${data.color}>${value}</color>`;
} else if (data instanceof RichText_Size) {
return `<size=${data.size}>${value}</size>`
} else if (data instanceof RichText_Outline) {
return `<outline color=${data.oColor} width=${data.oSize}>${value}</outline>`
} else if (data instanceof RichText_b) {
return `<b>${value}</b>`;
} else if (data instanceof RichText_i) {
return `<i>${value}c</i>`;
}
else if (data instanceof RichText_u) {
return `<u>${value}</u>`;
}
}
/**添加富文本点击事件 */
AddRichTextClick(tagetNode: Node | RichText, CallBack: Function, functionName?: string) {
let tRichText: RichText = null;
if (tagetNode instanceof RichText) {
tRichText = tagetNode;
} else {
tRichText = tagetNode.getComponent(RichText)
}
if (!tRichText) {
error("添加富文本点击失败 该节点没有富文本组件");
return;
}
functionName = functionName ? functionName : CallBack.name;
functionName = functionName.replace("bound", "");
functionName = functionName.replace(" ", "");
tRichText[functionName] = CallBack;
}
SwitchSpine(node: Node | sp.Skeleton, file: string, bundleName: string,
animation: string | string[] = 'idle', isLoop: boolean = true, skin: string = 'default', back: Function = null) {
const spine: sp.Skeleton = node instanceof Node ? node.getComponent(sp.Skeleton) : node;
// const url = EResUrls.Spine(file);
ResLoad.Ins.Spine(file, bundleName, (err, skeletonData: sp.SkeletonData) => {
if (!isValid(spine)) {
warn('spine 被移除')
return;
}
if (err || !skeletonData) {
error('加载资源出错: ' + file);
return;
}
spine.skeletonData = skeletonData;
if (Array.isArray(animation)) {
spine.setAnimation(0, animation[0], animation.length == 1);
animation.length > 1 && spine.addAnimation(0, animation[1], isLoop);
} else {
spine.setAnimation(0, animation, isLoop);
}
skin && spine.setSkin(skin);
back && back();
})
}
SwitchTexture(file: string, node: Node | Sprite, bundleName: string, width: number = 0, back: Function = null) {
const spr: Sprite = node instanceof Node ? node.getComponent(Sprite) : node;
if (!bundleName) { ConsoleEx.Error("路径错误",) }
ResLoad.Ins.Sprite(file, bundleName, (err, spriteFrame: SpriteFrame) => {
if (!isValid(spr)) {
warn('Sprite 被移除')
return;
}
if (err || !spriteFrame) {
error('加载资源出错: ' + file);
return;
}
if (spr["MY_ADD"] && spr.spriteFrame) {
spr.spriteFrame.decRef()
}
spr.spriteFrame = spriteFrame;
if (!spr.spriteFrame) {
error('加载资源出错: ' + file);
}
spr.spriteFrame.addRef();
spr["MY_ADD"] = 1;
width && this.scaleNode(spr.node, width);
back && back();
})
}
SwitchTextureByAutoAtlas(file: string, atlas: string, node: Node | Sprite, bundleName: string, width: number = 0, back: Function = null) {
const spr: Sprite = node instanceof Node ? node.getComponent(Sprite) : node;
ResLoad.Ins.AutoAtlas(atlas, bundleName, (err, spriteFrame) => {
if (!isValid(spr)) {
warn('Sprite 被移除')
return;
}
if (err) {
error('加载资源出错: ' + atlas);
return;
}
file = file.replace("Texture/", "");
spr.spriteFrame = spriteFrame.getSpriteFrame(file);
width && this.scaleNode(spr.node, width);
back && back();
})
}
SwitchTextureByData(node: Node | Sprite, data: IData, width: number = 0, back: Function = null) {
const url = GDUtils.getUrl(data);
const bundle = GDUtils.getUBundle(data.type, data.id);
const Atlas = GDUtils.getAtlas(data.type);
if (Atlas) {
this.SwitchTextureByAutoAtlas(url, Atlas, node, bundle, width, back)
} else {
this.SwitchTexture(url, node, bundle, width, back);
}
}
/**加载远程图片 */
SwitchRomateTexture(file: string, node: Node | Sprite, width: number = 0, back: Function = null) {
if (!file.includes("http")) {
back && back();
ConsoleEx.Error("请检查链接:", file)
return;
}
let isHaveExt: boolean = file.includes(".png") || file.includes(".jpg");
const spr: Sprite = node instanceof Node ? node.getComponent(Sprite) : node;
let lamuda = (err, res: ImageAsset) => {
if (!isValid(spr)) {
warn('Sprite 被移除')
return;
}
if (err) {
error('加载资源出错: ' + file);
return;
}
const spriteFrame = new SpriteFrame();
const texture = new Texture2D();
texture.image = res;
spriteFrame.texture = texture;
spr.spriteFrame = spriteFrame;
width && this.scaleNode(spr.node, width);
back && back();
}
if (isHaveExt) {
assetManager.loadRemote<ImageAsset>(file, lamuda)
} else {
assetManager.loadRemote<ImageAsset>(file, { ext: '.png' }, lamuda)
}
}
scaleNode(target: Node | Component, width: number, height = 0) {
const tNode = target instanceof Node ? target : target.node;
if (isValid(tNode)) {
if (!height) {
height = width;
}
const wScale = width / tNode.getComponent(UITransform).width;
const hScale = height / tNode.getComponent(UITransform).height;
const sc = Math.min(wScale, hScale);
tNode.setScale(v3(sc, sc, sc))
}
}
scaleNodeW(target: Node | Component, width: number) {
const tNode = target instanceof Node ? target : target.node;
if (isValid(tNode)) {
const wScale = width / tNode.getComponent(UITransform).width;
tNode.setScale(v3(wScale, wScale, wScale))
}
}
scaleNodeH(target: Node | Component, height) {
const tNode = target instanceof Node ? target : target.node;
if (isValid(tNode)) {
const wScale = height / tNode.getComponent(UITransform).height;
tNode.setScale(v3(wScale, wScale, wScale))
}
}
/**
* 二阶贝塞尔曲线 运动
* @param target
* @param {number} duration
* @param {} c1 起点坐标
* @param {} c2 控制点
* @param {Vec3} to 终点坐标
* @param opts
* @returns {any}
*/
bezierTo(target: Node, duration: number, c1: Vec2, c2: Vec2, to: Vec3, opts: any, Callback?: Function) {
opts = opts || Object.create(null);
/**
* @desc 二阶贝塞尔
* @param {number} t 当前百分比
* @param {} p1 起点坐标
* @param {} cp 控制点
* @param {} p2 终点坐标
* @returns {any}
*/
let twoBezier = (t: number, p1: Vec2, cp: Vec2, p2: Vec3) => {
let x = (1 - t) * (1 - t) * p1.x + 2 * t * (1 - t) * cp.x + t * t * p2.x;
let y = (1 - t) * (1 - t) * p1.y + 2 * t * (1 - t) * cp.y + t * t * p2.y;
return v3(x, y, 0);
};
opts.onUpdate = (arg: Vec3, ratio: number) => {
target.setPosition(twoBezier(ratio, c1, c2, to))
Callback && Callback();
};
return tween(target).to(duration, {}, opts);
}
/**
* 添加button事件
* @param node
* @param callback
* @param target
* @param isAudio
*/
addNodeTounch(node: Node, onMouseUp: Function, target?: any, onMouseEnter?: Function, onMouseMove?: Function, onMouseLeave?: Function, OnMoveDir?: Function) {
this.clearNodeTounch(node);
let isMove: boolean = false;
node.on(Node.EventType.TOUCH_START, (event: EventTouch) => {
onMouseEnter && onMouseEnter(event);
}, target);
node.on(Node.EventType.TOUCH_MOVE, (event: EventTouch) => {
let tempX = event.getStartLocation().x - event.getLocationX();
let tempY = event.getStartLocation().y - event.getLocationY();
let dirX = Math.abs(tempX);
let dirY = Math.abs(tempY);
if (dirX > 14 || dirY > 14) {
isMove = true;
OnMoveDir && OnMoveDir(event, tempX, tempY);
}
onMouseMove && onMouseMove(event);
}, target);
node.on(Node.EventType.TOUCH_END, (event) => {
onMouseUp && !isMove && onMouseUp(event);
isMove = false;
onMouseLeave && onMouseLeave(event);
}, target);
onMouseLeave && node.on(Node.EventType.TOUCH_CANCEL, onMouseLeave, target);
}
clearNodeTounch(node: Node) {
node.off(Node.EventType.TOUCH_START);
node.off(Node.EventType.TOUCH_MOVE);
node.off(Node.EventType.TOUCH_END);
node.off(Node.EventType.TOUCH_CANCEL);
}
//#endregion
setADWaitShow(btn_Reward: Node | Component) {
if (!window["tt"]) {
// this.SetNodeActive(btn_Reward, true)
// setTimeout(() => {
// if (isValid(btn_Reward)) {
// this.SetNodeActive(btn_Reward, true)
// }
// }, 1000)
}
}
GetPosRange(pos: Vec3, org: Node, Traget: Node, offset: IVec2Like) {
let tNode: Node = Traget instanceof Component ? Traget.node : Traget;
let tY = pos.y - offset.y - org.getComponent(UITransform).height / 2;
if (tY < (-tNode.getComponent(UITransform).height / 2)) {
tY = pos.y + offset.y;
} else {
tY = pos.y - offset.y;
}
let tX = pos.x;
tX = Math.max(-(tNode.getComponent(UITransform).width / 2 - org.getComponent(UITransform).width / 2) + 20, tX);
tX = Math.min(tNode.getComponent(UITransform).width / 2 - org.getComponent(UITransform).width / 2 - 20, tX);
return new Vec3(tX, tY, 0)
}
GetNodeInScreenRect(node: Node): { left: number, top: number, width: number, height: number } {
const rect = node.getComponent(UITransform)!.getBoundingBoxToWorld();
const dpi = screen.devicePixelRatio;
const scaleX = view.getScaleX();
const scaleY = view.getScaleY();
const left = (rect.x * scaleX) / dpi;
const top = (screen.windowSize.height - (rect.y + rect.height) * scaleY) / dpi;
const width = (rect.width * scaleX) / dpi;
const height = (rect.height * scaleY) / dpi;
return { left, top, width, height };
}
}
export default CNodeUtils.instance;
window["CNodeUtils"] = CNodeUtils.instance;