import { _decorator, Node, Label, EventTouch, Vec3, UITransform, tween} from 'cc'; import { BaseExp } from '../core/base/BaseExp'; import { autoBind } from '../extend/AutoBind'; import { Constants } from '../data/Constants'; import { userIns } from '../data/UserData'; import { Utils } from '../utils/Utils'; import { uiMgr } from '../core/manager/UIManager'; import { stateMgr } from '../core/manager/StateManager'; import { ITEM_TYPE, ResUtil } from '../utils/ResUtil'; import { PoolManager } from '../core/manager/PoolManager'; import { audioMgr } from '../core/manager/AudioManager'; import N from '../extend/Nodes'; const { ccclass, property } = _decorator; //扔飞镖 @ccclass('TossBoomerangUI') export class TossBoomerangUI extends BaseExp { @autoBind({ type: Node, tooltip: "靶面中的所有靶心" }) public content: Node; @autoBind({ type: Node, tooltip: "飞镖按钮" }) public shoot_btn: Node; @autoBind({ type: Label, tooltip: "金币文本" }) public gold_lable: Label; @autoBind({ type: Label, tooltip: "钻石文本" }) public diamond_lable: Label; @autoBind({ type: Label, tooltip: "飞镖个数文本" }) public boomerang_num_lable: Label; //是否正在扔飞镖 private isBoomeranging: boolean = false; start() { this.hasAnim = false; this.closeOnBlank = false; //注册动态变化值 stateMgr.registerUI(Constants.gold, this.gold_lable); stateMgr.registerUI(Constants.diamond, this.diamond_lable); this.content.children.forEach(child => { child.on(Node.EventType.TOUCH_END, this.startToss.bind(this), this); }) } public show(...args: any[]){ this.setBoomerang(); } /** * 设置飞镖个数 */ public setBoomerang(){ if(userIns.data.boomerang > 0){ this.boomerang_num_lable.string = userIns.data.boomerang; }else{ this.boomerang_num_lable.string = `Buy`; } } /** * 购买飞镖 */ public buyBoomerang(){ uiMgr.show(Constants.popUIs.buyBoomerangUI); } /** * 扔飞镖数据 */ public startToss(event:EventTouch = null,param:any = null){ if(this.isBoomeranging)return; if(userIns.data.boomerang <= 0){ this.buyBoomerang(); return; } let target_plate:Node = null; if(event){ target_plate = event.target; }else{ //获取靶心 let r:number = Utils.getRandomInt(0,this.content.children.length - 1); target_plate = this.content.children[r]; } audioMgr.playOneShot(Constants.audios.dart); this.isBoomeranging = true; userIns.data.boomerang -= 1; this.setBoomerang(); //总的权重 const totalWeight = userIns.boomerangTable.reduce((sum, item) => sum + item.weight, 0); //生成随机数(包含安全容错) const random = Math.min(Math.random() * totalWeight, totalWeight - 1e-6); let accumulatedWeight = 0; let selectedItem: any = null; //遍历查找命中的条目 for(const item of userIns.boomerangTable) { if (random < accumulatedWeight + item.weight) { selectedItem = item; break; } accumulatedWeight += item.weight; } if(!selectedItem) { selectedItem = userIns.boomerangTable[0]; } //加载飞镖预制体 ResUtil.loadRes(`items/tossBoomerang`).then((ret:Node)=>{ var boomerang:Node = PoolManager.getNode(ret,this.content); N(boomerang).order_n = 10000; //获取靶面尺寸(假设靶面锚点在中心) const targetSize = target_plate.getComponent(UITransform)?.contentSize; //生成随机角度(0-360度) const angle = Math.random() * Math.PI * 2; //根据权重计算偏移比例(权重越大,offsetRatio越小) 获取靶面中心的世界坐标 const targetWorldPos = target_plate.parent.getComponent(UITransform) .convertToWorldSpaceAR(target_plate.position); //转换为父节点坐标系下的坐标 const targetCenter = target_plate.parent.getComponent(UITransform) .convertToNodeSpaceAR(targetWorldPos); //生成基于靶心坐标的随机偏移(权重越大越靠近中心)平方曲线增强中心聚集 缩小最大偏移范围 const maxRadius = targetSize.width * 0.2; const offsetRatio = Math.pow(1 - (selectedItem.weight / totalWeight), 2); //最终落点计算(以靶心为基准) const targetPos = new Vec3( targetCenter.x + Math.cos(angle) * maxRadius * offsetRatio, targetCenter.y + Math.sin(angle) * maxRadius * offsetRatio, 0 ); const targetUITransform = target_plate.getComponent(UITransform); const radius = targetUITransform.contentSize.width / 2.0 * 1.5; // 半径取宽度的一半 const randomAngle = Math.random() * Math.PI * 2; // 生成0-360度的随机角度 //新起始点计算(靶面中心 + 半径 * 随机角度) const startPos = new Vec3( target_plate.position.x + radius * Math.cos(randomAngle), target_plate.position.y + radius * Math.sin(randomAngle), 0 ); //飞镖初始位置(从屏幕下方飞出) //let startPos: Vec3 = Utils.convertPosition(this.shoot_btn,this.target_plate.parent); boomerang.position = startPos.clone(); let middePos = Utils.randomUIPointGenerator(target_plate,startPos,200); let pos = Utils.calculateParabolaCenter(startPos,targetPos); let controlPoint = new Vec3(middePos.x + pos.x,middePos.y,middePos.z) //贝塞尔轨迹计算 const calculateTrajectory = (t: number, p0: Vec3, p1: Vec3, p2: Vec3) => { const u = 1 - t; return new Vec3( u*u*p0.x + 2*u*t*p1.x + t*t*p2.x, u*u*p0.y + 2*u*t*p1.y + t*t*p2.y, 0 ); }; tween(boomerang) .to(0.8, {}, { onUpdate: (_, ratio: number) => {//计算当前帧位置 const currentPos = calculateTrajectory( ratio, startPos, controlPoint, targetPos ); //更新位置和旋转 boomerang.position = currentPos; boomerang.angle = Utils.getAngle(currentPos,targetPos)-90; } }) .call(()=>{ boomerang.position = targetPos; }) .start(); this.scheduleOnce(() => { if(boomerang.parent) { PoolManager.putNode(boomerang); this.handleReward(selectedItem); this.isBoomeranging = false; } },1) }); } /** * 奖励处理方法 * @param item */ private handleReward(item: any) { const [type, amount] = item.quantity.split('_'); audioMgr.playOneShot(Constants.audios.reward); switch(type) { case '1001': {//金币 userIns.data.gold += parseInt(amount); uiMgr.show(Constants.popUIs.obtainUI,[item.id,()=>{ ResUtil.flyAnim(ITEM_TYPE.Coin, this.shoot_btn, this.gold_lable.node, 5, 50,(b) => {}); }]); } break; case '1002':{ //钻石 userIns.data.diamond += parseInt(amount); uiMgr.show(Constants.popUIs.obtainUI,[item.id,()=>{ ResUtil.flyAnim(ITEM_TYPE.Diamond, this.shoot_btn, this.diamond_lable.node, 5, 50,(b) => {}); }]); } break; default: {//武器解锁 获得显示武器栏 uiMgr.show(Constants.popUIs.obtainUI,[item.id,(isCover: boolean)=>{ //是否拥有这把需要转化 if(isCover){ const [good_id,amount] = item.value.split('_'); const sData:any = userIns.itemTable.find(e=>e.id == good_id); if(sData.id == 1001){//金币 userIns.data.gold += parseInt(amount); ResUtil.flyAnim(ITEM_TYPE.Coin, this.shoot_btn, this.gold_lable.node, 5, 50,(b) => {}); }else if(sData.id == 1002){//钻石 userIns.data.diamond += parseInt(amount); ResUtil.flyAnim(ITEM_TYPE.Diamond, this.shoot_btn, this.diamond_lable.node, 5, 50,(b) => {}); } } }]); } break; } } /** * 按钮点击事件 * @param event 事件 * @param param 参数 */ override onBtnClicked(event:EventTouch, param:any) { super.onBtnClicked(event,param); let btnName = event.target.name; if(btnName === 'shoot_btn'){//扔飞镖的操作 this.startToss(); } } }