TossBoomerangUI.ts 8.2 KB

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