TossBoomerangUI.ts 8.8 KB

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