TossBoomerangUI.ts 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import { _decorator, Node, Label, EventTouch, Vec3, UITransform, 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 { audioMgr } from '../core/manager/AudioManager';
  12. import N from '../extend/Nodes';
  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 - 1);
  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. N(boomerang).order_n = 10000;
  100. //获取靶面尺寸(假设靶面锚点在中心)
  101. const targetSize = target_plate.getComponent(UITransform)?.contentSize;
  102. //生成随机角度(0-360度)
  103. const angle = Math.random() * Math.PI * 2;
  104. //根据权重计算偏移比例(权重越大,offsetRatio越小) 获取靶面中心的世界坐标
  105. const targetWorldPos = target_plate.parent.getComponent(UITransform)
  106. .convertToWorldSpaceAR(target_plate.position);
  107. //转换为父节点坐标系下的坐标
  108. const targetCenter = target_plate.parent.getComponent(UITransform)
  109. .convertToNodeSpaceAR(targetWorldPos);
  110. //生成基于靶心坐标的随机偏移(权重越大越靠近中心)平方曲线增强中心聚集 缩小最大偏移范围
  111. const maxRadius = targetSize.width * 0.2;
  112. const offsetRatio = Math.pow(1 - (selectedItem.weight / totalWeight), 2);
  113. //最终落点计算(以靶心为基准)
  114. const targetPos = new Vec3(
  115. targetCenter.x + Math.cos(angle) * maxRadius * offsetRatio,
  116. targetCenter.y + Math.sin(angle) * maxRadius * offsetRatio,
  117. 0
  118. );
  119. const targetUITransform = target_plate.getComponent(UITransform);
  120. const radius = targetUITransform.contentSize.width / 2.0 * 1.5; // 半径取宽度的一半
  121. const randomAngle = Math.random() * Math.PI * 2; // 生成0-360度的随机角度
  122. //新起始点计算(靶面中心 + 半径 * 随机角度)
  123. const startPos = new Vec3(
  124. target_plate.position.x + radius * Math.cos(randomAngle),
  125. target_plate.position.y + radius * Math.sin(randomAngle),
  126. 0
  127. );
  128. //飞镖初始位置(从屏幕下方飞出)
  129. //let startPos: Vec3 = Utils.convertPosition(this.shoot_btn,this.target_plate.parent);
  130. boomerang.position = startPos.clone();
  131. let middePos = Utils.randomUIPointGenerator(target_plate,startPos,200);
  132. let pos = Utils.calculateParabolaCenter(startPos,targetPos);
  133. let controlPoint = new Vec3(middePos.x + pos.x,middePos.y,middePos.z)
  134. //贝塞尔轨迹计算
  135. const calculateTrajectory = (t: number, p0: Vec3, p1: Vec3, p2: Vec3) => {
  136. const u = 1 - t;
  137. return new Vec3(
  138. u*u*p0.x + 2*u*t*p1.x + t*t*p2.x,
  139. u*u*p0.y + 2*u*t*p1.y + t*t*p2.y,
  140. 0
  141. );
  142. };
  143. tween(boomerang)
  144. .to(0.8, {}, {
  145. onUpdate: (_, ratio: number) => {//计算当前帧位置
  146. const currentPos = calculateTrajectory(
  147. ratio,
  148. startPos,
  149. controlPoint,
  150. targetPos
  151. );
  152. //更新位置和旋转
  153. boomerang.position = currentPos;
  154. boomerang.angle = Utils.getAngle(currentPos,targetPos)-90;
  155. }
  156. })
  157. .call(()=>{
  158. boomerang.position = targetPos;
  159. })
  160. .start();
  161. this.scheduleOnce(() => {
  162. if(boomerang.parent) {
  163. PoolManager.putNode(boomerang);
  164. this.handleReward(selectedItem);
  165. this.isBoomeranging = false;
  166. }
  167. },1)
  168. });
  169. }
  170. /**
  171. * 奖励处理方法
  172. * @param item
  173. */
  174. private handleReward(item: any) {
  175. const [type, amount] = item.quantity.split('_');
  176. audioMgr.playOneShot(Constants.audios.reward);
  177. switch(type) {
  178. case '1001': {//金币
  179. userIns.data.gold += parseInt(amount);
  180. uiMgr.show(Constants.popUIs.obtainUI,[item.id,()=>{
  181. ResUtil.flyAnim(ITEM_TYPE.Coin, this.shoot_btn, this.gold_lable.node, 5, 50,(b) => {});
  182. }]);
  183. }
  184. break;
  185. case '1002':{ //钻石
  186. userIns.data.diamond += parseInt(amount);
  187. uiMgr.show(Constants.popUIs.obtainUI,[item.id,()=>{
  188. ResUtil.flyAnim(ITEM_TYPE.Diamond, this.shoot_btn, this.diamond_lable.node, 5, 50,(b) => {});
  189. }]);
  190. }
  191. break;
  192. default: {//武器解锁 获得显示武器栏
  193. uiMgr.show(Constants.popUIs.obtainUI,[item.id,(isCover: boolean)=>{
  194. //是否拥有这把需要转化
  195. if(isCover){
  196. const [good_id,amount] = item.value.split('_');
  197. const sData:any = userIns.itemTable.find(e=>e.id == good_id);
  198. if(sData.id == 1001){//金币
  199. userIns.data.gold += parseInt(amount);
  200. ResUtil.flyAnim(ITEM_TYPE.Coin, this.shoot_btn, this.gold_lable.node, 5, 50,(b) => {});
  201. }else if(sData.id == 1002){//钻石
  202. userIns.data.diamond += parseInt(amount);
  203. ResUtil.flyAnim(ITEM_TYPE.Diamond, this.shoot_btn, this.diamond_lable.node, 5, 50,(b) => {});
  204. }
  205. }
  206. }]);
  207. }
  208. break;
  209. }
  210. }
  211. /**
  212. * 按钮点击事件
  213. * @param event 事件
  214. * @param param 参数
  215. */
  216. override onBtnClicked(event:EventTouch, param:any) {
  217. super.onBtnClicked(event,param);
  218. let btnName = event.target.name;
  219. if(btnName === 'shoot_btn'){//扔飞镖的操作
  220. this.startToss();
  221. }
  222. }
  223. }