TossBoomerangUI.ts 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. import { _decorator, Node, Label, EventTouch, Vec3, UITransform, Prefab, Size, tween, easing} from 'cc';
  2. import { BaseExp } from '../core/base/BaseExp';
  3. import List from '../third/List';
  4. import { autoBind } from '../extend/AutoBind';
  5. import { Constants } from '../data/Constants';
  6. import { stateMgr } from '../core/manager/StateManager';
  7. import { userIns } from '../data/UserData';
  8. import { ITEM_TYPE, ResUtil } from '../utils/ResUtil';
  9. import MsgHints from '../utils/MsgHints';
  10. import { PoolManager } from '../core/manager/PoolManager';
  11. import { bundleMgr } from '../core/manager/BundleManager';
  12. import { Utils } from '../utils/Utils';
  13. import { uiMgr } from '../core/manager/UIManager';
  14. const { ccclass, property } = _decorator;
  15. //扔飞镖
  16. @ccclass('TossBoomerangUI')
  17. export class TossBoomerangUI extends BaseExp {
  18. @autoBind({ type: Node, tooltip: "靶面" })
  19. public target_plate: Node;
  20. @autoBind({ type: Node, tooltip: "飞镖按钮" })
  21. public shoot_btn: Node;
  22. @autoBind({ type: Label, tooltip: "金币文本" })
  23. public gold_lable: Label;
  24. @autoBind({ type: Label, tooltip: "钻石文本" })
  25. public diamond_lable: Label;
  26. @autoBind({ type: Label, tooltip: "飞镖个数文本" })
  27. public boomerang_num_lable: Label;
  28. //是否正在扔飞镖
  29. private isBoomeranging: boolean = false;
  30. start() {
  31. this.hasAnim = false;
  32. this.closeOnBlank = false;
  33. //注册动态变化值
  34. stateMgr.registerUI(Constants.gold, this.gold_lable);
  35. stateMgr.registerUI(Constants.diamond, this.diamond_lable);
  36. }
  37. public show(...args: any[]){
  38. this.setBoomerang();
  39. }
  40. /**
  41. * 设置飞镖个数
  42. */
  43. public setBoomerang(){
  44. if(userIns.data.boomerang > 0){
  45. this.boomerang_num_lable.string = userIns.data.boomerang;
  46. }else{
  47. this.boomerang_num_lable.string = `Buy`;
  48. }
  49. }
  50. /**
  51. * 购买飞镖
  52. */
  53. public buyBoomerang(){
  54. uiMgr.show(Constants.popUIs.buyBoomerangUI);
  55. }
  56. /**
  57. * 扔飞镖数据
  58. */
  59. public async startToss(){
  60. if(this.isBoomeranging)return;
  61. if(userIns.data.boomerang <= 0){
  62. this.buyBoomerang();
  63. return;
  64. }
  65. this.isBoomeranging = true;
  66. userIns.data.boomerang -= 1;
  67. this.setBoomerang();
  68. //总的权重
  69. const totalWeight = userIns.boomerangTable.reduce((sum, item) => sum + item.weight, 0);
  70. //生成随机数(包含安全容错)
  71. const random = Math.min(Math.random() * totalWeight, totalWeight - 1e-6);
  72. let accumulatedWeight = 0;
  73. let selectedItem: any = null;
  74. //遍历查找命中的条目
  75. for(const item of userIns.boomerangTable) {
  76. if (random < accumulatedWeight + item.weight) {
  77. selectedItem = item;
  78. break;
  79. }
  80. accumulatedWeight += item.weight;
  81. }
  82. if(!selectedItem) {
  83. selectedItem = userIns.boomerangTable[0];
  84. }
  85. //加载飞镖预制体
  86. const ret: any = await bundleMgr.loadAsset(Constants.bundleName.prefabs, `items/tossBoomerang`, Prefab);
  87. var boomerang:Node = PoolManager.getNode(ret,this.target_plate.parent);
  88. //获取靶面尺寸(假设靶面锚点在中心)
  89. const targetSize = this.target_plate.getComponent(UITransform)?.contentSize;
  90. //生成随机角度(0-360度)
  91. const angle = Math.random() * Math.PI * 2;
  92. //根据权重计算偏移比例(权重越大,offsetRatio越小) 获取靶面中心的世界坐标
  93. const targetWorldPos = this.target_plate.parent.getComponent(UITransform)
  94. .convertToWorldSpaceAR(this.target_plate.position);
  95. //转换为父节点坐标系下的坐标
  96. const targetCenter = this.target_plate.parent.getComponent(UITransform)
  97. .convertToNodeSpaceAR(targetWorldPos);
  98. //生成基于靶心坐标的随机偏移(权重越大越靠近中心)平方曲线增强中心聚集 缩小最大偏移范围
  99. const maxRadius = targetSize.width * 0.2;
  100. const offsetRatio = Math.pow(1 - (selectedItem.weight / totalWeight), 2);
  101. //最终落点计算(以靶心为基准)
  102. const targetPos = new Vec3(
  103. targetCenter.x + Math.cos(angle) * maxRadius * offsetRatio,
  104. targetCenter.y + Math.sin(angle) * maxRadius * offsetRatio,
  105. 0
  106. );
  107. const targetUITransform = this.target_plate.getComponent(UITransform);
  108. const radius = targetUITransform.contentSize.width / 2.0 * 1.5; // 半径取宽度的一半
  109. const randomAngle = Math.random() * Math.PI * 2; // 生成0-360度的随机角度
  110. //新起始点计算(靶面中心 + 半径 * 随机角度)
  111. const startPos = new Vec3(
  112. this.target_plate.position.x + radius * Math.cos(randomAngle),
  113. this.target_plate.position.y + radius * Math.sin(randomAngle),
  114. 0
  115. );
  116. //飞镖初始位置(从屏幕下方飞出)
  117. //let startPos: Vec3 = Utils.convertPosition(this.shoot_btn,this.target_plate.parent);
  118. boomerang.position = startPos.clone();
  119. let middePos = Utils.randomUIPointGenerator(this.target_plate,startPos,200);
  120. let pos = Utils.calculateParabolaCenter(startPos,targetPos);
  121. let controlPoint = new Vec3(middePos.x + pos.x,middePos.y,middePos.z)
  122. //贝塞尔轨迹计算
  123. const calculateTrajectory = (t: number, p0: Vec3, p1: Vec3, p2: Vec3) => {
  124. const u = 1 - t;
  125. return new Vec3(
  126. u*u*p0.x + 2*u*t*p1.x + t*t*p2.x,
  127. u*u*p0.y + 2*u*t*p1.y + t*t*p2.y,
  128. 0
  129. );
  130. };
  131. tween(boomerang)
  132. .to(0.4, {}, {
  133. onUpdate: (_, ratio: number) => {//计算当前帧位置
  134. const currentPos = calculateTrajectory(
  135. ratio,
  136. startPos,
  137. controlPoint,
  138. targetPos
  139. );
  140. //更新位置和旋转
  141. boomerang.position = currentPos;
  142. boomerang.angle = Utils.getAngle(currentPos,targetPos)-90;
  143. },
  144. onComplete: () => {
  145. if(boomerang.parent) {
  146. setTimeout(() => {
  147. PoolManager.putNode(boomerang);
  148. this.handleReward(selectedItem);
  149. this.isBoomeranging = false;
  150. },0.2);
  151. }
  152. }
  153. })
  154. .start();
  155. }
  156. /**
  157. * 奖励处理方法
  158. * @param item
  159. */
  160. private handleReward(item: any) {
  161. const [type, amount] = item.quantity.split('_');
  162. switch(type) {
  163. case '1001': {//金币
  164. userIns.data.gold += parseInt(amount);
  165. uiMgr.show(Constants.popUIs.obtainUI,[item.id,()=>{
  166. ResUtil.flyAnim(ITEM_TYPE.Coin, this.shoot_btn, this.gold_lable.node, 5, 50,(b) => {});
  167. }]);
  168. }
  169. break;
  170. case '1002':{ //钻石
  171. userIns.data.diamond += parseInt(amount);
  172. uiMgr.show(Constants.popUIs.obtainUI,[item.id,()=>{
  173. ResUtil.flyAnim(ITEM_TYPE.Diamond, this.shoot_btn, this.diamond_lable.node, 5, 50,(b) => {});
  174. }]);
  175. }
  176. break;
  177. default: {//武器解锁 获得显示武器栏
  178. const [gun_id,num] = item.quantity.split('_');
  179. userIns.unlockGun(gun_id);
  180. uiMgr.show(Constants.popUIs.obtainUI,[item.id,()=>{}]);
  181. }
  182. break;
  183. }
  184. }
  185. /**
  186. * 按钮点击事件
  187. * @param event 事件
  188. * @param param 参数
  189. */
  190. override onBtnClicked(event:EventTouch, param:any) {
  191. super.onBtnClicked(event,param);
  192. let btnName = event.target.name;
  193. if(btnName === 'shoot_btn'){//扔飞镖的操作
  194. this.startToss();
  195. }
  196. }
  197. }